OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 12501|回复: 12

STM32HAL库移植FreeModbus协议

[复制链接]

17

主题

56

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
394
金钱
394
注册时间
2015-1-7
在线时间
101 小时
发表于 2021-2-22 12:33:32 | 显示全部楼层 |阅读模式

需要源码的请关注我的公众号:

Modbus是一个非常好用的通讯协议,经常用在串口通讯中,也可以用在网口。它既简洁又规范,尤其在工业中应用非常广泛。Modbus的程序实现也比较简单,用户可以自己实现,也可以移植开源的协议代码,比如今天要介绍的FreeModbus。
硬件环境:STM32F103C8T6
软件环境:STM32CubeMXv6.1.1
HAL库:STM32CubeF1Firmware Package V1.8.3
FreeModbus版本:freemodbus-v1.6
FreeModbus文件说明
下载之后解压出来,可以看到文件夹内包含以下内容。我们需要关注的只有modbus文件夹和demo下的BARE文件夹。modbus文件夹下是协议的具体代码。Demo->BARE文件夹下是接口文件,需要用户进行移植和修改的。
1.png
STM32CubeMX配置
     需要使能一个串口和一个定时器,定时器的功能是用于检测3.5个字符的空闲,以判断一帧数据的结束。这里以USART1和TIM4为例进行介绍。
定时器配置:
2.png
串口配置:
3.png
定时器和串口的参数任意即可,具体在程序中进行配置。打开定时器和串口的中断,且串口中断的优先级要高于定时器中断。
4.png
串口和定时器中断程序我们自己编写,所以这里需要取消串口和定时器中断自动生成代码的选项。如下图:
5.png
FreeModbus移植
生成代码后,将下载的FreeModbus的源码复制到工程目录中,在Keil工程中新建两个Group组:FreeModbus和Port。添加modbus文件夹下的全部文件到FreeModbus组。将Demo->BARE->Port文件夹下的文件添加到Port组。同时新建一个Port.c文件也添加到Port组。
另外,别忘了在工程中添加包含路径,否则编译出错。
6.png
下面开始程序移植,首先是portserial.c文件,该文件是串口的接口文件,包含以下函数:串口使能、串口初始化、发送一个字节、接收一个字节等。我们需要自己实现完成这些函数的内容。完成后的内容如下:
  1. void
  2. vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
  3. {
  4.     /* If xRXEnable enable serial receive interrupts. If xTxENable enable
  5.      * transmitter empty interrupts.
  6.      */
  7.                 if(xRxEnable)
  8.     {
  9.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);                //使能接收中断
  10.     }
  11.     else
  12.     {
  13.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);                //关闭接收中断
  14.     }

  15.     if(xTxEnable)
  16.     {
  17.                                 SET_DE;        //485芯片设置为发送模式
  18.         __HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);                        //使能发送中断
  19.     }
  20.     else
  21.     {
  22.         __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);                //关闭发送为空中断
  23.                                 __HAL_UART_ENABLE_IT(&huart1, UART_IT_TC);                                 //使能发送完成中断
  24.     }
  25. }

  26. BOOL
  27. xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
  28. {
  29.     huart1.Instance = USART1;
  30.     huart1.Init.BaudRate = ulBaudRate;
  31.     huart1.Init.StopBits = UART_STOPBITS_1;
  32.     huart1.Init.Mode = UART_MODE_TX_RX;
  33.     huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  34.     huart1.Init.OverSampling = UART_OVERSAMPLING_16;

  35.     switch(eParity)
  36.     {
  37.                 // 奇校验
  38.     case MB_PAR_ODD:
  39.         huart1.Init.Parity = UART_PARITY_ODD;
  40.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        //带奇偶校验数据位为9bits
  41.         break;
  42.        
  43.                 //偶校验
  44.     case MB_PAR_EVEN:
  45.         huart1.Init.Parity = UART_PARITY_EVEN;
  46.         huart1.Init.WordLength = UART_WORDLENGTH_9B;                        //带奇偶校验数据位为9bits
  47.         break;
  48.        
  49.                 //无校验
  50.     default:
  51.         huart1.Init.Parity = UART_PARITY_NONE;
  52.         huart1.Init.WordLength = UART_WORDLENGTH_8B;                        //无奇偶校验数据位为8bits
  53.         break;
  54.     }
  55.     return HAL_UART_Init(&huart1) == HAL_OK ? TRUE : FALSE;
  56. }

  57. BOOL
  58. xMBPortSerialPutByte( CHAR ucByte )
  59. {
  60.     /* Put a byte in the UARTs transmit buffer. This function is called
  61.      * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
  62.      * called. */
  63.                 USART1->DR = ucByte;
  64.     return TRUE;
  65. }

  66. BOOL
  67. xMBPortSerialGetByte( CHAR * pucByte )
  68. {
  69.     /* Return the byte in the UARTs receive buffer. This function is called
  70.      * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
  71.      */
  72.                 *pucByte = (USART1->DR & (uint16_t)0x00FF);
  73.     return TRUE;
  74. }

  75. /* Create an interrupt handler for the transmit buffer empty interrupt
  76. * (or an equivalent) for your target processor. This function should then
  77. * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
  78. * a new character can be sent. The protocol stack will then call
  79. * xMBPortSerialPutByte( ) to send the character.
  80. */
  81. static void prvvUARTTxReadyISR( void )
  82. {
  83.     pxMBFrameCBTransmitterEmpty(  );
  84. }

  85. /* Create an interrupt handler for the receive interrupt for your target
  86. * processor. This function should then call pxMBFrameCBByteReceived( ). The
  87. * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
  88. * character.
  89. */
  90. static void prvvUARTRxISR( void )
  91. {
  92.     pxMBFrameCBByteReceived(  );
  93. }

  94. //串口中断函数
  95. void USART1_IRQHandler(void)
  96. {
  97.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE))                        //接收中断标
  98.     {
  99.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);                //清除中断标记
  100.         prvvUARTRxISR();                                                                                //通知modbus有数据到达
  101.     }

  102.     if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE))                                //发送中断标记被置位
  103.     {
  104.         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);                //清除中断标记
  105.         prvvUARTTxReadyISR();                                                                        //通知modbus数据可以发松
  106.     }
  107.                
  108.                 if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC))                        //发送完成中断
  109.                 {
  110.                                 __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TC);                //清除中断标记
  111.                                 CLR_DE;        //485芯片设置为接收模式
  112.                 }
  113. }
