Modbus RTU 从机代码实现——STM32

此专栏一共6篇文章

Modbus Poll 软件安装-CSDN博客

Modbus Slave 软件安装-CSDN博客

Modbus Poll 、Modbus Slave 软件使用-CSDN博客

Modbus RTU-CSDN博客

Modbus RTU主机代码实现——STM32-CSDN博客

Modbus RTU 从机代码实现——STM32-CSDN博客

外设配置

选用芯片——STM32f103C6T6

UART1

用于485多机通信

阻塞发送,中断接收

波特率115200

1起始位 8数据位 0校验位 1停止位

Timer3

用于T3.5判断

向上计数,72分频,重装载值100——100us中断

机程序

仅提供一个思路,具体项目里怎么写还需自己发挥

一共涉及8个文件

LH_ModbusSlave.c

LH_RS485.c

LH_UART.c

LH_ISR.c

main.c

LH_ModbusSlave.h

LH_RS485.h

LH_UART.h

LH_ModbusSlave.c

//*******************************************************

//作者------LH

//时间------2025.01.05

//模块功能--Modbus RTU 从机程序  已实现--03H  04H  10H  没有写广播模式

//*******************************************************

#include "stm32f1xx_hal.h"

#include "LH_ModbusSlave.h"

#include "LH_RS485.h"

#include "LH_UART.h"

#include "LH_LED.h"

 

uint8_t guca_ModS_RxBuff[256];//接收缓冲区-每帧最大256字节

uint8_t guca_ModS_TxBuff[256];//发送缓冲区-每帧最大256字节

 

//从机参数

uint8_t guc_ModS_ID = 0x09; //从机地址

 

//波特率115200   T3.5 = 1000000/115200*(1+8+0+1)*3.5 = 303us

//波特率115200   T3.5 = 1000000/115200*(1+8+1+1)*3.5 = 334us

uint32_t    guw_ModS_RxT35 = 334//3.5字符时间 us

uint32_t    guw_ModS_RXTCnt = 0;   //接收字符时间间隔

 

 

uint16_t    guh_ModS_RxLength = 0;//接收数据长度

uint8_t     guc_ModS_RXEndFlag = 0; //接收结束标志

 

 

#define  INPUTREGNUM 80  //输入寄存器数量

#define  HOLDREGNUM  100  //保持寄存器数量

 

uint16_t guhpa_ModS_InputReg[INPUTREGNUM];

uint32_t guhpa_ModS_HoldReg[HOLDREGNUM];

 

 

//使用外部变量

extern UART_HandleTypeDef huart1;

 

//寄存器与变量相结合--------------------------

//这里用define实现,将全局变量与寄存器相结合,读写寄存器值即读写相应的变量值

//建议用一个global.h文件来管理全局变量,将所有的 extern uintx_t xxx;写在里面

//当有.c文件要使用外部全局变量时,直接#include "global.h"

uint8_t  guc_a0;

uint16_t guh_a1;

uint32_t guw_a2;

 

#define guc_a0 guhpa_ModS_HoldReg[0]

#define guh_a1 guhpa_ModS_HoldReg[1]

#define guw_a2 (uint32_t)(guhpa_ModS_HoldReg[2] << 16) |(guhpa_ModS_HoldReg[3])

 

uint8_t guc_ModS_RxOneByte[1];

 

//从机初始化程序

void LH_ModS_Init(void)

{

  //调试时用的数据 可以不要

  guhpa_ModS_HoldReg[0] = 3210;

  guhpa_ModS_HoldReg[1] = 5678;

  guhpa_ModS_HoldReg[2] = 9999;

  guhpa_ModS_HoldReg[3] = 1111;

 

  guhpa_ModS_InputReg[0] = 2020;

  guhpa_ModS_InputReg[1] = 2021;

  guhpa_ModS_InputReg[2] = 2022;

  guhpa_ModS_InputReg[3] = 2023;

  //----------------------------------------

  LH_UART1_Start(guc_ModS_RxOneByte, 1);

 

}

 

//从机接收程序

//UART设置中断接收 每次1字节

//在串口中断服务函数里调用

void LH_ModS_RXISR(uint8_t RxOneByte)//

