当前位置: 首页 > backend >正文

【星闪】Hi2821 | UART通用异步收发器 + 串口中断收发例程

1. 简介

        UART(Universal Asynchronous Receiver/Transmitter),通用异步收发器,是一种串行、异步、全双工的通信协议,用于设备间的数据传输。一般串口通信的数据包如下图:

        通常由起始位、数据位、校检位和停止位组成。

  • 起始位:1位,用于标志数据包的开始;
  • 数据位:5-9位,包含要发送的数据;
  • 校检位:0-1位,可选无校检、奇校检和偶校检;
  • 停止位:1-2位,标志数据包的结束。

        在配置串口时有一个很重要的参数——波特率,它定义接口收发数据的速率。因为UART是异步通讯,没有时钟线,所以通讯双方需要保持同样的速率才能正常通信。波特率表示的是每秒传输的比特数,常见的有9600bps和115200bps。

2. 硬件配置

        Hi2821一共有3个UART——UART0(UART_L0)、UART1(UART_H0)、UART2(UART_L1)。其中UART0是默认作为系统debug和程序烧录的接口,其余的用户都可配置。

        UART支持流控工作模式,并支持配置RTS。内置 64×8 的 TX FIFO 以及 RX FIFO。支持接收 FIFO 中断、发送 FIFO 中断、接收超时中断、错误中断等中断屏蔽与响应。

3. 例程

3.1 Kconfig

        SDK是默认开启对UART的编译的,仅需要使能串口发送中断即可。

3.2 代码