复制代码

然后是porttimer.c文件,该文件时定时器的接口文件。定时器的作用是用于通知modbus协议栈3.5个字符的空闲时间已经到达。我们需要实现定时器的初始化和中断相关函数,定时需要配置为50us计数一次,具体计数周期与波特率有关。完成后的内容如下:
  1. BOOL
  2. xMBPortTimersInit( USHORT usTim1Timerout50us )
  3. {
  4.                 TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  5.                 TIM_MasterConfigTypeDef sMasterConfig = {0};

  6.                 htim4.Instance = TIM4;
  7.                 htim4.Init.Prescaler = 3599;                //50us计数一次
  8.                 htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
  9.                 htim4.Init.Period = usTim1Timerout50us - 1;  
  10.                 htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  11.                 htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  12.                 if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
  13.                 {
  14.                         Error_Handler();
  15.                 }
  16.                 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  17.                 if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
  18.                 {
  19.                         Error_Handler();
  20.                 }
  21.                 sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  22.                 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  23.                 if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
  24.                 {
  25.                         Error_Handler();
  26.                 }
  27.        
  28.                 __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);                                        //使能定时器更新中断
  29.                
  30.     return TRUE;
  31. }


  32. inline void
  33. vMBPortTimersEnable(  )
  34. {
  35.     /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
  36.                 __HAL_TIM_SET_COUNTER(&htim4, 0);                //清空定时器
  37.     __HAL_TIM_ENABLE(&htim4);                                                //使能定时器
  38. }

  39. inline void
  40. vMBPortTimersDisable(  )
  41. {
  42.     /* Disable any pending timers. */
  43.                 __HAL_TIM_DISABLE(&htim4);                                //关闭定时器
  44. }

  45. /* Create an ISR which is called whenever the timer has expired. This function
  46. * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
  47. * the timer has expired.
  48. */
  49. static void prvvTIMERExpiredISR( void )
  50. {
  51.     ( void )pxMBPortCBTimerExpired(  );
  52. }

  53. //定时器4中断函数
  54. void TIM4_IRQHandler(void)
  55. {
  56.     if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE))                        //判断更新中断
  57.     {
  58.         __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);                //清除中断标记
  59.         prvvTIMERExpiredISR();                                                                //通知modbus3.5个字符等待时间到
  60.     }
  61. }