{

  if (guh_ModS_RxLength<256) //一次帧接收字节<256

  {

    guca_ModS_RxBuff[guh_ModS_RxLength++] = RxOneByte;

    guw_ModS_RXTCnt = 0;

  }

  else

  {

    //一次接收超过256字节就不接收了

  }

}

 

void LH_ModS_ReceiveISR()

{

  if (guh_ModS_RxLength<256) //一次帧接收字节<256

  {

    guca_ModS_RxBuff[guh_ModS_RxLength++] = guc_ModS_RxOneByte[0];

    guw_ModS_RXTCnt = 0;

  }

  else

  {

    //一次接收超过256字节就不接收了

 

  }

 

  HAL_UART_Receive_IT(&huart1, guc_ModS_RxOneByte, 1); //再次开中断

 

}

 

 

//接收完成判断程序

//需要一个定时器 100us中断

//在定时器中断服务函数里调用

void LH_ModS_RXT35ISR(void) //100us中断

{

  guw_ModS_RXTCnt += 100;

 

  if ((guw_ModS_RXTCnt >  guw_ModS_RxT35)) //字符间隔超过T35

  {

    if (guh_ModS_RxLength != 0) //已经开始接收

    {

      guc_ModS_RXEndFlag =1;

    }

  }

 

}

 

 

//CRC16计算程序

uint16_t LH_ModS_GetCRC16(uint8_t *Data, uint16_t length)

{

  uint16_t CRC16 = 0xffff// CRC校验码

  uint16_t Polynomial = 0xa001; //生成多项式

 

  for (int i = 0; i < length; i++)

  {

    CRC16 ^= Data[i];

 

    for (int j = 0; j < 8; j++)

    {

      if ((CRC16 & 0x0001) != 0) //x&1 = 1

      {

        CRC16 >>= 1;

        CRC16 ^= Polynomial;

      }

      else

      {

        CRC16 >>= 1;

      }

    }

  }

 

  return CRC16;

}

 

 

 

 

//从机指令执行程序

//10ms调用一次

void LH_ModS_ExeCmd(void)

{

  if (guc_ModS_RXEndFlag == 1) //接收帧结束

  {

 

    uint8_t RecID = guca_ModS_RxBuff[0];

 

    if (RecID == guc_ModS_ID) //ID正确

    {

      uint16_t CalRxBuffCRC, RecRXBuffCRC;

      CalRxBuffCRC = LH_ModS_GetCRC16(guca_ModS_RxBuff, guh_ModS_RxLength-2);

 

      RecRXBuffCRC = guca_ModS_RxBuff[guh_ModS_RxLength - 2]

                     | (((uint16_t)guca_ModS_RxBuff[guh_ModS_RxLength-1])<<8);//接收CRC

 

      if (CalRxBuffCRC==RecRXBuffCRC) //CRC校验正确

      {

        //LH_LED_Change();

 

        switch (guca_ModS_RxBuff[1]) //根据不同的功能码进行处理

        {

          case 03: //读多个保持寄存器

          {

            LH_ModS_03H();

            break;

          }

 

          case 04: //读多个输入寄存器

          {

            LH_ModS_04H();

            break;

          }

 

          case 16: //读多个输入寄存器

          {

            LH_ModS_10H();

            break;

          }

 

          default :

            //非法功能码---------------------------------------------------

            //发送非法功能码

            break;

        }

      }

      else

      {

        //不响应

      }

 

    }

 

    guc_ModS_RXEndFlag = 0;

    guh_ModS_RxLength = 0;

    guw_ModS_RXTCnt = 0;

  }

 

}

 

 

 

 

//从机指令解析函数--各功能码函数

void LH_ModS_03H(void)

