STM32学习(三)串口实验
串口简介
UART:全双工、异步、串行通信方式
- 经常用来打印调试信息
物理层
串口物理层有很多标准及变种:TTL、RS232、RS422、RS485等;
RS232 标准
上图中,设备A 与 设备B 的 DB9接口 通过串口线连接,串口信号线中使用 RS232电平标准 传输数据信号。由于 RS232电平标准不能直接被控制器识别,所以需要通过一个 “电平转换芯片(如MA3232)” 来将 RS232电平 和 TTL电平进行转换(发送数据时,将TTL电平转换成RS232电平;接收数据时,将RS232电平转换成TTL电平)。
TTL电平标准 VS RS232电平标准
逻辑0 | 逻辑1 | |
---|---|---|
TTL | 0 ~ 0.5V | 2.4V ~ 5V |
RS232 | +3V ~ +15V | -15V ~ -3V |
电子电路中常用的是 TTL标准,理想状态下,用 0V 表示逻辑0,用 5V 表示逻辑1,而实际情况允许一定的误差,采用的是一个范围。
RS232标准,为了增加串口通讯传输距离和提高抗干扰能力,使用 -15V 表示逻辑1,+15V 表示逻辑0,下图是表示同一信号的 TTL电平 和 RS232电平:
物理连接
DB9接口的信号线有9根,目前一般只使用 RXD、TXD、GND这3条信号线就可以了。
设备A 设备B
TXD -- RXD
RXD -- TXD
GND -- GND
VCC -- VCC
硬件电路
以 USART1 为例,使用 PA9 和 PA10 引脚(也可以使用其他引脚组合,详见引脚复用)
编程指南
串口初始化指南
主要步骤如下:
1)GPIO 时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
2)USART1 时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
3)GPIO 复用模式初始化
- 使用 DEBUG_USART_RX_GPIO_PORT 和 DEBUG_USART_RX_PIN 是为了方便代码移植和书写,后续代码都是类似风格,不再单独说明。
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_PIN GPIO_Pin_10
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_PIN GPIO_Pin_9
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
4)引脚复用配置
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
5)UART 参数配置
主要设置如下参数:
- 波特率 115200
- 字长 8位
- 奇偶校验 无校验
- 不使用硬件流控
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
6)使能串口
USART_Cmd(USART1, ENABLE);
串口接收中断配置
7)配置 NVIC 嵌套中断向量控制器
中断优先级分组设置,一般是在 main() 函数中设置,后续默认已经配置,不再单独说明。
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC 参数配置
void uart1_nvic_config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
8)串口接收中断使能
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
串口收发数据接口
串口发送数据
如下是发送单个数据的接口,Data 最多是9位
// Transmits single data through the USARTx peripheral.
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{
USARTx->DR = (Data & (uint16_t)0x01FF);
}
串口接收数据
// Returns the most recent received data by the USARTx peripheral.
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
{
return (uint16_t)(USARTx->DR & (uint16_t)0x01FF);
}
重定向 printf() 和 scanf() 函数
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t)ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return (ch);
}
int fgetc(FILE *f)
{
/* 等待串口输入数据 */
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
主要代码
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_PIN GPIO_Pin_10
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_PIN GPIO_Pin_9
void uart1_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 引脚复用配置
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
uart1_nvic_config();
USART_Cmd(USART1, ENABLE);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
uint16_t uart1_send(USART_TypeDef* USARTx, char *data)
{
unsigned int k = 0;
do
{
USART_SendData(USARTx, *(data+ k));
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
k++;
} while (*(data+ k) != '\0');
/* 等待发送完成 */
while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET)
{
}
}