复制代码

接下来需要实现的就是port.c文件,需要实现各个寄存器/线圈的读写函数,可以参考demo->BARE文件夹下的demo.c文件。完成后的内容如下:
  1. //输入寄存器
  2. #define REG_INPUT_START  3000
  3. #define REG_INPUT_NREGS  4

  4. //保持寄存器
  5. #define REG_HOLD_START   4000
  6. #define REG_HOLD_NREGS   10

  7. //线圈
  8. #define REG_COILS_START  0
  9. #define REG_COILS_NREGS  4

  10. //开关寄存器
  11. #define REG_DISCRETE_START 1000
  12. #define REG_DISCRETE_NREGS 4
  13. /* ----------------------- Static variables ---------------------------------*/
  14. static USHORT   usRegInputStart = REG_INPUT_START;
  15. static USHORT   usRegInputBuf[REG_INPUT_NREGS];


  16. static USHORT   usRegHoldStart = REG_HOLD_START;
  17. static USHORT   usRegHoldBuf[REG_HOLD_NREGS];

  18. static USHORT   usRegCoilsStart = REG_COILS_START;
  19. static uint8_t  usRegCoilsBuf[REG_COILS_NREGS];

  20. static USHORT   usRegDiscreteStart = REG_DISCRETE_START;
  21. static uint8_t  usRegDiscreteBuf[REG_DISCRETE_NREGS];
  22. /* ----------------------- Start implementation -----------------------------*/
  23. //int
  24. //main( void )
  25. //{
  26. //    eMBErrorCode    eStatus;

  27. //    eStatus = eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN );

  28. //    /* Enable the Modbus Protocol Stack. */
  29. //    eStatus = eMBEnable(  );

  30. //    for( ;; )
  31. //    {
  32. //        ( void )eMBPoll(  );

  33. //        /* Here we simply count the number of poll cycles. */
  34. //        usRegInputBuf[0]++;
  35. //    }
  36. //}
  37. /****************************************************************************
  38. * 名          称:eMBRegInputCB
  39. * 功    能:读取输入寄存器,对应功能码是 04 eMBFuncReadInputRegister
  40. * 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
  41. *                                                usAddress: 寄存器地址
  42. *                                                usNRegs: 要读取的寄存器个数
  43. * 出口参数:
  44. * 注          意:上位机发来的 帧格式是: SlaveAddr(1 Byte)+FuncCode(1 Byte)
  45. *                                                                +StartAddrHiByte(1 Byte)+StartAddrLoByte(1 Byte)
  46. *                                                                +LenAddrHiByte(1 Byte)+LenAddrLoByte(1 Byte)+
  47. *                                                                +CRCAddrHiByte(1 Byte)+CRCAddrLoByte(1 Byte)
  48. *                                                        3 区
  49. ****************************************************************************/
  50. eMBErrorCode
  51. eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
  52. {
  53.     eMBErrorCode    eStatus = MB_ENOERR;
  54.     int             iRegIndex;
  55.                 usAddress = usAddress - 1;
  56.        
  57.     if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
  58.     {
  59.         iRegIndex = ( int )( usAddress - usRegInputStart );
  60.         while( usNRegs > 0 )
  61.         {
  62.             *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
  63.             *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
  64.             iRegIndex++;
  65.             usNRegs--;
  66.         }
  67.     }
  68.     else
  69.     {
  70.         eStatus = MB_ENOREG;
  71.     }

  72.     return eStatus;
  73. }
  74. /****************************************************************************
  75. * 名          称:eMBRegHoldingCB
  76. * 功    能:对应功能码有:06 写保持寄存器 eMBFuncWriteHoldingRegister
  77. *                                                                                                        16 写多个保持寄存器 eMBFuncWriteMultipleHoldingRegister
  78. *                                                                                                        03 读保持寄存器 eMBFuncReadHoldingRegister
  79. *                                                                                                        23 读写多个保持寄存器 eMBFuncReadWriteMultipleHoldingRegister
  80. * 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
  81. *                                                usAddress: 寄存器地址
  82. *                                                usNRegs: 要读写的寄存器个数
  83. *                                                eMode: 功能码
  84. * 出口参数:
  85. * 注          意:4 区
  86. ****************************************************************************/
  87. eMBErrorCode
  88. eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
  89. {
  90.     eMBErrorCode    eStatus = MB_ENOERR;
  91.     int             iRegIndex;
  92.                 usAddress = usAddress - 1;

  93.     if((usAddress >= REG_HOLD_START) && ((usAddress+usNRegs) <= (REG_HOLD_START + REG_HOLD_NREGS)))
  94.     {
  95.         iRegIndex = (int)(usAddress - usRegHoldStart);
  96.         switch(eMode)
  97.         {
  98.         case MB_REG_READ://读寄存器
  99.             while(usNRegs > 0)
  100.             {
  101.                 *pucRegBuffer++ = (uint8_t)(usRegHoldBuf[iRegIndex] >> 8);
  102.                 *pucRegBuffer++ = (uint8_t)(usRegHoldBuf[iRegIndex] & 0xFF);
  103.                 iRegIndex++;
  104.                 usNRegs--;
  105.             }
  106.             break;
  107.         case MB_REG_WRITE://写寄存器
  108.             while(usNRegs > 0)
  109.             {
  110.                 usRegHoldBuf[iRegIndex] = *pucRegBuffer++ << 8;
  111.                 usRegHoldBuf[iRegIndex] |= *pucRegBuffer++;
  112.                 iRegIndex++;
  113.                 usNRegs--;
  114.             }
  115.         }
  116.     }
  117.     else//错误
  118.     {
  119.         eStatus = MB_ENOREG;
  120.     }

  121.     return eStatus;
  122. }

  123. /****************************************************************************
  124. * 名          称:eMBRegCoilsCB
  125. * 功    能:对应功能码有:01 读线圈 eMBFuncReadCoils
  126. *                                                                                                        05 写线圈 eMBFuncWriteCoil
  127. *                                                                                                        15 写多个线圈 eMBFuncWriteMultipleCoils
  128. * 入口参数:pucRegBuffer: 数据缓存区,用于响应主机
  129. *                                                usAddress: 线圈地址
  130. *                                                usNCoils: 要读写的线圈个数
  131. *                                                eMode: 功能码
  132. * 出口参数:
  133. * 注          意:如继电器
  134. *                                                0 区
  135. ****************************************************************************/
  136. eMBErrorCode
  137. eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
  138. {
  139.     eMBErrorCode    eStatus = MB_ENOERR;
  140.     USHORT          iRegIndex;
  141.     USHORT usCoilGroups = ((usNCoils - 1) / 8 + 1);
  142.     UCHAR  ucStatus     = 0;
  143.     UCHAR  ucBits       = 0;
  144.     UCHAR  ucDisp       = 0;
  145.                 usAddress = usAddress - 1;
  146.        
  147.     if((usAddress >= REG_COILS_START) &&        ((usAddress + usNCoils) <= (REG_COILS_START + REG_COILS_NREGS)))
  148.     {
  149.         iRegIndex = (int)(usAddress - usRegCoilsStart);
  150.         switch(eMode)
  151.         {
  152.         case MB_REG_READ://读线圈
  153.             while(usCoilGroups--)
  154.             {
  155.                 ucDisp = 0;
  156.                 ucBits = 8;
  157.                 while((usNCoils--) != 0 && (ucBits--) != 0)
  158.                 {
  159.                     ucStatus |= (usRegCoilsBuf[iRegIndex++] << (ucDisp++));
  160.                 }
  161.                 *pucRegBuffer++ = ucStatus;
  162.             }
  163.             break;
  164.         case MB_REG_WRITE://写线圈
  165.             while(usCoilGroups--)
  166.             {
  167.                 ucStatus = *pucRegBuffer++;
  168.                 ucBits   = 8;
  169.                 while((usNCoils--) != 0 && (ucBits--) != 0)
  170.                 {
  171.                     usRegCoilsBuf[iRegIndex++] = ucStatus & 0X01;
  172.                     ucStatus >>= 1;
  173.                 }
  174.             }
  175.         }
  176.     }
  177.     else//错误
  178.     {
  179.         eStatus = MB_ENOREG;
  180.     }

  181.     return eStatus;
  182. }
  183. /****************************************************************************
  184. * 名          称:eMBRegDiscreteCB
  185. * 功    能:读取离散寄存器,对应功能码有:02 读离散寄存器 eMBFuncReadDiscreteInputs
  186. * 入口参数:pucRegBuffer: 数据缓存区,用于响应主机   
  187. *                                                usAddress: 寄存器地址
  188. *                                                usNDiscrete: 要读取的寄存器个数
  189. * 出口参数:
  190. * 注          意:1 区
  191. ****************************************************************************/
  192. eMBErrorCode
  193. eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
  194. {
  195.     eMBErrorCode    eStatus = MB_ENOERR;
  196.     USHORT          iRegIndex;
  197.     USHORT usDiscreteGroups = ((usNDiscrete - 1) / 8 + 1);
  198.     UCHAR  ucStatus     = 0;
  199.     UCHAR  ucBits       = 0;
  200.     UCHAR  ucDisp       = 0;
  201.                 usAddress = usAddress - 1;
  202.        
  203.     if((usAddress >= REG_DISCRETE_START) &&        ((usAddress + usNDiscrete) <= (REG_DISCRETE_START + REG_DISCRETE_NREGS)))
  204.     {
  205.         iRegIndex = (int)(usAddress - usRegDiscreteStart);

  206.                                 while(usDiscreteGroups--)
  207.                                 {
  208.                                                 ucDisp = 0;
  209.                                                 ucBits = 8;
  210.                                                 while((usNDiscrete--) != 0 && (ucBits--) != 0)
  211.                                                 {
  212.                                                                 if(usRegDiscreteBuf[iRegIndex])
  213.                                                                 {
  214.                                                                                 ucStatus |= (1 << ucDisp);
  215.                                                                 }
  216.                                                                 ucDisp++;
  217.                                                 }
  218.                                                 *pucRegBuffer++ = ucStatus;
  219.                                 }
  220.     }
  221.     else//错误
  222.     {
  223.         eStatus = MB_ENOREG;
  224.     }

  225.     return eStatus;
  226. }
