首页 > 其他分享 >13. 串口通信

13. 串口通信

时间:2023-08-08 19:34:28浏览次数:44  
标签:13 HAL USART UART 通信 huart 串口 define

一、什么是串口

  串口通信是一种设备间常用的串行通信方式,串口按位(bit)发送和接收字节。尽管比特字节(byte)的串行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。串口通信协议是指规定了数据包的内容,内容包含了起始位、主体数据、校验位及停止位,双方需要约定一致的数据包格式才能正常收发数据的有关规范。在串口通信中,常用的协议包括 RS-232、RS-422 和 RS-485 等。

  随着科技的发展,RS-232 在工业上还有广泛的使用,但是在商业技术上,已经慢慢的使用 USB 转串口取代了 RS-232 串口。我们只需要在电路中添加一个 USB 转串口芯片,就可以实现 USB 通信协议标准 UART 串行通信协议 的转换。

二、串口通信协议

  串口通信的数据包由发送设备的 TXD 接口 传输到接收设备的 RXD 接口。在串口通信的协议层中,规定了数据包的内容,它由 起始位主体数据校验位 以及 停止位 组成,通讯双方的数据包格式要约定一致才能正常收发数据。

img

  串口通信协议数据包组成可以分为 波特率数据帧格式 两部分。数据帧格式 需要我们提前约定好,串口通信的数据帧包括 起始位停止位有效数据位 以及 校验位

【1】、波特率

  异步通信是不需要时钟信号的,但是这里需要我们约定好两个设备的波特率。波特率表示每秒钟传送的码元符号的个数,所以它决定了数据帧里面每一个位的时间长度。两个要通信的设备的波特率一定要设置相同,我们常见的波特率是 4800、9600、115200 等。

【2】、起始位 和 停止位

  串口通信的一个数据帧是从起始位开始,直到停止位。数据帧中的 起始位 是由一个 逻辑 0 的数据位表示,而数据帧的 停止位 可以是0.5、1、1.5 或 2 个 逻辑 1 的数据位表示,只要双方约定一致即可。

【3】、有效数据位

  数据帧的起始位之后,就接着是数据位,也称有效数据位,这就是我们真正需要的数据,有效数据位通常会被约定为 5、6、7 或者 8 个位长有效数据位是低位(LSB)在前,高位(MSB)在后

【4】、校验位

  校验位可以认为是一个特殊的数据位。校验位一般用来判断接收的数据位有无错误,检验方法有:奇检验偶检验0 检验1 检验 以及 无检验

  奇校验 是指 有效数据位 和 校验位 中“1”的个数为奇数,比如一个 8 位长的有效数据为:10101001,总共有 4 个“1”,为达到奇校验效果,校验位设置为“1”,最后传输的数据是 8 位的有效数据加上 1 位的校验位总共 9 位。

  偶校验 是指 有效数据位 和 校验位 中“1”的个数为偶数,比如数据帧:11001010,此时数据帧“1”的个数为 4 个,所以偶校验位为 “0”。

  0 校验 是指不管有效数据中的内容是什么,校验位总为“0”1 校验校验位总为“1”

  无校验 是指数据帧中 不包含校验位

三、STM32的USART简介

  STM32F103ZET6 最多可提供 5 路串口,有分数波特率发生器、支持同步单线通信和半双工单线通讯、支持LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA 等。

  STM32F1 的串口分为两种:USART(Universal synchronous asynchronouns recevice transmitter)即 通用同步异步收发器UART(Universal asynchronous recevier transmitter)即 通用异步收发器UART 是在 USART 基础上裁剪掉了同步通信功能,只剩下异步通信功能。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用串口通信基本都是异步通信。

  STM32F1 有 3 个 USART 和 2 个 UART,其中 USART1 的时钟源来于 APB2 时钟,其最大频率为 72MHz,其它 4 个串口的时钟源可以来于 APB1 时钟,其最大频率为 36MHz。

  STM32 的串口输出的是 TTL 电平信号,如果需要 RS-232 标准的信号可使用 MAX3232 芯片进行转换,而一般我们是通过 USB 转串口芯片 CH340C 来与电脑的上位机进行通信。

四、USART框图

img

img

【1】、USART 信号引脚

  • TX:发送数据输出引脚
  • RX:接收数据输入引脚
  • SCLK:发送器时钟输出,适用于同步传输
  • SW_RX:数据接收引脚,属于内部引脚,用于智能卡模式
  • IrDA_RDI:IrDA 模式下的数据输入
  • IrDA_TDO:IrDA 模式下的数据输出
  • nRTS:发送请求,若是低电平,表示 USART 准备好接收数据
  • nCTS:清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送

【2】、数据寄存器

  USART_DR 包含了已发送或接收到的数据。由于它本身就是两个寄存器组成的,一个专门给发送用的(TDR),一个专门给接收用的(RDR),该寄存器具备读和写的功能。TDR寄存器 提供了内部总线和输出移位寄存器之间的并行接口。RDR寄存器 提供了输入移位寄存器和内部总线之间的并行接口。当进行数据发送操作时,往 USART_DR 中写入数据会自动存储在 TDR 内;当进行读取操作时,向 USART_DR 读取数据会自动提取 RDR 数据。

  USART 数据寄存器(USART_DR)低 9 位数据有效,其他数据位保留。USART_DR 的 第 9 位数据 是否有效跟 USART_CR1 的 M位 设置有关,当 M 位为 0 表示 8 位数据字长;当 M 位为 1 时表示 9 位数据字长,一般使用 8 位数据字长。

  当使能校验位(USART_CR1 中 PCE 位被置位)进行发送时,写到 MSB 的值(根据数据的长度不同,MSB 是第 7 位或者第 8 位)会被后来的校验位取代。

【3】、控制器

  USART 有专门控制发送的发送器,控制接收的接收器,还有唤醒单元、中断控制等等。

【4】、时钟与波特率

  波特率,即每秒钟传输的码元个数,在二进制系统中(串口的数据帧就是二进制的形式),波特率与波特率的数值相等。

  波特率通过以下公式得出:\(band = \frac{f_{ck}}{16 * USARTDIV}\)

  其中,\(f_{ck}\) 是给串口的时钟,USART2\3\3\4\5 的时钟源为 PCLK1(36MHz),USART1 的时钟源为 PCLK2(72MHz),USARTDIV 是一个无符号的定点数,存放在波特率寄存器(USART_BRR)的低 16 位,DIV_Man-tissa[11:0] 存放的是 USARTDIV 的整数部分,DIV_Fractionp[3:0] 存放的是 USARTDIV 的小数部分。

  当串口 1 设置需要得到 115200 的波特率,fck =72MHz,那么可得:\(115200 = \frac{72000000}{16 * USARTDIV}\)

  得到 USARTDIV = 39.0625,分离 USARTDIV 的整数部分与小数部分,整数部分为 39,即 0x27,那么 DIV_Mantissa = 0x27;小数部分为 0.0625,转化为十六进制即 0.0625*16=1,所以 DIV_Fractionp =0x1,USART_BRR 寄存器应该赋值为 0x271,成功设置波特率为 115200。

