04-HAL库UART配置及协议解析设计

原创 小飞哥玩嵌入式 2023-07-04 21:30

关注、星标公众号,直达精彩内容

本节内容介绍

  • 1、HAL库UART 在cubemx中的配置及注意事项;
  • 2、HAL库UART详解与结构介绍;
  • 3、实现简单地UART数据收发;

源码地址:https://gitee.com/MR_Wyf/hal-cubemx-rt-thread/tree/master/hal_cubemx_rtNano_UART

或者关注公众号,后台回复“UART”,获取本章节源码

HAL库UART在cubemx中的配置

串口原理图

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

cubemx中引脚选择预配置

选择PA0、PA1,配置为串口模式,波特率什么的见图示:

开启中断,优先级可以根据自己的需求配置,本次不使用DMA,所以DMA就先不进行配置了

配置是非常简单的,就不多啰嗦了,配置完直接生成代码就OK了

HAL库串口代码详解

cubemx里面配置了一大堆,生成的应用代码主要在初始化中:

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

HAL库数据接收的设计思想是底层配置完成后,暴露给用户的是一组回调函数,用户不用关心底层实现,只需要关注应用层逻辑即可,回调函数是定义为_weak属性的接口,用户可以在应用层实现

/**
  * @brief  Rx Transfer completed callback.
  * @param  huart UART handle.
  * @retval None
  */

__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);

  /* NOTE : This function should not be modified, when the callback is needed,
            the HAL_UART_RxCpltCallback can be implemented in the user file.
   */

}

发送也有对应的callback,我们只需要在callback处理我们的逻辑即可。

串口收发设计

教程不玩虚的,本章节小飞哥从实际应用出发,通过解析协议数据,顺便讲解uart的收发设计。

1、串口接收:

先来看看HAL库串口接收的接口函数,这就是使用库函数的好处,底层实现不用关心,只要会用接口就行了

/**
  * @brief Receive an amount of data in interrupt mode.
  * @note   When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
  *         the received data is handled as a set of u16. In this case, Size must indicate the number
  *         of u16 available through pData.
  * @param huart UART handle.
  * @param pData Pointer to data buffer (u8 or u16 data elements).
  * @param Size  Amount of data elements (u8 or u16) to be received.
  * @retval HAL status
  */

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

如何使用这个接口接收数据呢?

从接口描述可以看到,第1个参数是我们的串口号,第2个参数数我们用于接收数据的buffer,第3个参数是数据长度,即要接受的数据量,这里我们每次仅接收一个数据即进入逻辑处理

每次取一个数据,放到rxdata的变量中

    HAL_UART_Receive_IT(&huart4, &rxdata, 1);

HAL库所有的串口是共享一个回调函数的,那么如何区分数据是来自哪一个串口的?这个逻辑可以在应用实现,区分不同的串口号,根据对应的串口号实现对应的逻辑即可

void HAL_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,启动发送即可

/**
  * @brief Send an amount of data in blocking mode.
  * @note   When UART parity is not enabled (PCE = 0), and Word Length is configured to 9 bits (M1-M0 = 01),
  *         the sent data is handled as a set of u16. In this case, Size must indicate the number
  *         of u16 provided through pData.
  * @note When FIFO mode is enabled, writing a data in the TDR register adds one
  *       data to the TXFIFO. Write operations to the TDR register are performed
  *       when TXFNF flag is set. From hardware perspective, TXFNF flag and
  *       TXE are mapped on the same bit-field.
  * @param huart   UART handle.
  * @param pData   Pointer to data buffer (u8 or u16 data elements).
  * @param Size    Amount of data elements (u8 or u16) to be sent.
  * @param Timeout Timeout duration.
  * @retval HAL status
  */

HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout);

数据接收及协议帧解析设计

数据接收:

基于数据长度和超时时间完成数据帧发送完成的判断:

