添加文件

  1. 获取原始free modbus library(官网
  2. 将...\freemodbus-v1.5.0\demo\BARE中的所有文件复制到...\freemodbus-v1.5.0\modbus中,修改demo.c文件名为user_mb_app.c
  3. 将...\freemodbus-v1.5.0\modbus中的所有.c文件全部添加到项目中
  4. 在项目路径中添加所有.c、.h文件路径

添加完成后项目结构图:

移植修改

需要修改的文件:

  1. port.h:补全开关总中断的宏定义、宏定义串口和定时器

    #define ENTER_CRITICAL_SECTION( )   __set_PRIMASK(1); //关闭中断
    #define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0); //开启中断
  2. portserial.c:补全串口相关函数(串口中断使能选择、串口初始化、发送1字节、接收1字节、串口中断服务函数)
    • 注意事项:
      1. 使能中断:发送中断应使用TC而非TXE,否则可能会出现最后一个字节不能成功发送的情况。此外由于是用485发送,所以在使能中断时,应同时转换485收发转换引脚。使能中断前,应判断对应的标志位是否为1,为1则清除该标志位。
      2. 串口初始化:初始化前用USART_DeInit重置寄存器;引脚初始化(GPIO)->串口初始化(USART)->中断初始化(NVIC);参数中的ucPORT和eParity都应该忽略。
      3. 发送1字节:不用循环等待发送完成,因为已经有发送完成中断了
      4. 串口中断服务函数:在stm32f0xx_it.c中添加USART3_4_IRQHandler函数,跳转到本文件中的prvvModbusUARTISR函数。
    •  /*
      * FreeModbus Libary: BARE Port
      * Copyright (C) 2006 Christian Walter <wolti@sil.at>
      *
      * This library is free software; you can redistribute it and/or
      * modify it under the terms of the GNU Lesser General Public
      * License as published by the Free Software Foundation; either
      * version 2.1 of the License, or (at your option) any later version.
      *
      * This library is distributed in the hope that it will be useful,
      * but WITHOUT ANY WARRANTY; without even the implied warranty of
      * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      * Lesser General Public License for more details.
      *
      * You should have received a copy of the GNU Lesser General Public
      * License along with this library; if not, write to the Free Software
      * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      *
      * File: $Id: portserial.c,v 1.1 2006/08/22 21:35:13 wolti Exp $
      */ #include "port.h" /* ----------------------- Modbus includes ----------------------------------*/
      #include "mb.h"
      #include "mbport.h" /* ----------------------- static functions ---------------------------------*/
      static void prvvUARTTxReadyISR( void );
      static void prvvUARTRxISR( void ); #define MODBUS_SEND() (GPIO_SetBits(MODBUS_USART_CTRL_PORT, MODBUS_USART_CTRL_PIN))
      #define MODBUS_RECIEVE() (GPIO_ResetBits(MODBUS_USART_CTRL_PORT, MODBUS_USART_CTRL_PIN)) /* ----------------------- Start implementation -----------------------------*/
      void
      vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
      {
      /* If xRXEnable enable serial receive interrupts. If xTxENable enable
      * transmitter empty interrupts.
      */
      if(xRxEnable==TRUE) {
      MODBUS_RECIEVE();
      if(USART_GetFlagStatus(MODBUS_USART, USART_FLAG_RXNE) == SET) {
      USART_ClearFlag(MODBUS_USART, USART_FLAG_RXNE);
      }
      USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);
      } else if(xRxEnable == FALSE) {
      MODBUS_SEND();
      USART_ITConfig(MODBUS_USART, USART_IT_RXNE, DISABLE);
      } if(xTxEnable==TRUE) {
      MODBUS_SEND();
      if(USART_GetFlagStatus(MODBUS_USART, USART_FLAG_TC) == SET) {
      USART_ClearFlag(MODBUS_USART, USART_FLAG_TC);
      }
      USART_ITConfig(MODBUS_USART, USART_IT_TC, ENABLE);
      } else if(xTxEnable == FALSE) {
      MODBUS_RECIEVE();
      USART_ITConfig(MODBUS_USART, USART_IT_TC, DISABLE);
      }
      } BOOL
      xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
      {
      /*****************************引脚初始化*************************************/
      GPIO_InitTypeDef GPIO_InitStructure; //时钟使能
      RCC_AHBPeriphClockCmd(MODBUS_USART_TX_CLK | MODBUS_USART_RX_CLK | MODBUS_USART_CTRL_CLK, ENABLE);
      MODBUS_USART_CLK_INIT(MODBUS_USART_CLK, ENABLE); //复用功能定义
      GPIO_PinAFConfig(MODBUS_USART_TX_PORT, MODBUS_USART_TX_SOURCE, MODBUS_USART_TX_AF);
      GPIO_PinAFConfig(MODBUS_USART_RX_PORT, MODBUS_USART_RX_SOURCE, MODBUS_USART_RX_AF); //引脚功能定义
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
      GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
      GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3; //50MHz //TX
      GPIO_InitStructure.GPIO_Pin = MODBUS_USART_TX_PIN;
      GPIO_Init(MODBUS_USART_TX_PORT, &GPIO_InitStructure); //RX
      GPIO_InitStructure.GPIO_Pin = MODBUS_USART_RX_PIN;
      GPIO_Init(MODBUS_USART_RX_PORT, &GPIO_InitStructure); //CTRL
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
      GPIO_InitStructure.GPIO_Pin = MODBUS_USART_CTRL_PIN;
      GPIO_Init(MODBUS_USART_CTRL_PORT, &GPIO_InitStructure);
      MODBUS_RECIEVE(); //接收模式 /*****************************串口初始化*************************************/
      USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate = ulBaudRate;
      USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
      USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
      USART_InitStructure.USART_Parity = USART_Parity_No;
      USART_InitStructure.USART_StopBits = USART_StopBits_1;
      USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_Init(MODBUS_USART, &USART_InitStructure);
      USART_Cmd(MODBUS_USART, ENABLE);
      vMBPortSerialEnable(FALSE, FALSE); /*****************************中断初始化*************************************/
      NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART3_4_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_InitStructure.NVIC_IRQChannelPriority = ; NVIC_Init(&NVIC_InitStructure); return TRUE;
      } BOOL
      xMBPortSerialPutByte( CHAR ucByte )
      {
      /* Put a byte in the UARTs transmit buffer. This function is called
      * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
      * called. */
      MODBUS_USART->TDR = ucByte; //此处不用等待发送完毕(TC),因为有发送完成中断
      return TRUE;
      } BOOL
      xMBPortSerialGetByte( CHAR * pucByte )
      {
      /* Return the byte in the UARTs receive buffer. This function is called
      * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
      */
      *pucByte = MODBUS_USART->RDR;
      return TRUE;
      } /* Create an interrupt handler for the transmit buffer empty interrupt
      * (or an equivalent) for your target processor. This function should then
      * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
      * a new character can be sent. The protocol stack will then call
      * xMBPortSerialPutByte( ) to send the character.
      */
      static void prvvUARTTxReadyISR( void )
      {
      pxMBFrameCBTransmitterEmpty( );
      } /* Create an interrupt handler for the receive interrupt for your target
      * processor. This function should then call pxMBFrameCBByteReceived( ). The
      * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
      * character.
      */
      static void prvvUARTRxISR( void )
      {
      pxMBFrameCBByteReceived( );
      } void prvvModbusUARTISR( void )
      {
      if(USART_GetITStatus(MODBUS_USART, USART_IT_TC) == SET) {
      prvvUARTTxReadyISR();
      USART_ClearITPendingBit(MODBUS_USART, USART_IT_TC);
      } if(USART_GetITStatus(MODBUS_USART, USART_IT_RXNE) == SET) {
      prvvUARTRxISR();
      USART_ClearITPendingBit(MODBUS_USART, USART_IT_RXNE);
      }
      }

      portserial.c

  3. porttimer.c:补全定时器相关函数(定时器初始化、定时器使能、定时器关闭使能、定时器中断服务函数)
    • 注意事项:
      1. 初始化:初始化前应用TIM_DeInit函数重置寄存器值;初始化后要清标志位
      2. 定时器使能:要先关中断、关定时器,然后清标志位、重置计数器,最后开中断、开定时器
      3. 中断服务函数:要先判断中断标志位,再清标志位、进入操作部分。
    •  /*
      * FreeModbus Libary: BARE Port
      * Copyright (C) 2006 Christian Walter <wolti@sil.at>
      *
      * This library is free software; you can redistribute it and/or
      * modify it under the terms of the GNU Lesser General Public
      * License as published by the Free Software Foundation; either
      * version 2.1 of the License, or (at your option) any later version.
      *
      * This library is distributed in the hope that it will be useful,
      * but WITHOUT ANY WARRANTY; without even the implied warranty of
      * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      * Lesser General Public License for more details.
      *
      * You should have received a copy of the GNU Lesser General Public
      * License along with this library; if not, write to the Free Software
      * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
      *
      * File: $Id: porttimer.c,v 1.1 2006/08/22 21:35:13 wolti Exp $
      */ /* ----------------------- Platform includes --------------------------------*/
      #include "port.h" /* ----------------------- Modbus includes ----------------------------------*/
      #include "mb.h"
      #include "mbport.h" /* ----------------------- static functions ---------------------------------*/
      static void prvvTIMERExpiredISR( void ); /* ----------------------- Start implementation -----------------------------*/
      BOOL
      xMBPortTimersInit( USHORT usTim1Timerout50us )
      {
      /***************************定时器初始化*************************************/
      TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_DeInit(MODBUS_TIM);
      MODBUS_TIM_CLK_INIT(MODBUS_TIM_CLK, ENABLE);
      TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
      TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
      TIM_InitStructure.TIM_Period = usTim1Timerout50us - ;
      TIM_InitStructure.TIM_Prescaler = - ; //48MHz/20kHz=2400
      TIM_InitStructure.TIM_RepetitionCounter = ; TIM_TimeBaseInit(MODBUS_TIM, &TIM_InitStructure);
      TIM_ClearFlag(MODBUS_TIM, TIM_FLAG_Update);
      TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, ENABLE);
      TIM_Cmd(MODBUS_TIM, DISABLE); /*****************************中断初始化*************************************/
      NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_InitStructure.NVIC_IRQChannelPriority = ; NVIC_Init(&NVIC_InitStructure); return TRUE;
      } inline void
      vMBPortTimersEnable( )
      {
      /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
      TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, DISABLE);
      TIM_Cmd(MODBUS_TIM, DISABLE);
      TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
      TIM_ClearFlag(MODBUS_TIM, TIM_FLAG_Update);
      TIM_SetCounter(MODBUS_TIM, );
      TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, ENABLE);
      TIM_Cmd(MODBUS_TIM, ENABLE);
      } inline void
      vMBPortTimersDisable( )
      {
      /* Disable any pending timers. */
      TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, DISABLE);
      TIM_Cmd(MODBUS_TIM, DISABLE);
      } /* Create an ISR which is called whenever the timer has expired. This function
      * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
      * the timer has expired.
      */
      static void prvvTIMERExpiredISR( void )
      {
      ( void )pxMBPortCBTimerExpired( );
      } void prvvModbusTIMISR( void )
      {
      if(TIM_GetITStatus(MODBUS_TIM, TIM_IT_Update) == SET) {
      TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
      prvvTIMERExpiredISR();
      }
      }

      porttimer.c

  4. user_mb_app.h:定义各模拟寄存器地址
    • 注意事项:
      1. 每个寄存器固定长度16位(2字节)
      2. 寄存器地址:从机内部定义的寄存器地址,要对寻址用的寄存器地址+1【Modbus标准协议规定】。即:从机内部定义的寄存器地址,必须大于1;对于寄存器1-16,寻址时通过0-15来寻址(如:查询寄存器1的值时,指令中的寄存器地址为00 00)。
      3. 换句话说,就是:从机程序中定义寄存器地址为1-16时,文档中的寄存器地址要写成0-15。
  5. user_mb_app.c:补全输入寄存器操作函数、保持寄存器操作函数(操作方式可参见...\freemodbus-v1.5.0\demo\ATSAM3S\demo.c),将main分解为modbus初始化函数和modbus进程函数,添加结构体与模拟寄存器之间的数据交互函数。
    • 注意事项:
      1. eMBInit初始化:仅需更改地址和波特率。
      2. user_mb_app函数中不要用循环(本身就会被应用到主程序中的while(1)中)
    •  /* ----------------------- Modbus includes ----------------------------------*/
      #include "mb.h"
      #include "mbport.h"
      #include "user_mb_app.h" /* ----------------------- Defines ------------------------------------------*/
      #define REG_INPUT_START ((uint16_t)0x0000)
      #define REG_INPUT_NREGS 25
      #define REG_HOLD_START ((uint16_t)0x0000)
      #define REG_HOLD_NREGS 30 /* ----------------------- Static variables ---------------------------------*/
      static USHORT usRegInputStart = REG_INPUT_START;
      static USHORT usRegInputBuf[REG_INPUT_NREGS];
      static USHORT usRegHoldStart = REG_HOLD_START;
      static USHORT usRegHoldBuf[REG_HOLD_NREGS];
      extern uint8_t g_Meter_Data[];
      extern uint8_t g_Check_Data[];
      /* ----------------------- Start implementation -----------------------------*/
      /******************************************************************************
      ** 函数名称: mb_Modbus_Init
      ** 功能描述: modbus初始化
      ** 入口参数: 无
      ** 返 回 值: 无
      **
      ** 作 者: Cage
      ** 日 期: 2018年3月9日
      **-----------------------------------------------------------------------------
      ******************************************************************************/
      void mb_Modbus_Init(void) { uint8_t address = dio_Get_DIP_Value(); //获取拨码开关信息,获得本机地址
      ( void )eMBInit( MB_RTU, address, , , MB_PAR_NONE ); /* Enable the Modbus Protocol Stack. */
      ( void )eMBEnable( );
      } /******************************************************************************
      ** 函数名称: user_mb_app
      ** 功能描述: modbus进程函数
      ** 入口参数: 无
      ** 返 回 值: 无
      **
      ** 作 者: Cage
      ** 日 期: 2018年3月13日
      **-----------------------------------------------------------------------------
      ******************************************************************************/
      void user_mb_app( void )
      { ( void )eMBPoll( ); } /******************************************************************************
      ** 函数名称: _mb_Fresh_Input_Reg
      ** 功能描述: 将计量数据结构体中的数据刷新到Modbus模拟寄存器中
      ** 入口参数: 无
      ** 返 回 值: 无
      **
      ** 作 者: Cage
      ** 日 期: 2018年3月13日
      **-----------------------------------------------------------------------------
      ******************************************************************************/
      static void _mb_Fresh_Input_Reg(void) {
      uint8_t i;
      USHORT *pmeter_data = (USHORT*)g_Meter_Data;
      for(i = ; i<REG_INPUT_NREGS; i++) {
      usRegInputBuf[i] = *pmeter_data++;
      }
      } /******************************************************************************
      ** 函数名称: _mb_Fresh_Check_Struct
      ** 功能描述: 将Modbus模拟寄存器中的数据刷新到校表数据结构体
      ** 入口参数: 无
      ** 返 回 值: 无
      **
      ** 作 者: Cage
      ** 日 期: 2018年3月13日
      **-----------------------------------------------------------------------------
      ******************************************************************************/
      static void _mb_Fresh_Check_Struct(void) {
      uint8_t i;
      USHORT *pcheck_data = (USHORT*)g_Check_Data;
      for(i = ; i<REG_HOLD_NREGS; i++) {
      *pcheck_data++ = usRegHoldBuf[i];
      }
      } //读输入寄存器
      eMBErrorCode
      eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
      {
      eMBErrorCode eStatus = MB_ENOERR;
      int iRegIndex; if( ( usAddress >= REG_INPUT_START )
      && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
      {
      _mb_Fresh_Input_Reg(); //将计量数据结构体中的数据刷新到Modbus模拟寄存器中
      iRegIndex = ( int )( usAddress - usRegInputStart );
      while( usNRegs > )
      {
      *pucRegBuffer++ =
      ( unsigned char )( usRegInputBuf[iRegIndex] >> );
      *pucRegBuffer++ =
      ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
      iRegIndex++;
      usNRegs--;
      }
      }
      else
      {
      eStatus = MB_ENOREG;
      } return eStatus;
      } //写保持寄存器--未定义
      eMBErrorCode
      eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
      eMBRegisterMode eMode )
      {
      eMBErrorCode eStatus = MB_ENOERR;
      int iRegIndex; if( ( usAddress >= REG_HOLD_START )
      && ( usAddress + usNRegs <= REG_HOLD_START + REG_HOLD_NREGS ) )
      {
      iRegIndex = ( int )( usAddress - usRegHoldStart );
      if(eMode == MB_REG_WRITE) {
      while( usNRegs > )
      {
      usRegHoldBuf[iRegIndex] = *pucRegBuffer++ << ;
      usRegHoldBuf[iRegIndex] |= *pucRegBuffer++;
      iRegIndex++;
      usNRegs--;
      }
      }
      _mb_Fresh_Check_Struct(); //将Modbus模拟寄存器中的数据刷新到校表数据结构体
      }
      else
      {
      eStatus = MB_ENOREG;
      } return eStatus;
      } /*********************************不使用的功能*********************************/
      eMBErrorCode
      eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
      eMBRegisterMode eMode )
      {
      return MB_ENOREG;
      } eMBErrorCode
      eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
      {
      return MB_ENOREG;
      }

      user_mb_app.c

  6. mb.c:在eMBPoll函数中,EV_EXECUTE状态下,准备好发送后,手动发送第一个字节,启动发送
    •          case EV_EXECUTE:
      ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
      eException = MB_EX_ILLEGAL_FUNCTION;
      for( i = ; i < MB_FUNC_HANDLERS_MAX; i++ )
      {
      /* No more function handlers registered. Abort. */
      if( xFuncHandlers[i].ucFunctionCode == )
      {
      break;
      }
      else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
      {
      eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
      break;
      }
      } /* If the request was not sent to the broadcast address we
      * return a reply. */
      if( ucRcvAddress != MB_ADDRESS_BROADCAST )
      {
      if( eException != MB_EX_NONE )
      {
      /* An exception occured. Build an error frame. */
      usLength = ;
      ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
      ucMBFrame[usLength++] = eException;
      }
      if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
      {
      vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
      }
      eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );
      /*发送数组准备完毕,串口切换到了发送状态,但TC标志位为0,不能自动开始发送*/
      if( eStatus == MB_ENOERR )
      {
      xMBRTUTransmitFSM(); //发送第一个字节,启动发送
      }
      }
      break;

      mb.c节选

