当前位置:首页 > 单片机 > 单片机
[导读]这几天因为工作需要,移植了modbus RTU到STM32来,之前也听说过modbus,但是没有深入了解过,还以为会像usb 那样复杂的,经过这几天的折腾,发现真的太简单了。为了防止过段时间又忘记了怎么移植,在这里把移植过程记

 

这几天因为工作需要,移植了modbus RTU到STM32来,之前也听说过modbus,但是没有深入了解过,还以为会像usb 那样复杂的,经过这几天的折腾,发现真的太简单了。为了防止过段时间又忘记了怎么移植,在这里把移植过程记录下来,也为了方便初次接触modbus的人。

废话少说,首先去下载源码,我下载的是freemodbus-v1.5.0,解压后如图所示:

在demo文件夹中有很多移植好的例子,但是没有STM32的,不要紧,我们参考已有的例子来操作就可以了。在demo文件夹下新建一个文件夹,命名为STM32,将BARE文件夹里的文件全部复制过来。
然后,我们建立一个STM32的工程,我用的是mdk4.72,关于怎么建立工程我就不啰嗦了,在工程里添加modbus 和 port两个文件夹,并在文件夹里添加需要的文件,这些modbus的是在modbus文件夹下面,port的是在刚才新建的那个stm32文件夹下,port.c是我从别的地方弄过来的,里面就是一个开中断和一个关中断的函数,可以不要,我的工程如图:

乍一看,也有十几个文件,其实这些文件内容不多,很好理解,而且需要修改的只有port文件夹下的portserial.c 和 porttimer.c 。这两个文件里面有几个空函数,我们看名字就知道这些函数的作用了。
portserial.c如下:

[C]纯文本查看复制代码

?

010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/#include "mb.h"#include "mbport.h"/* ----------------------- static functions ---------------------------------*/staticvoidprvvUARTTxReadyISR(void);staticvoidprvvUARTRxISR(void);/* ----------------------- Start implementation -----------------------------*/voidvMBPortSerialEnable(BOOLxRxEnable,BOOLxTxEnable ){/* If xRXEnable enable serial receive interrupts. If xTxENable enable* transmitter empty interrupts.*/}BOOLxMBPortSerialInit(UCHARucPORT,ULONGulBaudRate,UCHARucDataBits, eMBParity eParity ){returnFALSE;}BOOLxMBPortSerialPutByte(CHARucByte ){/* Put a byte in the UARTs transmit buffer. This function is called* by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been* called. */returnTRUE;}BOOLxMBPortSerialGetByte(CHAR* pucByte ){/* Return the byte in the UARTs receive buffer. This function is called* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.*/returnTRUE;}/* Create an interrupt handler for the transmit buffer empty interrupt* (or an equivalent) for your target processor. This function should then* call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that* a new character can be sent. The protocol stack will then call* xMBPortSerialPutByte( ) to send the character.*/staticvoidprvvUARTTxReadyISR(void){pxMBFrameCBTransmitterEmpty( );}/* Create an interrupt handler for the receive interrupt for your target* processor. This function should then call pxMBFrameCBByteReceived( ). The* protocol stack will then call xMBPortSerialGetByte( ) to retrieve the* character.*/staticvoidprvvUARTRxISR(void){pxMBFrameCBByteReceived( );}

根据注释,可以知道vMBPortSerialEnable是串口发送和接收中断的控制的,包括发送中断和接收中断,在这里,我们用的是RXNE 和 TXE中断,代码如下:

[C]纯文本查看复制代码

?

0102030405060708091011121314151617181920voidvMBPortSerialEnable(BOOLxRxEnable,BOOLxTxEnable ){if(TRUE==xRxEnable){USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);}else{USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);}if(TRUE==xTxEnable){USART_ITConfig(USART1, USART_IT_TXE, ENABLE);}else{USART_ITConfig(USART1, USART_IT_TXE, DISABLE);}}

而xMBPortSerialInit函数显然是串口初始化的了,因为我在usart.c已经有一个串口初始化函数,这里直接调用该初始化函数usart_init(ulBaudRate);同时将return FALSE 改成 return TRUE; 注意这里我们只用了波特率这个参数,其他参数直接忽略,你也可以根据自己需要改一下。
然后xMBPortSerialPutByte 和xMBPortSerialGetByte 分别是发送和接收一个字节数据的函数,这里我直接调用库函数;

[C]纯文本查看复制代码

?

0102030405060708091011121314151617BOOLxMBPortSerialPutByte(CHARucByte ){USART_SendData(USART1, ucByte);while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)/*????·????ê??*/{}returnTRUE;}BOOLxMBPortSerialGetByte(CHAR* pucByte ){*pucByte = USART_ReceiveData(USART1);returnTRUE;}

最后还有两个中断处理函数,把前面的static 去掉,因为我不想把我的串口中断函数放到这个文件。然后我们在stm32f10x_it.c添加串口中断函数,如下:

[C]纯文本查看复制代码

?