USARTDIV 是允许有余数的,我们用四舍五入进行取整,这样会导致波特率会有所偏差,而这样的小误差是可以被允许的。

五、串口引脚及复用功能重映像

【1】、USART1 引脚分布及复用功能重映像

复用功能 USART1_REMAP = 0 USART1_REMAP = 1
USART1_TX PA9 PB6
USART1_RX PA10 PB7

【2】、USART2 引脚分布及复用功能重映像

复用功能 USART2_REMAP = 0 USART2_REMAP = 1
USART2_CTS PA0 PD3
USART2_RTS PA1 PD4
USART2_TX PA2 PD5
USART2_RX PA3 PD6
USART2_CK PA4 PD7

USART2 重映像只适用于 100 和 144 脚的封装

【3】、USART3 引脚分布及复用功能重映像

复用功能 USART3_REMAP[1:0] = 00
(没有重映像)
USART3_REMAP[1:0] = 01
(部分重映像)
USART3_REMAP[1:0] = 11
(完全重映像)
USART3_TX PB10 PC10 PD8
USART3_RX PB11 PC11 PD9
USART3_CK PB12 PC12 PD10
USART3_CTS PB13 PC11
USART3_RTS PB14 PC12

USART3 部分重映像只适用于 64、100 和 144 脚的封装;

USART3 完全重映像只适用于100和144脚的封装

【4】、UART4 引脚分布

功能 IO 引脚
UART_TX PC10
UART_RX PC11

UART4 没有重映像功能;

【5】、UART5 引脚分布

功能 IO 引脚
UART5_TX PC12
UART5_RX PD2

UART5 没有重映像功能;

六、USART寄存器介绍

6.1、状态寄存器(USART_SR)

img

  串口状态通过状态寄存器 USART_SR 读取。

  RXNE(读数据寄存器非空),当该位被 置1 的时候,就是提示已经有数据被接收到了,并且可以读出来了。这时候我们要做的就是尽快去读取 USART_DR,通过读 USART_DR 可以将该位清零,也可以向该位写 0,直接清除。

  TC(发送完成),当该位被 置位 的时候,表示 USART_DR 内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:

  • 读 USART_SR,写 USART_DR。
  • 直接向该位写 0

6.2、数据寄存器(USART_DR)

img

STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收到数据的时候,也是存在该寄存器内。

6.3、波特比率寄存器(USART_BRR)

img

每个串口都有一个自己独立的波特率寄存器 USART_BRR,通过设置该寄存器就可以达到配置不同波特率的目的。

6.4、控制寄存器(UART_CR)

img

img

img

  STM32F103 每个串口都有 3 个控制寄存器 USART_CR1~3,串口的很多配置都是通过这 3 个寄存器来设置的。该寄存器的高18 位没有用到,低 14 位用于串口的功能设置。

  UASRT_CR1 寄存器的 UE 为串口使能位,通过该位置 1,使能串口。M 为字长,当该位为 0 的时候设置串口为 8 个字长外加 n 个停止位,停止位的个数(n)是根据 USART_CR2 的 [13:12] 位设置来决定的,默认为 0。PCE 为校验使能位,设置为 0,即禁止校验,否则使能校验。PS 为校验位选择,设置为 0 为偶校验,否则奇校验。TXIE 为发送缓冲区空中断使能位,设置该位为 1,当 USART_SR 中的 TXE 位为 1 时,将产生串口中断。TCIE 为发送完成中断使能位,设置该位为 1,当 USART_SR 中的 TC 位为 1 时,将产生串口中断。RXNEIE 为接收缓冲区非空中断使能,设置该位为 1,当 USART_SR 中的 ORE 或者 RXNE 位为 1 时,将产生串口中断。TE 为发送使能位,设置为 1,将开启串口的发送功能。RE 为接收使能位,用法同 TE。

6.5、保护时间和预分频寄存器(UASRT_GTPR)

img

七、USART的配置步骤

7.1、使能串口和GPIO口时钟

/** @defgroup RCC_APB2_Clock_Enable_Disable APB2 Clock Enable Disable
  * @brief  Enable or disable the High Speed APB (APB2) peripheral clock.
  * @note   After reset, the peripheral clock (used for registers read/write access)
  *         is disabled and the application software has to enable this clock before
  *         using it.
  * @{
  */
#define __HAL_RCC_USART1_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB2ENR, RCC_APB2ENR_USART1EN);\
                                        /* Delay after an RCC peripheral clock enabling */\
                                        tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_USART1EN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

#define __HAL_RCC_USART2_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_USART2EN);\
                                        /* Delay after an RCC peripheral clock enabling */\
                                        tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_USART2EN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)
/** @defgroup RCCEx_APB2_Clock_Enable_Disable APB2 Clock Enable Disable
  * @brief  Enable or disable the High Speed APB (APB2) peripheral clock.
  * @note   After reset, the peripheral clock (used for registers read/write access)
  *         is disabled and the application software has to enable this clock before
  *         using it.
  * @{
  */
#if defined(STM32F100xB) || defined(STM32F100xE) || defined(STM32F101xB)\
 || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F102xB)\
 || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG)\
 || defined(STM32F105xC) || defined(STM32F107xC)

#define __HAL_RCC_USART3_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_USART3EN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_USART3EN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

#endif /* STM32F100xB || STM32F101xB || STM32F101xE || (...) || STM32F105xC || STM32F107xC */
#if defined(STM32F101xE) || defined(STM32F103xE) || defined(STM32F101xG)\
 || defined(STM32F103xG) || defined(STM32F105xC) || defined(STM32F107xC)

#define __HAL_RCC_UART4_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_UART4EN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_UART4EN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

#define __HAL_RCC_UART5_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB1ENR, RCC_APB1ENR_UART5EN);\
                                        /* Delay after an RCC peripheral clock enabling */ \
                                        tmpreg = READ_BIT(RCC->APB1ENR, RCC_APB1ENR_UART5EN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

#endif /* STM32F101xE || STM32F103xE || STM32F101xG || (...) || STM32F105xC || STM32F107xC */
/** @defgroup RCC_APB2_Clock_Enable_Disable APB2 Clock Enable Disable
  * @brief  Enable or disable the High Speed APB (APB2) peripheral clock.
  * @note   After reset, the peripheral clock (used for registers read/write access)
  *         is disabled and the application software has to enable this clock before
  *         using it.
  * @{
  */
#define __HAL_RCC_GPIOA_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);\
                                        /* Delay after an RCC peripheral clock enabling */\
                                        tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPAEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

#define __HAL_RCC_GPIOB_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPBEN);\
                                        /* Delay after an RCC peripheral clock enabling */\
                                        tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPBEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

#define __HAL_RCC_GPIOC_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPCEN);\
                                        /* Delay after an RCC peripheral clock enabling */\
                                        tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPCEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

#define __HAL_RCC_GPIOD_CLK_ENABLE()   do { \
                                        __IO uint32_t tmpreg; \
                                        SET_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPDEN);\
                                        /* Delay after an RCC peripheral clock enabling */\
                                        tmpreg = READ_BIT(RCC->APB2ENR, RCC_APB2ENR_IOPDEN);\
                                        UNUSED(tmpreg); \
                                      } while(0U)

