一、MODBUS 通讯协议
MODBUS 通讯协议,是1979年由美国Modicon 公司提出的,就是被称为PLC 之父的迪克·莫利先生创造的品牌。 MODBUS 是世界上第一个用于工业现场的总线协议,可以说,它的出现标志着工业现场从模拟量时代向通讯时代迈进。
下载链接:https://github.com/cwalter-at/freemodbus
二、移植准备
解压并打开刚刚下载的文件,点进demo里,新建一个STM32MB文件夹
将BARE文件夹里的文件全部复制到刚刚建立的STM32MB文件夹里
再把modbus文件夹全部复制到刚刚新建的STM32MB文件夹,到最后的结果如下图:
三、使用cubeMX建立项目
选择自己的芯片后,设置外部时钟:
使能一个USART为异步通信,并且加上中断:
进入时钟设置,将HCLK改为72,然后外部时钟频率为8(不同板子不一样,如果不一样建议上网查一下,如果不和你所用的一致,后面用ModbusPoll会报超时):
使能定时器4,预分频系数为3600-1,对应的分频频率为20KHz,自动重载值设置为35,得到超时时间1750us,并设置中断:
配置中断优先级,定时器中断优先级低于串口中断即可:
项目管理,然后生成项目就行了:
四、移植
在文件夹里打开项目里的MDK-ARM文件,将开始新建的STM32MB文件复制到这里
新建名为MB和MB_Port的组,MB内添加STM32MB文件夹下modbus文件夹内所有.c文件,MB_Port内添加STM32MB文件夹下port文件夹内所有.c文件以及根目录的demo.c文件:
添加头文件路径: portserial.c文件更改 : vMBPortSerialEnable函数改为
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
if (xRxEnable)
{
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
}
else
{
__HAL_UART_DISABLE_IT(&huart1,UART_IT_RXNE);
}
if (xTxEnable)
{
__HAL_UART_ENABLE_IT(&huart1,UART_IT_TXE);
}
else
{
__HAL_UART_DISABLE_IT(&huart1,UART_IT_TXE);
}
}
xMBPortSerialInit函数的返回值改为true:
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
return TRUE;
}
xMBPortSerialPutByte和xMBPortSerialGetByte更改为:
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
if(HAL_UART_Transmit (&huart1 ,(uint8_t *)&ucByte,1,0x01) != HAL_OK )
return FALSE ;
else
return TRUE;
}
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
if(HAL_UART_Receive (&huart1 ,(uint8_t *)pucByte,1,0x01) != HAL_OK )
return FALSE ;
else
return TRUE;
}
将prvvUARTTxReadyISR和prvvUARTRxISR前面的static删除,记得也在对应声明里删掉:
void prvvUARTTxReadyISR( void )
{
pxMBFrameCBTransmitterEmpty( );
}
void prvvUARTRxISR( void )
{
pxMBFrameCBByteReceived( );
}
porttimer.c文件 :
#include "port.h"
#include "stm32f1xx_hal.h"
#include "tim.h"
#include "mb.h"
#include "mbport.h"
void prvvTIMERExpiredISR( void );
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
return TRUE;
}
inline void
vMBPortTimersEnable( )
{
__HAL_TIM_CLEAR_IT(&htim4,TIM_IT_UPDATE);
__HAL_TIM_ENABLE_IT(&htim4,TIM_IT_UPDATE);
__HAL_TIM_SET_COUNTER(&htim4,0);
__HAL_TIM_ENABLE(&htim4);
}
inline void
vMBPortTimersDisable( )
{
__HAL_TIM_DISABLE(&htim4);
__HAL_TIM_SET_COUNTER(&htim4,0);
__HAL_TIM_DISABLE_IT(&htim4,TIM_IT_UPDATE);
__HAL_TIM_CLEAR_IT(&htim4,TIM_IT_UPDATE);
}
void prvvTIMERExpiredISR( void )
{
( void )pxMBPortCBTimerExpired( );
}
port.h文件 :
#ifndef _PORT_H
#define _PORT_H
#include <assert.h>
#include <inttypes.h>
#include "stm32f1xx_hal.h"
#define INLINE inline
#define PR_BEGIN_EXTERN_C extern "C" {
#define PR_END_EXTERN_C }
#define ENTER_CRITICAL_SECTION( ) __set_PRIMASK(1)
#define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0)
stm32f1xx_it.c文件 :
extern void prvvUARTTxReadyISR(void);
extern void prvvUARTRxISR(void);
extern void prvvTIMERExpiredISR( void );
更改串口1的中断处理函数:
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1);
if(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_RXNE)!= RESET)
{
prvvUARTRxISR();
}
if(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TXE)!= RESET)
{
prvvUARTTxReadyISR();
}
HAL_NVIC_ClearPendingIRQ(USART1_IRQn);
HAL_UART_IRQHandler(&huart1);
}
增加定时器中断回调函数,注意增加的位置:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
prvvTIMERExpiredISR( );
}
demo.c文件 :
#include "mb.h"
#include "mbport.h"
#define REG_INPUT_START 0
#define REG_INPUT_NREGS 5
static USHORT usRegInputStart = REG_INPUT_START;
uint16_t usRegInputBuf[REG_INPUT_NREGS];
uint16_t InputBuff[5];
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
int i;
InputBuff[0] = 0x11;
InputBuff[1] = 0x22;
InputBuff[2] = 0x33;
InputBuff[3] = 0x44;
if( ( usAddress >= REG_INPUT_START )
&& ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
{
iRegIndex = ( int )( usAddress - usRegInputStart );
for(i=0;i<usNRegs;i++)
{
*pucRegBuffer=InputBuff[i+usAddress-1]>>8;
pucRegBuffer++;
*pucRegBuffer=InputBuff[i+usAddress-1]&0xff;
pucRegBuffer++;
}
}
else
{
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;
}
main.c : 启动modbus需要在main函数进行初始化、开启侦听操作,所以需要在main.c里添加代码,添加头文件,注意添加的位置:
#include "mb.h"
#include "mbport.h"
mian函数:
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM4_Init();
MX_USART1_UART_Init();
eMBInit( MB_RTU, 0x01, 1, 115200, MB_PAR_NONE);
eMBEnable( );
while (1)
{
( void )eMBPoll( );
}
}
所有程序修改完毕,编译烧写到板子上。
五、测试
打开modbuspoll,点击setup第一个读写定义,设置如下图2:
点击connection的connect,进入连接设置,选项如图,我使用的是usb转ttl,用的是com端口,我连接的口是com4,所以选择的是port4:
连接之后,结果如下,这里的值与demo.c里设置的值一致,移植成功。:
六、总结
一定要仔细,错了一步可能就得不到输出,又不好排查是哪里错了。
参考
https://blog.csdn.net/junseven164/article/details/121698256 https://blog.csdn.net/ASWaterbenben/article/details/105549750
|