STM32串口通信学习总结
1.概述
1.1学习目的
通过基于AWA5812平台,学习STM32F767芯片的串口通信程序开发。本人学习串口通信比较曲折,一开始使用的芯片时STM32新开发出来的一块STM32L496ZG的板子,这块板子的优点时低功耗,性能在M4系列中也比较强大,满足了公司对现有产品升级的需求,而对于我这个新入手不到一个月的职场小白来说,这块板子时相对的不友好了,所先从资料来说除了其芯片手册,参考手册,数据手册,STM32官网包括论坛就几乎没有其他资料,而且全是英文,曾看到论坛的人在喊说大家一起来翻译中文版吧,一人一段,看似很令人兴奋的事,可这也说明现在是别想指望由中文版的了,所以只有硬着头皮上吧;其次STM32官方给的例程在板子上没跑通,这就奇了怪了,例程能错嘛?坚持了2周我果断实现了从入门到放弃的过程。可工作要求实在没办法,煎熬了一周,果断向正点原子爸爸靠拢,花了千元买了块阿波罗F767开发板,毫无疑问,正点原子爸爸给视频给例程还给中文参考手册,手把手教你编程,百度以下所遇到的问题,只应证了一点,你经历的别人都经历过。好的话不多说,来讲讲我的STM32串口通信的学习历程吧。
1.2.学习过程
接下来我将从以下几个方面介绍我学习过程
1、物理层
2、通用同步异步收发器
3、STM32串口通信过程
4、串口配置的一般步骤及代码分析
5、问题总结
1.3参考文档
《STM32F767xx数据手册》
《STM32F4开发指南 v1.1 HAL库版本》
《RM0385参考手册》
- 物理层
2.1通讯标准说明
串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,大部分电子设备都支持该通讯方式,我们在调试设备时也经常使用该通讯方式输出调试信息。
串口通信的物理层主要在两设备间,主要用的标准是RS-232标准,它规定了信号的用途、通讯接口及信号的电平水平。
2.2 DB9接口中的公头及母头的各引脚示意图
2.3 ST-Link/v2接口说明
2.3.1 LED状态说明:
闪烁红色:ST-LINK/V2连接到计算机后,第一次USB枚举过程
红色:ST-LINK/V2与计算机已建立连接
闪烁绿色/红色:目标板和计算机在进行数据交换
绿色:通讯完成
橙色(红色+绿色):通讯失败
2.3.2 STM8接口定义
仿真端口 | 连接目标板 | 功能 |
1、VDD | MCU VCC | 连接STM8目标板的电源VCC |
2、DATA | MCU SWIM | 连接STM8目标板的SWIM PIN |
3、GND | GND | 连接STM8目标板的电源GND |
4、RESET | MCU RESET PIN | 连接STM8目标板的RESET PIN |
排列示意图如下
2.3.3 STM32接口定义
仿真器端口 | 连接目标板 | 功能 |
1. TVCC | MCU电源VCC | 连接STM32目标板的电源VCC |
2. TVCC | MCU电源VCC | 连接STM32目标板的电源VCC |
3. TRST | GND | GROUND |
4. UART-RX | GND | GROUND |
5. TDI | TDI | 连接STM32的JTAG TDI |
6. UART-TX | GND | GROUND |
7. TMS, SWIO | TMS, SWIO | 连接STM32的JTAG的TMS, SWD的SW IO |
8. BOOT0 | GND | GROUND |
9. TCK, SWCLK | TCK, SWCLK | 连接STM32的JTAG的TCK, SWD的SW CLK |
10. SWIM | GND | GROUND |
11. NC | NC | Unused |
12. GND | GND | GROUND |
13. TDO | TDO | 连接STM32的JTAG TDO |
14. SWIM-RST | GND | GROUND |
15. STM32-RESET | RESET | 连接STM32目标板的RESET端口 |
16. KEY | NC | GROUND |
17. NC | NC | Unused |
18. GND | GND | GROUND |
19. VDD | NC | VDD (3.3V) |
20. GND | GND | GROUN |
排列示意图
- 通用同步异步收发器
通用同步异步收发器是一个串行通信设备,可以灵活的与外部设备进行全双工数据交换。有别与USART,还有一个UART,它在USART基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART,因此在不考虑同步通信时,USART与UART没有太大差别。
串口通信一般是以帧格式传输数据,即一帧一帧传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息。
USART满足外部设备对工业标准 NRZ 异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率,使得它的应用更加广泛。USART 支持同步单向通信和半双工单线通信;还支持局域互连网络 LIN、智能卡(SmartCard)协议与 lrDA(红外线数据协会) SIR ENDEC规范。
USART支持使用 DMA,可实现高速数据通信。
以下时USART的框图:
注:fCK 可以是 fLSE、fHSI、fPCLK、fSYS.
由框图我将其分为四部分来学习:
- 功能引脚
- 寄存器
- 控制器
- 波特率生成
3.1 功能引脚
引脚 | 功能 |
TX | 发送数据输出引脚。 |
RX | 接收数据输出引脚 |
nRTS | 请求以发送,n表示低电平有效。如果使能 RTS 流控制,当USART接收器准备好接收新数据时就会将nRTS变成低电平;当接收寄存器已满时,nRTS将被设置为高电平。该引脚只适用于硬件流控制。 |
nCTS | 清除以发送(Clear To Send),n表示低电平有效。如果使能 CTS流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制 |
SCLK | 发送器时钟输出引脚。这个引脚仅适用于同步模式。 |
3.2数据寄存器
USART说数据寄存器(USART_DR)只有低 9 位有效,并且第 9 位数据是否有效要取决于USART 控制寄存器 1(USART_CR1)的 M 位设置,当 M 位为 0 时表示 8 位数据字长,当 M位为 1 表示 9 位数据字长,我们一般使用 8位数据字长。
USART_DR包含了已发送的数据或者接收到的数据。USART_DR实际是包含了两个寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。当进行发送操作时,往 USART_DR写入数据会自动存储在 TDR内;当进行读取操作时,向 USART_DR读取数据会自动提取 RDR 数据。
TDR和RDR都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。
USART 支持 DMA 传输,可以实现高速数据传输。
3.3 控制器
USART有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等。
使用USART之前需要向USART_CR1寄存器的UE位置1使能USART,UE位用于开启供给串口的时钟。发送或者接收数据字长可选8或9位,由USARTT_CR1的M位控制。
3.3.1发送器
当USART_CR1寄存器的发送使能位TE置1时,启动数据发送,发送移位寄存器的数据会在TX引脚输出,低位在前,高位在后。如果是同步模式SCLK也输出时钟信号。
一个字符帧发送需要3部分:起始位、数据帧、停止位。起始位是一个位周期的低电平,位周期就是每一位占用的时间;数据帧就是我们要发送的7或8或9位数据,数据是最低位开始传输的;停止位是一定时间周期的高电平。
停止位的时间长短可以通过USART控制寄存器2(USART_CR2)的STOP[1:0]位控制,可选0.5个、1个、1.5个、2个停止位。默认使用1个停止位。2个停止位适用于正常USART模式、单线模式和调制解调器模式。0.5和1.5个停止位用于智能卡模式。
当发使能位TE置1之后,发送器开始会发送一个空闲帧(一个数据帧长度的高电平),接下来就可以往USART_DR寄存器写入要发送的数据。在写入最后一个数据后,需等待USART状态寄存器(USART_SR)的TC位为1,表示数据传输完成。USART_CR1寄存器的TCIE位置1,则产生中断。
发送数据时,几个重要的标志位如下:
TE:发送使能。
TXE:发送寄存器为空,发送单个字节时使用。
TC:发送完成,发送多个字节数据时候使用。
TXIE:发送完成中断使能。
3.3.2 接收器
将CR1寄存器的RE位置1,使能USART接收,使得接收器在RX线开始搜索起始位。在确定起始位后,就根据RX线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器的数据移到PDR内,并把USART_SR寄存器的RXNE位置。如果USART_CR2寄存器的RXNEIE置1可以产生中断。
接收数据时,几个重要的标志位如下:
RE: 接收使能。
RXNE:读数据寄存器非空。
RXNEIE:发送完成中断使能。
3.4 波特率生产
接收器和发送器(Rx和Tx)的波特率设置为USARTx_BRR寄存器中编程的相同值。
公式1;适用于标准USART(包括SPI模式)的波特率(OVER8=0或1)
16倍过采样时的公式为;
8倍过采样时的公式为:
如何从USARTx_BRR寄存器中获取USARTDIV呢?
如本次实验的要通过获取115200波特率。
16倍过采样时:
USARTDIV=8000000/115200
BRR=USARTDIV=69d=0x45
16倍过采样时:
USARTDIV=2*8000000/115200
USARTDIV=138.89(139d=8Bh)
BRR[3:0]=Bh>>1=1h
BRR=0x8B
3.5 总结
UART异步通信方式特点:
- 全双工异步通信
- 小数波特率发送器系统,提供精准的波特率
- 可配置的16倍过采样或8倍过采样,因此为速度容差与时间容差的灵灵活配置提供了可能
- 可编码的数据字长度(8位或者9位)
- 可配置的停止位(支持1或者2位停止位)
- 可配置的适用DMA多缓存器通信
- 单独的发送器和接收器使能位
- 检测标志:1.接收缓存器2.发送缓冲器3.传输结束标志
- 多个带标志的中断源,触发中断。
- 其他:校验控制,四个错误检测标志
4、STM32串口通信过程
USAT异步通信方式引脚(STM32F767IGT6)
串口号 | USART_RX | USART_TX |
USART1 | PA10(PB7) | PA9(PB6) |
USART2 | PA3(PD6) | PA2(PD5) |
USART3 | PB11(PC11)(PD9) | PB10(PC10)(PD8) |
UART4 | PA1(PC11) | PA0(PC10) |
UART5 | PD2 | PC12 |
USART6 | PC7(PG9) | PC6(PG14) |
5、串口配置的一般步骤及代码分析
步骤 | 使用代码 |
| RCC_APBxPeriphClockCmd(); |
GPIO时钟使能 | RCC_AHB1PeriphClockCmd(); |
| GPIO_PinAFConfig(); |
| GPIO_Init();模式设置为GPIO_MoDe_AF |
| USART_Init(); |
| NVIC_Init(); |
| USART_ITConfig(); |
| USART_Cmd; |
| USARTx_IRQHandler(); |
| Void USART SentDate();//发送数据到串口 |
| Uint16_t USART_ReceiveDate();//接收数据 |
| FlagStatus USART_GetFlagStatus(); |
| Void USART_ClearITPendingBit(); |
5.1代码分析
5.1.1 USART初始化结构体
初始化结构体的定义在stm32f7xx_hal_uart.h中,初始化库函数定义在stm32f7xx_hal_uart.c中。
- Instance:串口选择
- USART_BaudRate:波特率设置。标准库函数会根据设定值计算得到USARTDIV值,从而设置USART_BRR的寄存器值,我们通常设置为115200。
- USART_WordLength:.数据帧字长,它设定USART_CR1寄存器M位的值。如果没有使能奇偶位校验控制,一般使用8数据位。
- USART_StopBits停止位设置。停止位有1、1.5、2三种,通常有一个停止位
- USART_Parity:奇偶校验控制选择。通常选择无奇偶校验。
- USART_Mode:USART模式选择有USART_Mode_Rx和USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1寄存器的 RE 位和 TE位。所以通常设置为收发模式
- USART_HardwareFlowControl:硬件流控制选择,只有在硬件流控制模式下才有效,可选有,使能RTS、使能CTS、同时使能RTS和CTS、不使用硬件流。
- HAL_UART_Init:初始化设置并使能USART6后调用HAL_UART_MspInit()函数。
- HAL_USART_Receive:该函数会开启接收中断,标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
5.1.2 UART底层初始化,时钟使能,引脚配置,中断配置
HAL_UART_MspInit()函数会被HAL_UART_Init()函数调用,在stm32f7xx_hal_uart.c中可看见一个__weak void HAL_UART_MspInit(UART_HandleTypeDef *huart)函数,weak即为弱定义,如果其他地方定义了,则执行定义的,不会出现重定义错误,如果其他地方未定义,只有这个弱定义,则执行此弱定义。
- GPIO_InitTypeDef GPIO_Initure:句柄定义
- If条件语句:判断是否时串口x,此处可以将6个串口的GPIO配置都写入,自建库,以后就可直接调用
- _HAL_RCC_GPIOC_CLK_ENABLE():使能GPIOC时钟
- __HAL_RCC_USART6_CLK_ENABLE():使能USART6时钟
- GPIO_Initure:GPIO初始化配置,包括工作模式、输出模式、输出速度、复用功能
- HAL_GPIO_Init:初始化GPIO结构体
- HAL_NVIC_EnableIRQ:使能USART6中断通道
- HAL_NVIC_SetPriority:设置中断优先级,抢占优先级3,子优先级3
5.1.3 接收器
HAL_UART_RxCpltCallback(huart)函数在使用时,就调用HAL_UART_RxCpltCallback函数进行数据的接收。数据从串口接收端接收,并在使能情况下,自动写入接收数据寄存器。在定义中已定义USART_RX_STA标志位,bit15位为接收完成标志,即接收到0x0a,bit14位为接收到0x0d,bit13~bit0位接收到的有效字节数目,因此,本代码主要判断数据结尾是否到0x0d0a.
5.1.4中断服务程序
本中断服务程序主要处理超时处理
5.1.5 发送器
发送器位于主循环中,主要是判断已接收到标志位0x0a0d后,将接收寄存器的值赋给发送寄存器,而发送寄存器向串口发送数据。
4.1.6 实验结果
6、问题总结
6.1 调试中遇到的问题
1.无论PC发什么,stm32都没有回应?
调试过程:把初始化的程序与网上众多程序员写的初始化程序做了比较,没有发现不一样的地方。觉得可能是是程序的问题,因此dubug了一下,发现在我设置的接收数组中,是有值写入的,也就是接收寄存器能够接收到PC端发送的值,按程序的运行,接下来应该是将接收数组赋值给发送寄存器,可问题就在于发送寄存器中没有收到赋值。检查程序后发现赋值处是有问题的,于是进行修改
- 无论PC端发送什么都乱码?
调试过程:在确定了软件上能过接收到PC端发来的信号,并且能发送出去的前提下,
PC窗口却出现乱码现象,判断软件上是没有问题的,于是用示波器去测量硬件数据,发现PC接收端与STM32F767芯片之间的一块控压芯片存在虚焊。发现问题不要一味认为是软件问题,需要软硬件兼顾的区考虑。
3.stm32发回来的内容与PC发送的不一致?
调试过程:用示波器观测数据,发现收发的数据都是正确的,但电平宽度不一致,由此得知两者的波特率不一致,进一步计算得知是stm32的串口波特率不对,后发现程序默认时钟用的MSI内部时钟,而波特率固定的四个时钟中没有MSI,因此修改使用外部高速时钟HSE,其时钟频率为8MHz。
需说明的是,配置时钟需要了解时钟树如何区配置,建议利用STM32CubeMX来学习,如果不会编写其时钟代码,可用其生成程序。