高重用ModBus通讯框架
本文章基于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