复制代码

最后在主程序中初始化并调用相关函数即可:
  1. eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_NONE);                // 初始化modbus为RTU方式,地址0x01, 波特率9600,无校验
  2.         eMBEnable();                                                                        // 使能modbus协议栈
  3.   /* USER CODE END 2 */

  4.   /* Infinite loop */
  5.   /* USER CODE BEGIN WHILE */
  6.   while (1)
  7.   {
  8.                 eMBPoll();
  9.     /* USER CODE END WHILE */

  10.     /* USER CODE BEGIN 3 */
  11.   }
复制代码

总结
FreeModbus实现了Modbus协议的全部功能,移植和使用起来也比较简单。唯一不足的是只有从机协议是开源的,而主机协议是收费的。

https://shop72315368.taobao.com
承接软硬件项目开发
微信搜索 “嵌入式技术开发”,关注我的公众号
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

45

主题

267

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1091
金钱
1091
注册时间
2018-6-24
在线时间
156 小时
发表于 2021-3-31 22:46:30 来自手机 | 显示全部楼层
freemodbus协议栈有从机,主机和TCP,均可实现
回复 支持 反对

使用道具 举报

0

主题

10

帖子

0

精华

新手上路

积分
45
金钱
45
注册时间
2019-12-27
在线时间
6 小时
发表于 2021-4-30 21:28:48 | 显示全部楼层
厉害 厉害  感谢
回复 支持 反对