7.2、配置串口工作参数

【1】、HAL_UART_Init() 函数

  用于初始化异步模式的收发器,其声明如下:

/**
  * @brief  Initializes the UART mode according to the specified parameters in
  *         the UART_InitTypeDef and create the associated handle.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart);
  • 函数形参

  huart 是串口的句柄,结构体类型是 UART_HandleTypeDef,其定义如下:

/**
  * @brief  UART handle Structure definition
  */
typedef struct __UART_HandleTypeDef
{
  USART_TypeDef                 *Instance;        /*!< UART registers base address        */

  UART_InitTypeDef              Init;             /*!< UART communication parameters      */

  uint8_t                       *pTxBuffPtr;      /*!< Pointer to UART Tx transfer Buffer */

  uint16_t                      TxXferSize;       /*!< UART Tx Transfer size              */

  __IO uint16_t                 TxXferCount;      /*!< UART Tx Transfer Counter           */

  uint8_t                       *pRxBuffPtr;      /*!< Pointer to UART Rx transfer Buffer */

  uint16_t                      RxXferSize;       /*!< UART Rx Transfer size              */

  __IO uint16_t                 RxXferCount;      /*!< UART Rx Transfer Counter           */

  __IO HAL_UART_RxTypeTypeDef ReceptionType;      /*!< Type of ongoing reception          */

  DMA_HandleTypeDef             *hdmatx;          /*!< UART Tx DMA Handle parameters      */

  DMA_HandleTypeDef             *hdmarx;          /*!< UART Rx DMA Handle parameters      */

  HAL_LockTypeDef               Lock;             /*!< Locking object                     */

  __IO HAL_UART_StateTypeDef    gState;           /*!< UART state information related to global Handle management
                                                       and also related to Tx operations.
                                                       This parameter can be a value of @ref HAL_UART_StateTypeDef */

  __IO HAL_UART_StateTypeDef    RxState;          /*!< UART state information related to Rx operations.
                                                       This parameter can be a value of @ref HAL_UART_StateTypeDef */

  __IO uint32_t                 ErrorCode;        /*!< UART Error code                    */

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
  void (* TxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Tx Half Complete Callback        */
  void (* TxCpltCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Tx Complete Callback             */
  void (* RxHalfCpltCallback)(struct __UART_HandleTypeDef *huart);        /*!< UART Rx Half Complete Callback        */
  void (* RxCpltCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Rx Complete Callback             */
  void (* ErrorCallback)(struct __UART_HandleTypeDef *huart);             /*!< UART Error Callback                   */
  void (* AbortCpltCallback)(struct __UART_HandleTypeDef *huart);         /*!< UART Abort Complete Callback          */
  void (* AbortTransmitCpltCallback)(struct __UART_HandleTypeDef *huart); /*!< UART Abort Transmit Complete Callback */
  void (* AbortReceiveCpltCallback)(struct __UART_HandleTypeDef *huart);  /*!< UART Abort Receive Complete Callback  */
  void (* WakeupCallback)(struct __UART_HandleTypeDef *huart);            /*!< UART Wakeup Callback                  */
  void (* RxEventCallback)(struct __UART_HandleTypeDef *huart, uint16_t Pos); /*!< UART Reception Event Callback     */

  void (* MspInitCallback)(struct __UART_HandleTypeDef *huart);           /*!< UART Msp Init callback                */
  void (* MspDeInitCallback)(struct __UART_HandleTypeDef *huart);         /*!< UART Msp DeInit callback              */
#endif  /* USE_HAL_UART_REGISTER_CALLBACKS */

} UART_HandleTypeDef;

  Instance:指向 UART 寄存器基地址。实际上这个基地址 HAL库 已经定义好了,可以选择范围:USART1~ USART3、UART4、UART5。

#define USART1              ((USART_TypeDef *)USART1_BASE)
#define USART2              ((USART_TypeDef *)USART2_BASE)
#define USART3              ((USART_TypeDef *)USART3_BASE)
#define UART4               ((USART_TypeDef *)UART4_BASE)
#define UART5               ((USART_TypeDef *)UART5_BASE)

  Init:UART 初始化结构体,用于配置通讯参数,如波特率、数据位数、停止位等等。

  pTxBuffPtr,TxXferSize,TxXferCount:分别是指向发送数据缓冲区的指针,发送数据的大小,发送数据的个数

  pRxBuffPtr,RxXferSize,RxXferCount:分别是指向接收数据缓冲区的指针,接受数据的大小,接收数据的个数

  hdmatx,hdmarx:配置串口发送接收数据的 DMA 具体参数

  Lock:对资源操作增加操作锁保护功能,可选 HAL_UNLOCKED 或者 HAL_LOCKED 两个参数。如果gState的值等于HAL_UART_STATE_RESET,则可认为串口未被初始化,此时,分配锁资源,并且调用HAL_UART_MspInit函数来对串口的GPIO和时钟进行初始化。

  gState,RxState:分别是UART的发送状态、工作状态的结构体 和 UART接受状态的结构体。HAL_UART_StateTypeDef 是一个枚举类型,列出串口在工作过程中的状态值,有些值只适用于 gState。

  ErrorCode:串口错误操作信息。主要用于存放串口操作的错误信息。

  UART_InitTypeDef 这个结构体类型,该结构体用于配置 UART 的各个通信参数,包括波特率,停止位等,具体说明如下:

/**
  * @brief UART Init Structure definition
  */
typedef struct
{
  uint32_t BaudRate;                  /*!< This member configures the UART communication baud rate.
                                           The baud rate is computed using the following formula:
                                           - IntegerDivider = ((PCLKx) / (16 * (huart->Init.BaudRate)))
                                           - FractionalDivider = ((IntegerDivider - ((uint32_t) IntegerDivider)) * 16) + 0.5 */

  uint32_t WordLength;                /*!< Specifies the number of data bits transmitted or received in a frame.
                                           This parameter can be a value of @ref UART_Word_Length */

  uint32_t StopBits;                  /*!< Specifies the number of stop bits transmitted.
                                           This parameter can be a value of @ref UART_Stop_Bits */

  uint32_t Parity;                    /*!< Specifies the parity mode.
                                           This parameter can be a value of @ref UART_Parity
                                           @note When parity is enabled, the computed parity is inserted
                                                 at the MSB position of the transmitted data (9th bit when
                                                 the word length is set to 9 data bits; 8th bit when the
                                                 word length is set to 8 data bits). */

  uint32_t Mode;                      /*!< Specifies whether the Receive or Transmit mode is enabled or disabled.
                                           This parameter can be a value of @ref UART_Mode */

  uint32_t HwFlowCtl;                 /*!< Specifies whether the hardware flow control mode is enabled or disabled.
                                           This parameter can be a value of @ref UART_Hardware_Flow_Control */

  uint32_t OverSampling;              /*!< Specifies whether the Over sampling 8 is enabled or disabled, to achieve higher speed (up to fPCLK/8).
                                           This parameter can be a value of @ref UART_Over_Sampling. This feature is only available 
                                           on STM32F100xx family, so OverSampling parameter should always be set to 16. */
} UART_InitTypeDef;

  BaudRate:波特率设置。一般设置为 2400、9600、19200、115200。

  WordLength:数据帧字长,可选 8 位或 9 位,一般我们选择 8 位

/** @defgroup UART_Word_Length UART Word Length
  * @{
  */
#define UART_WORDLENGTH_8B                  0x00000000U
#define UART_WORDLENGTH_9B                  ((uint32_t)USART_CR1_M)

  StopBits:停止位设置,可选 1 位和 2 位停止位,一般我们选择 1 位停止位

/** @defgroup UART_Stop_Bits UART Number of Stop Bits
  * @{
  */
#define UART_STOPBITS_1                     0x00000000U
#define UART_STOPBITS_2                     ((uint32_t)USART_CR2_STOP_1)

  Parity:奇偶校验控制选择,一般我们设定为 无奇偶校验位

/** @defgroup UART_Parity UART Parity
  * @{
  */
#define UART_PARITY_NONE                    0x00000000U
#define UART_PARITY_EVEN                    ((uint32_t)USART_CR1_PCE)
#define UART_PARITY_ODD                     ((uint32_t)(USART_CR1_PCE | USART_CR1_PS))

  Mode:UART 模式选择,可以设置为 只收模式,只发模式,或者 收发模式。一般我们设置为 全双工收发模式

/** @defgroup UART_Mode UART Transfer Mode
  * @{
  */
#define UART_MODE_RX                        ((uint32_t)USART_CR1_RE)
#define UART_MODE_TX                        ((uint32_t)USART_CR1_TE)
#define UART_MODE_TX_RX                     ((uint32_t)(USART_CR1_TE | USART_CR1_RE))

  HwFlowCtl:硬件流控制选择,一般我们设置为 无硬件流控制

/** @defgroup UART_Hardware_Flow_Control UART Hardware Flow Control
  * @{
  */
#define UART_HWCONTROL_NONE                  0x00000000U
#define UART_HWCONTROL_RTS                   ((uint32_t)USART_CR3_RTSE)
#define UART_HWCONTROL_CTS                   ((uint32_t)USART_CR3_CTSE)
#define UART_HWCONTROL_RTS_CTS               ((uint32_t)(USART_CR3_RTSE | USART_CR3_CTSE))

  OverSampling:过采样选择,选择 8 倍过采样 或者 16 倍过采样,一般选择 16 倍过采样

/** @defgroup UART_Over_Sampling UART Over Sampling
  * @{
  */
#define UART_OVERSAMPLING_16                    0x00000000U
#if defined(USART_CR1_OVER8)
#define UART_OVERSAMPLING_8                     ((uint32_t)USART_CR1_OVER8)
#endif /* USART_CR1_OVER8 */
  • 函数返回值

  HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是:HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

/**
  * @brief  HAL Status structures definition
  */
typedef enum
{
  HAL_OK       = 0x00U,
  HAL_ERROR    = 0x01U,
  HAL_BUSY     = 0x02U,
  HAL_TIMEOUT  = 0x03U
} HAL_StatusTypeDef;

7.3、GPIO模式设置

【1】、HAL_GPIO_Init() 函数

  GPIO 模式设置通过调用 HAL_GPIO_Init() 函数实现。将 USART_TXD 引脚设置为 复用推挽输出模式USART_RXD 引脚设置为 输入模式

/**
  * @brief  Initializes the GPIOx peripheral according to the specified parameters in the GPIO_Init.
  * @param  GPIOx: where x can be (A..G depending on device used) to select the GPIO peripheral
  * @param  GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains
  *         the configuration information for the specified GPIO peripheral.
  * @retval None
  */
void  HAL_GPIO_Init(GPIO_TypeDef  *GPIOx, GPIO_InitTypeDef *GPIO_Init);
  • 函数形参

  GPIOx 是 端口号

/** @addtogroup Peripheral_declaration
  * @{
  */  
#define GPIOA               ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC               ((GPIO_TypeDef *)GPIOC_BASE)
#define GPIOD               ((GPIO_TypeDef *)GPIOD_BASE)

  GPIO_Init 是 GPIO_InitTypeDef类型的结构体变量

/**
  * @brief GPIO Init structure definition
  */
typedef struct
{
  uint32_t Pin;       /*!< Specifies the GPIO pins to be configured.
                           This parameter can be any value of @ref GPIO_pins_define */

  uint32_t Mode;      /*!< Specifies the operating mode for the selected pins.
                           This parameter can be a value of @ref GPIO_mode_define */

  uint32_t Pull;      /*!< Specifies the Pull-up or Pull-Down activation for the selected pins.
                           This parameter can be a value of @ref GPIO_pull_define */

  uint32_t Speed;     /*!< Specifies the speed for the selected pins.
                           This parameter can be a value of @ref GPIO_speed_define */
} GPIO_InitTypeDef;
  • 函数返回值

  无;

7.4、开启串口异步接收中断

【1】、HAL_UART_Receive_IT() 函数

  HAL_UART_Receive_IT() 函数是开启串口接收中断函数。它用于开启以中断的方式接收指定字节。数据接收在中断处理函数里面实现。其声明如下:

/**
  * @brief  Receives an amount of data in non blocking 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 Pointer to a UART_HandleTypeDef structure that contains
  *               the configuration information for the specified UART module.
  * @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);
  • 函数参数

  huart 是 UART_HandleTypeDef 结构体指针类型的串口句柄;这里我们使用它的 Instance 成员,Instance 成员的取值为:

#define USART1              ((USART_TypeDef *)USART1_BASE)
#define USART2              ((USART_TypeDef *)USART2_BASE)
#define USART3              ((USART_TypeDef *)USART3_BASE)
#define UART4               ((USART_TypeDef *)UART4_BASE)
#define UART5               ((USART_TypeDef *)UART5_BASE)

  pData 是要接收的数据地址

  Size 是要接收的数据大小,以字节为单位

  • 函数返回值

  HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是:HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

/**
  * @brief  HAL Status structures definition
  */
typedef enum
{
  HAL_OK       = 0x00U,
  HAL_ERROR    = 0x01U,
  HAL_BUSY     = 0x02U,
  HAL_TIMEOUT  = 0x03U
} HAL_StatusTypeDef;

7.5、设置优先级,使能中断

  设置 NVIC 分为三步,即:设置优先级分组设置优先级使能中断

  中断分组 使用 HAL_NVIC_SetPriorityGrouping() 函数设置,中断优先级 使用 HAL_NVIC_SetPriority() 函数设置,中断使能 使用 HAL_NVIC_EnableIRQ() 函数设置。

【1】、HAL_NVIC_SetPriorityGrouping() 函数

/**
  * @brief  Sets the priority grouping field (preemption priority and subpriority)
  *         using the required unlock sequence.
  * @param  PriorityGroup: The priority grouping bits length. 
  *         This parameter can be one of the following values:
  *         @arg NVIC_PRIORITYGROUP_0: 0 bits for preemption priority
  *                                    4 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_1: 1 bits for preemption priority
  *                                    3 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_2: 2 bits for preemption priority
  *                                    2 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_3: 3 bits for preemption priority
  *                                    1 bits for subpriority
  *         @arg NVIC_PRIORITYGROUP_4: 4 bits for preemption priority
  *                                    0 bits for subpriority
  * @note   When the NVIC_PriorityGroup_0 is selected, IRQ preemption is no more possible. 
  *         The pending IRQ priority will be managed only by the subpriority. 
  * @retval None
  */
void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup);
  • 函数参数

  PriorityGroup 是中断优先级分组号,可以选择范围:NVIC_PRIORITYGROUP_0 到 NVIC_PRIORITYGROUP_4(共5组);

/** @defgroup CORTEX_Preemption_Priority_Group CORTEX Preemption Priority Group
  * @{
  */
#define NVIC_PRIORITYGROUP_0         0x00000007U /*!< 0 bits for pre-emption priority
                                                      4 bits for subpriority */
#define NVIC_PRIORITYGROUP_1         0x00000006U /*!< 1 bits for pre-emption priority
                                                      3 bits for subpriority */
#define NVIC_PRIORITYGROUP_2         0x00000005U /*!< 2 bits for pre-emption priority
                                                      2 bits for subpriority */
#define NVIC_PRIORITYGROUP_3         0x00000004U /*!< 3 bits for pre-emption priority
                                                      1 bits for subpriority */
#define NVIC_PRIORITYGROUP_4         0x00000003U /*!< 4 bits for pre-emption priority
                                                      0 bits for subpriority */
  • 函数返回值

  无;

【2】、HAL_NVIC_SetPriority() 函数

/**
  * @brief  Sets the priority of an interrupt.
  * @param  IRQn: External interrupt number.
  *         This parameter can be an enumerator of IRQn_Type enumeration
  *         (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xx.h))
  * @param  PreemptPriority: The preemption priority for the IRQn channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority 
  * @param  SubPriority: the subpriority level for the IRQ channel.
  *         This parameter can be a value between 0 and 15
  *         A lower priority value indicates a higher priority.  
  * @retval None
  */
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
  • 函数参数

  IRQn 是中断号,可以选择范围:IRQn_Type 定义的枚举类型,定义在 stm32f103xe.h;

/**
 * @brief STM32F10x Interrupt Number Definition, according to the selected device 
 *        in @ref Library_configuration_section 
 */

 /*!< Interrupt Number Definition */
typedef enum
{

/******  STM32 specific Interrupt Numbers *********************************************************/
  USART1_IRQn                 = 37,     /*!< USART1 global Interrupt                              */
  USART2_IRQn                 = 38,     /*!< USART2 global Interrupt                              */
  USART3_IRQn                 = 39,     /*!< USART3 global Interrupt                              */
  UART4_IRQn                  = 52,     /*!< UART4 global Interrupt                               */
  UART5_IRQn                  = 53,     /*!< UART5 global Interrupt                               */
} IRQn_Type;

  PreemptPriority 是 抢占优先级,可以选择范围:0 到 15

  SubPriority 是 响应优先级,可以选择范围:0 到 15

  • 函数返回值

  无;

【3】、HAL_NVIC_EnableIRQ() 函数

/**
  * @brief  Enables a device specific interrupt in the NVIC interrupt controller.
  * @note   To configure interrupts priority correctly, the NVIC_PriorityGroupConfig()
  *         function should be called before. 
  * @param  IRQn External interrupt number.
  *         This parameter can be an enumerator of IRQn_Type enumeration
  *         (For the complete STM32 Devices IRQ Channels list, please refer to the appropriate CMSIS device file (stm32f10xxx.h))
  * @retval None
  */
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
  • 函数参数

  IRQn 是中断号,可以选择范围:IRQn_Type 定义的枚举类型,定义在 stm32f103xe.h;

/**
 * @brief STM32F10x Interrupt Number Definition, according to the selected device 
 *        in @ref Library_configuration_section 
 */

 /*!< Interrupt Number Definition */
typedef enum
{

/******  STM32 specific Interrupt Numbers *********************************************************/
  USART1_IRQn                 = 37,     /*!< USART1 global Interrupt                              */
  USART2_IRQn                 = 38,     /*!< USART2 global Interrupt                              */
  USART3_IRQn                 = 39,     /*!< USART3 global Interrupt                              */
  UART4_IRQn                  = 52,     /*!< UART4 global Interrupt                               */
  UART5_IRQn                  = 53,     /*!< UART5 global Interrupt                               */
} IRQn_Type;
  • 函数返回值

  无;

7.6、编写中断服务函数

  每开启一个中断,就必须编写其对应的中断服务函数,否则将导致死机(CPU 将找不到中断服务函数)。中断服务函数接口厂家已经在startup_stm32f103xe.s 中做好了,STM32F1 的 串口中断函数有 5 个,分别为:

void USART1_IRQHandler();
void USART2_IRQHandler();
void USART3_IRQHandler();
void UART4_IRQHandler();
void UART5_IRQHandler();

  当发生串口中断的时候,程序就会执行串口中断服务函数。HAL库 为了使用方便,提供了一个串口中断通用处理函数HAL_UART_IRQHandler(),该函数在串口接收完数据后,又会调用回调函数 HAL_UART_RxCpltCallback() ,用于给用户处理串口接收到的数据。

  HAL_UART_IRQHandler() 函数是 HAL库 中断处理公共函数,在串口中断服务函数中被调用。其定义如下:

/**
  * @brief  This function handles UART interrupt request.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @retval None
  */
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
  uint32_t isrflags   = READ_REG(huart->Instance->SR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);
  uint32_t errorflags = 0x00U;
  uint32_t dmarequest = 0x00U;

  /* If no error occurs */
  // 如果没有发生错误
  errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE));
  if (errorflags == RESET)
  {
    /* UART in mode Receiver -------------------------------------------------*/
    // UART接收模式。 RXNE:读数据寄存器非空 RXNEIE:接收缓冲区非空中断使能
    if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
    {
      // 在该函数里清除相关中断标志位并调用HAL_UART_RxCpltCallback()回调函数
      UART_Receive_IT(huart);
      return;
    }
  }

  /* If some errors occur */
  // 如果发生了错误
  if ((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET)))
  {
    /* UART parity error interrupt occurred ----------------------------------*/
    if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_PE;
    }

    /* UART noise error interrupt occurred -----------------------------------*/
    if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_NE;
    }

    /* UART frame error interrupt occurred -----------------------------------*/
    if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET))
    {
      huart->ErrorCode |= HAL_UART_ERROR_FE;
    }

    /* UART Over-Run interrupt occurred --------------------------------------*/
    if (((isrflags & USART_SR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_EIE) != RESET)))
    {
      huart->ErrorCode |= HAL_UART_ERROR_ORE;
    }

    /* Call UART Error Call back function if need be --------------------------*/
    if (huart->ErrorCode != HAL_UART_ERROR_NONE)
    {
      /* UART in mode Receiver -----------------------------------------------*/
      if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
      {
        UART_Receive_IT(huart);
      }

      /* If Overrun error occurs, or if any error occurs in DMA mode reception,
         consider error as blocking */
      dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR);
      if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest)
      {
        /* Blocking error : transfer is aborted
           Set the UART state ready to be able to start again the process,
           Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
        UART_EndRxTransfer(huart);

        /* Disable the UART DMA Rx request if enabled */
        if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
        {
          CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

          /* Abort the UART DMA Rx channel */
          if (huart->hdmarx != NULL)
          {
            /* Set the UART DMA Abort callback :
               will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
            huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;
            if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
            {
              /* Call Directly XferAbortCallback function in case of error */
              huart->hdmarx->XferAbortCallback(huart->hdmarx);
            }
          }
          else
          {
            /* Call user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
            /*Call registered error callback*/
            huart->ErrorCallback(huart);
#else
            /*Call legacy weak error callback*/
            HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
          }
        }
        else
        {
          /* Call user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
          /*Call registered error callback*/
          huart->ErrorCallback(huart);
#else
          /*Call legacy weak error callback*/
          HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
        }
      }
      else
      {
        /* Non Blocking error : transfer could go on.
           Error is notified to user through user error callback */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered error callback*/
        huart->ErrorCallback(huart);
#else
        /*Call legacy weak error callback*/
        HAL_UART_ErrorCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */

        huart->ErrorCode = HAL_UART_ERROR_NONE;
      }
    }
    return;
  } /* End if some error occurs */

  /* Check current reception Mode :
     If Reception till IDLE event has been selected : */
  if (  (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
      &&((isrflags & USART_SR_IDLE) != 0U)
      &&((cr1its & USART_SR_IDLE) != 0U))
  {
    __HAL_UART_CLEAR_IDLEFLAG(huart);

    /* Check if DMA mode is enabled in UART */
    if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
    {
      /* DMA mode enabled */
      /* Check received length : If all expected data are received, do nothing,
         (DMA cplt callback will be called).
         Otherwise, if at least one data has already been received, IDLE event is to be notified to user */
      uint16_t nb_remaining_rx_data = (uint16_t) __HAL_DMA_GET_COUNTER(huart->hdmarx);
      if (  (nb_remaining_rx_data > 0U)
          &&(nb_remaining_rx_data < huart->RxXferSize))
      {
        /* Reception is not complete */
        huart->RxXferCount = nb_remaining_rx_data;

        /* In Normal mode, end DMA xfer and HAL UART Rx process*/
        if (huart->hdmarx->Init.Mode != DMA_CIRCULAR)
        {
          /* Disable PE and ERR (Frame error, noise error, overrun error) interrupts */
          CLEAR_BIT(huart->Instance->CR1, USART_CR1_PEIE);
          CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

          /* Disable the DMA transfer for the receiver request by resetting the DMAR bit
             in the UART CR3 register */
          CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

          /* At end of Rx process, restore huart->RxState to Ready */
          huart->RxState = HAL_UART_STATE_READY;
          huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;

          CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);

          /* Last bytes received, so no need as the abort is immediate */
          (void)HAL_DMA_Abort(huart->hdmarx);
        }
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered Rx Event callback*/
        huart->RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));
#else
        /*Call legacy weak Rx Event callback*/
        HAL_UARTEx_RxEventCallback(huart, (huart->RxXferSize - huart->RxXferCount));
#endif
      }
      return;
    }
    else
    {
      /* DMA mode not enabled */
      /* Check received length : If all expected data are received, do nothing.
         Otherwise, if at least one data has already been received, IDLE event is to be notified to user */
      uint16_t nb_rx_data = huart->RxXferSize - huart->RxXferCount;
      if (  (huart->RxXferCount > 0U)
          &&(nb_rx_data > 0U) )
      {
        /* Disable the UART Parity Error Interrupt and RXNE interrupts */
        CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));

        /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
        CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

        /* Rx process is completed, restore huart->RxState to Ready */
        huart->RxState = HAL_UART_STATE_READY;
        huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;

        CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered Rx complete callback*/
        huart->RxEventCallback(huart, nb_rx_data);