{

  //操作寄存器起始地址

  uint16_t StartRegAddr = (((uint16_t)guca_ModS_RxBuff[2]) << 8) | guca_ModS_RxBuff[3];

  //操作寄存器数量

  uint16_t OpRegNum = (((uint16_t)guca_ModS_RxBuff[4]) << 8) | guca_ModS_RxBuff[5];

 

  if ((StartRegAddr + OpRegNum) < HOLDREGNUM) // 寄存器地址+数量 在设定地址范围内

  {

    guca_ModS_TxBuff[0] =  guca_ModS_RxBuff[0] ;

    guca_ModS_TxBuff[1] =  guca_ModS_RxBuff[1] ;

    guca_ModS_TxBuff[2] =  OpRegNum * 2;

 

    for (int i=0 ; i<OpRegNum*2; i++)

    {

      //先发送高位

      guca_ModS_TxBuff[3+i*2] = ((guhpa_ModS_HoldReg[StartRegAddr+i]) >> 8) &0x00FF;//先发送高字节--再发送低字节

      guca_ModS_TxBuff[4+i*2] = ((guhpa_ModS_HoldReg[StartRegAddr+i]) & 0x00FF); //

      //方法二——联合体

    }

 

    uint16_t TxCRC;

    TxCRC  = LH_ModS_GetCRC16(guca_ModS_TxBuff, 3 + OpRegNum*2);;

 

    guca_ModS_TxBuff[3 + OpRegNum*2] = TxCRC ;

    guca_ModS_TxBuff[4 + OpRegNum*2] = (TxCRC >> 8);//先发送CRC低位 后发送高位

 

    LH_ModS_SendData(guca_ModS_TxBuff, 5 + OpRegNum*2);

 

  }

  else

  {

    guca_ModS_TxBuff[0] =  guca_ModS_RxBuff[0] ;

    guca_ModS_TxBuff[1] =  guca_ModS_RxBuff[1] | 0x80 ;

    guca_ModS_TxBuff[2] =  0x02; //地址异常-----------------------------------

 

    uint16_t TxCRC;

    TxCRC = LH_ModS_GetCRC16(guca_ModS_TxBuff, 3);

 

    guca_ModS_TxBuff[3] = TxCRC ;

    guca_ModS_TxBuff[4] = (TxCRC >> 8); //先发送CRC低位 后发送高位

 

    LH_ModS_SendData(guca_ModS_TxBuff, 5);

  }

 

}

 

 

 

void LH_ModS_04H(void)

{

  //操作寄存器起始地址

  uint16_t StartRegAddr = (((uint16_t)guca_ModS_RxBuff[2]) << 8) | guca_ModS_RxBuff[3];

  //操作寄存器数量

  uint16_t OpRegNum = (((uint16_t)guca_ModS_RxBuff[4]) << 8) | guca_ModS_RxBuff[5];

 

  if ((StartRegAddr + OpRegNum) < INPUTREGNUM) // 寄存器地址+数量 在设定地址范围内

  {

    guca_ModS_TxBuff[0] =  guca_ModS_RxBuff[0] ;

    guca_ModS_TxBuff[1] =  guca_ModS_RxBuff[1] ;

    guca_ModS_TxBuff[2] =  OpRegNum * 2;

 

    for (int i=0 ; i<OpRegNum*2; i++)

    {

      //先发送高位

      guca_ModS_TxBuff[3+i*2] = ((guhpa_ModS_InputReg[StartRegAddr+i]) >> 8) &0x00FF;//先发送高字节--再发送低字节

      guca_ModS_TxBuff[4+i*2] = ((guhpa_ModS_InputReg[StartRegAddr+i]) & 0x00FF); //

      //方法二——联合体

    }

 

    uint16_t TxCRC;

    TxCRC  = LH_ModS_GetCRC16(guca_ModS_TxBuff, 3 + OpRegNum*2);;

 

    guca_ModS_TxBuff[3 + OpRegNum*2] = TxCRC ;

    guca_ModS_TxBuff[4 + OpRegNum*2] = (TxCRC >> 8);//先发送CRC低位 后发送高位

 

    LH_ModS_SendData(guca_ModS_TxBuff, 5 + OpRegNum*2);

 

  }

  else

  {

    guca_ModS_TxBuff[0] =  guca_ModS_RxBuff[0] ;

    guca_ModS_TxBuff[1] =  guca_ModS_RxBuff[1] | 0x80 ;

    guca_ModS_TxBuff[2] =  0x02; //地址异常-----------------------------------

 

    uint16_t TxCRC;

    TxCRC = LH_ModS_GetCRC16(guca_ModS_TxBuff, 3);

 

    guca_ModS_TxBuff[3] = TxCRC ;

    guca_ModS_TxBuff[4] = (TxCRC >> 8); //先发送CRC低位 后发送高位

 

    LH_ModS_SendData(guca_ModS_TxBuff, 5);

  }

 

}

 

 

 