0102030405060708091011121314voidUSART1_IRQHandler(void){if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET){prvvUARTRxISR();USART_ClearITPendingBit(USART1, USART_IT_RXNE);}if(USART_GetITStatus(USART1, USART_IT_TXE) == SET){prvvUARTTxReadyISR();// USART_ClearITPendingBit(USART1, USART_IT_TXE);}}

至此,portserial.c处理完毕。
porttimer.c的移植和portserial.c十分相似,但是要特别注意定时器中断的时间长度应该是3.5个字符时间,我这里只是简单粗暴的按照波特率是9600时候计算的。文件很短,直接上代码

[C]纯文本查看复制代码

?

01020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849BOOLxMBPortTimersInit(USHORTusTim1Timerout50us ){TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_DeInit(TIM2);#if 0TIM_TimeBaseStructure.TIM_Period = 0x7E54;//CLK==24MHz ((1000000000/9600)*11*3.5)/(1000/24) == 0x7e54TIM_TimeBaseStructure.TIM_Prescaler = 0x3;#endif// ?????????¤·?????????7200/72M = 0.0001,????100us????????1//10us x 50 = 5ms,??5ms????????TIM_TimeBaseStructure.TIM_Period = 50;TIM_TimeBaseStructure.TIM_Prescaler = (7200 - 1);TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);// TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);TIM_Cmd(TIM2, ENABLE);returnTRUE;}voidvMBPortTimersEnable( ){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);TIM_SetCounter(TIM2, 0);//TIM_Cmd(TIM2, ENABLE);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);}voidvMBPortTimersDisable( ){TIM_SetCounter(TIM2, 0);//TIM_Cmd(TIM2, DISABLE);TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);}voidTIMERExpiredISR(void){(void)pxMBPortCBTimerExpired();}

同样,在stm32f10x_it.c添加定时器中断处理函数,

[C]纯文本查看复制代码

?

12345voidTIM2_IRQHandler(void){TIMERExpiredISR();TIM_ClearITPendingBit(TIM2, TIM_IT_Update);}


然后,我们还需要自己写四个回调函数,分别是读输入寄存器函数、读写保持寄存器函数、读写线圈函数和读离散寄存器函数,一般只用读写保持寄存器函数即可,具体怎么实现可以参考demo文件夹里面众多的demo.c文件。

 

 

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
换一批
延伸阅读

STM32系列微控制器因其高性能和丰富的外设接口被广泛应用于各类场景。当涉及USB高速(HS)与全速(FS)接口设计时,开发者常因对信号完整性、ESD防护及电源管理的理解不足而陷入调试困境。本文将从原理出发,结合实际案例...

关键字: STM32 USB

在工业物联网、汽车电子等高频应用场景中,STM32的SPI、USB、Ethernet等高速外设常因信号失真或电磁干扰(EMI)导致通信失败。本文基于STM32H7系列的实际工程案例,从物理层设计到系统级优化,提炼出10个...

关键字: STM32 EMI

STM32通过SDIO接口驱动SD卡时,信号完整性问题已成为制约系统稳定性的关键因素。当SDIO工作频率突破25MHz后,传输线效应主导的信号畸变会导致数据采样错误、读写失败甚至系统崩溃。眼图分析作为评估数字信号质量的核...

关键字: STM32 SDIO

STM32的内存管理效率直接影响系统性能,以某智能电表项目为例,其数据采集模块每秒需处理12000次ADC采样,传统malloc/free机制导致内存碎片率超过40%,系统运行12小时后出现内存分配失败。通过引入ART内...

关键字: STM32 内存加速器

在工业控制、音频处理等实时性要求严苛的场景中,传统单缓冲DMA模式常因数据覆盖导致系统崩溃。以某自动化产线为例,当PLC以115200bps速率接收Modbus RTU指令时,若采用单缓冲模式,CPU处理延迟超过50μs...

关键字: STM32 多线程DMA

以STM32F103为例,当使用USART1以115200bps速率连续接收数据时,若采用传统轮询方式,每接收1字节需至少5条指令(读DR、写内存、增址、判数、跳转),在72MHz主频下耗时约200ns。表面看CPU仍有...

关键字: STM32 DMA

DMA(Direct Memory Access)技术通过硬件自治机制实现高速数据传输,但实际工程中常因内存对齐、缓存一致性、外设同步等问题导致数据错位。本文以STM32为例,结合STM32CubeMonitor工具,解...

关键字: STM32 DMA传输

工业HMI、医疗影像处理等高性能嵌入式场景中,STM32通过FSMC/FMC接口外扩SRAM已成为突破片内资源限制的关键方案。然而,当总线频率突破50MHz时,信号完整性(SI)问题凸显:某智慧园区监控系统采用STM32...

关键字: STM32 FSMC

在嵌入式存储领域,STM32的SDIO接口凭借其硬件加速能力成为高速SD卡通信的核心方案。随着SD卡规格从Class 10向UHS-I/UHS-II演进,传统48MHz时钟配置已无法满足现代应用对带宽的需求。本文通过硬件...

关键字: STM32 SDIO

在工业自动化、高速数据采集和实时控制领域,USB 3.0凭借其5Gbps的理论带宽和全双工通信能力,成为STM32微控制器扩展高速外设的核心接口。然而,其超高速信号(2.5GHz基频)对PCB设计提出严苛要求,需通过差分...

关键字: USB 3.0 STM32
关闭