调试

  1. 串口收到数据后,无限进入中断
    1. 现象:仿真时,一直进入中断服务函数,不作任何处理后跳出;如此反复进入中断服务函数。
    2. 判断:ORE标志位未清除。RXNEIE中断使能包括RXNE标志位和ORE标志位,中断服务函数中只判断、处理了RXNE标志位。
    3. 处理:在portserial.c的中断服务函数prvvModbusUARTISR中,加入USART_IT_ORE标志位的判断与处理【注:stm32f072库函数中的USART_IT_ORE标志位定义错误,需要修改】
    4. 结果:问题解决
  2. Modbus收到数据后不响应
    1. 现象:跟踪发现,Modbus协议一直进行到“串口发送”之前都是正常的,可是之后却没有发送
    2. 原因:上一次发送最后一个字节时,发送中断中清除了TC标志位;切换到发送状态、使能TCIE后,TC标志位为0,无法启动发送。
    3. 解决方案:
      1. TXE+TC发送:
        1. 中断使能函数:切换为发送状态时,使能TXE中断而非TC中断(由TXE启动发送,TXE永不手动清零)
        2. 中断服务函数:TXE启动发送后,将中断使能由TXE改为TC,之后由TC判断发送完成、清TC标志位
      2. 手动启动:切换为发送状态后,手动启动发送(发送第一个字节)
    4. 处理:选择了方案2,问题解决。

