哈尔滨有哪些做网站的公司,周宁县建设局网站,信息如何优化上百度首页,单页竞价网站说起通信#xff0c;首先想到的肯定是串口#xff0c;日常中232和485的使用比比皆是#xff0c;数据的发送、接收是串口通信最基础的内容。这篇文章主要讨论串口接收数据的断帧操作。 空闲中断断帧 一些mcu#xff08;如#xff1a;stm32f103#xff09;在出厂时就已经在…说起通信首先想到的肯定是串口日常中232和485的使用比比皆是数据的发送、接收是串口通信最基础的内容。这篇文章主要讨论串口接收数据的断帧操作。 空闲中断断帧 一些mcu如stm32f103在出厂时就已经在串口中封装好了一种中断——空闲帧中断用户可以通过获取该中断标志位来判断数据是否接收完成中断标志在中断服务函数中获取使用起来相对简单。 例程中当接收完成标志 Lora_RecvData.Rx_over 为1时就可以获取 uart4 接收到的一帧数据该数据存放在 Lora_RecvData.RxBuf 中。 超时断帧 空闲帧中断的使用固然方便但是并不是每个mcu都有这种中断存在只有个别高端mcu才有那么这个时候就可以考虑使用超时断帧了。
Modbus协议中规定一帧数据的结束标志为3.5个字符时长那么同样的可以把这种断帧方式类比到串口的接收上这种方法需要搭配定时器使用。
其作用原理就是串口进一次接收中断就打开定时器超时中断同时装载值清零具体的装载值可以自行定义只要触发了定时器的超时中断说明在用户规定的时间间隔内串口接收中断里没有新的数据进来可以认为数据接收完成。 uint16_t Time3_CntValue 0;//计数器初值
/*******************************************************************************
TIM3中断服务函数 ******************************************************************************/ void Tim3_IRQHandler(void) { if(TRUE Tim3_GetIntFlag(Tim3UevIrq)) { Tim3_M0_Stop(); //关闭定时器3 Uart0_Rec_Count 0;//接收计数清零 Uart0_Rec_Flag 1; //接收完成标志 Tim3_ClearIntFlag(Tim3UevIrq); //清除定时器中断 } }
void Time3_Init(uint16_t Frame_Spacing) { uint16_t u16ArrValue;//自动重载值 uint32_t u32PclkValue;//PCLK频率
stc_tim3_mode0_cfg_t stcTim3BaseCfg;//结构体初始化清零
DDL_ZERO_STRUCT(stcTim3BaseCfg);Sysctrl_SetPeripheralGate(SysctrlPeripheralTim3, TRUE); //Base Timer外设时钟使能stcTim3BaseCfg.enWorkMode Tim3WorkMode0; //定时器模式
stcTim3BaseCfg.enCT Tim3Timer; //定时器功能计数时钟为内部PCLK
stcTim3BaseCfg.enPRS Tim3PCLKDiv1; //不分频
stcTim3BaseCfg.enCntMode Tim316bitArrMode; //自动重载16位计数器/定时器
stcTim3BaseCfg.bEnTog FALSE;
stcTim3BaseCfg.bEnGate FALSE;
stcTim3BaseCfg.enGateP Tim3GatePositive;Tim3_Mode0_Init(stcTim3BaseCfg); //TIM3 的模式0功能初始化u32PclkValue Sysctrl_GetPClkFreq(); //获取Pclk的值//u16ArrValue 65535-(u32PclkValue/1000); //1ms测试 u16ArrValue 65536 - (uint16_t)((float)(Frame_Spacing10)/RS485_BAUDRATEu32PclkValue);//根据帧间隔计算超时时间 Time3_CntValue u16ArrValue; //计数初值 Tim3_M0_ARRSet(u16ArrValue); //设置重载值 Tim3_M0_Cnt16Set(u16ArrValue); //设置计数初值
Tim3_ClearIntFlag(Tim3UevIrq); //清中断标志
Tim3_Mode0_EnableIrq(); //使能TIM3中断(模式0时只有一个中断)
EnableNvic(TIM3_IRQn, IrqLevel3, TRUE); //TIM3 开中断 }
/**此处省略串口初始化部分/ //串口0中断服务函数 void Uart0_IRQHandler(void) { uint8_t rec_data0;
if(Uart_GetStatus(M0P_UART0, UartRC))
{Uart_ClrStatus(M0P_UART0, UartRC); rec_data Uart_ReceiveData(M0P_UART0); if(Uart0_Rec_CountUART0_BUFF_LENGTH)//帧长度{Uart0_Rec_Buffer[Uart0_Rec_Count] rec_data; }Tim3_M0_Cnt16Set(Time3_CntValue);//设置计数初值 Tim3_M0_Run(); //开启定时器3 超时即认为一帧接收完成
}}
例程所用的是华大的hc32l130系列mcu其它类型的mcu也可以参考这种写法。其中超时时间的计算尤其要注意数据类型的问题u16ArrValue 65536 - (uint16_t)((float)(Frame_Spacing * 10)/RS485_BAUDRATE * u32PclkValue);其中Frame_Spacing为用户设置的字符个数uart模式为一个“181”共10bits。
状态机断帧 状态机状态机又是状态机没办法谁让它使用起来方便呢其实这种方法我用的也不多但是状态机的思想还是要有的很多逻辑用状态机梳理起来会更加的清晰。
相对于超时断帧状态机断帧的方法节约了一个定时器资源一般的mcu外设资源是足够的但是做一些资源冗余也未尝不是一件好事万一呢对吧。 //状态机断帧 void UART_IRQHandler(void) //作为485的接收中断 { uint8_t count 0; unsigned char lRecDat 0;
if(/*触发接收中断标志*/)
{//清中断状态位rec_timeout 5;if((count 0)) //接收数据头长度可以自定义{RUart0485_DataC[count] /*串口接收到的数据*/;gRecStartFlag 1;return;}if(gRecStartFlag 1){RUart0485_DataC[count] /*串口接收到的数据*/;if(count MAXLEN) //一帧数据接收完成{count0;gRecStartFlag 0;if(RUart0485_DataC[MAXLEN]CRC16(RUart0485_DataC,MAXLEN)){memcpy(gRecFinshData,RUart0485_DataC,13);gRcvFlag 1; //接收完成标志位 }} }return;
}
return ;}
这种做法适合用在一直有数据接收的场合每次接收完一帧有效数据后就把数据放到缓冲区中去解析同时还不影响下一帧数据的接收。
整个接收状态分为两个状态——接收数据头和接收数据块如果一帧数据存在多个部分的话还可以在此基础上再增加几种状态这样不仅可以提高数据接收的实时性还能够随时看到数据接收到哪一部分还是比较实用的。 状态机FIFO断帧 如果串口有大量数据要接收同时又没有空闲帧中断你会怎么做
没错就是FIFO当时并没有回答上来因为没用过说白了就是开辟一个缓冲区每次接收到的数据都放到这个缓冲区里同时记录数据在缓冲区中的位置当数据到达要求的长度的时候再把数据取出来然后放到状态机中去解析。 当然FIFO的使用场合有很多很多数据处理都可以用FIFO去做有兴趣的可以多去了解一下。 /**串口初始化省略华大mcu hc32l130/ void Uart1_IRQHandler(void) { uint8_t data; if(Uart_GetStatus(M0P_UART1, UartRC)) //UART0数据接收 { Uart_ClrStatus(M0P_UART1, UartRC); //清中断状态位 data Uart_ReceiveData(M0P_UART1); //接收数据字节 comFIFO(data,1); } }
/FIFO*/ volatile uint8_t fifodata[FIFOLEN],fifoempty,fifofull; volatile uint8_t uart_datatemp0;
uint8_t comFIFO(uint8_t *data,uint8_t cmd) { static uint8_t rpos0; //当前写的位置 position 0–99 static uint8_t wpos0; //当前读的位置
if(cmd0) //写数据
{if(fifoempty!0) //1 表示有数据 不为空0表示空{*datafifodata[rpos];fifofull0;rpos;if(rposFIFOLEN) rpos0;if(rposwpos) fifoempty0;return 0x01;} elsereturn 0x00;}
else if(cmd1) //读数据
{if(fifofull0){fifodata[wpos]*data;fifoempty1;wpos;if(wposFIFOLEN) wpos0;if(wposrpos) fifofull1;return 0x01;} elsereturn 0x00;
}
return 0x02;}
/*状态机处理/ void LoopFor485ReadCom(void) { uint8_t data;
while(comFIFO(data,0)0x01)
{if(rEadFlagSAVE_HEADER_STATUS) //读取头{if(dataHeader_H){buffread[0]data;continue;}if(dataHeader_L){buffread[1]data;if(buffread[0]Header_H){rEadFlagSAVE_DATA_STATUS;}} else{memset(buffread,0,Length_Data);}} else if(rEadFlagSAVE_DATA_STATUS) //读取数据{buffread[i4852]data;i485;if(i485(Length_Data-2)) //数据帧除去头{unsigned short crc16CRC16_MODBUS(buffread,Length_Data-2);if((buffread[Length_Data-2](crc168))(buffread[Length_Data-1](crc160xff))){rEadFlagSAVE_OVER_STATUS;memcpy(cmddata,buffread,Length_Data); //拷贝Length_Struct个字节完整的结构体} else{rEadFlagSAVE_HEADER_STATUS;}memset(buffread,0,Length_Data);i4850;break;}}
}}