定时器中断回调设计,实现逻辑为,当收到串口数据时,开始计时,超过100ms无数据进来,认为数据帧结束,同时释放数据接收完成的信号量,接收到接受完成的信号量之后,重置一些数据,为下一次接收做好准备

void HAL_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);
      }
    }

    // }
  }
}

串口回调设计:

串口回调要实现的逻辑比较简单,主要是数据接收、解析:


void HAL_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);
  process_frame(rxdata,CHANNEL_UART4);
  }
}

/协议架构/

/数据头(2字节)+数据长度(2字节,不包含数据头)+功能码+数据+校验码(CRC16-MODBUS)/

我们采用这个协议框架来解析数据,数据解析可以设计成一个简单的状态机,根据每一步决定下一步做什么

比如针对上面的协议,我们就可以分几步设计:

  • 1、解析数据头1;
  • 2、解析数据头2;
  • 3、解析数据长度;
  • 4、接收数据;
  • 5、校验数据CRC;
  • 6、调用命令回调函数;

把握好这个步骤,设计其实非常简单

先来定义一个简单的枚举,表示每一个状态:

typedef enum
{
    STATUS_HEAD1 = 0,
    STATUS_HEAD2,
    STATUS_LEN,
    STATUS_HANDLE_PROCESS
} frame_status_e;

然后封装数据解析函数:

/*协议架构*/

/**数据头(1字节)+数据长度(2字节,不包含数据头)+功能码+数据+校验码(CRC16-MODBUS)**/

#define PROTOCOL_HEAD1 0x5A
#define PROTOCOL_HEAD2 0xA5

int process_frame(const uint8_t data, const uint8_t channel)
{
    uint16_t crc = 0;
    uint16_t len = 0;

    static frame_status_e frame_status;
    static uint16_t index = 0;

    /*timeout reset the receive status*/
    if (RT_EOK == rt_sem_take(sem_uart_timeout, RT_WAITING_NO))
    {
        index = 0;
        frame_status = STATUS_HEAD1;
    }
    switch (frame_status)
    {
    case STATUS_HEAD1:
        if (data == PROTOCOL_HEAD1)
        {
            frame_status = STATUS_HEAD2;
            buffer[index++] = data;
        }
        else
        {
            frame_status = STATUS_HEAD1;
            index = 0;
        }
        break;
    case STATUS_HEAD2:
        if (data == PROTOCOL_HEAD2)
        {
            frame_status = STATUS_LEN;
            buffer[index++] = data;
        }
        else
        {
            frame_status = STATUS_HEAD1;
            index = 0;
        }
        break;
    case STATUS_LEN:
        if (data >= 0 && data <= MAX_DATA_LEN)
        {
            frame_status = STATUS_HANDLE_PROCESS;
            buffer[index++] = data;
        }
        else
        {
            frame_status = STATUS_HEAD1;
            index = 0;
        }
        break;
    case STATUS_HANDLE_PROCESS:
        buffer[index++] = data;
        len = buffer[LEN_POS];
        if (index - 3 == len)
        {
            crc = embedded_mbcrc16(buffer, index - 2);
            if (crc == (buffer[index - 1] | buffer[index - 2] << 8))
            {
                call_reg_cb(buffer, index, channel, buffer[CMD_POS]);
            }
            index = 0;
            frame_status = STATUS_HEAD1;
        }

        break;

    default:
        frame_status = STATUS_HEAD1;
        index = 0;
    }
}

对用的功能函数:

我们采用 attribute at机制的方式,将我们的回调函数注册进去:

typedef void (*uart_dispatcher_func_t)(const uint32_t, const uint8_t *, const uint32_t);
typedef struct uart_dispatcher_item
{
    union
    {
        struct
        {
            uint8_t channel;
            uint8_t cmd_id;
        };

        uint32_t magic_number;
    };

    uart_dispatcher_func_t function;

} uart_dispatcher_item_t;

