USART—串口通讯
本章中主要讲解的是串口异步通讯,异步通讯中由于没有时钟信号, 所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码 。
串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一
个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示,
只要双方约定一致即可。
STM32 芯片具有多个 USART 外设用于串口通讯,它是 Universal Synchronous
Asynchronous Receiver and Transmitter 的缩写,即通用同步异步收发器可以灵活地与外部设
备进行全双工数据交换。有别于 USART,它还有具有 UART 外设(Universal Asynchronous
Receiver and Transmitter),它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。
简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基
本都是 UART。
STM32 的 USART 输出的是 TTL 电平信号,若需要 RS-232 标准的信号可使用
MAX3232 芯片进行转换。
①功能引脚
TX:发送数据输出引脚。
RX:接收数据输入引脚。
SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引
脚。
nRTS:请求以发送(Request To Send), n 表示低电平有效。如果使能 RTS 流控制,当
USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时,
nRTS 将被设置为高电平。该引脚只适用于硬件流控制。
nCTS:清除以发送(Clear To Send), n 表示低电平有效。如果使能 CTS 流控制,发送
器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为
高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。
USART 引脚在 STM32F429IGT6 芯片具体发布见表 20-3。
应用型编程,先会使用,有需要再去解析内部实现:
int main(void) { /*初始化USART 配置模式为 115200 8-N-1,中断接收*/ Debug_USART_Config(); /* 发送一个字符串 */ Usart_SendString( DEBUG_USART,"这是一个串口中断接收回显实验\n"); printf("这是一个串口中断接收回显实验\n"); while(1) { } }
void Debug_USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_AHB1PeriphClockCmd(DEBUG_USART_RX_GPIO_CLK|DEBUG_USART_TX_GPIO_CLK,ENABLE); /* 使能 USART 时钟 */ RCC_APB2PeriphClockCmd(DEBUG_USART_CLK, ENABLE); /* GPIO初始化 */ GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /* 配置Tx引脚为复用功能 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN ; GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure); /* 配置Rx引脚为复用功能 */ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN; GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure); /* 连接 PXx 到 USARTx_Tx*/ GPIO_PinAFConfig(DEBUG_USART_RX_GPIO_PORT,DEBUG_USART_RX_SOURCE,DEBUG_USART_RX_AF); /* 连接 PXx 到 USARTx__Rx*/ GPIO_PinAFConfig(DEBUG_USART_TX_GPIO_PORT,DEBUG_USART_TX_SOURCE,DEBUG_USART_TX_AF); /* 配置串DEBUG_USART 模式 */ /* 波特率设置:DEBUG_USART_BAUDRATE */ USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; /* 字长(数据位+校验位):8 */ USART_InitStructure.USART_WordLength = USART_WordLength_8b; /* 停止位:1个停止位 */ USART_InitStructure.USART_StopBits = USART_StopBits_1; /* 校验位选择:不使用校验 */ USART_InitStructure.USART_Parity = USART_Parity_No; /* 硬件流控制:不使用硬件流 */ USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; /* USART模式控制:同时使能接收和发送 */ USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; /* 完成USART初始化配置 */ USART_Init(DEBUG_USART, &USART_InitStructure); /* 嵌套向量中断控制器NVIC配置 */ NVIC_Configuration(); /* 使能串口接收中断 */ USART_ITConfig(DEBUG_USART, USART_IT_RXNE, ENABLE); /* 使能串口 */ USART_Cmd(DEBUG_USART, ENABLE); }
从上面的红色部分我们可以知道,和往常一样,开启时钟是第一步,尤其注意的是引脚复用调用的函数,注意是GPIO_Pinsource而不是GPIO_Pin.
在编写程序的时候,首先参考官方固件库,然后谨记编程要点,比如什么时钟应该开启,引脚功能如何配置,是否有复用模式,中断是否需要配置。这需要我们熟悉外设挂载在哪个总线上,已及总线的最高时钟,这些都知道以后,固件库编程是很快速的。至于固件库的具体实现,也是很有参考价值的,但是如果你手上还有很多需要学习的东西,可以暂时不管固件库底层如何实现,先完成基本设计,再慢慢深入。这是一个长期累积的过程。
串口调试助手,不勾选发送模式,默认是以字符的方式发送。
编程时需要用到的固件库函数
要使用printf重定向,需要重写fputc函数,并且在keil中勾选使用微库。
NOTE:
STM32默认就是小端模式:
The processor can access data words in memory in little-endian format or big-endian
format. It always accesses code in little-endian format.
Note:
Little-endian is the default memory format for ARM processors.
在发送一个字节8位模式时,是从高位开始向低位传输的:
但是,要是我们想要发送一个16位的数据,是先发送高8位呢还是低8位?通过参考历程可以看出,是先发送的高8位,再发送低8位。
那么为什么要先发送高8位再发送底八位呢?
例如一个16位数据0x1234;在寄存器中是高位在前,低位在后的(stm32默认小端模式)。
二进制 0001 0010 0011 0100;
要想把这一串二进制以8位一次的方式发送,如果我们先发送低8位,再发送高8位,最后出来将会是0x3412,所以,我们应该先发送高位再发送低位。
重要寄存器,解释为什么发送接收库函数要那么去写:
//接收库函数
//发送库函数
首先,这是中断服务函数调用的,首先调用接收函数,能进接收中断,所以肯定有数据发来单片机接收到了,此时把DR寄存器的值得低9位(在我们的历程中使用的8位,到底是9位还是8位要看M的设置),这个值返回出来,确实应该是接收函数。但是,为什么像DR中写入数据,就是发送了?刚开始我也不理解,但是参看参考手册之后,发现,写数据倒DR寄存器中会硬件触发标志,这样,内核可以知道你是接收还是发送。
发送端:
7. 在 USART_DR 寄存器中写入要发送的数据(该操作将清零 TXE 位)。为每个要在单缓
冲区模式下发送的数据重复这一步骤。
8. 向 USART_DR 寄存器写入最后一个数据后,等待至 TC=1。这表明最后一个帧的传送已
完成。禁止 USART 或进入暂停模式时需要此步骤,以避免损坏最后一次发送。
接收端:
这样,读DR寄存器,会产生一些硬件标志置位,所以内核可以知道是否接收完成,向DR寄存器写数据,也会产生一些硬件标志位,这样内核就知道是否发送发送完成。
嵌入式开发,离不开和芯片手册折腾,但是这又是必备技能,而且这里STMF4还是中文参考手册,更多时候,我们开发是英文手册,更加困难,所以,软件程序员,必须熟练运用英语和数学。