欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

高重用ModBus通讯框架

程序员文章站 2022-03-18 09:48:12
本文章基于STM32 UART创建高重用性的ModBus通讯框架,主要分为CPU_UART底层驱动,通信帧断帧校验逻辑,以及RTU协议帧解析,试图构建一个高重用,跨平台易于移植,易于使用和扩展的ModBus通讯框架。1.CPU外设框架结构typedef struct {Uint16 *p_utbuf;Uint16 *p_urbuf;Uint16 *p_utbuf_used;Uint16 *p_urbuf_used;Uint16 *p_T_break_fram;Uint16 *is_sendi...

本文章基于STM32 UART创建高重用性的ModBus通讯框架,主要分为CPU_UART底层驱动,通信帧断帧校验逻辑,以及RTU协议帧解析,试图构建一个高重用,跨平台易于移植,易于使用和扩展的ModBus通讯框架。

1.CPU外设框架结构

typedef struct {
Uint16 *p_utbuf;
Uint16 *p_urbuf;
Uint16 *p_utbuf_used;
Uint16 *p_urbuf_used;
Uint16 *p_T_break_fram;
Uint16 *is_sending;
void (*SciSendFrame)();
void (*SetSendIO)();
void (*SetRecIO)();
void (*SetBaudrate)(Uint16 a);
void (*SetDateFram)(Uint16 a);
Uint16 (*Send_Over)();
void (*BaseInit)();
}SCI_DateStru;

SCI_DateStru SCI1DateStru;
Uint16 SCI1_RBuff[60];
Uint16 SCI1_RBuffUse = 0;
Uint16 SCI1_TBuff[100];
Uint16 SCI1_TBuffUse = 0;
Uint16 SCI1_Sending = 0;
Uint16 SCI1_BreakFram = 0;
Uint16 SCI1_TLEN = 0;
Uint16 SCI1BufHead = 0;

void SCI1_DateStruDefault()
{
SCI1DateStru.p_utbuf = SCI1_TBuff; //发送缓存
SCI1DateStru.p_urbuf = SCI1_RBuff; //接收缓存
SCI1DateStru.p_utbuf_used = &SCI1_TBuffUse;//发送缓存使用长度
SCI1DateStru.p_urbuf_used = &SCI1_RBuffUse;//接收缓存使用长度
SCI1DateStru.p_T_break_fram = &SCI1_BreakFram;//断帧用于标记系统时间
SCI1DateStru.is_sending = &SCI1_Sending;//判断是否发送中

SCI1DateStru.SciSendFrame = UART4_Trans;//底层发送函数
SCI1DateStru.SetSendIO = UART4_OccuBus;//使用该函数用于占据总线
SCI1DateStru.SetRecIO = UART4_ReleBus;//使用该函数用于释放总线
SCI1DateStru.SetBaudrate = UART4_SetBaud;//设置波特率参数
SCI1DateStru.SetDateFram = UART4_SetDateFram;//设置数据帧形式RTUorTCp
SCI1DateStru.Send_Over = UART4_TOver;//判断底层发送完成
SCI1DateStru.BaseInit = UART4_Init;
}
除此之外还有一个接收函数用于中断接收每一个字节

void UART4_IRQHandler()
{
u8 a = 0;
if(UART4->SR&(0x20))
{
if(SCI1_RBuffUse > 25)SCI1_RBuffUse = 0;
SCI1_RBuff[SCI1_RBuffUse] = UART4->DR;
SCI1_RBuffUse++;
SCI1_BreakFram = P_SysTime->Cnt_ms; //记录接收数据的时间用于断帧处理
}
if(UART4->SR&0x08)
{
a= UART4->DR;
a= UART4->SR;
a= UART4->DR;
a= UART4->CR1;
a= UART4->CR2;
a= UART4->CR3;
}
UART4->SR = 0;
}

接下来就在于实现以上的几个函数注册到结构体上的函数了。

初始化CPU外设函数:

void UART4_Init()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
UartXInit(UART4,(u32 * )SCI1_TBuff,DMA2_Channel5,USART_StopBits_2);
}

void UART4_Trans()
{
uint16_t i=0;
UART4->CR3=1<<7;
if(SCI1_TBuffUse > 80) SCI1_TBuffUse = 0;
for(i = 0;i<SCI1_TBuffUse;i++)
{
buf[i] = SCI1_TBuff[i]&0xff;
}
MYDMA_Enable(DMA2_Channel5,(u32)buf, SCI1_TBuffUse);
SCI1_TBuffUse = 0;
}

void UART4_OccuBus()
{
//在485总线时 置位RTS占据总线
}

void UART4_ReleBus()
{
//在485总线时复位RTS释放总线
}

void UART4_SetBaud(u16 a)
{
//UART4->BRR 修改波特率
}

void UART4_SetDateFram(u16 a)
{
//设置格式 也不用现在
}

u16 UART4_TOver()
{
//判断DMA发完了没有
Uint16 sover = 0;
if(DMA2->ISR & 0x00020000) //finish
{
DMA2->IFCR |= 0x000f0000;
sover = 1;
}
return sover;
}
最终调用以下函数初始化完成底层配置:
SCI1_DateStruDefault();
*(SCI1DateStru.BaseInit)();

本文地址:https://blog.csdn.net/zhaopeng6b/article/details/110198479

相关标签: 通讯 单片机