此专栏一共6篇文章
Modbus Poll 、Modbus Slave 软件使用-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(); } } |


4530

被折叠的 条评论
为什么被折叠?