#else
        /*Call legacy weak Rx Event callback*/
        HAL_UARTEx_RxEventCallback(huart, nb_rx_data);
#endif
      }
      return;
    }
  }

  /* UART in mode Transmitter ------------------------------------------------*/
  // UART发送模式。 TXE:发送数据寄存器空 TXEIE:发送缓冲区中断使能
  if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET))
  {
    UART_Transmit_IT(huart);
    return;
  }

  /* UART in mode Transmitter end --------------------------------------------*/
  // UART发送模式结束。 TC:发送完成    TCIE:发送完成中断使能
  if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
  {
    // 在该函数里清除相关中断标志位并调用HAL_UART_TxCpltCallback()回调函数
    UART_EndTransmit_IT(huart);
    return;
  }
}
  • 函数参数

  huart 是 UART_HandleTypeDef 结构体指针类型的串口句柄。这里我们使用它的 Instance 成员,Instance 成员的取值为:

#define USART1              ((USART_TypeDef *)USART1_BASE)
#define USART2              ((USART_TypeDef *)USART2_BASE)
#define USART3              ((USART_TypeDef *)USART3_BASE)
#define UART4               ((USART_TypeDef *)UART4_BASE)
#define UART5               ((USART_TypeDef *)UART5_BASE)
  • 函数返回值

  无;