#define UART_DISPATCHER_CALLBACK_REGISTER(ch, id, fn) static const uart_dispatcher_item_t uart_dis_table_##ch##_##id                         \
                                                   __attribute__((section("uart_dispatcher_table"), __used__, aligned(sizeof(void *)))) 
= \
                                                          {.channel = ch, .cmd_id = id, .function = fn}
int call_reg_cb(uint8_t *frame, uint8_t data_len, int channel, uint8_t cmd_id);

回调函数:

这样设计可以把驱动层,协议解析层和应用层完全分开,用户只需要注册相关的命令,实现回调即可,完全不用关心底层实现

void dispatcher_on_02_callback(const uint32_t channel, const uint8_t *data, const uint32_t data_len)
{
 const char *str = "func02 is running\r\n";
 uart_write((uint8_t *)str, rt_strlen(str), 100);
 rt_kprintf("func02 is running\r\n");
}
UART_DISPATCHER_CALLBACK_REGISTER(10x02, dispatcher_on_02_callback);

void dispatcher_on_03_callback(const uint32_t channel, const uint8_t *data, const uint32_t data_len)
{
 const char *str = "func03 is running\r\n";
 uart_write((uint8_t *)str, rt_strlen(str), 100);
 rt_kprintf("func03 is running\r\n");
}
UART_DISPATCHER_CALLBACK_REGISTER(10x03, dispatcher_on_03_callback);

void dispatcher_on_04_callback(const uint32_t channel, const uint8_t *data, const uint32_t data_len)
{
 const char *str = "func04 is running\r\n";
 uart_write((uint8_t *)str, rt_strlen(str), 100);
 rt_kprintf("func04 is running\r\n");
}
UART_DISPATCHER_CALLBACK_REGISTER(10x04, dispatcher_on_04_callback);

void dispatcher_on_05_callback(const uint32_t channel, const uint8_t *data, const uint32_t data_len)
{
 rt_kprintf("func05 is running\r\n");
}
UART_DISPATCHER_CALLBACK_REGISTER(10x05, dispatcher_on_05_callback);

void dispatcher_on_06_callback(const uint32_t channel, const uint8_t *data, const uint32_t data_len)
{
 rt_kprintf("func06 is running\r\n");
}
UART_DISPATCHER_CALLBACK_REGISTER(10x06, dispatcher_on_06_callback);

测试效果

通过上面的回调函数注册,我们来测试下是不是达到预期情况:

测试是完全OK的,本次的教程到这里就结束了,代码会上传到gitee上,里面有很多不错的设计,小伙伴们可以自行下载下来看看

彩蛋:目前固件是支持MODBUS的,对MODBUS感兴趣的小伙伴也可以下载下来看看,目前支持了03、06、16功能码,也欢迎小伙伴们提交代码进来,一起学习!!!


