时间:2023-07-05 09:47
人气:
作者:admin
串口原理图
串口1咱们已经用作rtt的print使用了,所以使用另外一组串口来进行串口的教程,这里一定要注意下,alios的这个板子原理图是有点问题的,标注的是串口3PA2和PA3,实际上小飞哥调了好久,最后万用表量引脚才发现是原理图标注错误,实际上是UART4,PA0和PA1

cubemx中引脚选择预配置
选择PA0、PA1,配置为串口模式,波特率什么的见图示:
开启中断,优先级可以根据自己的需求配置,本次不使用DMA,所以DMA就先不进行配置了

配置是非常简单的,就不多啰嗦了,配置完直接生成代码就OK了
HAL库串口代码详解
cubemx里面配置了一大堆,生成的应用代码主要在初始化中:


关于串口的接口是很多的,本次主要使用3个接口,发送、接收和接收回调

HAL库数据接收的设计思想是底层配置完成后,暴露给用户的是一组回调函数,用户不用关心底层实现,只需要关注应用层逻辑即可,回调函数是定义为_weak属性的接口,用户可以在应用层实现
/**
*@briefRxTransfercompletedcallback.
*@paramhuartUARThandle.
*@retvalNone
*/
__weakvoidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)
{
/*Preventunusedargument(s)compilationwarning*/
UNUSED(huart);
/*NOTE:Thisfunctionshouldnotbemodified,whenthecallbackisneeded,
theHAL_UART_RxCpltCallbackcanbeimplementedintheuserfile.
*/
}
发送也有对应的callback,我们只需要在callback处理我们的逻辑即可。
串口收发设计
教程不玩虚的,本章节小飞哥从实际应用出发,通过解析协议数据,顺便讲解uart的收发设计。
1、串口接收:
先来看看HAL库串口接收的接口函数,这就是使用库函数的好处,底层实现不用关心,只要会用接口就行了
/** *@briefReceiveanamountofdataininterruptmode. *@noteWhenUARTparityisnotenabled(PCE=0),andWordLengthisconfiguredto9bits(M1-M0=01), *thereceiveddataishandledasasetofu16.Inthiscase,Sizemustindicatethenumber *ofu16availablethroughpData. *@paramhuartUARThandle. *@parampDataPointertodatabuffer(u8oru16dataelements). *@paramSizeAmountofdataelements(u8oru16)tobereceived. *@retvalHALstatus */ HAL_StatusTypeDefHAL_UART_Receive_IT(UART_HandleTypeDef*huart,uint8_t*pData,uint16_tSize);
如何使用这个接口接收数据呢?
从接口描述可以看到,第1个参数是我们的串口号,第2个参数数我们用于接收数据的buffer,第3个参数是数据长度,即要接受的数据量,这里我们每次仅接收一个数据即进入逻辑处理
每次取一个数据,放到rxdata的变量中
HAL_UART_Receive_IT(&huart4,&rxdata,1);
HAL库所有的串口是共享一个回调函数的,那么如何区分数据是来自哪一个串口的?这个逻辑可以在应用实现,区分不同的串口号,根据对应的串口号实现对应的逻辑即可
voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)
{
if(huart->Instance==UART4)
{
//rt_sem_release(sem_uart_rec);
embedded_set_uart_rec_flag(RT_TRUE);
embedded_set_uart_timeout_cnt(0);
HAL_UART_Receive_IT(&huart4,&rxdata,1);
mb_process_frame(rxdata,CHANNEL_MODBUS);
}
}
2、数据帧接收完成判断
通讯基本上都是不定长数据的接收,一般对于一个完整的通讯帧来说,是有长度字段的,分以下几种接收完成判断方式
特殊数据格式,比如结束符,像正点原子串口教程的“回车、换行(0x0D,0x0A)”
数据长度,适用已知数据长度的数据帧,根据接收到的数据长度跟数据帧里面的长度是否一致,判断接受是否完成
超时判断,定时器设计一个超时机制,一定时间内没有数据进来即认为数据传输结束
空闲中断,串口是有个空闲中断的,这个实现类似于超时机制
也可以从软件设计实现,比如设计一个队列,取数据即可,队列中没数据即认为数据接受完成
方式有很多,本章节主要使用数据长度和定时器超时两种方式来讲解
3、串口发送
串口发送比较简单,先来看看发送接口函数,类似接收函数,只需要把我们的数据放进发送buffer,启动发送即可
/** *@briefSendanamountofdatainblockingmode. *@noteWhenUARTparityisnotenabled(PCE=0),andWordLengthisconfiguredto9bits(M1-M0=01), *thesentdataishandledasasetofu16.Inthiscase,Sizemustindicatethenumber *ofu16providedthroughpData. *@noteWhenFIFOmodeisenabled,writingadataintheTDRregisteraddsone *datatotheTXFIFO.WriteoperationstotheTDRregisterareperformed *whenTXFNFflagisset.Fromhardwareperspective,TXFNFflagand *TXEaremappedonthesamebit-field. *@paramhuartUARThandle. *@parampDataPointertodatabuffer(u8oru16dataelements). *@paramSizeAmountofdataelements(u8oru16)tobesent. *@paramTimeoutTimeoutduration. *@retvalHALstatus */ HAL_StatusTypeDefHAL_UART_Transmit(UART_HandleTypeDef*huart,constuint8_t*pData,uint16_tSize,uint32_tTimeout);
数据接收及协议帧解析设计
数据接收:
基于数据长度和超时时间完成数据帧发送完成的判断:
定时器中断回调设计,实现逻辑为,当收到串口数据时,开始计时,超过100ms无数据进来,认为数据帧结束,同时释放数据接收完成的信号量,接收到接受完成的信号量之后,重置一些数据,为下一次接收做好准备
voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim)
{
if(htim->Instance==TIM15)
{
//if(RT_EOK==rt_sem_take(sem_uart_rec,RT_WAITING_NO))
//{
if(embedded_get_uart_rec_flag())
{
/*100ms超时无数据接收*/
if(embedded_get_uart_timeout_cnt()>9)
{
embedded_set_uart_rec_flag(RT_FALSE);
rt_sem_release(sem_uart_timeout);
}
}
/
从profibusDP转ModbusTCP,一网打尽转换技巧!