7.7、编写中断处理回调函数

  HAL 库为了使用方便,提供了一个串口中断通用处理函数 HAL_UART_IRQHandler(),该函数在串口接收完数据后,又会调用回调函数HAL_UART_RxCpltCallback() ,用于给用户处理串口接收到的数据。

  UART 中断回调函数:

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);                // 发送完成回调函数
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);            // 半发送完成回调该函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);                // 接收完成回调函数
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);            // 半接收完成回调函数
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】、HAL_UART_RxCpltCallback() 函数

/**
  * @brief  Rx Transfer completed callbacks.
  * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
  *                the configuration information for the specified UART module.
  * @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 could be implemented in the user file
   */
}

7.8、串口数据发送

【1】、HAL_UART_Transmit() 函数

  HAL_UART_Transmit() 函数以阻塞的方式发送指定字节的数据。

/**
  * @brief  Sends 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.
  * @param  huart Pointer to a UART_HandleTypeDef structure that contains
  *               the configuration information for the specified UART module.
  * @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, uint8_t *pData, uint16_t Size, uint32_t Timeout);
  • 函数参数

  huart 是 UART_HandleTypeDef 结构体指针类型的串口句柄。这里我们使用它的 Instance 成员,Instance 成员的取值为:

#define USART1              ((USART_TypeDef *)USART1_BASE)
#define USART2              ((USART_TypeDef *)USART2_BASE)
#define USART3              ((USART_TypeDef *)USART3_BASE)
#define UART4               ((USART_TypeDef *)UART4_BASE)
#define UART5               ((USART_TypeDef *)UART5_BASE)

  pData 是要接收的数据地址

  Size 是要接收的数据大小,以字节为单位

  Timeout 是超时时间,以毫秒为单位

  • 函数返回值

  HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是:HAL_OK 表示 成功HAL_ERROR 表示 错误HAL_BUSY 表示 忙碌HAL_TIMEOUT 表示 超时

/**
  * @brief  HAL Status structures definition
  */