void LH_ModS_10H(void)

{

  //操作寄存器起始地址

  uint16_t StartRegAddr = (((uint16_t)guca_ModS_RxBuff[2]) << 8) | guca_ModS_RxBuff[3];

  //操作寄存器数量

  uint16_t OpRegNum = (((uint16_t)guca_ModS_RxBuff[4]) << 8) | guca_ModS_RxBuff[5];

 

  if ((StartRegAddr + OpRegNum) < HOLDREGNUM) // 寄存器地址+数量 在设定地址范围内

  {

    //写数据

    for (int i=0 ; i<OpRegNum; i++)

    {

      guhpa_ModS_HoldReg[StartRegAddr+i] = (((uint16_t)guca_ModS_RxBuff[7+i*2]) << 8) | guca_ModS_RxBuff[8+i*2];

    }

 

    //响应报文

    guca_ModS_TxBuff[0] =  guca_ModS_RxBuff[0] ;

    guca_ModS_TxBuff[1] =  guca_ModS_RxBuff[1] ;

    guca_ModS_TxBuff[2] =  guca_ModS_RxBuff[2] ;

    guca_ModS_TxBuff[3] =  guca_ModS_RxBuff[3];

    guca_ModS_TxBuff[4] =  guca_ModS_RxBuff[4] ;

    guca_ModS_TxBuff[5] =  guca_ModS_RxBuff[5];

 

    uint16_t TxCRC;

    TxCRC  = LH_ModS_GetCRC16(guca_ModS_TxBuff, 6);

 

    guca_ModS_TxBuff[6] = TxCRC ;

    guca_ModS_TxBuff[7] = (TxCRC >> 8);//先发送CRC低位 后发送高位

 

    LH_ModS_SendData(guca_ModS_TxBuff, 8);

 

  }

  else

  {

    guca_ModS_TxBuff[0] =  guca_ModS_RxBuff[0] ;

    guca_ModS_TxBuff[1] =  guca_ModS_RxBuff[1] | 0x80 ;

    guca_ModS_TxBuff[2] =  0x02; //地址异常---------------------------------

 

    uint16_t TxCRC;

    TxCRC = LH_ModS_GetCRC16(guca_ModS_TxBuff, 3);

 

    guca_ModS_TxBuff[3 ] = TxCRC ;

    guca_ModS_TxBuff[4 ] = (TxCRC >> 8); //先发送CRC低位 后发送高位

 

    LH_ModS_SendData(guca_ModS_TxBuff, 5);

  }

 

}

 

 

//从机响应程序

//接收指令分析完调用

void LH_ModS_SendData(uint8_t *TxBuff, uint16_t length)

{

  LH_RS485_SendData(TxBuff, length);

  LH_LED_Change();

}

LH_ModbusSlave.h

#ifndef __LH_MODBUSSLAVE_H

#define __LH_MODBUSSLAVE_H

 

#include "stdint.h"

 

 

void LH_ModS_Init(void);

   

void LH_ModS_SendData(uint8_t *p ,uint16_t length);

void LH_ModS_ReceiveISR(void);

 

void LH_ModS_ExeCmd(void);//从机指令执行程序——10ms周期调用一次

 

void LH_ModS_RXT35ISR(void); //T35判断程序

 

void LH_ModS_03H(void);

void LH_ModS_04H(void);

void LH_ModS_10H(void);

#endif

 

LH_RS485.c

//*******************************************************

//作者------LH

//时间------2025.01.05

//模块功能--485发送 接收数据

//用的485电路是自控流向的,所以不需要引脚控制流向

//*******************************************************

 

#include "LH_RS485.h"

#include "LH_UART.h"

#include "LH_ModbusSlave.h"

#include "LH_ModbusMaster.h"

 

//485发送函数

void LH_RS485_SendData(uint8_t *TxBuff, uint16_t length)

{

  LH_UART1_SendData(TxBuff, length);

 

}

 