仿真跟踪--Modbus数据处理流程(RTU)

  1. modbus poll主进程(eMBPoll)获取消息状态,执行指令
  2. 数据接收:串口中断接收数据(数据存入ucRTUBuf数组)->超过3.5us没有收到数据->判断一串数据接收完毕->将“接收完毕”消息添加到消息队列
  3. 数据处理:
    1. eMBPoll获取到“接收完毕”消息,令ucMBFrame指针指向ucRTUBuf数组的第1位(命令字),获取地址位和数据长度(不含地址位和校验位的长度),将“执行”消息添加到消息队列
    2. eMBPoll获取到“执行”消息,通过ucMBFrame中的命令字,进行对应的操作(比如:04--读输入寄存器),将要发回的数据存入ucMBFrame(不含地址位和校验位)
    3. 将ucMBFrame中的数据加上地址位后计算校验位,将地址位和校验位存入ucRTUBuf数组,将发送状态标志由空闲转为发送,串口状态转为发送
      • --至此发送数组(ucRTUBuf)和串口状态都已经准备完毕,但没有发送指令
    4. 【添加】手动发送发送数组的第一个字节,启动发送
  4. 数据发送:通过串口将发送数组逐字节发送。

stm32--free modbus 1.5.0移植(作为从机)的更多相关文章

  1. ORTP-0.27.0移植

    注意: a. 对于0.27一下版本的ORTP的交叉编译则没有一下依赖库 b. 交叉编译工具链是: arm-linux-gnueabihf-gcc-4.9.1 (4.9版本一下的编译bctoolbox出 ...

  2. vm虚拟机Kali2.0实现与物理机之间的文件拖动共享

    MarkdownPad Document html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,ab ...

  3. 【转自知乎】:localhost、127.0.0.1 和 本机IP 三者的区别?

    作者:知乎用户链接:https://www.zhihu.com/question/23940717/answer/26230963来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  4. localhost,127.0.0.1 和 本机IP 三者的区别

    localhost.127.0.0.1和本机IP的区别如下: 1.首先 localhost 是一个域名,在过去它指向 127.0.0.1 这个IP地址.在操作系统支持 ipv6 后,它同时还指向ipv ...

  5. localhost、127.0.0.1和本机IP

    localhost 是个域名,不是地址,它可以被配置为任意的 IP 地址,不过通常情况下都指向 127.0.0.1(ipv4)和 [::1](ipv6) 整个127.* 网段通常被用作 loopbac ...

  6. # localhost 、217.0.0.1 、本机IP

    localhost .217.0.0.1 .本机IP localhost是一个域名,性质跟 "www.baidu.com" 差不多,指向127.0.0.1这个IP地址,在windo ...

  7. FreeModbus 移植于STM32 实现Modbus RTU通信

    http://ntn314.blog.163.com/blog/static/161743584201233084434579/ 毕业设计自己要做个基于STM32的PLC能直接跑语句表的,现在看来好像 ...

  8. [stm32] MPU6050 HMC5883 Kalman 融合算法移植

    一.卡尔曼滤波九轴融合算法stm32尝试 1.Kalman滤波文件[.h已经封装为结构体] /* Copyright (C) 2012 Kristian Lauszus, TKJ Electronic ...

  9. qt-5.6.0 移植之qt文件系统的建立

    经过差不多两个星期的奋斗,终于在板子里面跑起来了qt 程序,虽然现在还没有把触摸屏驱动加上去,但是我相信已经不远了!!!!! 在前两篇的随笔里面 , 已经编译好了最纯净的文件系统以及交叉编译完成了qt ...

随机推荐

  1. html:<link> 标签中的 media 属性

    HTML <link> 标签的 media 属性 定义和用法 media 属性规定被链接文档将显示在什么设备上. media 属性用于为不同的媒介类型规定不同的样式. media属性值 ( ...

  2. 打开excl链接时总是出现问题

    主要现象:1.提示"发生了意外错误":2.报错"由于本机限制无法打开链接" 原因: 这个是由于默认浏览器异常造成的,就是比如你下载了新的浏览器,然后为默认浏览器 ...

  3. C语言中的特殊变量

    auto: 函数中的局部变量,动态地分配存储空间,数据存储在动态存储区中,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间. register: 为了提高效率,C语言允许 ...

  4. MD5的32位加密方法

    /// <summary> /// MD532位加密方式 /// </summary> /// <param name="str">用户原始密码 ...

  5. EF写distinct

    在日常开发中常常是这么写的 var logErrorRequest = from l in _logErrorRepository.Table select new { WrongTime = l.W ...

  6. Uva 10820 交表

    题目链接:https://uva.onlinejudge.org/external/108/10820.pdf 题意: 对于两个整数 x,y,输出一个函数f(x,y),有个选手想交表,但是,表太大,需 ...

  7. 关于VMware给系统分区扩容的一点经验

    我的VMware版本是8.0.6 build-1035888,里面安装的是Windows XP SP3 首先,在VM关机状态下使用Hard disk设置中的Utilities下的Expand给整个磁盘 ...

  8. Spring Boot的Maven插件Spring Boot Maven plugin详解

    Spring Boot的Maven插件(Spring Boot Maven plugin)能够以Maven的方式为应用提供Spring Boot的支持,即为Spring Boot应用提供了执行Mave ...

  9. iPad游戏 Calcculator: The Game 程序自动计算求解方法

    今天在iPad上下了个小游戏,主要是一个计算器的界面,有开始值,目标值,限定步数,以及一些加减乘除,还有作者脑洞想出来的功能键,主要有左移,直接把一个数加到末尾,将其中的某个数改为另一个数等等..玩到 ...

  10. 2、SpringBoot------数据转换

    开发工具:STS 代码下载链接:https://github.com/theIndoorTrain/Springboot/tree/083bb312526653d27ca56abf4f586e097c ...