使用道具 举报

2

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
66
金钱
66
注册时间
2019-12-28
在线时间
13 小时
发表于 2021-8-15 11:20:15 | 显示全部楼层
大佬,那个SET_DE;        //485芯片设置为发送模式和 CLR_DE;        //485芯片设置为接收模式  是要设置一个RS485引脚吗?有具体代码吗?
回复 支持 反对

使用道具 举报

2

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
66
金钱
66
注册时间
2019-12-28
在线时间
13 小时
发表于 2021-8-15 11:21:03 | 显示全部楼层
qiuzhizhe 发表于 2021-8-15 11:20
大佬,那个SET_DE;        //485芯片设置为发送模式和 CLR_DE;        //485芯片设置为接收模式  是要设置 ...

485收发控制引脚
回复 支持 反对

使用道具 举报

2

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
66
金钱
66
注册时间
2019-12-28
在线时间
13 小时
发表于 2021-8-16 19:31:11 | 显示全部楼层

搞明白了
回复 支持 反对

使用道具 举报

2

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
66
金钱
66
注册时间
2019-12-28
在线时间
13 小时
发表于 2021-8-16 19:31:45 | 显示全部楼层
wdmvw 发表于 2021-3-31 22:46
freemodbus协议栈有从机,主机和TCP,均可实现

