添加文件
- 获取原始free modbus library(官网)
- 将...\freemodbus-v1.5.0\demo\BARE中的所有文件复制到...\freemodbus-v1.5.0\modbus中,修改demo.c文件名为user_mb_app.c
- 将...\freemodbus-v1.5.0\modbus中的所有.c文件全部添加到项目中
- 在项目路径中添加所有.c、.h文件路径
添加完成后项目结构图:
移植修改
需要修改的文件:
- port.h:补全开关总中断的宏定义、宏定义串口和定时器
#define ENTER_CRITICAL_SECTION( ) __set_PRIMASK(1); //关闭中断
#define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0); //开启中断
- portserial.c:补全串口相关函数(串口中断使能选择、串口初始化、发送1字节、接收1字节、串口中断服务函数)
- 注意事项:
- 使能中断:发送中断应使用TC而非TXE,否则可能会出现最后一个字节不能成功发送的情况。此外由于是用485发送,所以在使能中断时,应同时转换485收发转换引脚。使能中断前,应判断对应的标志位是否为1,为1则清除该标志位。
- 串口初始化:初始化前用USART_DeInit重置寄存器;引脚初始化(GPIO)->串口初始化(USART)->中断初始化(NVIC);参数中的ucPORT和eParity都应该忽略。
- 发送1字节:不用循环等待发送完成,因为已经有发送完成中断了
- 串口中断服务函数:在stm32f0xx_it.c中添加USART3_4_IRQHandler函数,跳转到本文件中的prvvModbusUARTISR函数。
1 /*
2 * FreeModbus Libary: BARE Port
3 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * File: $Id: portserial.c,v 1.1 2006/08/22 21:35:13 wolti Exp $
20 */
21
22 #include "port.h"
23
24 /* ----------------------- Modbus includes ----------------------------------*/
25 #include "mb.h"
26 #include "mbport.h"
27
28 /* ----------------------- static functions ---------------------------------*/
29 static void prvvUARTTxReadyISR( void );
30 static void prvvUARTRxISR( void );
31
32 #define MODBUS_SEND() (GPIO_SetBits(MODBUS_USART_CTRL_PORT, MODBUS_USART_CTRL_PIN))
33 #define MODBUS_RECIEVE() (GPIO_ResetBits(MODBUS_USART_CTRL_PORT, MODBUS_USART_CTRL_PIN))
34
35 /* ----------------------- Start implementation -----------------------------*/
36 void
37 vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
38 {
39 /* If xRXEnable enable serial receive interrupts. If xTxENable enable
40 * transmitter empty interrupts.
41 */
42 if(xRxEnable==TRUE) {
43 MODBUS_RECIEVE();
44 if(USART_GetFlagStatus(MODBUS_USART, USART_FLAG_RXNE) == SET) {
45 USART_ClearFlag(MODBUS_USART, USART_FLAG_RXNE);
46 }
47 USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);
48 } else if(xRxEnable == FALSE) {
49 MODBUS_SEND();
50 USART_ITConfig(MODBUS_USART, USART_IT_RXNE, DISABLE);
51 }
52
53 if(xTxEnable==TRUE) {
54 MODBUS_SEND();
55 if(USART_GetFlagStatus(MODBUS_USART, USART_FLAG_TC) == SET) {
56 USART_ClearFlag(MODBUS_USART, USART_FLAG_TC);
57 }
58 USART_ITConfig(MODBUS_USART, USART_IT_TC, ENABLE);
59 } else if(xTxEnable == FALSE) {
60 MODBUS_RECIEVE();
61 USART_ITConfig(MODBUS_USART, USART_IT_TC, DISABLE);
62 }
63 }
64
65 BOOL
66 xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
67 {
68 /*****************************引脚初始化*************************************/
69 GPIO_InitTypeDef GPIO_InitStructure;
70
71 //时钟使能
72 RCC_AHBPeriphClockCmd(MODBUS_USART_TX_CLK | MODBUS_USART_RX_CLK | MODBUS_USART_CTRL_CLK, ENABLE);
73 MODBUS_USART_CLK_INIT(MODBUS_USART_CLK, ENABLE);
74
75 //复用功能定义
76 GPIO_PinAFConfig(MODBUS_USART_TX_PORT, MODBUS_USART_TX_SOURCE, MODBUS_USART_TX_AF);
77 GPIO_PinAFConfig(MODBUS_USART_RX_PORT, MODBUS_USART_RX_SOURCE, MODBUS_USART_RX_AF);
78
79 //引脚功能定义
80 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
81 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
82 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
83 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3; //50MHz
84
85 //TX
86 GPIO_InitStructure.GPIO_Pin = MODBUS_USART_TX_PIN;
87 GPIO_Init(MODBUS_USART_TX_PORT, &GPIO_InitStructure);
88
89 //RX
90 GPIO_InitStructure.GPIO_Pin = MODBUS_USART_RX_PIN;
91 GPIO_Init(MODBUS_USART_RX_PORT, &GPIO_InitStructure);
92
93 //CTRL
94 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
95 GPIO_InitStructure.GPIO_Pin = MODBUS_USART_CTRL_PIN;
96 GPIO_Init(MODBUS_USART_CTRL_PORT, &GPIO_InitStructure);
97 MODBUS_RECIEVE(); //接收模式
98
99 /*****************************串口初始化*************************************/
100 USART_InitTypeDef USART_InitStructure;
101
102 USART_InitStructure.USART_BaudRate = ulBaudRate;
103 USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
104 USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
105 USART_InitStructure.USART_Parity = USART_Parity_No;
106 USART_InitStructure.USART_StopBits = USART_StopBits_1;
107 USART_InitStructure.USART_WordLength = USART_WordLength_8b;
108
109 USART_Init(MODBUS_USART, &USART_InitStructure);
110 USART_Cmd(MODBUS_USART, ENABLE);
111 vMBPortSerialEnable(FALSE, FALSE);
112
113 /*****************************中断初始化*************************************/
114 NVIC_InitTypeDef NVIC_InitStructure;
115
116 NVIC_InitStructure.NVIC_IRQChannel = USART3_4_IRQn;
117 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
118 NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
119
120 NVIC_Init(&NVIC_InitStructure);
121
122 return TRUE;
123 }
124
125 BOOL
126 xMBPortSerialPutByte( CHAR ucByte )
127 {
128 /* Put a byte in the UARTs transmit buffer. This function is called
129 * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
130 * called. */
131 MODBUS_USART->TDR = ucByte; //此处不用等待发送完毕(TC),因为有发送完成中断
132 return TRUE;
133 }
134
135 BOOL
136 xMBPortSerialGetByte( CHAR * pucByte )
137 {
138 /* Return the byte in the UARTs receive buffer. This function is called
139 * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
140 */
141 *pucByte = MODBUS_USART->RDR;
142 return TRUE;
143 }
144
145 /* Create an interrupt handler for the transmit buffer empty interrupt
146 * (or an equivalent) for your target processor. This function should then
147 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
148 * a new character can be sent. The protocol stack will then call
149 * xMBPortSerialPutByte( ) to send the character.
150 */
151 static void prvvUARTTxReadyISR( void )
152 {
153 pxMBFrameCBTransmitterEmpty( );
154 }
155
156 /* Create an interrupt handler for the receive interrupt for your target
157 * processor. This function should then call pxMBFrameCBByteReceived( ). The
158 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
159 * character.
160 */
161 static void prvvUARTRxISR( void )
162 {
163 pxMBFrameCBByteReceived( );
164 }
165
166 void prvvModbusUARTISR( void )
167 {
168 if(USART_GetITStatus(MODBUS_USART, USART_IT_TC) == SET) {
169 prvvUARTTxReadyISR();
170 USART_ClearITPendingBit(MODBUS_USART, USART_IT_TC);
171 }
172
173 if(USART_GetITStatus(MODBUS_USART, USART_IT_RXNE) == SET) {
174 prvvUARTRxISR();
175 USART_ClearITPendingBit(MODBUS_USART, USART_IT_RXNE);
176 }
177 }
- portserial.c
- porttimer.c:补全定时器相关函数(定时器初始化、定时器使能、定时器关闭使能、定时器中断服务函数)
- 注意事项:
- 初始化:初始化前应用TIM_DeInit函数重置寄存器值;初始化后要清标志位
- 定时器使能:要先关中断、关定时器,然后清标志位、重置计数器,最后开中断、开定时器
- 中断服务函数:要先判断中断标志位,再清标志位、进入操作部分。
1 /*
2 * FreeModbus Libary: BARE Port
3 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * File: $Id: porttimer.c,v 1.1 2006/08/22 21:35:13 wolti Exp $
20 */
21
22 /* ----------------------- Platform includes --------------------------------*/
23 #include "port.h"
24
25 /* ----------------------- Modbus includes ----------------------------------*/
26 #include "mb.h"
27 #include "mbport.h"
28
29 /* ----------------------- static functions ---------------------------------*/
30 static void prvvTIMERExpiredISR( void );
31
32 /* ----------------------- Start implementation -----------------------------*/
33 BOOL
34 xMBPortTimersInit( USHORT usTim1Timerout50us )
35 {
36 /***************************定时器初始化*************************************/
37 TIM_TimeBaseInitTypeDef TIM_InitStructure;
38
39 TIM_DeInit(MODBUS_TIM);
40 MODBUS_TIM_CLK_INIT(MODBUS_TIM_CLK, ENABLE);
41 TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
42 TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
43 TIM_InitStructure.TIM_Period = usTim1Timerout50us - 1;
44 TIM_InitStructure.TIM_Prescaler = 2400 - 1; //48MHz/20kHz=2400
45 TIM_InitStructure.TIM_RepetitionCounter = 0;
46
47 TIM_TimeBaseInit(MODBUS_TIM, &TIM_InitStructure);
48 TIM_ClearFlag(MODBUS_TIM, TIM_FLAG_Update);
49 TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, ENABLE);
50 TIM_Cmd(MODBUS_TIM, DISABLE);
51
52 /*****************************中断初始化*************************************/
53 NVIC_InitTypeDef NVIC_InitStructure;
54
55 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
56 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
57 NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
58
59 NVIC_Init(&NVIC_InitStructure);
60
61 return TRUE;
62 }
63
64
65 inline void
66 vMBPortTimersEnable( )
67 {
68 /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
69 TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, DISABLE);
70 TIM_Cmd(MODBUS_TIM, DISABLE);
71 TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
72 TIM_ClearFlag(MODBUS_TIM, TIM_FLAG_Update);
73 TIM_SetCounter(MODBUS_TIM, 0);
74 TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, ENABLE);
75 TIM_Cmd(MODBUS_TIM, ENABLE);
76 }
77
78 inline void
79 vMBPortTimersDisable( )
80 {
81 /* Disable any pending timers. */
82 TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, DISABLE);
83 TIM_Cmd(MODBUS_TIM, DISABLE);
84 }
85
86 /* Create an ISR which is called whenever the timer has expired. This function
87 * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
88 * the timer has expired.
89 */
90 static void prvvTIMERExpiredISR( void )
91 {
92 ( void )pxMBPortCBTimerExpired( );
93 }
94
95 void prvvModbusTIMISR( void )
96 {
97 if(TIM_GetITStatus(MODBUS_TIM, TIM_IT_Update) == SET) {
98 TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
99 prvvTIMERExpiredISR();
100 }
101 }
- porttimer.c
- user_mb_app.h:定义各模拟寄存器地址
- 注意事项:
- 每个寄存器固定长度16位(2字节)
- 寄存器地址:从机内部定义的寄存器地址,要对寻址用的寄存器地址+1【Modbus标准协议规定】。即:从机内部定义的寄存器地址,必须大于1;对于寄存器1-16,寻址时通过0-15来寻址(如:查询寄存器1的值时,指令中的寄存器地址为00 00)。
- 换句话说,就是:从机程序中定义寄存器地址为1-16时,文档中的寄存器地址要写成0-15。
- user_mb_app.c:补全输入寄存器操作函数、保持寄存器操作函数(操作方式可参见...\freemodbus-v1.5.0\demo\ATSAM3S\demo.c),将main分解为modbus初始化函数和modbus进程函数,添加结构体与模拟寄存器之间的数据交互函数。
- 注意事项:
- eMBInit初始化:仅需更改地址和波特率。
- user_mb_app函数中不要用循环(本身就会被应用到主程序中的while(1)中)
1 /* ----------------------- Modbus includes ----------------------------------*/
2 #include "mb.h"
3 #include "mbport.h"
4 #include "user_mb_app.h"
5
6 /* ----------------------- Defines ------------------------------------------*/
7 #define REG_INPUT_START ((uint16_t)0x0000)
8 #define REG_INPUT_NREGS 25
9 #define REG_HOLD_START ((uint16_t)0x0000)
10 #define REG_HOLD_NREGS 30
11
12 /* ----------------------- Static variables ---------------------------------*/
13 static USHORT usRegInputStart = REG_INPUT_START;
14 static USHORT usRegInputBuf[REG_INPUT_NREGS];
15 static USHORT usRegHoldStart = REG_HOLD_START;
16 static USHORT usRegHoldBuf[REG_HOLD_NREGS];
17 extern uint8_t g_Meter_Data[];
18 extern uint8_t g_Check_Data[];
19 /* ----------------------- Start implementation -----------------------------*/
20 /******************************************************************************
21 ** 函数名称: mb_Modbus_Init
22 ** 功能描述: modbus初始化
23 ** 入口参数: 无
24 ** 返 回 值: 无
25 **
26 ** 作 者: Cage
27 ** 日 期: 2018年3月9日
28 **-----------------------------------------------------------------------------
29 ******************************************************************************/
30 void mb_Modbus_Init(void) {
31
32 uint8_t address = dio_Get_DIP_Value(); //获取拨码开关信息,获得本机地址
33 ( void )eMBInit( MB_RTU, address, 0, 9600, MB_PAR_NONE );
34
35 /* Enable the Modbus Protocol Stack. */
36 ( void )eMBEnable( );
37 }
38
39
40 /******************************************************************************
41 ** 函数名称: user_mb_app
42 ** 功能描述: modbus进程函数
43 ** 入口参数: 无
44 ** 返 回 值: 无
45 **
46 ** 作 者: Cage
47 ** 日 期: 2018年3月13日
48 **-----------------------------------------------------------------------------
49 ******************************************************************************/
50 void user_mb_app( void )
51 {
52
53 ( void )eMBPoll( );
54
55 }
56
57
58 /******************************************************************************
59 ** 函数名称: _mb_Fresh_Input_Reg
60 ** 功能描述: 将计量数据结构体中的数据刷新到Modbus模拟寄存器中
61 ** 入口参数: 无
62 ** 返 回 值: 无
63 **
64 ** 作 者: Cage
65 ** 日 期: 2018年3月13日
66 **-----------------------------------------------------------------------------
67 ******************************************************************************/
68 static void _mb_Fresh_Input_Reg(void) {
69 uint8_t i;
70 USHORT *pmeter_data = (USHORT*)g_Meter_Data;
71 for(i = 0; i<REG_INPUT_NREGS; i++) {
72 usRegInputBuf[i] = *pmeter_data++;
73 }
74 }
75
76
77 /******************************************************************************
78 ** 函数名称: _mb_Fresh_Check_Struct
79 ** 功能描述: 将Modbus模拟寄存器中的数据刷新到校表数据结构体
80 ** 入口参数: 无
81 ** 返 回 值: 无
82 **
83 ** 作 者: Cage
84 ** 日 期: 2018年3月13日
85 **-----------------------------------------------------------------------------
86 ******************************************************************************/
87 static void _mb_Fresh_Check_Struct(void) {
88 uint8_t i;
89 USHORT *pcheck_data = (USHORT*)g_Check_Data;
90 for(i = 0; i<REG_HOLD_NREGS; i++) {
91 *pcheck_data++ = usRegHoldBuf[i];
92 }
93 }
94
95
96 //读输入寄存器
97 eMBErrorCode
98 eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
99 {
100 eMBErrorCode eStatus = MB_ENOERR;
101 int iRegIndex;
102
103 if( ( usAddress >= REG_INPUT_START )
104 && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
105 {
106 _mb_Fresh_Input_Reg(); //将计量数据结构体中的数据刷新到Modbus模拟寄存器中
107 iRegIndex = ( int )( usAddress - usRegInputStart );
108 while( usNRegs > 0 )
109 {
110 *pucRegBuffer++ =
111 ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
112 *pucRegBuffer++ =
113 ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
114 iRegIndex++;
115 usNRegs--;
116 }
117 }
118 else
119 {
120 eStatus = MB_ENOREG;
121 }
122
123 return eStatus;
124 }
125
126 //写保持寄存器--未定义
127 eMBErrorCode
128 eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
129 eMBRegisterMode eMode )
130 {
131 eMBErrorCode eStatus = MB_ENOERR;
132 int iRegIndex;
133
134 if( ( usAddress >= REG_HOLD_START )
135 && ( usAddress + usNRegs <= REG_HOLD_START + REG_HOLD_NREGS ) )
136 {
137 iRegIndex = ( int )( usAddress - usRegHoldStart );
138 if(eMode == MB_REG_WRITE) {
139 while( usNRegs > 0 )
140 {
141 usRegHoldBuf[iRegIndex] = *pucRegBuffer++ << 8;
142 usRegHoldBuf[iRegIndex] |= *pucRegBuffer++;
143 iRegIndex++;
144 usNRegs--;
145 }
146 }
147 _mb_Fresh_Check_Struct(); //将Modbus模拟寄存器中的数据刷新到校表数据结构体
148 }
149 else
150 {
151 eStatus = MB_ENOREG;
152 }
153
154 return eStatus;
155 }
156
157 /*********************************不使用的功能*********************************/
158 eMBErrorCode
159 eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
160 eMBRegisterMode eMode )
161 {
162 return MB_ENOREG;
163 }
164
165 eMBErrorCode
166 eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
167 {
168 return MB_ENOREG;
169 }
- user_mb_app.c
- mb.c:在eMBPoll函数中,EV_EXECUTE状态下,准备好发送后,手动发送第一个字节,启动发送
1 case EV_EXECUTE:
2 ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];
3 eException = MB_EX_ILLEGAL_FUNCTION;
4 for( i = 0; i < MB_FUNC_HANDLERS_MAX; i++ )
5 {
6 /* No more function handlers registered. Abort. */
7 if( xFuncHandlers[i].ucFunctionCode == 0 )
8 {
9 break;
10 }
11 else if( xFuncHandlers[i].ucFunctionCode == ucFunctionCode )
12 {
13 eException = xFuncHandlers[i].pxHandler( ucMBFrame, &usLength );
14 break;
15 }
16 }
17
18 /* If the request was not sent to the broadcast address we
19 * return a reply. */
20 if( ucRcvAddress != MB_ADDRESS_BROADCAST )
21 {
22 if( eException != MB_EX_NONE )
23 {
24 /* An exception occured. Build an error frame. */
25 usLength = 0;
26 ucMBFrame[usLength++] = ( UCHAR )( ucFunctionCode | MB_FUNC_ERROR );
27 ucMBFrame[usLength++] = eException;
28 }
29 if( ( eMBCurrentMode == MB_ASCII ) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS )
30 {
31 vMBPortTimersDelay( MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS );
32 }
33 eStatus = peMBFrameSendCur( ucMBAddress, ucMBFrame, usLength );
34 /*发送数组准备完毕,串口切换到了发送状态,但TC标志位为0,不能自动开始发送*/
35 if( eStatus == MB_ENOERR )
36 {
37 xMBRTUTransmitFSM(); //发送第一个字节,启动发送
38 }
39 }
40 break;
- mb.c节选
调试
- 串口收到数据后,无限进入中断
- 现象:仿真时,一直进入中断服务函数,不作任何处理后跳出;如此反复进入中断服务函数。
- 判断:ORE标志位未清除。RXNEIE中断使能包括RXNE标志位和ORE标志位,中断服务函数中只判断、处理了RXNE标志位。
- 处理:在portserial.c的中断服务函数prvvModbusUARTISR中,加入USART_IT_ORE标志位的判断与处理【注:stm32f072库函数中的USART_IT_ORE标志位定义错误,需要修改】
- 结果:问题解决
- Modbus收到数据后不响应
- 现象:跟踪发现,Modbus协议一直进行到“串口发送”之前都是正常的,可是之后却没有发送
- 原因:上一次发送最后一个字节时,发送中断中清除了TC标志位;切换到发送状态、使能TCIE后,TC标志位为0,无法启动发送。
- 解决方案:
- TXE+TC发送:
- 中断使能函数:切换为发送状态时,使能TXE中断而非TC中断(由TXE启动发送,TXE永不手动清零)
- 中断服务函数:TXE启动发送后,将中断使能由TXE改为TC,之后由TC判断发送完成、清TC标志位
- 手动启动:切换为发送状态后,手动启动发送(发送第一个字节)
- 处理:选择了方案2,问题解决。
仿真跟踪--Modbus数据处理流程(RTU)
- modbus poll主进程(eMBPoll)获取消息状态,执行指令
- 数据接收:串口中断接收数据(数据存入ucRTUBuf数组)->超过3.5us没有收到数据->判断一串数据接收完毕->将“接收完毕”消息添加到消息队列
- 数据处理:
- eMBPoll获取到“接收完毕”消息,令ucMBFrame指针指向ucRTUBuf数组的第1位(命令字),获取地址位和数据长度(不含地址位和校验位的长度),将“执行”消息添加到消息队列
- eMBPoll获取到“执行”消息,通过ucMBFrame中的命令字,进行对应的操作(比如:04--读输入寄存器),将要发回的数据存入ucMBFrame(不含地址位和校验位)
- 将ucMBFrame中的数据加上地址位后计算校验位,将地址位和校验位存入ucRTUBuf数组,将发送状态标志由空闲转为发送,串口状态转为发送
- --至此发送数组(ucRTUBuf)和串口状态都已经准备完毕,但没有发送指令
- 【添加】手动发送发送数组的第一个字节,启动发送
- 数据发送:通过串口将发送数组逐字节发送。