|
|
|
## Modbus
|
|
|
|
https://blog.csdn.net/ASWaterbenben/article/details/105549750
|
|
|
|
https://blog.51cto.com/u_15830484/5767899
|
|
|
|
https://www.amobbs.com/thread-5491615-1-1.html
|
|
|
|
https://blog.csdn.net/qq153471503/article/details/104840279
|
|
|
|
https://blog.csdn.net/childbor/article/details/123803534 野火
|
|
|
|
30001为输入寄存器,需要使用04指令访问,而40001为保持寄存器,可以使用03、06和16指令访问
|
|
|
|
|
|
|
|
在modbus中,需要判断一帧什么时候发送结束,我们采用定时器进行判断。
|
|
|
|
当定时器时间大于我们设定的时间时,发生定时器中断,告诉系统,一帧数据发送完成。
|
|
|
|
|
|
|
|
首先选择TIM7下的Parameter Settings,将PSC设置为7199,Counter Mode设置为UP,Counter Period 设置为39,Auto-reload preload设置为Enable。
|
|
|
|
拷贝mobdus文件加
|
|
|
|
拷贝demo/bare 到 port文件夹, demo.c 改modbus_port.c
|
|
|
|
mbconfig.h 配置启用哪些模块
|
|
|
|
对于元文件需要做修改的,加 .port 后缀,放到code/port文件夹
|
|
|
|
#### 移植思路
|
|
|
|
##### 串口定时器初始化放到 Modbus里
|
|
|
|
暂无
|
|
|
|
##### 串口定时器单独初始化
|
|
|
|
|
|
|
|
***portserial.c***
|
|
|
|
extern UART_HandleTypeDef huart2; // 引入串口
|
|
|
|
|
|
|
|
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
|
|
|
|
依据参考开启相关中断
|
|
|
|
UART_IT_RXNE 接收非空
|
|
|
|
UART_IT_TXE 发送完成
|
|
|
|
|
|
|
|
xMBPortSerialPutByte( CHAR ucByte ) // 处理发送字节
|
|
|
|
```C
|
|
|
|
|
|
|
|
```
|
|
|
|
xMBPortSerialGetByte( CHAR * pucByte ) //处理接收字节
|
|
|
|
|
|
|
|
void USART2_IRQHandler(void)
|
|
|
|
prvvUARTRxISR();//接收中断
|
|
|
|
prvvUARTTxReadyISR();//发送中断
|
|
|
|
|
|
|
|
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);//发送完成回调函数
|
|
|
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);//接收完成回调函数
|
|
|
|
|
|
|
|
***porttimer.c***
|
|
|
|
定时器启用,停用。 请中断等操作
|
|
|
|
定时器是中断回调
|
|
|
|
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
|
|
|
prvvTIMERExpiredISR( ); //定时器中断
|
|
|
|
|
|
|
|
***modbus_port.c***
|
|
|
|
定义Input Holding 寄存器操作
|
|
|
|
|
|
|
|
中断建立联系 -- 如何无缝,不改中断函数代码,回调函数
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
***前端初始化***
|
|
|
|
eMBInit( MB_RTU, 0x01, 2, 115200, MB_PAR_NONE);//初始化modbus,走modbusRTU,从站地址为0x01,端口为1。
|
|
|
|
eMBEnable( );//使能modbus
|
|
|
|
|
|
|
|
while ( void )eMBPoll( );//启动modbus侦听
|
|
|
|
### 源码结构
|
|
|
|
|
|
|
|
定义结构体接收数据 usart.h
|
|
|
|
```C
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
uint8_t *rx_buf; //接收缓冲数组
|
|
|
|
uint16_t rx_buf_cnt; //接收缓冲计数值
|
|
|
|
uint16_t rx_size; //接收数据大小
|
|
|
|
uint8_t rx_flag; //接收完成标志位
|
|
|
|
|
|
|
|
uint8_t *tx_buf; //发送缓冲数组
|
|
|
|
uint16_t tx_buf_cnt; //发送缓冲计数值
|
|
|
|
uint16_t tx_size; //实际发送数据大小
|
|
|
|
}UART_BUF; //串口结构体
|
|
|
|
|
|
|
|
extern UART_BUF uart_buf_2; //预留一个定义的buf
|
|
|
|
|
|
|
|
```
|
|
|
|
usart.c
|
|
|
|
```C
|
|
|
|
/* USER CODE BEGIN 0 */
|
|
|
|
#include "string.h"
|
|
|
|
#include "tim.h"
|
|
|
|
#define UART4_RXSIZE 1024 //一帧接收数据的最大值
|
|
|
|
#define UART4_TXSIZE 1024 //一帧发送数据的最大值
|
|
|
|
|
|
|
|
uint8_t uart4_rx_buf[UART4_RXSIZE]; //发送数据缓冲数组
|
|
|
|
uint8_t uart4_tx_buf[UART4_TXSIZE]; //接收数据缓冲数据
|
|
|
|
|
|
|
|
UART_BUF uart4; //串口结构体实体
|
|
|
|
uint8_t RxBuffer; //接收数据中间变量
|
|
|
|
/* USER CODE END 0 */
|
|
|
|
```
|
|
|
|
|
|
|
|
重写回调
|
|
|
|
|
|
|
|
```C
|
|
|
|
/*****************************重写回调函数,实现串口数据接收**********************/
|
|
|
|
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
|
|
|
|
{
|
|
|
|
if(huart->Instance == UART4)
|
|
|
|
{
|
|
|
|
if(uart4.rx_buf_cnt >= UART4_RXSIZE-1) //接收数据量超限,错误
|
|
|
|
{
|
|
|
|
uart4.rx_buf_cnt = 0;
|
|
|
|
memset(uart4.rx_buf, 0x00, sizeof(uart4.rx_buf));
|
|
|
|
HAL_UART_Transmit(huart, (uint8_t *)"数据溢出", 10, 0xFFFF);
|
|
|
|
}
|
|
|
|
else //接收正常
|
|
|
|
{
|
|
|
|
uart4.rx_buf[uart4.rx_buf_cnt++] = RxBuffer; //接收数据存储到rx_buf
|
|
|
|
HAL_TIM_Base_Stop_IT(&htim7);
|
|
|
|
__HAL_TIM_SET_COUNTER(&htim7, 0);
|
|
|
|
HAL_TIM_Base_Start_IT(&htim7); //将定时器7的计数值清零后重新计数
|
|
|
|
}
|
|
|
|
HAL_UART_Receive_IT(huart, (uint8_t *)&RxBuffer, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
port.h
|
|
|
|
#define ENTER_CRITICAL_SECTION( ) __set_PRIMASK(1);//关总中断
|
|
|
|
#define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0);//开总中断
|
|
|
|
如何改DMA ?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tim 初始化及回调
|
|
|
|
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
|
|
|
|
|
|
|
### main
|
|
|
|
buf 重置 ---写函数实现供调用
|
|
|
|
HAL_UART_Receive_IT(huart, uart4.rx_buf, 1); //开启接收中断
|
|
|
|
|
|
|
|
tim清理标记 -- 写函数实现供调用
|
|
|
|
开启中断
|
|
|
|
|
|
|
|
### modbus slave
|
|
|
|
将已经编写好的modbus.c和modbus.h分别复制到Src和Inc文件夹内
|
|
|
|
|
|
|
|
#### freemodbus
|
|
|
|
|
|
|
|
解压freemodbus文件后打开,
|
|
|
|
modbus 目录组合,重新分Src Inc
|
|
|
|
我们需要demo目录下的BARE,该目录下的代码是空的,STM32移植工作基本就是修改:portserial.c、porttimer.c、port.h这三个文件。
|
|
|
|
|
|
|
|
参考:
|
|
|
|
https://github.com/eziya/STM32_HAL_FREEMODBUS_RTU/tree/master/Middlewares/Third_Party/modbus
|
|
|
|
bare/port 目录下的Makefile 可以直接编译
|
|
|
|
modbus 目录拷贝到 Middlewares/Third_Party/modbus/
|
|
|
|
bare/port 目录下的Makefile 可以直接编译, demo 包含main()
|
|
|
|
|
|
|
|
functions include 是基础目录
|
|
|
|
rtu 需要引用 rtu目录
|
|
|
|
|
|
|
|
|
|
|
|
#### porttimer.c
|
|
|
|
将定时器设置为每50us的时长记一个数,传入的usTim1Timerout50us变量给自动装载即可
|
|
|
|
prvvTIMERExpiredISR函数需要在定时器中断服务函数中调用,它的作用是用于通知modbus协议栈3.5个字符的等待时间已经到达
|
|
|
|
|
|
|
|
注意一些标志清除
|
|
|
|
|
|
|
|
定时器中断 TimX_IRQHandler 通知通知modbus3.5个字符等待时间到
|
|
|
|
prvvTIMERExpiredISR(); // 通知modbus3.5个字符等待时间到
|
|
|
|
|
|
|
|
如果使用了485芯片的话,那么同一时刻只能接收或者发送,可以将函数vMBPortSerialEnable修改成这样
|
|
|
|
拉高拉低,并增加延时。
|
|
|
|
|
|
|
|
|
|
|
|
#### 定义port.c供前端调用
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
***数据定义***
|
|
|
|
```C
|
|
|
|
// 十路输入寄存器
|
|
|
|
#define REG_INPUT_SIZE 10
|
|
|
|
uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
|
|
|
|
|
|
|
|
|
|
|
|
// 十路保持寄存器
|
|
|
|
#define REG_HOLD_SIZE 10
|
|
|
|
uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
|
|
|
|
|
|
|
|
|
|
|
|
// 十路线圈
|
|
|
|
#define REG_COILS_SIZE 10
|
|
|
|
uint8_t REG_COILS_BUF[REG_COILS_SIZE] = {1, 1, 1, 1, 0, 0, 0, 0, 1, 1};
|
|
|
|
|
|
|
|
|
|
|
|
// 十路离散量
|
|
|
|
// 十路离散量
|
|
|
|
#define REG_DISC_SIZE 10
|
|
|
|
uint8_t REG_DISC_BUF[REG_DISC_SIZE] = {1,1,1,1,0,0,0,0,1,1};
|
|
|
|
```
|
|
|
|
|
|
|
|
对应功能函数
|
|
|
|
```C
|
|
|
|
/* 功能码 4*/
|
|
|
|
eMBErrorCode
|
|
|
|
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
|
|
|
|
{
|
|
|
|
|
|
|
|
return eStatus;
|
|
|
|
}
|
|
|
|
/* 功能码 6 3 16*/
|
|
|
|
eMBErrorCode
|
|
|
|
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
|
|
|
|
eMBRegisterMode eMode )
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 功能码 1 5 15*/
|
|
|
|
eMBErrorCode
|
|
|
|
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
|
|
|
|
eMBRegisterMode eMode )
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 功能码 2*/
|
|
|
|
eMBErrorCode
|
|
|
|
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
完整port.c
|
|
|
|
|
|
|
|
```C
|
|
|
|
#include "mb.h"
|
|
|
|
#include "mbport.h"
|
|
|
|
|
|
|
|
|
|
|
|
// 十路输入寄存器
|
|
|
|
#define REG_INPUT_SIZE 10
|
|
|
|
uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];
|
|
|
|
|
|
|
|
|
|
|
|
// 十路保持寄存器
|
|
|
|
#define REG_HOLD_SIZE 10
|
|
|
|
uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];
|
|
|
|
|
|
|
|
|
|
|
|
// 十路线圈
|
|
|
|
#define REG_COILS_SIZE 10
|
|
|
|
uint8_t REG_COILS_BUF[REG_COILS_SIZE] = {1, 1, 1, 1, 0, 0, 0, 0, 1, 1};
|
|
|
|
|
|
|
|
|
|
|
|
// 十路离散量
|
|
|
|
#define REG_DISC_SIZE 10
|
|
|
|
uint8_t REG_DISC_BUF[REG_DISC_SIZE] = {1,1,1,1,0,0,0,0,1,1};
|
|
|
|
|
|
|
|
|
|
|
|
/// CMD4命令处理回调函数
|
|
|
|
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
|
|
|
|
{
|
|
|
|
USHORT usRegIndex = usAddress - 1;
|
|
|
|
|
|
|
|
// 非法检测
|
|
|
|
if((usRegIndex + usNRegs) > REG_INPUT_SIZE)
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 循环读取
|
|
|
|
while( usNRegs > 0 )
|
|
|
|
{
|
|
|
|
*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );
|
|
|
|
*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );
|
|
|
|
usRegIndex++;
|
|
|
|
usNRegs--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 模拟输入寄存器被改变
|
|
|
|
for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++)
|
|
|
|
{
|
|
|
|
REG_INPUT_BUF[usRegIndex]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MB_ENOERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// CMD6、3、16命令处理回调函数
|
|
|
|
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
|
|
|
|
{
|
|
|
|
USHORT usRegIndex = usAddress - 1;
|
|
|
|
|
|
|
|
// 非法检测
|
|
|
|
if((usRegIndex + usNRegs) > REG_HOLD_SIZE)
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 写寄存器
|
|
|
|
if(eMode == MB_REG_WRITE)
|
|
|
|
{
|
|
|
|
while( usNRegs > 0 )
|
|
|
|
{
|
|
|
|
REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];
|
|
|
|
pucRegBuffer += 2;
|
|
|
|
usRegIndex++;
|
|
|
|
usNRegs--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 读寄存器
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while( usNRegs > 0 )
|
|
|
|
{
|
|
|
|
*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );
|
|
|
|
*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );
|
|
|
|
usRegIndex++;
|
|
|
|
usNRegs--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MB_ENOERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// CMD1、5、15命令处理回调函数
|
|
|
|
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
|
|
|
|
{
|
|
|
|
USHORT usRegIndex = usAddress - 1;
|
|
|
|
UCHAR ucBits = 0;
|
|
|
|
UCHAR ucState = 0;
|
|
|
|
UCHAR ucLoops = 0;
|
|
|
|
|
|
|
|
// 非法检测
|
|
|
|
if((usRegIndex + usNCoils) > REG_COILS_SIZE)
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(eMode == MB_REG_WRITE)
|
|
|
|
{
|
|
|
|
ucLoops = (usNCoils - 1) / 8 + 1;
|
|
|
|
while(ucLoops != 0)
|
|
|
|
{
|
|
|
|
ucState = *pucRegBuffer++;
|
|
|
|
ucBits = 0;
|
|
|
|
while(usNCoils != 0 && ucBits < 8)
|
|
|
|
{
|
|
|
|
REG_COILS_BUF[usRegIndex++] = (ucState >> ucBits) & 0X01;
|
|
|
|
usNCoils--;
|
|
|
|
ucBits++;
|
|
|
|
}
|
|
|
|
ucLoops--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ucLoops = (usNCoils - 1) / 8 + 1;
|
|
|
|
while(ucLoops != 0)
|
|
|
|
{
|
|
|
|
ucState = 0;
|
|
|
|
ucBits = 0;
|
|
|
|
while(usNCoils != 0 && ucBits < 8)
|
|
|
|
{
|
|
|
|
if(REG_COILS_BUF[usRegIndex])
|
|
|
|
{
|
|
|
|
ucState |= (1 << ucBits);
|
|
|
|
}
|
|
|
|
usNCoils--;
|
|
|
|
usRegIndex++;
|
|
|
|
ucBits++;
|
|
|
|
}
|
|
|
|
*pucRegBuffer++ = ucState;
|
|
|
|
ucLoops--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MB_ENOERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// CMD2命令处理回调函数
|
|
|
|
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
|
|
|
|
{
|
|
|
|
USHORT usRegIndex = usAddress - 1;
|
|
|
|
UCHAR ucBits = 0;
|
|
|
|
UCHAR ucState = 0;
|
|
|
|
UCHAR ucLoops = 0;
|
|
|
|
|
|
|
|
// 非法检测
|
|
|
|
if((usRegIndex + usNDiscrete) > REG_DISC_SIZE)
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
ucLoops = (usNDiscrete - 1) / 8 + 1;
|
|
|
|
while(ucLoops != 0)
|
|
|
|
{
|
|
|
|
ucState = 0;
|
|
|
|
ucBits = 0;
|
|
|
|
while(usNDiscrete != 0 && ucBits < 8)
|
|
|
|
{
|
|
|
|
if(REG_DISC_BUF[usRegIndex])
|
|
|
|
{
|
|
|
|
ucState |= (1 << ucBits);
|
|
|
|
}
|
|
|
|
usNDiscrete--;
|
|
|
|
usRegIndex++;
|
|
|
|
ucBits++;
|
|
|
|
}
|
|
|
|
*pucRegBuffer++ = ucState;
|
|
|
|
ucLoops--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 模拟离散量输入被改变
|
|
|
|
for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++)
|
|
|
|
{
|
|
|
|
REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];
|
|
|
|
}
|
|
|
|
|
|
|
|
return MB_ENOERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### main.c
|
|
|
|
将串口定时器的初始化都整到 Modbus 部分,这里只需要调用modbus初始化就可以
|
|
|
|
```C
|
|
|
|
int main(void)
|
|
|
|
{
|
|
|
|
HAL_Init();
|
|
|
|
SystemClock_Config();
|
|
|
|
MX_GPIO_Init();
|
|
|
|
eMBInit(MB_RTU, 0x01, 0, 9600, MB_PAR_ODD); // 初始化modbus为RTU方式,波特率9600,奇校验
|
|
|
|
eMBEnable(); // 使能modbus协议栈
|
|
|
|
|
|
|
|
for( ;; )
|
|
|
|
{
|
|
|
|
eMBPoll(); // 轮训查询
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
我们需要demo目录下的BARE,该目录下的代码是空的,STM32移植工作基本就是修改:portserial.c
|
|
|
|
串口的中断服务程序,
|
|
|
|
prvvUARTTxReadyISR和prvvUARTRxISR函数需要填写进中断服务程序,
|
|
|
|
前者得到作用为通知modbus协议栈串口已经空闲可以发送数据了
|
|
|
|
后者的作用为通知modbus串口1有数据到达
|
|
|
|
#### 调用
|
|
|
|
|
|
|
|
```C
|
|
|
|
|
|
|
|
#include "stm32f4xx_hal.h"
|
|
|
|
#include "cmsis_os.h"
|
|
|
|
|
|
|
|
#include "mb.h"
|
|
|
|
#include "mbport.h"
|
|
|
|
|
|
|
|
#define REG_INPUT_START 1000
|
|
|
|
#define REG_INPUT_NREGS 8
|
|
|
|
|
|
|
|
static USHORT usRegInputStart = REG_INPUT_START;
|
|
|
|
static USHORT usRegInputBuf[REG_INPUT_NREGS];
|
|
|
|
|
|
|
|
void ModbusRTUTask(void const * argument)
|
|
|
|
{
|
|
|
|
/* ABCDEF */
|
|
|
|
usRegInputBuf[0] = 11;
|
|
|
|
usRegInputBuf[1] = 22;
|
|
|
|
usRegInputBuf[2] = 33;
|
|
|
|
usRegInputBuf[3] = 44;
|
|
|
|
usRegInputBuf[4] = 55;
|
|
|
|
usRegInputBuf[5] = 66;
|
|
|
|
usRegInputBuf[6] = 77;
|
|
|
|
usRegInputBuf[7] = 88;
|
|
|
|
|
|
|
|
eMBErrorCode eStatus = eMBInit( MB_RTU, 1, 3, 19200, MB_PAR_NONE );
|
|
|
|
eStatus = eMBEnable();
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
eMBPoll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 ) )
|
|
|
|
{
|
|
|
|
iRegIndex = ( int )( usAddress - usRegInputStart );
|
|
|
|
while( usNRegs > 0 )
|
|
|
|
{
|
|
|
|
*pucRegBuffer++ =
|
|
|
|
( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
|
|
|
|
*pucRegBuffer++ =
|
|
|
|
( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
|
|
|
|
iRegIndex++;
|
|
|
|
usNRegs--;
|
|
|
|
}
|
|
|
|
|
|
|
|
HAL_GPIO_TogglePin(LD4_GPIO_Port, LD4_Pin);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
HAL_GPIO_TogglePin(LD5_GPIO_Port, LD5_Pin);
|
|
|
|
eStatus = MB_ENOREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
return eStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
eMBErrorCode
|
|
|
|
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
|
|
|
|
eMBRegisterMode eMode )
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
eMBErrorCode
|
|
|
|
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
|
|
|
|
eMBRegisterMode eMode )
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
eMBErrorCode
|
|
|
|
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
|
|
|
|
{
|
|
|
|
return MB_ENOREG;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
#### 备注
|
|
|
|
tim
|
|
|
|
由于不能在中断中启用timer, 所以不能直接用freertos 定时器去操作
|
|
|
|
|
|
|
|
serial
|
|
|
|
(__HAL_UART_GET_IT_SOURCE(pMobusHuart, UART_IT_RXNE) != RESET
|
|
|
|
&& __HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE) != RESET )
|
|
|
|
|
|
|
|
***串口中断优先级别高于 定时器***
|
|
|
|
不添加 __HAL_UART_GET_IT_SOURCE(pMobusHuart, UART_IT_RXNE , eSndState != STATE_TX_IDLE
|
|
|
|
具体原因不详
|
|
|
|
Checks whether the specified UART interrupt source is enabled or not.
|
|
|
|
启用中断改变的是这个值
|
|
|
|
|
|
|
|
#### 启用modbus的不同功能码
|
|
|
|
|
|
|
|
eMBRegHoldingCB 函数对应功能码 04
|
|
|
|
eMBRegInputCB 函数对应功能码 03 06
|
|
|
|
|
|
|
|
snd 01 06 00 01 0A 9D 1F 03, 返回同样, 对应的查询值 06 将地址为01的寄存器 写为 0A 9D (2177)
|
|
|
|
snd 01 10 00 00 00 02 04 87 65 43 21 3A 2C , 返回 01 10 00 00 00 02 41 C8
|
|
|
|
id func(0x10 16 ), 起始地址 00 寄存器个数2个, 返回4个字节, 0下7654321 CRC16
|
|
|
|
|
|
|
|
|
|
|
|
```C
|
|
|
|
if((usAddress >= REG_HOLD_START) && ((usAddress+usNRegs) <= (REG_HOLD_START + REG_HOLD_NREGS)))
|
|
|
|
{
|
|
|
|
iRegIndex = (int)(usAddress - usRegHoldStart);
|
|
|
|
switch(eMode)
|
|
|
|
{
|
|
|
|
case MB_REG_READ://读寄存器
|
|
|
|
while(usNRegs > 0)
|
|
|
|
{
|
|
|
|
*pucRegBuffer++ = (uint8_t)(usRegHoldBuf[iRegIndex] >> 8);
|
|
|
|
*pucRegBuffer++ = (uint8_t)(usRegHoldBuf[iRegIndex] & 0xFF);
|
|
|
|
iRegIndex++;
|
|
|
|
usNRegs--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case MB_REG_WRITE://写寄存器
|
|
|
|
|
|
|
|
while(usNRegs > 0)
|
|
|
|
{
|
|
|
|
usRegHoldBuf[iRegIndex] = *pucRegBuffer++ << 8;
|
|
|
|
usRegHoldBuf[iRegIndex] |= *pucRegBuffer++;
|
|
|
|
iRegIndex++;
|
|
|
|
usNRegs--;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
eMBRegCoilsCB 线圈 01
|
|
|
|
snd 01 01 00 00 00 03 7C 0B 读三个线圈
|
|
|
|
rcv 01 01 01 06 D1 8A 返回值为06 (0000 0110)的后三位 ,反过来 0,1,1
|
|
|
|
|
|
|
|
eMBRegDiscreteCB 线圈 02
|
|
|
|
snd 01 02 00 00 00 03 7C 0B 读三个线圈
|
|
|
|
rcv 01 01 01 06 D1 8A 返回值为06 (0000 0110)的后三位 ,反过来 0,1,1
|
|
|
|
|
|
|
|
报告从机状态 17功能码 0x01 0x11
|
|
|
|
01 11 C0 2C
|
|
|
|
|
|
|
|
了解接收机制:xMBRTUReceiveFSM
|
|
|
|
|
|
|
|
了解发送机制: xMBRTUTransmitFSM
|