小飞哥玩嵌入式 分享嵌入式开发相关知识,喜欢DIY分享
评论 (0)
  • 引言:语音交互的智能化跃迁在全球化与智能化深度融合的今天,语音交互设备的应用场景已从单一提示功能向多语言支持、情感化表达及AI深度交互演进。传统离线语音方案受限于语种单一、存储容量不足等问题,而纯在线方案又依赖网络稳定性,难以满足复杂场景需求。WT3000A离在线TTS方案,通过“本地+云端”双引擎驱动,集成16国语种、7种方言切换、AI大模型对话扩展等创新功能,重新定义语音提示器的边界,为智能硬件开发者提供更灵活、更具竞争力的语音交互解决方案。一、方案核心亮点离在线双模融合,场景全覆盖离线模式
    广州唯创电子 2025-04-25 09:14 41浏览
  • 引言在智能语音技术飞速发展的今天,语音交互已成为消费电子、智能家居、工业控制等领域的标配功能。传统的ISD系列录音芯片虽应用广泛,但其高成本与功能局限性逐渐难以满足市场对高性价比、高灵活性的需求。推出的WT2000P录音语音芯片,凭借其卓越性能、低功耗设计及高度可定制化特性,成为ISD系列芯片的理想替代方案,助力开发者突破产品创新瓶颈。一、WT2000P产品概述WT2000P是一款专为嵌入式语音场景设计的多功能录音芯片,采用ESOP8封装,体积小巧(尺寸仅4.9mm×3.9mm),集成度高,支持
    广州唯创电子 2025-04-25 08:44 28浏览
  •   通用装备论证与评估系统平台解析   北京华盛恒辉通用装备论证与评估系统平台是服务军事装备全生命周期管理的综合性信息化平台,通过科学化、系统化手段,实现装备需求论证、效能分析等核心功能,提升装备建设效益。   应用案例   目前,已有多个通用装备论证与评估系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润通用装备论证与评估系统。这些成功案例为通用装备论证与评估系统的推广和应用提供了有力支持。   一、系统分层架构   (一)数据层   整合装备性能、作战、试验等多源异
    华盛恒辉l58ll334744 2025-04-24 16:14 137浏览
  •   有效样本分析决策系统平台全面解析   一、引言   北京华盛恒辉有效样本分析决策系统在当今数据驱动的时代,企业、科研机构等面临着海量数据的处理与分析挑战。有效样本分析决策系统平台应运而生,它通过对样本数据的精准分析,为决策提供有力支持,成为提升决策质量和效率的关键工具。   应用案例   目前,已有多个有效样本分析决策系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润有效样本分析决策系统。这些成功案例为有效样本分析决策系统的推广和应用提供了有力支持。   二、平台概述
    华盛恒辉l58ll334744 2025-04-24 11:13 114浏览
  •   高海拔区域勤务与装备保障调度系统平台解析   北京华盛恒辉高海拔区域勤务与装备保障调度系统平台专为高海拔特殊地理环境打造,致力于攻克装备适应、人员健康保障、物资运输及应急响应等难题。以下从核心功能、技术特点、应用场景及发展趋势展开全面解读。   应用案例   目前,已有多个高海拔区域勤务与装备保障调度系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润高海拔区域勤务与装备保障调度系统。这些成功案例为高海拔区域勤务与装备保障调度系统的推广和应用提供了有力支持。   一、核心
    华盛恒辉l58ll334744 2025-04-24 10:13 120浏览
  •   海上训练与保障调度指挥平台系统解析   北京华盛恒辉海上训练与保障调度指挥平台系统是现代海上作战训练的核心枢纽,融合信息技术、GIS、大数据及 AI 等前沿技术,旨在实现海上训练高效组织、作战保障科学决策。以下从架构功能、应用场景、系统优势及发展挑战展开解读。   应用案例   目前,已有多个海上训练与保障调度指挥平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润海上训练与保障调度指挥平台。这些成功案例为海上训练与保障调度指挥平台的推广和应用提供了有力支持。   一
    华盛恒辉l58ll334744 2025-04-24 15:26 130浏览
  • 最近,途虎养车发布的2024年财报数据,可谓相当吸睛。全年营收达到147.59亿元,同比增长8.5%,这个数字直观地展现了途虎在市场上的强大吸金能力,在行业里稳稳占据前列。利润方面同样出色,毛利37.46亿元,毛利率提升0.7个百分点至25.4%;经调整净利润6.24亿元,同比增长 29.7%,经营利润同比更是增长104%至3.31亿元,盈利能力显著增强,这样的利润增长幅度,在同行业中十分亮眼。在用户规模上,途虎养车同样成绩斐然。累计注册用户近1.4亿,同比增长20.4%,交易用户数达2410万
    用户1742991715177 2025-04-24 19:12 31浏览
  • 为通过金融手段积极推进全球绿色发展,国际金融论坛(IFF)于2020年创立了“IFF全球绿色金融奖”,旨在对全球绿色金融领域取得突出成绩的机构及创新性的解决方案进行表彰和奖励。该奖项依托IFF“高层次、高水平、国际化”一流智库资源优势,积极促进绿色金融领域的国际交流合作和创新实践,助力联合国可持续发展目标的实现。“IFF全球绿色金融奖”重点关注和鼓励那些促进经济增长模式转型、防治环境污染、应对气候变化,以及致力于提高能效水平、强化节能减排实效的绿色金融创新解决方案。该奖项面向全球,是对政策创新、
    华尔街科技眼 2025-04-24 20:43 20浏览
  • 2025-4-25全球信息报告出版商Global Info Research(环洋市场咨询)发布了【2025年全球市场高介电常数材料总体规模、主要生产商、主要地区、产品和应用细分研究报告】,报告主要调研全球高介电常数材料总体规模、主要地区规模、主要生产商规模和份额、产品分类规模、下游主要应用规模以及未来发展前景预测。统计维度包括销量、价格、收入,和市场份额。同时也重点分析全球市场主要厂商(品牌)产品特点、产品规格、价格、销量、销售收入及发展动态。历史数据为2020至2024年,预测数据为2025
    用户1745398400862 2025-04-25 08:48 40浏览
  •   电磁频谱数据综合管理平台系统解析   一、系统定义与目标   北京华盛恒辉电磁频谱数据综合管理平台融合无线传感器、软件定义电台等前沿技术,是实现无线电频谱资源全流程管理的复杂系统。其核心目标包括:优化频谱资源配置,满足多元通信需求;运用动态管理与频谱共享技术,提升资源利用效率;强化频谱安全监管,杜绝非法占用与干扰;为电子战提供频谱监测分析支持,辅助作战决策。   应用案例   目前,已有多个电磁频谱数据综合管理平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润电磁频谱数
    华盛恒辉l58ll334744 2025-04-23 16:27 212浏览
  •   陆地装备体系论证与评估综合平台系统解析   北京华盛恒辉陆地装备体系论证与评估综合平台系统是契合现代军事需求而生的专业系统,借助科学化、智能化手段,实现对陆地装备体系的全方位论证与评估,为军事决策和装备发展提供关键支撑。以下从功能、技术、应用及展望展开分析。   应用案例   目前,已有多个陆地装备体系论证与评估综合平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润陆地装备体系论证与评估综合平台。这些成功案例为陆地装备体系论证与评估综合平台的推广和应用提供了有力支持。
    华盛恒辉l58ll334744 2025-04-24 10:53 125浏览
  • 随着轻薄笔记本的普及,再加上电竞玩家对于高画质音视频体验的需求日益高涨,如何让轻薄笔记本在兼顾轻便携带性的同时,还能提供足以支持3A(AAA/Triple-A game)大作的良好运算性能,便成为各家品牌急欲突破的共同难题。然而,对于主打轻巧便携的轻薄笔记本而言,若要内置独立显卡,势必要先突破空间受限的瓶颈,同时还需解决散热问题,确实难以兼顾两全!对此,“Thunderbolt”与“OCuLink”这两项技术应运而生。用户可以通过这两种传输接口,再搭配外接显卡盒(eGPU)及高性能显卡(如NVI
    百佳泰测试实验室 2025-04-24 17:56 32浏览
  •   航空兵训练与战术对抗仿真平台系统解析   北京华盛恒辉航空兵训练与战术对抗仿真平台系统是现代军事训练的关键工具,借助计算机技术构建虚拟战场,支持多兵种协同作战模拟,为军事决策、训练及装备研发提供科学依据。   应用案例   目前,已有多个航空兵训练与战术对抗仿真平台在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润航空兵训练与战术对抗仿真平台。这些成功案例为航空兵训练与战术对抗仿真平台的推广和应用提供了有力支持。   一、系统架构与核心功能   系统由模拟器、计算机兵力生
    华盛恒辉l58ll334744 2025-04-24 16:34 148浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