一, 前言
1, 简介
写的这篇博客, 是为了简单讲解一下 UART 通信协议, 以及 UART 能够实现的一些功能, 还有有关使用 STM32CubeMX 来配置芯片的一些操作, 在后面我会以我使用的 STM32F429 开发板来举例讲解(其他 STM32 系列芯片大多数都可以按照这些步骤来操作的), 如有不足请多多指教.
2,UART 简介
嵌入式开发中, UART 串口通信协议是我们常用的通信协议 (UART,I2C,SPI 等) 之一, 全称叫做通用异步收发传输器(Universal Asynchronous Receiver/Transmitter).
3, 准备工作
1)Keil5
链接: 点击下载
提取码: wrt9
2)STMCubeMX5.1.0 版本
链接: 点击下载
提取码: 20xs
3)STMF429 开发板
注:
只要是 stm32 的开发板都可以用到的, 在 STM32CubeMx 里选对型号, 配置好就行了.
二, UART 详解
1,UART 简介
嵌入式开发中, UART 串口通信协议是我们常用的通信协议 (UART,I2C,SPI 等) 之一, 全称叫做通用异步收发传输器(Universal Asynchronous Receiver/Transmitter), 是异步串口通信协议的一种, 工作原理是将传输数据的每个字符一位接一位地传输, 它能将要传输的资料在串行通信与并行通信之间加以转换, 能够灵活地与外部设备进行全双工数据交换.
注:
在此开发板中, 是有 USART(Universal Synchronous Asynchronous Receiver and Transmitter 通用同步异步收发器)串口的, USART 相当于 UART 的升级版, USART 支持同步模式, 因此 USART 需要同步始终信号 USART_CK(如 STM32 单片机), 通常情况同步信号很少使用, 因此一般的单片机 UART 和 USART 使用方式是一样的, 都使用异步模式. 因为 USART 的使用方法上跟 UART 基本相同, 所以在此就以 UART 来讲该通信协议了.
2,UART 通信协议
1)起始位
当未有数据发送时, 数据线处于逻辑 "1" 状态; 先发出一个逻辑 "0" 信号, 表示开始传输字符.
2)数据位
紧接着起始位之后. 资料位的个数可以是 4,5,6,7,8 等, 构成一个字符. 通常采用 ASCII 码. 从最低位开始传送, 靠时钟定位.
3)奇偶校验位
资料为加上这一位后, 使得 "1" 的位数应为偶数 (偶校验) 或奇数(奇校验), 以此来校验资料传送的正确性.
4)停止位
它是一个字符数据的结束标志. 可以是 1 位, 1.5 位, 2 位的高电平. 由于数据是在传输线上定时的, 并且每一个设备有其自己的时钟, 很可能在通信中两台设备间出现了小小的不同步. 因此停止位不仅仅是表示传输的结束, 并且提供计算机校正时钟同步的机会. 适用于停止位的位数越多, 不同时钟同步的容忍程度越大, 但是数据传输率同时也越慢.
5)空闲位或起始位
处于逻辑 "1" 状态, 表示当前线路上没有资料传送, 进入空闲状态.
处于逻辑 "0" 状态, 表示开始传送下一数据段.
6)波特率
表示每秒钟传送的码元符号的个数, 是衡量数据传送速率的指标, 它用单位时间内载波调制状态改变的次数来表示.
常用的波特率有: 9600,115200......
时间间隔计算: 1 秒除以波特率得出的时间, 例如, 波特率为 9600 的时间间隔为 1s / 9600(波特率) = 104us.
3,UART 功能说明
接口通过三个引脚从外部连接到其它设备. 任何 USART 双向通信均需要 至少两个引脚: 接收数据输入引脚 (RX) 和发送数据引脚输出 (TX):
RX: 接收数据输入引脚就是串行数据输入引脚. 过采样技术可区分有效输入数据和噪声, 从而用于恢复数据.
TX: 发送数据输出引脚. 如果关闭发送器, 该输出引脚模式由其 I/O 端口配置决定. 如果使 能了发送器但没有待发送的数据, 则 TX 引脚处于高电平. 在单线和智能卡模式下, 该 I/O 用于发送和接收数据(USART 电平下, 随后在 SW_RX 上接收数据).
1)正常 USART 模式下, 通过这些引脚以帧的形式发送和接收串行数据:
发送或接收前保持空闲线路
起始位
数据(字长 8 位或 9 位), 最低有效位在前
用于指示帧传输已完成的 0.5 个, 1 个, 1.5 个, 2 个停止位
该接口使用小数波特率发生器 - 带 12 位尾数和 4 位小数
状态寄存器 (USART_SR)
数据寄存器 (USART_DR)
波特率寄存器 (USART_BRR) - 12 位尾数和 4 位小数.
智能卡模式下的保护时间寄存器 (USART_GTPR).
2)在同步模式下连接时需要以下引脚:
SCLK: 发送器时钟输出. 该引脚用于输出发送器数据时钟, 以便按照 SPI 主模式进行同步发送(起始位和结束位上无时钟脉冲, 可通过软件向最后一个数据位发送时钟脉冲).RX 上可同步接收并行数据. 这一点可用于控制带移位寄存器的外设(如 LCD 驱动器). 时钟相位和极性可通过软件编程. 在智能卡模式下, SCLK 可向智能卡提供时钟. 在硬件流控制模式下需要以下引脚:
nCTS:"清除以发送" 用于在当前传输结束时阻止数据发送(高电平时).
nRTS:"请求以发送" 用于指示 USART 已准备好接收数据(低电平时).
USART 框图如下:
4,UART 工作原理
1)发送接收
发送逻辑对从发送 FIFO 读取的数据执行 "并→串" 转换. 控制逻辑输出起始位在先的串行位流, 并且根据控制寄存器中已编程的配置, 后面紧跟着数据位(注意: 最低位 LSB 先输出), 奇偶校验位和停止位.
在检测到一个有效的起始脉冲后, 接收逻辑对接收到的位流执行 "串→并" 转换. 此外还会对溢出错误, 奇偶校验错误, 帧错误和线中止 (line-break) 错误进行检测, 并将检测到的状态附加到被写入接收 FIFO 的数据中.
2)波特率产生
波特率除数 (baud-rate divisor) 是一个 22 位数, 它由 16 位整数和 6 位小数组成. 波特率发生器使用这两个值组成的数字来决定位周期. 通过带有小数波特率的除法器, 在足够高的系统时钟速率下, UART 可以产生所有标准的波特率, 而误差很小.
3)数据收发
发送时, 数据被写入发送 FIFO. 如果 UART 被使能, 则会按照预先设置好的参数 (波特率, 数据位, 停止位, 校验位等) 开始发送数据, 一直到发送 FIFO 中没有数据. 一旦向发送 FIFO 写数据(如果 FIFO 未空),UART 的忙标志位 BUSY 就有效, 并且在发送数据期间一直保持有效. BUSY 位仅在发送 FIFO 为空, 且已从移位寄存器发送最后一个字符, 包括停止位时才变无效. 即 UART 不再使能, 它也可以指示忙状态.
在 UART 接收器空闲时, 如果数据输入变成 "低电平", 即接收到了起始位, 则接收计数器开始运行, 并且数据在 Baud16 的第 8 个周期被采样. 如果 Rx 在 Baud16 的第 8 周期仍然为低电平, 则起始位有效, 否则会被认为是错误的起始位并将其忽略.
如果起始位有效, 则根据数据字符被编程的长度, 在 Baud16 的每第 16 个周期 (即一个位周期之后) 对连续的数据位进行采样. 如果奇偶校验模式使能, 则还会检测奇偶校验位.
最后, 如果 Rx 为高电平, 则有效的停止位被确认, 否则发生帧错误. 当接收到一个完整的字符时, 将数据存放在接收 FIFO 中.
4)中断控制
出现以下情况时, 可使 UART 产生中断:
FIFO 溢出错误
线中止错误(line-break, 即 Rx 信号一直为 0 的状态, 包括校验位和停止位在内)
奇偶校验错误
帧错误(停止位不为 1)
接收超时(接收 FIFO 已有数据但未满, 而后续数据长时间不来)
发送
接收
由于所有中断事件在发送到中断控制器之前会一起进行 "或运算" 操作, 所以任意时刻 UART 只能向中断产生一个中断请求. 通过查询中断状态函数, 软件可以在同一个中断服务函数里处理多个中断事件(多个并列的 if 语句).
5)FIFO 操作
FIFO 是 "First-In First-Out" 的缩写, 意为 "先进先出", 是一种常见的队列操作. Stellaris 系列 ARM 的 UART 模块包含有 2 个 16 字节的 FIFO: 一个用于发送, 另一个用于接收. 可以将两个 FIFO 分别配置为以不同深度触发中断. 可供选择的配置包括: 1/8, 1/4,1/2,3/4 和 7/8 深度. 例如, 如果接收 FIFO 选择 1/4, 则在 UART 接收到 4 个数据时产生接收中断.
发送 FIFO 的基本工作过程: 只要有数据填充到发送 FIFO 里, 就会立即启动发送过程. 由于发送本身是个相对缓慢的过程, 因此在发送的同时其它需要发送的数据还可以继续填充到发送 FIFO 里. 当发送 FIFO 被填满时就不能再继续填充了, 否则会造成数据丢失, 此时只能等待. 这个等待并不会很久, 以 9600 的波特率为例, 等待出现一个空位的时间在 1ms 上下. 发送 FIFO 会按照填入数据的先后顺序把数据一个个发送出去, 直到发送 FIFO 全空时为止. 已发送完毕的数据会被自动清除, 在发送 FIFO 里同时会多出一个空位.
接收 FIFO 的基本工作过程: 当硬件逻辑接收到数据时, 就会往接收 FIFO 里填充接收到的数据. 程序应当及时取走这些数据, 数据被取走也是在接收 FIFO 里被自动删除的过程, 因此在接收 FIFO 里同时会多出一个空位. 如果在接收 FIFO 里的数据未被及时取走而造成接收 FIFO 已满, 则以后再接收到数据时因无空位可以填充而造成数据丢失.
收发 FIFO 主要是为了解决 UART 收发中断过于频繁而导致 CPU 效率不高的问题而引入的. 在进行 UART 通信时, 中断方式比轮询方式要简便且效率高. 但是, 如果没有收发 FIFO, 则每收发一个数据都要中断处理一次, 效率仍然不够高. 如果有了收发 FIFO, 则可以在连续收发若干个数据 (可多至 14 个) 后才产生一次中断然后一并处理, 这就大大提高了收发效率.
完全不必要担心 FIFO 机制可能带来的数据丢失或得不到及时处理的问题, 因为它已经帮你想到了收发过程中存在的任何问题, 只要在初始化配置 UART 后, 就可以放心收发了, FIFO 和中断例程会自动搞定一切.
6)回环操作
UART 可以进入一个内部回环 (Loopback) 模式, 用于诊断或调试. 在回环模式下, 从 Tx 上发送的数据将被 Rx 输入端接收.
三, CubeMx 配置
说明:
在使用 STM32CubeMx 配置的时候, 首先要选择正在使用的芯片的型号, 再配置芯片的时钟, 然后才去配置所需要用到的功能.
1, 新建项目
1)选择新建
2)选择芯片型号
2, 时钟配置
1)配置界面
2)时钟模式配置
3)设置调试接口
4)时钟配置(尽量将下面方框内的值设成最高值即可)
3, 功能配置
1)启用串口
2)配置串口(默认即可, 波特率为 115200)
4, 生成工程
1)项目信息设置
2)选择生成必要的代码
3)生成代码
4)打开项目(生成代码成功后会弹出窗口, 可以直接打开工程)
注:
因为 STM32CubeMX 自动生成的代码中, 没有设置把每次下载烧写都重置一下, 所以生成代码后, 我们需要自己选上该功能, 步骤如下:
1)功能界面
2)选择小锤子
3)选择 Debug->Settings
4)选择 Flash Download->勾选 Reset and Run
完成上面的操作后, 在每次烧写都会重置, 并运行新下载烧写的程序了.
四, HAL 库关键函数说明
1, 初始化 / 还原初始化函数
- /* Initialization/de-initialization functions **********************************/
- HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart); // 根据 UART_InitTypeDef 中指定的参数初始化 UART 模式, 并创建关联的句柄.
- HAL_StatusTypeDef HAL_HalfDuplex_Init(UART_HandleTypeDef *huart); // 根据 UART_InitTypeDef 中指定的参数初始化半双工模式并创建关联句柄.
- HAL_StatusTypeDef HAL_LIN_Init(UART_HandleTypeDef *huart, uint32_t BreakDetectLength); // 根据 UART_InitTypeDef 中指定的参数初始化 LIN 模式, 并创建关联的句柄.
- HAL_StatusTypeDef HAL_MultiProcessor_Init(UART_HandleTypeDef *huart, uint8_t Address, uint32_t WakeUpMethod); // 根据 UART_InitTypeDef 中指定的参数初始化多处理器模式, 并创建关联的句柄.
- HAL_StatusTypeDef HAL_UART_DeInit(UART_HandleTypeDef *huart); // 非初始化 UART 外围设备.
- void HAL_UART_MspInit(UART_HandleTypeDef *huart); // 弱函数 UART MSP 初始化
- void HAL_UART_MspDeInit(UART_HandleTypeDef *huart); // 弱函数 UART MSP 初始化还原
2,IO 口操作函数
- /* IO operation functions *******************************************************/
- HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);// 以阻塞模式发送大量数据.
- HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout); // 在阻塞模式下接收大量数据.
- HAL_StatusTypeDef HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); // 以非阻塞模式发送大量数据.
- HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); // 在非阻塞模式下接收大量数据.
- HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); // 以非阻塞模式发送大量数据.
- HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size); // 在非阻塞模式下接收大量数据.
- HAL_StatusTypeDef HAL_UART_DMAPause(UART_HandleTypeDef *huart); // 暂停 DMA 传输.
- HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart); // 恢复 DMA 传输.
- HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart); // 停止 DMA 传输.
3, 传输中断函数
- /* Transfer Abort functions */
- HAL_StatusTypeDef HAL_UART_Abort(UART_HandleTypeDef *huart); // 中止正在进行的传输(阻塞模式).
- HAL_StatusTypeDef HAL_UART_AbortTransmit(UART_HandleTypeDef *huart); // 中止正在进行的传输传输(阻塞模式).
- HAL_StatusTypeDef HAL_UART_AbortReceive(UART_HandleTypeDef *huart); // 中止正在进行的接收传输(阻塞模式).
- HAL_StatusTypeDef HAL_UART_Abort_IT(UART_HandleTypeDef *huart); // 中止正在进行的传输(中断模式).
- HAL_StatusTypeDef HAL_UART_AbortTransmit_IT(UART_HandleTypeDef *huart); // 中止正在进行的传输(中断模式).
- HAL_StatusTypeDef HAL_UART_AbortReceive_IT(UART_HandleTypeDef *huart); // 中止正在进行的接收传输(中断模式).
4, 中断处理及回调函数
- void HAL_UART_IRQHandler(UART_HandleTypeDef *huart); // 函数处理 UART 中断请求.
- void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart); //Tx 传输完成回调函数.
- void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart); //Tx 半传输完成回调函数.
- void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart); //Rx 传输完成回调函数.
- void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart); //Rx 完成一半传输回调函数.
- void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart); //UART 错误回调函数.
- void HAL_UART_AbortCpltCallback(UART_HandleTypeDef *huart); //UART 中止完成回调函数.
- void HAL_UART_AbortTransmitCpltCallback(UART_HandleTypeDef *huart); //UART 中止完成回调函数.
- void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart); //UART 中止接收完整的回调函数.
五, 结尾
1, 总结
这篇博客主要是讲解一下 UART 串口通信协议的时序, 功能以及工作原理, 还有使用 STM32CubeMX 来配置 USART. 而还未讲到有关 HAL 库函数的函数调用, 有了 STM32CubeMX 生成的这个 HAL 库函数, 我们基本不用管协议上的事情了, 可以直接调用里面的发送或接收函数来实现 UART 通信. 而我也会在后续继续编写有关 HAL 库的调用说明, 详细说一下 HAL 库是如何使用的.
2, 后续
1)
UART 发送(一天内再发布)
2)待续未完......
~
~
~
~
最后~最后~最后~
欢迎大家关注我的博客, 一起分享嵌入式知识~
来源: https://www.cnblogs.com/ChurF-Lin/p/10793111.html