一、利用STM32CubeMX新建工程
1、配置时钟
时钟源选择外部晶振。
配置时钟树。
2·、配置定时器
暂时先勾选internal clock就行,在modbus移植过程中还会对定时器重新初始化。
3、配置串口
随便配置就行,在modbus移植过程中还会对串口重新初始化。
4、中断配置
这里注意,串口的优先级是要比定时器优先高的。
取消掉自动生成中断服务程序,在移植过程中我们要自己编写串口和定时器的中断服务程序。
5、配置GPIO
如果使用了485模式,还需要一个发送接收控制端,该IO配置为推挽输出模式。也可以为该引脚设置User Label。
6、创建工程
二、下载freemodbus源码并且添加到刚才创建的工程
1、复制freemodbus源码到工程文件夹
2、在demo目录下的BARE文件夹里面新建port.c文件
3、在keil中添加freemodbus源码
将modbus和demo->BARE目录下的所有C文件添加进来。
4、编辑头文件包含路径
三、编写代码
1、物理接口文件的修改
在物理层,用户只需完成串行口及超时定时器的配置即可。具体应修改接口文件portserial.c及porttimer.c。
1)portserial.c中函数的修改
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
if(xRxEnable)
{
__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE); //使能接收寄存器非空中断
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); //MAX485操作 低电平为接收模式
}
else
{
__HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE); //禁能接收寄存器非空中断
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); //MAX485操作 高电平为发送模式
}
if (TRUE == xTxEnable)
{
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); //MAX485操作 高电平为发送模式
__HAL_UART_ENABLE_IT(&huart2, UART_IT_TC); //使能发送完成中断
}
else
{
__HAL_UART_DISABLE_IT(&huart2, UART_IT_TC); //禁能发送完成中断
}
}
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
huart2.Instance = USART2;
huart2.Init.BaudRate = ulBaudRate;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); //MAX485操作 低电平为接收模式
switch(eParity)
{
// 奇校验
case MB_PAR_ODD:
huart2.Init.Parity = UART_PARITY_ODD;
huart2.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
break;
// 偶校验
case MB_PAR_EVEN:
huart2.Init.Parity = UART_PARITY_EVEN;
huart2.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
break;
// 无校验
default:
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.WordLength = UART_WORDLENGTH_8B; // 无奇偶校验数据位为8bits
break;
}
return HAL_UART_Init(&huart2) == HAL_OK ? TRUE : FALSE;
}
BOOL xMBPortSerialPutByte( CHAR ucByte )
{
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); //MAX485操作 高电平为发送模式
USART2->DR = ucByte;
return TRUE;
}
BOOL xMBPortSerialGetByte( CHAR * pucByte )
{
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); //MAX485操作 低电平为接收模式
*pucByte = (USART2->DR & (uint16_t)0x00FF);
return TRUE;
}
static void prvvUARTTxReadyISR( void )
{
pxMBFrameCBTransmitterEmpty( );
}
static void prvvUARTRxISR( void )
{
pxMBFrameCBByteReceived( );
}
void USART2_IRQHandler(void)
{
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE)) // 接收非空中断标记被置位
{
__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE); // 清除中断标记
prvvUARTRxISR();
}
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TC)) // 发送完成中断标记被置位
{
__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_TC); // 清除中断标记
prvvUARTTxReadyISR();
}
}
2)porttimer.c中函数的修改
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim4.Instance = TIM4;
htim4.Init.Prescaler = 3599; // 50us记一次数
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = usTim1Timerout50us - 1;//usTim1Timerout50us*50即为定时器溢出时间
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
return FALSE;
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
return FALSE;
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
return FALSE;
}
__HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE); // 使能定时器更新中断
return TRUE;
}
inline void vMBPortTimersEnable( )
{
__HAL_TIM_SET_COUNTER(&htim4, 0); // 清空计数器
__HAL_TIM_ENABLE(&htim4); // 使能定时器
}
inline void
vMBPortTimersDisable( )
{
__HAL_TIM_DISABLE(&htim4); // 禁能定时器
}
static void prvvTIMERExpiredISR( void )
{
( void )pxMBPortCBTimerExpired( );
}
// 定时器4中断服务程序
void TIM4_IRQHandler(void)
{
if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE)) // 更新中断标记被置位
{
__HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);// 清除中断标记
prvvTIMERExpiredISR(); // 通知modbus3.5个字符等待时间到
}
}
2、应用层回函数的修改
在port中,定义所需要使用的寄存器,并修改对应的回函数。以下是操作保持寄存器的示例代码。
#include "port.h"
#include "mb.h"
//保持寄存器起始地址
#define REG_HOLDING_START 0x0001
//保持寄存器数量
#define REG_HOLDING_NREGS 10
//保持寄存器内容
uint16_t usRegHoldingBuf[REG_HOLDING_NREGS]
= {0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e,0x1111,0x1111};
/**
* @brief 保持寄存器处理函数,保持寄存器可读,可读可写
* @param pucRegBuffer 读操作时--返回数据指针,写操作时--输入数据指针
* usAddress 寄存器起始地址
* usNRegs 寄存器长度
* eMode 操作方式,读或者写
* @retval eStatus 寄存器状态
*/
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
eMBRegisterMode eMode )
{
//错误状态
eMBErrorCode eStatus = MB_ENOERR;
//偏移量
int16_t iRegIndex;
//判断寄存器是不是在范围内
test = usAddress;
if ((usAddress >= (USHORT)REG_HOLDING_START)
&& (usAddress + usNRegs <= (USHORT)(REG_HOLDING_START + REG_HOLDING_NREGS)) )
{
//计算偏移量
iRegIndex = ( int16_t )( usAddress - REG_HOLDING_START);switch ( eMode ){
//读处理函数
case MB_REG_READ:
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 );
*pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
break;
//写处理函数
case MB_REG_WRITE:
while( usNRegs > 0 )
{
usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
break;
}
}
else
{
//返回错误状态
eStatus = MB_ENOREG;
}
return eStatus;
}
/// 未使用
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
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;
}
3、主函数的编写
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
//初始化 RTU模式 从机地址为1 USART1 9600 无校验
eMBInit(MB_RTU, 0x01, 0x02, 9600, MB_PAR_NONE);
//启动FreeModbus
eMBEnable();
while (1)
{
//FreeMODBUS不断查询
eMBPoll();
HAL_Delay(50);
}
}
四、特别注意
这里使能了串口的“发送完成中断”,而不是官方代码建议的“发送为空中断”。由于单片机复位后,发送完成标志位TC置0,或者由于上一次发送最后一个字节时,发送中断中清除了TC标志位,所以无法进入USART2_IRQHandler()函数中启动发送。因此,在eMBPoll()函数中插入如下代码,发送一帧数据中的第一个字节,触发发送完成中断,随后就会自动完成一帧数据的发送。
eMBErrorCode
eMBPoll( void )
{
static UCHAR *ucMBFrame;
static UCHAR ucRcvAddress;
static UCHAR ucFunctionCode;
static USHORT usLength;
static eMBException eException;
int i;
eMBErrorCode eStatus = MB_ENOERR;
eMBEventType eEvent;
/* Check if the protocol stack is ready. */
if( eMBState != STATE_ENABLED )
{
return MB_EILLSTATE;
}
/* Check if there is a event available. If not return control to caller.
* Otherwise we will handle the event. */
if( xMBPortEventGet( &eEvent ) == TRUE )
{
switch ( eEvent )
{
case EV_READY:
break;
case EV_FRAME_RECEIVED:
eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
if( eStatus == MB_ENOERR )
{
/* Check if the frame is for us. If not ignore the frame. */
if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
{
( void )xMBPortEventPost( EV_EXECUTE );
}
}
break;
case EV_EXECUTE:
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
eException = MB_EX_ILLEGAL_FUNCTION;
for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
/* No more function handlers registered. Abort. */
if( xFuncHandlers
.ucFunctionCode == 0 )
{
break;
}
else if( xFuncHandlers.ucFunctionCode == ucFunctionCode )
{
eException = xFuncHandlers.pxHandler( ucMBFrame, &usLength );
break;
}
}
/* If the request was not sent to the broadcast address we
* return a reply. */
if( ucRcvAddress != MB_ADDRESS_BROADCAST )
{
if( eException != MB_EX_NONE )
{
/* An exception occured. Build an error frame. */
usLength = 0;
ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
ucMBFrame[usLength++] = eException;
}
if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
{
vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
}
eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );
//插入代码 begin
if( eStatus == MB_ENOERR ) //no error
{
xMBRTUTransmitFSM(); //发送一帧数据中的第一个字节,触发发送完成中断
}
//插入代码 end
}
break;
case EV_FRAME_SENT:
break;
}
}
return MB_ENOERR;
}
一、利用STM32CubeMX新建工程
1、配置时钟
时钟源选择外部晶振。
配置时钟树。
2·、配置定时器
暂时先勾选internal clock就行,在modbus移植过程中还会对定时器重新初始化。
3、配置串口
随便配置就行,在modbus移植过程中还会对串口重新初始化。
4、中断配置
这里注意,串口的优先级是要比定时器优先高的。
取消掉自动生成中断服务程序,在移植过程中我们要自己编写串口和定时器的中断服务程序。
5、配置GPIO
如果使用了485模式,还需要一个发送接收控制端,该IO配置为推挽输出模式。也可以为该引脚设置User Label。
6、创建工程
二、下载freemodbus源码并且添加到刚才创建的工程
1、复制freemodbus源码到工程文件夹
2、在demo目录下的BARE文件夹里面新建port.c文件
3、在keil中添加freemodbus源码
将modbus和demo->BARE目录下的所有C文件添加进来。
4、编辑头文件包含路径
三、编写代码
1、物理接口文件的修改
在物理层,用户只需完成串行口及超时定时器的配置即可。具体应修改接口文件portserial.c及porttimer.c。
1)portserial.c中函数的修改
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
if(xRxEnable)
{
__HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE); //使能接收寄存器非空中断
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); //MAX485操作 低电平为接收模式
}
else
{
__HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE); //禁能接收寄存器非空中断
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); //MAX485操作 高电平为发送模式
}
if (TRUE == xTxEnable)
{
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); //MAX485操作 高电平为发送模式
__HAL_UART_ENABLE_IT(&huart2, UART_IT_TC); //使能发送完成中断
}
else
{
__HAL_UART_DISABLE_IT(&huart2, UART_IT_TC); //禁能发送完成中断
}
}
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
huart2.Instance = USART2;
huart2.Init.BaudRate = ulBaudRate;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); //MAX485操作 低电平为接收模式
switch(eParity)
{
// 奇校验
case MB_PAR_ODD:
huart2.Init.Parity = UART_PARITY_ODD;
huart2.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
break;
// 偶校验
case MB_PAR_EVEN:
huart2.Init.Parity = UART_PARITY_EVEN;
huart2.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
break;
// 无校验
default:
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.WordLength = UART_WORDLENGTH_8B; // 无奇偶校验数据位为8bits
break;
}
return HAL_UART_Init(&huart2) == HAL_OK ? TRUE : FALSE;
}
BOOL xMBPortSerialPutByte( CHAR ucByte )
{
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_SET); //MAX485操作 高电平为发送模式
USART2->DR = ucByte;
return TRUE;
}
BOOL xMBPortSerialGetByte( CHAR * pucByte )
{
HAL_GPIO_WritePin(RS485_DE_GPIO_Port, RS485_DE_Pin, GPIO_PIN_RESET); //MAX485操作 低电平为接收模式
*pucByte = (USART2->DR & (uint16_t)0x00FF);
return TRUE;
}
static void prvvUARTTxReadyISR( void )
{
pxMBFrameCBTransmitterEmpty( );
}
static void prvvUARTRxISR( void )
{
pxMBFrameCBByteReceived( );
}
void USART2_IRQHandler(void)
{
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_RXNE)) // 接收非空中断标记被置位
{
__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_RXNE); // 清除中断标记
prvvUARTRxISR();
}
if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TC)) // 发送完成中断标记被置位
{
__HAL_UART_CLEAR_FLAG(&huart2, UART_FLAG_TC); // 清除中断标记
prvvUARTTxReadyISR();
}
}
2)porttimer.c中函数的修改
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim4.Instance = TIM4;
htim4.Init.Prescaler = 3599; // 50us记一次数
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = usTim1Timerout50us - 1;//usTim1Timerout50us*50即为定时器溢出时间
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
return FALSE;
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
return FALSE;
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
return FALSE;
}
__HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE); // 使能定时器更新中断
return TRUE;
}
inline void vMBPortTimersEnable( )
{
__HAL_TIM_SET_COUNTER(&htim4, 0); // 清空计数器
__HAL_TIM_ENABLE(&htim4); // 使能定时器
}
inline void
vMBPortTimersDisable( )
{
__HAL_TIM_DISABLE(&htim4); // 禁能定时器
}
static void prvvTIMERExpiredISR( void )
{
( void )pxMBPortCBTimerExpired( );
}
// 定时器4中断服务程序
void TIM4_IRQHandler(void)
{
if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE)) // 更新中断标记被置位
{
__HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);// 清除中断标记
prvvTIMERExpiredISR(); // 通知modbus3.5个字符等待时间到
}
}
2、应用层回函数的修改
在port中,定义所需要使用的寄存器,并修改对应的回函数。以下是操作保持寄存器的示例代码。
#include "port.h"
#include "mb.h"
//保持寄存器起始地址
#define REG_HOLDING_START 0x0001
//保持寄存器数量
#define REG_HOLDING_NREGS 10
//保持寄存器内容
uint16_t usRegHoldingBuf[REG_HOLDING_NREGS]
= {0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e,0x1111,0x1111};
/**
* @brief 保持寄存器处理函数,保持寄存器可读,可读可写
* @param pucRegBuffer 读操作时--返回数据指针,写操作时--输入数据指针
* usAddress 寄存器起始地址
* usNRegs 寄存器长度
* eMode 操作方式,读或者写
* @retval eStatus 寄存器状态
*/
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
eMBRegisterMode eMode )
{
//错误状态
eMBErrorCode eStatus = MB_ENOERR;
//偏移量
int16_t iRegIndex;
//判断寄存器是不是在范围内
test = usAddress;
if ((usAddress >= (USHORT)REG_HOLDING_START)
&& (usAddress + usNRegs <= (USHORT)(REG_HOLDING_START + REG_HOLDING_NREGS)) )
{
//计算偏移量
iRegIndex = ( int16_t )( usAddress - REG_HOLDING_START);switch ( eMode ){
//读处理函数
case MB_REG_READ:
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 );
*pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
break;
//写处理函数
case MB_REG_WRITE:
while( usNRegs > 0 )
{
usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
break;
}
}
else
{
//返回错误状态
eStatus = MB_ENOREG;
}
return eStatus;
}
/// 未使用
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
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;
}
3、主函数的编写
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
//初始化 RTU模式 从机地址为1 USART1 9600 无校验
eMBInit(MB_RTU, 0x01, 0x02, 9600, MB_PAR_NONE);
//启动FreeModbus
eMBEnable();
while (1)
{
//FreeMODBUS不断查询
eMBPoll();
HAL_Delay(50);
}
}
四、特别注意
这里使能了串口的“发送完成中断”,而不是官方代码建议的“发送为空中断”。由于单片机复位后,发送完成标志位TC置0,或者由于上一次发送最后一个字节时,发送中断中清除了TC标志位,所以无法进入USART2_IRQHandler()函数中启动发送。因此,在eMBPoll()函数中插入如下代码,发送一帧数据中的第一个字节,触发发送完成中断,随后就会自动完成一帧数据的发送。
eMBErrorCode
eMBPoll( void )
{
static UCHAR *ucMBFrame;
static UCHAR ucRcvAddress;
static UCHAR ucFunctionCode;
static USHORT usLength;
static eMBException eException;
int i;
eMBErrorCode eStatus = MB_ENOERR;
eMBEventType eEvent;
/* Check if the protocol stack is ready. */
if( eMBState != STATE_ENABLED )
{
return MB_EILLSTATE;
}
/* Check if there is a event available. If not return control to caller.
* Otherwise we will handle the event. */
if( xMBPortEventGet( &eEvent ) == TRUE )
{
switch ( eEvent )
{
case EV_READY:
break;
case EV_FRAME_RECEIVED:
eStatus = peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength );
if( eStatus == MB_ENOERR )
{
/* Check if the frame is for us. If not ignore the frame. */
if( ( ucRcvAddress == ucMBAddress ) || ( ucRcvAddress == MB_ADDRESS_BROADCAST ) )
{
( void )xMBPortEventPost( EV_EXECUTE );
}
}
break;
case EV_EXECUTE:
ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
eException = MB_EX_ILLEGAL_FUNCTION;
for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
{
/* No more function handlers registered. Abort. */
if( xFuncHandlers
.ucFunctionCode == 0 )
{
break;
}
else if( xFuncHandlers.ucFunctionCode == ucFunctionCode )
{
eException = xFuncHandlers.pxHandler( ucMBFrame, &usLength );
break;
}
}
/* If the request was not sent to the broadcast address we
* return a reply. */
if( ucRcvAddress != MB_ADDRESS_BROADCAST )
{
if( eException != MB_EX_NONE )
{
/* An exception occured. Build an error frame. */
usLength = 0;
ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
ucMBFrame[usLength++] = eException;
}
if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
{
vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
}
eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );
//插入代码 begin
if( eStatus == MB_ENOERR ) //no error
{
xMBRTUTransmitFSM(); //发送一帧数据中的第一个字节,触发发送完成中断
}
//插入代码 end
}
break;
case EV_FRAME_SENT:
break;
}
}
return MB_ENOERR;
}
举报