------------------紧接上一帖----------------
3.从0到1移植freemodbus到stm32平台
移植之前需要准备:STM32基础工程(标准库、HAL库均可);FreeModbus Library V1.6;IDE(这里选择常用的MDK);如果需要添加操作系统的,可将STM32的基础工程改为带有操作系统的基础工程,比如常用的FreeRTOS、RT-Thread等。
01.复制正点原子战舰V3库函基础工程,在此工程上添加FreeModbus,先在工程中新建一个Modbus文件,并将freemodbus v1.6下的modbus文件夹下的文件全部复制过来,再将Demo文件夹下的BARE文件夹复制过来,Modbus文件内的具体内容如下:

02.使用MDK打开基础工程,向工程中添加Modbus分组,将Modbus文件夹下对应的文件都添加到Modbus分组中,主要包括:ascii文件夹的mbascii.c文件;function文件夹下的所有.c文件;rtu和tcp文件夹下的.c文件;mb.c文件。添加后的效果如下:

03.添加对应的头文件,工程需要添加以下文件夹:ascii文件夹、BARE/port文件夹、include文件夹、rtu文件夹、tcp文件夹;总之有.h的文件夹统统包含,具体如下:

04.需要对添加过来部分文件进行补充,以完成Modbus对串口和定时器的需要,第一个需要补充的是portserial.c文件,补充Modbus串口发送中断和接收中断使能函数、Modbus串口初始化函数xMBPortSerialInit,具体代码如下:(这里只实现了232功能,没有加485,其实两个代码几乎一致)
//该函数实现STM32串口发送中断和接收中断使能
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
//STM32串口 接收中断使能
if(xRxEnable==TRUE)
{
//使能接收和接收中断
USART_ITConfig(MODBUS_USART, USART_IT_RXNE, ENABLE);
}
else if(xRxEnable == FALSE)
{
//禁止接收和接收中断
USART_ITConfig(MODBUS_USART, USART_IT_RXNE, DISABLE);
}
//STM32串口 发送中断使能
if(xTxEnable==TRUE)
{
//使能发送完成中断
USART_ITConfig(MODBUS_USART, USART_IT_TXE, ENABLE);
}
else if(xTxEnable == FALSE)
{
//禁止发送完成中断
USART_ITConfig(MODBUS_USART, USART_IT_TXE, DISABLE);
}
} else if(xTxEnable == FALSE)
{
MODBUS_RECIEVE();
USART_ITConfig(MODBUS_USART, USART_IT_TC, DISABLE);
}
}
/*******************************************************************/
//对串口进行初始化由eMBRTUInt函数进行调用
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
(void)ucPORT; //不修改串口
(void)ucDataBits; //不修改数据长度
(void)eParity; //不许改效验格式
/***引脚初始化*************************************/
//时钟使能
RCC_APB2PeriphClockCmd(MODBUS_USART_GPIO_CLK,ENABLE);
RCC_APB1PeriphClockCmd(MODBUS_USART_CLK,ENABLE);
//TX
GPIO_InitStructure.GPIO_Pin = MODBUS_USART_TX_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(MODBUS_USART_TX_PORT, &GPIO_InitStructure);
//RX
GPIO_InitStructure.GPIO_Pin = MODBUS_USART_RX_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(MODBUS_USART_RX_PORT, &GPIO_InitStructure);
/***************串口初始化********************/
USART_InitStructure.USART_BaudRate = ulBaudRate;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(MODBUS_USART, &USART_InitStructure);
USART_Cmd(MODBUS_USART, ENABLE);
/*****************************中断初始化*************************************/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = MODBUS_USART_IRQ ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
return TRUE;
}
/*******************************************************************/ |
05.补充串口发送函数和接收函数、中断处理函数,将STM32串口发送函数和接收函数进行封装,供协议栈使用。代码如下:
//串口发送
BOOL xMBPortSerialPutByte( CHAR ucByte )
{
/* Put a byte in the UARTs transmit buffer. This function is called
* by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
* called. */
USART_SendData(MODBUS_USART, ucByte); //??????
while (USART_GetFlagStatus(MODBUS_USART, USART_FLAG_TC) == RESET){};
return TRUE;
}
/*******************************************************************/
//串口接收
BOOL xMBPortSerialGetByte( CHAR * pucByte )
{
/* Return the byte in the UARTs receive buffer. This function is called
* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
*/
*pucByte = USART_ReceiveData(MODBUS_USART);
return TRUE;
}
/*******************************************************************/
//串口中断处理函数
void MODBUS_USART_IRQHandler(void)
{
if(USART_GetITStatus(MODBUS_USART, USART_IT_TXE) == SET)
{
prvvUARTTxReadyISR();
USART_ClearITPendingBit(MODBUS_USART, USART_IT_TXE);
}
if(USART_GetITStatus(MODBUS_USART, USART_IT_RXNE) == SET)
{
prvvUARTRxISR();
USART_ClearITPendingBit(MODBUS_USART, USART_IT_RXNE);
}
} |
06.第二个需要补充的是porttimerl.c文件,需要补充的就是Modbus定时器初始化函数、Modbus定时器使能和失能函数,以及Modbus 定时器中断函数。这个对于熟悉stm32编程的就是分分中的事。具体代码如下:
//Modbus定时器初始化函数
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
uint16_t PrescalerValue = 0;
//使能定时器4的时钟
RCC_APB1PeriphClockCmd(MODBUS_TIM_CLK, ENABLE); //时钟使能
//定时器4时间配置说明
//HCLK为72MHz,APB1经2分频为36MHz
//TIM4时钟倍频后为72MHz(硬件自动倍频,达到最大)
//TIM4的分频系数为3599,时间基频率为:72 / (1 + Prescaler) = 20KHz,基准为50us
//TIM最大计数值为:usTim1Timerout50u
PrescalerValue = (uint16_t) (SystemCoreClock / 20000) - 1;
//定时器TIM4初始化
TIM_TimeBaseStructure.TIM_Period = usTim1Timerout50us;
TIM_TimeBaseStructure.TIM_Prescaler =PrescalerValue;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(MODBUS_TIM, &TIM_TimeBaseStructure);
//使能预装载
TIM_ARRPreloadConfig(MODBUS_TIM, ENABLE);
//中断优先级NVIC设置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = MODBUS_TIM_IRQ ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//清除溢出中断标志位
TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
//定时器溢出中断关闭
TIM_ITConfig(MODBUS_TIM,TIM_IT_Update,DISABLE);
//失能定时器
TIM_Cmd(MODBUS_TIM, DISABLE);
return TRUE;
}
/*******************************************************************/
//Modbus定时器使能函数
void vMBPortTimersEnable()
{
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, ENABLE);
//设置定时器的初值
TIM_SetCounter(MODBUS_TIM,0x0000);
TIM_Cmd(MODBUS_TIM, ENABLE);
}
/*******************************************************************/
//Modbus定时器失能函数
void vMBPortTimersDisable()
{
/* Disable any pending timers. */
TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
TIM_ITConfig(MODBUS_TIM, TIM_IT_Update, DISABLE);
TIM_SetCounter(MODBUS_TIM,0x0000);
//关闭定时器
TIM_Cmd(MODBUS_TIM, DISABLE);
}
/*******************************************************************/
//Modbus 定时器中断函数
void MODBUS_TIM_IRQHandler( void )
{
if(TIM_GetITStatus(MODBUS_TIM, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(MODBUS_TIM, TIM_IT_Update);
prvvTIMERExpiredISR();
}
} |
07.第三个需要修改的文件是port.h文件,这里文件我放置了一些需要定义的宏,方便后面修改,万一哪一天Modbus、串口2、定时器4打起来了,那我有的忙了,一个伟大程序员总是在考虑谁会打起来,不希望出现劝架的那一天。port.h文件中主要补充进入临界区和退出临界区的宏定义、portserial.c 文件中用到的宏、porttimer.c 文件中用到的宏。(注:是补充哦!不是全部的port.h文件内容,不要傻傻的去全部替换!)
#define ENTER_CRITICAL_SECTION( ) __set_PRIMASK(1); //关闭中断 #define EXIT_CRITICAL_SECTION( ) __set_PRIMASK(0); //开启中断 //Modbus串口 #define MODBUS_USART USART2 #define MODBUS_USART_CLK RCC_APB1Periph_USART2 #define MODBUS_USART_GPIO_CLK RCC_APB2Periph_GPIOA #define MODBUS_USART_TX_PORT GPIOA #define MODBUS_USART_RX_PORT GPIOA #define MODBUS_USART_TX_PIN GPIO_Pin_2 #define MODBUS_USART_RX_PIN GPIO_Pin_3 #define MODBUS_USART_IRQ USART2_IRQn #define MODBUS_USART_IRQHandler USART2_IRQHandler //Modbus定时器 #define MODBUS_TIM TIM4 #define MODBUS_TIM_CLK RCC_APB1Periph_TIM4 #define MODBUS_TIM_IRQ TIM4_IRQn #define MODBUS_TIM_IRQHandler TIM4_IRQHandler |
08.编译之后,果然不出所料,有错误。学会修改错误,是一个程序猿的基本修养。如果连这点基本修养都没有,那只能说,路漫漫其修远兮,还得继续上下左右之求索。在这里我们尝试着去删除porttimer.c 中定时器使能和失能函数前的inline 字样,咦,奇怪的东西消失了呢。

09.当然困难不止一个,就像当前的新冠病毒,一波未平一波又起。再次编译,出现关于assert的错误...........,不要挠头皮,继续想法搞定吧!于是打开搜索引擎,开始找对应的解决办法的过程。

10. 搜索并思考了很久,偶然在CSDN上看到一位博主是这么解决的:在主函数下面添加以下代码(代码在下方),即可解决以上问题,硬着头皮先试一试(因为暂时不相信也没有其它办法),复制过来,直接编译,居然轻松解决,至于原因嘛,就留给各位读者自己思索咯。
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#else
void __aeabi_assert(const char * x1, const char * x2, int x3)
{
}
#endif |
好了各位小伙伴,到这里FreeModbus已经移植到STM32平台上了,下一步就是测试验证移植的FreeModbus是否可以正常通讯。(验证方式和全部代码下载,请见下一帖)
----未完待续---- 等你来哦
我要赚赏金
