第30章 STM32H7的USART应用之八个串口FIFO实现
本章节为大家讲解STM327的8个串口的FIFO驱动实现,后面的ESP8266,GPS,RS485,GPRS等试验都是建立在这个驱动的基础上实现。
除了串口FIFO的驱动实现,RS232通信也通过本章节做个讲解。
30.1 初学者重要提示
30.2 硬件设计
30.3 串口驱动设计
30.4 串口FIFO板级支持包(bsp_uart_fifo.c)
30.5 串口FIFO驱动移植和使用
30.6 实验例程设计框架
30.7 实验例程说明(MDK)
30.8 实验例程说明(IAR)
30.9 总结
30.1 初学者重要提示
学习本章节前,务必优先学习第29章。
串口FIFO的实现跟前面章节按键FIFO的机制是一样的。
本章节比较重要,因为后面的ESP8266,GPS,RS485,GPRS等试验都是建立在这个驱动的基础上实现。
大家自己做的板子,测试串口收发是乱码的话,重点看stm32h7xx_hal_conf.h文件中的HSE_VALUE的大小跟板子上实际晶振大小是否一致,然后再看PLL配置。
CH340/CH341的USB转串口Windows驱动程序的安装包,支持32/64位 Windows 10/8.1/8/7。http://www.armbbs.cn/forum.php?mod=viewthread&tid=32826 。
30.2 硬件设计
STM32H743XIH6最多可以支持8个独立的串口。其中串口4和串口5和SDIO的GPIO是共用的,也就是说,如果要用到SD卡,那么串口4和串口5将不能使用。串口7和SPI3共用,串口8和RGB硬件接口共用。串口功能可以分配到不同的GPIO。我们常用的引脚分配如下:
串口USART1 TX = PA9, RX = PA10
串口USART2 TX = PA2, RX = PA3
串口USART3 TX = PB10, RX = PB11
串口UART4 TX = PC10, RX = PC11 (和SDIO共用)
串口UART5 TX = PC12, RX = PD2 (和SDIO共用)
串口USART6 TX = PG14, RX = PC7
串口UART7 TX = PB4, RX = PB3 (和SPI1/3共用)
串口UART8 TX = PJ8, RX =PJ9 (和RGB硬件接口共用)
STM32-V7开发板使用了4个串口设备。
串口1用于RS232接口,很多例子的pritnf结果就是输出到串口1
串口2用于GPS
串口3用于RS485接口
串口6 用于TTL串口插座,板子上有GPRS插座和串口WIFI插座。
下面是RS232的原理图:
关于232的PHY芯片SP3232E要注意以下几个问题:
SP3232E的作用是TTL电平转RS232电平。
电阻R130的作用是避免CPU复位期间,TX为高阻时串口线上出现异常数据。
检测SP3232E的好坏可以采用回环的方式,即短接T1OUT和R1IN,对应到DB9插座上就是短接引脚2和引脚3。
实际效果如下:
通过这种方式,可以在应用程序中通过串口发送几个字符,查看是否可以正确接收来判断232 PHY芯片是否有问题。
由于这里是TTL转RS232,如果电脑端自带DB9串口,可以找根交叉线直接接上。如果电脑端没有,就需要用RS232转USB的串口线。这里要注意是RS232转USB,不是TTL转USB。像我们用的CH340就是RS232转USB芯片。
检测串口线的好坏跟板子上的232 PHY一样,将电脑端的串口助手打开,串口线接到电脑端并短接串口线的2脚和3脚,然后使用串口助手进行自收发测试即可。
30.3 串口FIFO驱动设计
30.3.1 串口FIFO框架
为了方便大家理解,先来看下串口FIFO的实现框图:
第1阶段,初始化:
通过函数bsp_InitUart初始化串口结构体,串口硬件参数。
第2阶段,串口中断服务程序:
接收中断是一直开启的。
做了发送空中断和发送完成中断的消息处理。
第3阶段,串口数据的收发:
串口发送函数会开启发送空中断。
串口接收中断接收到函数后,可以使用函数comGetChar获取数据。
30.3.2 串口FIFO之相关的变量定义
串口驱动的核心文件为:bsp_uart_fifo.c, bsp_uart_fifo.h。
这里面包括有串口硬件的配置函数、中断处理函数,以及串口的读写接口函数。还有ptinft函数的实现。
每个串口都有2个FIFO缓冲区,一个是用于发送数据的TX_FIFO,一个用于保存接收数据的RX_FIFO。
我们来看下这个FIFO的定义,在bsp_uart_fifo.h文件。
/*定义串口波特率和FIFO缓冲区大小,分为发送缓冲区和接收缓冲区, 支持全双工*/
#if UART1_FIFO_EN == 1
#define UART1_BAUD 115200
#define UART1_TX_BUF_SIZE 1*1024
#define UART1_RX_BUF_SIZE 1*1024
#endif
/*串口设备结构体*/typedefstruct{
USART_TypeDef*uart; /*STM32内部串口设备指针*/uint8_t*pTxBuf; /*发送缓冲区*/uint8_t*pRxBuf; /*接收缓冲区*/uint16_t usTxBufSize;/*发送缓冲区大小*/uint16_t usRxBufSize;/*接收缓冲区大小*/__IO uint16_t usTxWrite;/*发送缓冲区写指针*/__IO uint16_t usTxRead;/*发送缓冲区读指针*/__IO uint16_t usTxCount;/*等待发送的数据个数*/__IO uint16_t usRxWrite;/*接收缓冲区写指针*/__IO uint16_t usRxRead;/*接收缓冲区读指针*/__IO uint16_t usRxCount;/*还未读取的新数据个数*/
void (*SendBefor)(void); /*开始发送之前的回调函数指针(主要用于RS485切换到发送模式)*/
void (*SendOver)(void); /*发送完毕的回调函数指针(主要用于RS485将发送模式切换为接收模式)*/
void (*ReciveNew)(uint8_t _byte); /*串口收到数据的回调函数指针*/uint8_t Sending;/*正在发送中*/}UART_T;
bsp_uart_fifo.c文件定义变量。我们以串口1为例,其他的串口都是一样的代码。
/*定义每个串口结构体变量*/
#if UART1_FIFO_EN == 1
staticUART_T g_tUart1;static uint8_t g_TxBuf1[UART1_TX_BUF_SIZE]; /*发送缓冲区*/
static uint8_t g_RxBuf1[UART1_RX_BUF_SIZE]; /*接收缓冲区*/
#endif
关于FIFO的机制,我们在按键FIFO驱动已经做过详细的介绍,这个地方就不赘述了。每个串口有两个FIFO缓冲区,每个FIFO对应一个写指针和一个读指针。这个结构中还有三个回调函数。回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。
30.3.3 串口FIFO初始化
串口的初始化代码如下;
/**********************************************************************************************************
* 函 数 名: bsp_InitUart
* 功能说明: 初始化串口硬件,并对全局变量赋初值.
* 形 参: 无
* 返 回 值: 无
**********************************************************************************************************/
void bsp_InitUart(void)
{
UartVarInit();/*必须先初始化全局变量,再配置硬件*/InitHardUart();/*配置串口的硬件参数(波特率等)*/RS485_InitTXE();/*配置RS485芯片的发送使能硬件,配置为推挽输出*/}
下面将初始化代码实现的功能依次为大家做个说明。
函数UartVarInit
这个函数实现的功能比较好理解,主要是串口设备结构体变量的初始化,代码如下:
/**********************************************************************************************************
* 函 数 名: UartVarInit
* 功能说明: 初始化串口相关的变量
* 形 参: 无
* 返 回 值: 无
**********************************************************************************************************/
static void UartVarInit(void)
{#if UART1_FIFO_EN == 1g_tUart1.uart= USART1; /*STM32 串口设备*/g_tUart1.pTxBuf= g_TxBuf1; /*发送缓冲区指针*/g_tUart1.pRxBuf= g_RxBuf1; /*接收缓冲区指针*/g_tUart1.usTxBufSize= UART1_TX_BUF_SIZE; /*发送缓冲区大小*/g_tUart1.usRxBufSize= UART1_RX_BUF_SIZE; /*接收缓冲区大小*/g_tUart1.usTxWrite= 0; /*发送FIFO写索引*/g_tUart1.usTxRead= 0; /*发送FIFO读索引*/g_tUart1.usRxWrite= 0; /*接收FIFO写索引*/g_tUart1.usRxRead= 0; /*接收FIFO读索引*/g_tUart1.usRxCount= 0; /*接收到的新数据个数*/g_tUart1.usTxCount= 0; /*待发送的数据个数*/g_tUart1.SendBefor= 0; /*发送数据前的回调函数*/g_tUart1.SendOver= 0; /*发送完毕后的回调函数*/g_tUart1.ReciveNew= 0; /*接收到新数据后的回调函数*/g_tUart1.Sending= 0; /*正在发送中标志*/
#endif
/*串口2-8的初始化省略未写*/}
函数InitHardUart
此函数主要用于串口的GPIO,中断和相关参数的配置。
1. /*串口1的GPIO PA9, PA10 RS323 DB9接口*/
2. #define USART1_CLK_ENABLE() __HAL_RCC_USART1_CLK_ENABLE()
3.4. #define USART1_TX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
5. #define USART1_TX_GPIO_PORT GPIOA
6. #define USART1_TX_PIN GPIO_PIN_9
7. #define USART1_TX_AF GPIO_AF7_USART1
8.9. #define USART1_RX_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
10. #define USART1_RX_GPIO_PORT GPIOA
11. #define USART1_RX_PIN GPIO_PIN_10
12. #define USART1_RX_AF GPIO_AF7_USART1
13.14. /*串口2-8的引脚和时钟宏定义未写*/
15.16. /*17. ******************************************************************************************************
18. * 函 数 名: InitHardUart
19. * 功能说明: 配置串口的硬件参数(波特率,数据位,停止位,起始位,校验位,中断使能)适合于STM32-H7开
20. * 发板
21. * 形 参: 无
22. * 返 回 值: 无
23. ******************************************************************************************************
24.*/
25. static void InitHardUart(void)26. {27. GPIO_InitTypeDef GPIO_InitStruct;28. RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit;29.30. /*31. 下面这个配置可以注释掉,预留下来是为了方便以后选择其它时钟使用
32. 默认情况下,USART1和USART6选择的PCLK2,时钟100MHz。
33. USART2,USART3,UART4,UART5,UART6,UART7和UART8选择的时钟是PLCK1,时钟100MHz。
34.*/
35. RCC_PeriphClkInit.PeriphClockSelection =RCC_PERIPHCLK_USART16;36. RCC_PeriphClkInit.Usart16ClockSelection =RCC_USART16CLKSOURCE_D2PCLK2;37. HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit);38.39. #if UART1_FIFO_EN == 1 /* 串口1 */
40. /*使能 GPIO TX/RX 时钟*/
41. USART1_TX_GPIO_CLK_ENABLE();42. USART1_RX_GPIO_CLK_ENABLE();43.44. /*使能 USARTx 时钟*/
45. USART1_CLK_ENABLE();46.47. /*配置TX引脚*/
48. GPIO_InitStruct.Pin =USART1_TX_PIN;49. GPIO_InitStruct.Mode =GPIO_MODE_AF_PP;50. GPIO_InitStruct.Pull =GPIO_PULLUP;51. GPIO_InitStruct.Speed =GPIO_SPEED_FREQ_VERY_HIGH;52. GPIO_InitStruct.Alternate =USART1_TX_AF;53. HAL_GPIO_Init(USART1_TX_GPIO_PORT, &GPIO_InitStruct);54.55. /*配置RX引脚*/
56. GPIO_InitStruct.Pin =USART1_RX_PIN;57. GPIO_InitStruct.Alternate =USART1_RX_AF;58. HAL_GPIO_Init(USART1_RX_GPIO_PORT, &GPIO_InitStruct);59.60. /*配置NVIC the NVIC for UART*/
61. HAL_NVIC_SetPriority(USART1_IRQn, 0, 1);62. HAL_NVIC_EnableIRQ(USART1_IRQn);63.64. /*配置波特率、奇偶校验*/
65. bsp_SetUartParam(USART1, UART1_BAUD, UART_PARITY_NONE, UART_MODE_TX_RX);66.67. SET_BIT(USART1->ICR, USART_ICR_TCCF); /*清除TC发送完成标志*/
68. SET_BIT(USART1->RQR, USART_RQR_RXFRQ); /*清除RXNE接收标志*/
69. //USART_CR1_PEIE | USART_CR1_RXNEIE
70. SET_BIT(USART1->CR1, USART_CR1_RXNEIE); /*使能PE. RX接受中断*/
71. #endif
72. /*串口2-8的初始化省略未写*/
73. }
第2-12行,以宏定义的方式设置串口1-8的GPIO时钟、引脚和串口时钟,方便修改。
第35-37行ÿ