void LH_RS485_ReceiveISR(void)

{

 

  //主机

  //LH_ModM_ReceiveISR();

  //LH_ModM_ReceiveISR里再次开中断

 

  //从机

  LH_ModS_ReceiveISR();

  //LH_ModS_ReceiveISR里再次开中断

 

}

LH_RS485.h

#ifndef __LH_RS485_H

#define __LH_RS485_H

 

#include "stdint.h"

 

 

//485发送函数

void LH_RS485_SendData(uint8_t *TxBuff ,uint16_t length);

 

void LH_RS485_ReceiveISR(void);

 

#endif

LH_UART.c

//*******************************************************

//作者------LH

//时间------2024.12.08

//模块功能--阻塞发送 中断接收

//*******************************************************

#include "stm32f1xx_hal.h"

#include "LH_UART.h"

#include "LH_RS485.h"

 

extern UART_HandleTypeDef huart1;//485 主从机Modbus通信

extern UART_HandleTypeDef huart2;//串口助手调试

 

//开启UART1接收中断

void LH_UART1_Start(uint8_t *RxBuff, uint16_t length)

{

  HAL_UART_Receive_IT(&huart1, RxBuff, length); //接收够字节长度才触发中断,且每次设置只进入一次中断

}

 

 

//UART1发送数据

void LH_UART1_SendData(uint8_t *TxBuff, uint16_t length)

{

  HAL_UART_Transmit(&huart1, TxBuff, length, 1); //阻塞发送

}

 

 

//UART1接收中断服务函数

void LH_UART1_ReceiveISR(void)

{

  LH_RS485_ReceiveISR();

  // LH_RS485_ReceiveISR 里再次开中断

}

 

//UART2发送数据

void LH_UART2_SendData(uint8_t *TxBuff, uint16_t length)

{

  HAL_UART_Transmit(&huart2, TxBuff, length, 1); //阻塞发送

}

LH_UART.h

#ifndef __LH_UART_H

#define __LH_UART_H

 

#include "stdint.h"

 

//开启UART1接收中断

void LH_UART1_Start(uint8_t *RxBuff ,uint16_t length);

//UART1发送数据

void LH_UART1_SendData(uint8_t *TxBuff ,uint16_t length);

//UART1中断接收数据

void LH_UART1_ReceiveISR(void );

 

 

//UART2发送数据

void LH_UART2_SendData(uint8_t *TxBuff ,uint16_t length);

#endif

LH_ISR.c

//*******************************************************

//作者------LH

//时间------2024.12.23

//模块功能--所有中断回调函数

//*******************************************************

#include "stm32f1xx_hal.h"

#include "LH_Delay.h"

#include "LH_AUART.h"

#include "LH_UART.h"

#include "LH_ModbusSlave.h"

#include "LH_ModbusMaster.h"

#include "LH_LED.h"

 

extern TIM_HandleTypeDef htim2;

extern UART_HandleTypeDef huart1;

 

//重写HAL库定时中断回调函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

{

 

  if (htim->Instance == TIM3)

  {

    //主机

    //LH_ModM_RXT35ISR();

    //从机

    LH_ModS_RXT35ISR();

  }

}

 

 

//串口接收中断回调函数

uint8_t guc_RxOneByte[1];

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

{

  if (huart->Instance == USART1)

  {

    LH_UART1_ReceiveISR();

    // LH_UART1_ReceiveISR 里再次开中断

  }

}

 

 

main.c

int main(void)

{

  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();

  MX_TIM2_Init();

  MX_USART1_UART_Init();

  MX_TIM3_Init();

  MX_USART2_UART_Init();

  /* USER CODE BEGIN 2 */

  HAL_TIM_Base_Start_IT(&htim3);

  LH_ModM_Init();

  LH_ModS_Init();

  /* USER CODE END 2 */

  while (1)

  {

 

        //主机——1s发送一次指令

        //LH_Delay_Us(1000000);       

        //LH_ModM_ProcessRxData();//处理上一次从机回复数据

        //LH_ModM_SendCmd();//发送下一次报文指令

                

        //从机——10ms检查一次

        LH_Delay_Us(10000);       

        LH_ModS_ExeCmd();         

  }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值