typedef enum
{
  HAL_OK       = 0x00U,
  HAL_ERROR    = 0x01U,
  HAL_BUSY     = 0x02U,
  HAL_TIMEOUT  = 0x03U
} HAL_StatusTypeDef;

八、串口接收发送数据

8.1、原理图

img

8.2、程序设计

  配置串口工作参数:

UART_HandleTypeDef huart1;

void USART1_Init(void)
{
  huart1.Instance = USART1;                             // 要配置的串口
  huart1.Init.BaudRate = 115200;                        // 波特率
  huart1.Init.WordLength = UART_WORDLENGTH_8B;          // 字长为8位数据格式
  huart1.Init.StopBits = UART_STOPBITS_1;               // 一个停止位
  huart1.Init.Parity = UART_PARITY_NONE;                // 无奇偶校验位
  huart1.Init.Mode = UART_MODE_TX_RX;                   // 收发模式
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;          // 无硬件流控
  HAL_UART_Init(&huart1);                               // 串口初始化
}

  串口底层初始化:

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)		    // 判断是否是串口1
  {
    /* USART1 clock enable */
    __HAL_RCC_USART1_CLK_ENABLE();                  // 使能串口时钟

    __HAL_RCC_GPIOA_CLK_ENABLE();                   // 使能串口对应的GPIO的时钟
    /**USART1 GPIO Configuration
    PA9     ------> USART1_TX
    PA10     ------> USART1_RX
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9;               // USART1的TX引脚
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;         // 模式为复用推挽输出
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;   // 输出速度为高速
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);         // 初始化PA9

    GPIO_InitStruct.Pin = GPIO_PIN_10;              // USART1的RX引脚
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;         // 模式为输入
    GPIO_InitStruct.Pull = GPIO_NOPULL;             // 不使用上下拉
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);         // 初始化PA10

    /* USART1 interrupt Init */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);        // 设置优先级
    HAL_NVIC_EnableIRQ(USART1_IRQn);                // 使能串口中断
  }
}

  开启串口异步接收中断:

HAL_UART_Receive_IT(huart,(uint8_t *)&usart_new_data,1);	// 开启串口异步接收中断

  串口中断服务函数:

void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1);     // 用HAL库串口中断处理公用函数
}

  串口数据接收完成回调函数:

#define USART_REC_LEN           200                                             // 定义最大接收字符数

/*
 * 接收状态
 * bit15        接收完成标志
 * bit14        接收到0x0D
 * bit13~0      接收到的有效字节数目
*/
uint16_t g_usart1_rx_sta = 0;                                                   // USART1接收状态标志
uint8_t g_usart1_new_data;                                                      // USART1串口中断接收的1个字节数据的缓存
uint8_t g_usart1_rx_buffer[USART_REC_LEN];                                      // USART1接收缓冲区,最大USART_REC_LEN个字节
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1)                                                // 如果是串口1
  {
//    printf("%c",g_usart1_new_data);                                           // 将接受的数据发送回电脑
    if ((g_usart1_rx_sta & 0x8000) == 0)                                        // 接收未完成
    {
      if (g_usart1_rx_sta & 0x4000)                                             // 接收到了0x0D,即回车键
      {
        if (g_usart1_new_data != 0x0A)                                          // 接收到的不是0x0A,即不是换行键
        {
          g_usart1_rx_sta = 0;                                                  // 接收错误,重新开始
        } 
        else 
        {
          g_usart1_rx_sta |= 0x8000;                                            // 接收完成了
        }
      } 
      else                                                                      // 还未接收到0x0D,即换行键
      {
        if (g_usart1_new_data == 0x0D)                                          // 接收到0xoD
        {
          g_usart1_rx_sta |= 0x4000;
        }
        else                                                                    // 接收到的不是0x0D,而是普通数据
        {                                    
          g_usart1_rx_buffer[g_usart1_rx_sta & 0x3FFF] = g_usart1_new_data;     // 存放到接收数据缓冲区
          g_usart1_rx_sta++;
          if (g_usart1_rx_sta > (USART_REC_LEN - 1))                            // 如果接收的数据超过缓冲区大小
          {   
            g_usart1_rx_sta = 0;                                                // 接收数据错误,重新开始接收
          }
        }
      }
    }
  
    HAL_UART_Receive_IT(huart,(uint8_t *)&g_usart1_new_data,1);                 // 再开启接收中断
  }
}

  重定向 printf() 函数:

  MDK 开发环境需要添加入内容:

/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */

#define USART   USART1

#if 1

#if (__ARMCC_VERSION >= 6010050)            /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t");  /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t");    /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */

#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)

struct __FILE
{
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
};

#endif

/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
    ch = ch;
    return ch;
}

/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
    x = x;
}

char *_sys_command_string(char *cmd, int len)
{
    return NULL;
}


/* FILE 在 stdio.h里面定义. */
FILE __stdout;

/* MDK下需要重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
    while ((USART->SR & 0X40) == 0);     /* 等待上一个字符发送完成 */

    USART->DR = (uint8_t)ch;             /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}
#endif

  如果使用 IAR 9 以上的环境,需要添加如下内容:

/*******************************************************************************
 *
 * Copyright 1998-2017 IAR Systems AB.
 *
 * This is a template implementation of the "__write" function used by
 * the standard library.  Replace it with a system-specific
 * implementation.
 *
 * The "__write" function should output "size" number of bytes from
 * "buffer" in some application-specific way.  It should return the
 * number of characters written, or _LLIO_ERROR on failure.
 *
 * If "buffer" is zero then __write should perform flushing of
 * internal buffers, if any.  In this case "handle" can be -1 to
 * indicate that all handles should be flushed.
 *
 * The template implementation below assumes that the application
 * provides the function "MyLowLevelPutchar".  It should return the
 * character written, or -1 on failure.
 *
 ******************************************************************************/

#include <LowLevelIOInterface.h>
#include "stm32f1xx_hal.h"

#define USART   USART1

#pragma module_name = "?__write"

int putChar(int ch)
{   
    while((USART->SR&0X40)==0);
    USART->DR = (uint8_t) ch;  
    return ch;
}

/*
 * If the __write implementation uses internal buffering, uncomment
 * the following line to ensure that we are called with "buffer" as 0
 * (i.e. flush) when the application terminates.
 */

size_t __write(int handle, const unsigned char * buffer, size_t size)
{
  /* Remove the #if #endif pair to enable the implementation */
#if 1

  size_t nChars = 0;

  if (buffer == 0)
  {
    /*
     * This means that we should flush internal buffers.  Since we
     * don't we just return.  (Remember, "handle" == -1 means that all
     * handles should be flushed.)
     */
    return 0;
  }

  /* This template only writes to "standard out" and "standard err",
   * for all other file handles it returns failure. */
  if (handle != _LLIO_STDOUT && handle != _LLIO_STDERR)
  {
    return _LLIO_ERROR;
  }

  for (/* Empty */; size != 0; --size)
  {
    if (putChar(*buffer++) < 0)
    {
      return _LLIO_ERROR;
    }

    ++nChars;
  }

  return nChars;

#else

  /* Always return error code when implementation is disabled. */
  return _LLIO_ERROR;

#endif

}

  main() 函数:

int main(void)
{
  int length = 0;

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  USART1_Init();
  
  HAL_UART_Receive_IT(&huart1,(uint8_t *)&usart1_new_data,1);	                // 开启串口异步接收中断
  
  while (1)
  {
    length = 0;
    if(g_usart1_rx_sta & 0x8000)                                                // 串口接收到了数据
    {             
      length = g_usart1_rx_sta & 0x3fff;                                        // 此次接收到的数据的长庿
      printf("你发送的数据为:\r\n");
      HAL_UART_Transmit(&huart1,(uint8_t *)g_usart1_rx_buffer,length,1000);     // 串口发鿁数捿
      while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC) != SET);
      printf("\r\n");
      g_usart1_rx_sta = 0;
    }
  }
}

标签:13,HAL,USART,UART,通信,huart,串口,define
From: https://www.cnblogs.com/kurome/p/17615210.html

相关文章

  • SD130T带充电平衡,双节锂电池充电控制芯片
    SD130T是一款完整的双节锂离子电池充电器,带电池正负极反接保护,采用恒定电流/恒定电压线性控制。只需较少的外部元件数目使得SD130T便携式应用的理想选择。SD130T可以适合USB电源和适配器电源工作。由于采用了内部PMOSFET架构,加上防倒充电路,所以不需要外部检测电阻器和隔离二极管......
  • 从Angular 13升级到Angular 15
    1.前言升级应用程序或者框架是软件生命周期中非常重要的一项活动.因为其有风险性,很多人不愿意去做,久而久之随着技术债务的积累变成了一件不能去做的事情.在我的职业生涯中见到过很大这样逐渐失去生命活力的系统,这里就不具体举例了,以免引起不必要的争论,明白的人自然......
  • ABC313C 扩展
    简要题意:给定长为\(n\)的序列,再给定\(k\),可以进行若干次以下操作:每次选定一个数\(i(1\lei\len)\)使得\(a_{i}\leftarrowa_{i}+k\)或者\(a_{i}\leftarrowa_{i}-k\),最小化最终数组的最大值与最小值之差。这个题是去年学校模拟赛的题目,这两天又给翻了出来((......
  • H7-TOOL的高速DAPLINK用于新版STM32CubeIDE V1.13及其以上版本的超简单实现方法(2023-0
    之前分享了一个方法,太繁琐了,H7-TOOL群的群友提供了一个方法,实现非常简单。1、使用STM32CubeMX或者自己创建一个STM32CubeIDE工程后,设置这两个地方即可: 配置调试器,设置完毕记得点击右下角的Apply2、然后修改这个cfg文件,F407IGTDebug.cfg,注意和第1步cfg是一个文件。修改......
  • ubuntu 串口使用
    两种方法:第一种:添加串口设备访问规则以root身份执行:vim/etc/udev/rules.d/70-ttyusb.rules内容为:KERNEL=="ttyUSB[0-9]*",MODE="0666"注意:内容里边的双引号不能丢,否则不会生效此方法有两个弊端:1.该方法会让所有的普通用户都具有访问这些串口设备的权限,存在一定的安全隐......
  • 「P4313」文理分科 解题报告
    「P4313」文理分科题目描述文理分科是一件很纠结的事情!(虽然看到这个题目的人肯定都没有纠结过)小P所在的班级要进行文理分科。他的班级可以用一个\(n\timesm\)的矩阵进行描述,每个格子代表一个同学的座位。每位同学必须从文科和理科中选择一科。同学们在选择科目的时候会获......
  • 光纤通信与无线电通信的区别有哪些?
    光纤通信和无线电通信是两种不同的通信方式,它们之间的区别如下:传输介质:光纤通信使用光纤作为传输介质,而无线电通信使用空气作为传输介质。传输距离:光纤通信的传输距离较短,一般在几十公里到几百公里之间,而无线电通信的传输距离较远,可以覆盖数千公里以上的范围。抗干扰能力:光纤通......
  • event 10513将禁止smon进程进行事务回滚
    Oracle参数之event10513发布时间:[2012-4-10]    来源:    作者:    点击:我们设置Oracle参数event10513将禁止smon进程进行事务回滚。在进行troubleshooting时,如shutdownabort后设置该速度可以加快数据库的open速度,注意加快速度的同时,也可能带来数据库一致性问......
  • Siemens 西门子S7-200 SMART PLC与组态王以太网通信
    组态王与S7-200SMARTPLC以太网通信,对于刚接触者有点难度,不知如何进行配置,如何通信,那么接下来教大家来一步一步来做通信。一、下载驱动第一步:(在组态王官方下载TCP驱动文件)下载链接:http://www.kingview.com/第二步:(下载完成,如下图所示)压缩文件二、配置TCP驱动文件第一步:(解......
  • POJ 1392 Ouroboros Snake
    \(POJ\)\(1392\)-\(Ouroboros\)\(Snake\)//这道题和上面那道题几乎一样,算是变形题把,这道题要求构造的01字符串就是必须是字典序最小的,//在上面那道题的注意下建边的顺序即可.因为是链式前向星法,应该大边在前。\(Code\)#include<iostream>#include<algorithm>#......