大佬,这个代码是主从都可以用吗?
回复 支持 反对

使用道具 举报

17

主题

56

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
394
金钱
394
注册时间
2015-1-7
在线时间
101 小时
 楼主| 发表于 2021-8-21 15:08:30 | 显示全部楼层
qiuzhizhe 发表于 2021-8-16 19:31
大佬,这个代码是主从都可以用吗?

这个只是从机的
https://shop72315368.taobao.com
承接软硬件项目开发
微信搜索 “嵌入式技术开发”,关注我的公众号
回复 支持 反对

使用道具 举报

0

主题

125

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2728
金钱
2728
注册时间
2015-10-29
在线时间
428 小时
发表于 2022-9-1 12:57:40 | 显示全部楼层
谢谢分享!
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手入门

积分
2
金钱
2
注册时间
2022-12-26
在线时间
0 小时
发表于 2022-12-26 22:32:41 | 显示全部楼层
厉害 厉害  感谢
回复 支持 反对

使用道具 举报

30

主题

149

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
417
金钱
417
注册时间
2018-2-9
在线时间
117 小时
发表于 2023-1-18 23:18:56 | 显示全部楼层
有朋友按这个教程走下来运行成功的么,我怎么一直有问题呢
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手入门

积分
14
金钱
14
注册时间
2023-10-10
在线时间
4 小时
发表于 2023-10-11 14:02:09 | 显示全部楼层
请问485接口被设置成自动收发,还需要SET_DE和 CLR_DE吗?
回复 支持 反对

使用道具 举报

7

主题

81

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
288
金钱
288
注册时间
2017-7-19
在线时间
85 小时
发表于 2024-4-10 17:22:11 | 显示全部楼层
我测试后 线圈写入与开关寄存器有问题
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 04:19

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表