#include "soc_osal.h"
#include "app_init.h"
#include "common_def.h"
#include "securec.h"
#include "pinctrl.h"
#include "gpio.h"
#include "uart.h"#include <string.h>
#include <stdint.h>#define UART_TX_GPIO 17
#define UART_RX_GPIO 18#define UART_BUS UART_BUS_1
#define UART_BUF_LEN 64static uint8_t rx_buf[UART_BUF_LEN] = {0};static uart_buffer_config_t buf_conf = {.rx_buffer = rx_buf,.rx_buffer_size = UART_BUF_LEN
};static const char* tx_msg = "Hello from hi2821";static void uart_read_int_handler(const void *buffer, uint16_t length, bool error)
{unused(error);if (buffer == NULL || length == 0) {osal_printk("buffer empty or receive error\r\n");return;}osal_printk("receive %d bytes of data: %s\r\n", length, (const char*) buffer);
}static void uart_write_int_handler(const void *buffer, uint32_t length, const void *params)
{unused(params);osal_printk("write %d bytes of data: %s\r\n", length, (const char*) buffer);
}void app_main(void *unused)
{UNUSED(unused);/* 初始化GPIO */uapi_pin_set_ie(UART_RX_GPIO, PIN_IE_1);uapi_pin_set_mode(UART_TX_GPIO, HAL_PIO_UART_H0_TXD);uapi_pin_set_mode(UART_RX_GPIO, HAL_PIO_UART_H0_RXD);/* 初始化UART */uart_attr_t attr = {.baud_rate = 115200,  // 波特率.data_bits = UART_DATA_BIT_8,  // 8位数据位.stop_bits = UART_STOP_BIT_1,  // 1位停止位.parity = UART_PARITY_NONE,  // 无校检.flow_ctrl = UART_FLOW_CTRL_NONE  // 无流控};uart_pin_config_t pin_config = {.tx_pin = UART_TX_GPIO,.rx_pin = UART_RX_GPIO,.cts_pin = PIN_NONE,.rts_pin = PIN_NONE};uapi_uart_deinit(UART_BUS);uapi_uart_init(UART_BUS, &pin_config, &attr, NULL, &buf_conf);uapi_uart_register_rx_callback(UART_BUS, UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE, 1, uart_read_int_handler);while (1) {/* 串口发送 */errcode_t ret = uapi_uart_write_int(UART_BUS, (const uint8_t*) tx_msg, strlen(tx_msg), NULL, uart_write_int_handler);if (ret == ERRCODE_SUCC) {osal_printk("send message succ\r\n");} else {osal_printk("send message failed, err: %08X\r\n", ret);}osal_msleep(2000);}
}

1. 初始化 Pinctrl。

        例程中使用的是 UART1,对应 UART_H0,调 uapi_pin_set_mode 函数使能管脚的复用。如果 Kconfig 里面使能了 CONFIG_PINCTRL_SUPPORT_IE,那么还需要调 uapi_pin_set_ie 使能串口接收管脚的输入功能。

2. 初始化 UART。

        串口的初始化(中断模式)需要三个结构体——uart_attr_t、uart_pin_config_t 和 uart_buffer_config_t,定义如下:

typedef struct uart_attr {uint32_t baud_rate;                 /*!< @if Eng UART baud rate@else UART波特率 @endif */uint8_t data_bits;                  /*!< @if Eng UART data bits, For details see .@else   UART数据位,参考 @ref uart_data_bit_t @endif */uint8_t stop_bits;                  /*!< @if Eng UART stop bits, For details see .@else   UART数据位,参考 @ref uart_stop_bit_t @endif */uint8_t parity;                     /*!< @if Eng UART parity, For details see .@else   UART 奇偶校验位,参考 @ref uart_parity_t @endif */uint8_t flow_ctrl;                  /*!< @if Eng UART flow control, For details see @ref uart_flow_ctrl_t.@else   UART 流控,参考 @ref uart_flow_ctrl_t @endif */
} hal_uart_attr_t;typedef struct {pin_t tx_pin;                   /*!< @if Eng Transmission PIN.@else   发送引脚 @endif */pin_t rx_pin;                   /*!< @if Eng Reception PIN.@else   接收引脚 @endif */pin_t cts_pin;                  /*!< @if Eng Clear to send PIN.@else   发送就绪引脚 @endif */pin_t rts_pin;                  /*!< @if Eng Request to send PIN to use.@else   接收就绪引脚 @endif */
} hal_uart_pin_config_t;typedef struct uart_buffer_config {void *rx_buffer;            /*!< @if Eng Reception buffer pointer.@else   接收数据的buffer指针  @endif */size_t rx_buffer_size;      /*!< @if Eng Reception buffer size in bytes.@else   接收Buffer的长度  @endif */
} uart_buffer_config_t;

        uart_attr_t 结构体用于配置通讯的参数,例程中是配置为常见的 115200bps,8位数据位,无校检,1位停止位

        uart_pin_config_t 结构体用于配置通讯管脚,例程中没有用到流控,所以只需设置发送和接收管脚即可,我这里设置的是 IO17 和 IO18。

        uart_buffer_config_t 结构体用于设置接收缓冲区。

        在初始化 UART 前建议调 uapi_uart_deinit 函数反初始化一下,然后再调 uapi_uart_init 函数初始化 UART。

        对于中断模式下的数据接收,需调 uapi_uart_register_rx_callback 函数去注册接收回调。参数一为 UART 的总线号;参数二为中断回调的触发条件,有以下可选项:

typedef enum uart_rx_condition {/** @if Eng  A call-back will be made if the RX data pauses or there is no more RX buffer space. \n*           So data is no longer being accepted. When registering this condition, a back-log of.*  @else    如果数据接收暂停,或者接收缓存已经满了,就触发数据接收回调,\n*           在接收回调处理过程中接收到的UART数据,会直接丢弃。*  @endif */UART_RX_CONDITION_FULL_OR_IDLE = (UART_RX_CONDITION_MASK_FULL | UART_RX_CONDITION_MASK_IDLE),/** @if Eng A call-back will be made as soon as possible after the specified amount of data is*          received or there is no more RX buffer space. \n*          So data is no longer being accepted. More than the requested data may be*          provided to the call-back if there was a back-log of received data.*  @else   如果接收缓存已满,或者接收的数据量到达指定的数据长度,就触发数据接收回调,\n*          在接收回调处理过程中接收到的UART数据,会直接丢弃。*          如果存在接收数据的积压数据,则可以向回调提供超过请求的数据。 @endif */UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA = (UART_RX_CONDITION_MASK_FULL | UART_RX_CONDITION_MASK_SUFFICIENT_DATA),/** @if Eng A call-back will be made if the RX data buffer is full or*          the specified number of bytes has been received or there is a pause. \n*          So data is no longer being accepted.*  @else   如果接收缓存已满,或者接收的数据量到达指定的数据长度,或者接收数据暂停,就触发数据接收回调,\n*          在接收回调处理过程中接收到的UART数据,会直接丢弃。 @endif */UART_RX_CONDITION_FULL_OR_SUFFICIENT_DATA_OR_IDLE = (UART_RX_CONDITION_MASK_FULL |UART_RX_CONDITION_MASK_SUFFICIENT_DATA |UART_RX_CONDITION_MASK_IDLE)
} uart_rx_condition_t;

        参数三为中断触发的缓冲区数据阈值,即当接收的数据量达到该阈值才会触发回调函数,函数执行完后缓冲区就会清空重新计数;参数四就是回调函数的指针,回调中例程只是简单地打印了接收到的数据。

3. 主函数。

        任务循环里面调用 uapi_uart_write_int 函数定期向串口发送数据;参数一为 UART 总线号;参数二为要发送的数据;参数三为数据的长度;参数四为回调函数的用户参数;参数五为回调函数指针。回调函数里面也是简单地打印发送的数据。

3.3 测试

        测试时需额外准备一个串口调试助手,接线按照发送管脚和接收管脚两两连接的线序,同时两者间的地线也要相连

        固件烧录后能看到串口助手不断打印单片机串口发送过来的数据,串口助手中发送数据到单片机,单片机的log同样能打印发送过来的数据。

http://www.xdnf.cn/news/17437.html

相关文章:

  • 【人工智能99问】BERT的原理什么?(23/99)
  • 开启单片机
  • 编程基础之多维数组——矩阵交换行
  • 【YOLOv8改进 - C2f融合】C2f融合Outlook Attention:将细粒度特征编码到 token 中, 提高在图像分类和语义分割等任务中的性能
  • 【算法题】:斐波那契数列
  • 【Python】常用内置模块
  • 安全运维工具链全解析
  • Android快速视频解码抽帧FFmpegMediaMetadataRetriever,Kotlin(2)
  • 大模型开发工具的汇总
  • SQL Server从入门到项目实践(超值版)读书笔记 23
  • cursor, vscode黄色波浪线警告问题
  • 从零到精通:嵌入式BLE开发实战指南
  • 计算机网络:(十四)传输层(下)详细讲解TCP报文段的首部格式,TCP 可靠传输的实现与TCP 的流量控制
  • Mybatis和MybatisPlus的对比区分理解法
  • 基于 RabbitMQ 死信队列+TTL 实现延迟消息+延迟插件基本使用
  • 给AI装上“翻译聚光灯”:注意力机制的机器翻译革命
  • Docker 镜像常见标签(如 `标准`、`slim`、`alpine` 和 `noble`)详细对比
  • 编程基础之字符串——统计数字字符个数
  • TypeScript 中的as const是什么?
  • React:useEffect 与副作用
  • token危机解决?扩散模型数据潜力3倍于自回归,重训480次性能仍攀升
  • 浏览器CEFSharp88+X86+win7 之多页面展示(四)
  • LLaMA-Adapter Efficient Fine-tuning of Language Models with Zero-init Attention
  • Redis - 使用 Redis HyperLogLog 进行高效基数统计
  • Spring Boot与WebSocket构建物联网实时通信系统
  • 基于Spring Boot和WebSocket的实时聊天系统
  • go语言运算符
  • 遇到前端导出 Excel 文件出现乱码或文件损坏的问题
  • Linux 管道命令及相关命令练习与 Shell 编程、Tomcat 安装
  • 基于Ubuntu20.04的环境,编译QT5.15.17源码