一、串口通讯协议
1. 串口通讯数据包的构成
- 起始位:必须由一个逻辑0的数据位表示
- 有效数据:起始位之后紧接着的就是要传输的主体数据内容,也称有效数据,有效数据的长度常被约定为5、6、7或8位长。
- 校验位:在有效数据之后,有一个可选的数据校验位。校验方法有奇校验(odd)、偶校验
(even)、0 校验 (space)、1 校验 (mark) 及无校验(noparity)。
奇校验(odd) | 有效数据与校验位中“1”的个数总和为奇数 |
偶校验(even) | 有效数据与校验位中“1”的个数总和为偶数 |
0 校验 (space) | 无论有效数据中的内容是什么,校验位总为“0” |
1 校验 (mark) | 无论有效数据中的内容是什么,校验位总为“1” |
无校验(noparity) | 数据包中不包含校验位 |
- 停止位:由0.5、1、1.5或2个逻辑1的数据位表示
二、STM32 USART
- STM32芯片具有多个USART外设用于串口通讯
- USART是UniversalSynchronousAsynchronousReceiver and Transmitter 的缩写,即通用同步异步收发器可以灵活地与外部设备进行全双工数据交换。
- STM32芯片还有具有UART外设(Universal Asynchronous Receiver and Transmitter),它是在USART 基础上裁剪掉了同步通信功能,只有异步通信。
1. 引脚介绍
- TX:发送数据输出引脚
- RX:接收数据输入引脚
- SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚
- nRTS:请求发送,n表示低电平有效; nCTS:允许发送,n表示低电平有效。相当于一个握手的过程
- SCLK:发送器时钟输出引脚,仅适用于同步模式
注: 以上引脚,通常仅使用TX和RX
- STM32F429IGT6芯片的USART引脚
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数据。
3. 数据发送/接收流程
- 字长由USART控制寄存器1(USART_CR1)的M位设置:
- 停止位由USART控制寄存器2(USART_CR2)的第12位和13位控制
- 在利用USART收发数据时,需要先使能USART,再使能发送/接收
- 发送数据时,涉及的重要标志位如下:
- 接收数据时,涉及的重要标志位如下:
三、应用
1. 目标:利用 USART1 数据发送、数据接收、接收中断功能实现自动回传上位机发来的数据
- usart.c
#include "usart.h"
static void USART1_NVIC_Config(void);
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_AHB1PeriphClockCmd(USART1_TX_CLK | USART1_RX_CLK, ENABLE); // 使能相应 GPIO 时钟
RCC_APB2PeriphClockCmd(USART1_CLK, ENABLE); // 使能 UART1 时钟
/* 1. 配置 GPIO */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用功能模式
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽输出 + 上拉电阻,以确保信号稳定和电平匹配
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed; // 50MHz
GPIO_InitStructure.GPIO_Pin = USART1_TX_PIN;
GPIO_Init(USART1_TX_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = USART1_RX_PIN;
GPIO_Init(USART1_RX_PORT, &GPIO_InitStructure);
/* 2. 连接 GPIOX_X 到 UARTx */
GPIO_PinAFConfig(USART1_TX_PORT, USART1_TX_SOURCE, USART1_TX_AF);
GPIO_PinAFConfig(USART1_RX_PORT, USART1_RX_SOURCE, USART1_RX_AF);
/* 3. 配置 USART */
USART_InitStructure.USART_BaudRate = USART1_BAUDRATE; // 波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长 (数据位 + 校验位):8
USART_InitStructure.USART_Parity = USART_Parity_No; // 无奇偶校验
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 1位停止位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 不使用硬件流
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 同时使能发送和接收
USART_Init(USART1, &USART_InitStructure);
/* 4. 配置 USART 中断 */
USART1_NVIC_Config();
/* 5. 使能 USART 接收中断 */
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
/* 6. 使能 USART1 */
USART_Cmd(USART1, ENABLE);
}
void USART1_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置中断优先级分组
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = USART1_EXTI_IRQ; // 中断通道
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief 通过 USART 发送 8 bit 数据
* @param USARTx: 发送数据的USART
* @param data : 待发送的数据
* @retval 无
*/
void Usart_SendByte(USART_TypeDef* USARTx, uint8_t data)
{
USART_SendData(USARTx, data); // 发送数据
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET); // 等待发送数据寄存器为空
}
/**
* @brief 通过 USART 发送 16 bit 数据
* @param USARTx: 发送数据的USART
* @param data : 待发送的数据
* @retval 无
*/
void Usart_SendHalfWord(USART_TypeDef* USARTx, uint16_t data)
{
uint8_t temp_h, temp_l;
temp_h = (data & 0XFF00)>>8; // 取出高八位
temp_l = data & 0XFF; // 取出低八位
USART_SendData(USARTx,temp_h); // 发送高八位
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
USART_SendData(USARTx,temp_l); // 发送低八位
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
/**
* @brief 通过 USART 发送字符串
* @param USARTx: 发送数据的USART
* @param *str : 待发送的字符串
* @retval 无
*/
void Usart_SendString(USART_TypeDef * USARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte(USARTx, *(str + k) );
k++;
} while( *(str + k) != '\0' );
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
}
- usart.h
#ifndef __UART_H
#define __UART_H
#include "stm32f4xx.h"
#define USART1_BAUDRATE 115200 // UART1波特率
#define USART1_CLK RCC_APB2Periph_USART1
#define USART1_EXTI_IRQ USART1_IRQn
#define USART1_IRQHandler USART1_IRQHandler
#define USART1_TX_CLK RCC_AHB1Periph_GPIOA
#define USART1_TX_PORT GPIOA
#define USART1_TX_PIN GPIO_Pin_9
#define USART1_TX_SOURCE GPIO_PinSource9
#define USART1_TX_AF GPIO_AF_USART1
#define USART1_RX_CLK RCC_AHB1Periph_GPIOA
#define USART1_RX_PORT GPIOA
#define USART1_RX_PIN GPIO_Pin_10
#define USART1_RX_SOURCE GPIO_PinSource10
#define USART1_RX_AF GPIO_AF_USART1
void USART_Config(void);
void Usart_SendByte(USART_TypeDef* USARTx, uint8_t data);
void Usart_SendHalfWord(USART_TypeDef* USARTx, uint16_t data);
void Usart_SendString(USART_TypeDef * USARTx, char *str);
#endif
- 在stm32f4xx_it.c文件中添加如下代码
#include "usart.h"
/* USART1 数据接收中断 */
void USART1_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
ucTemp = USART_ReceiveData(USART1);
USART_SendData(USART1, ucTemp);
}
}
- main.c
#include "stm32f4xx.h"
#include "usart.h"
int main(void)
{
USART_Config();
while(1)
{
}
}
- 编译运行即可
2. 目标:使USART1支持 printf 和 scanf
- 在 usart.h 中添加如下代码
#include <stdio.h>
- 在 usart.c 中添加如下代码
/**
* @brief 重定向c库函数printf到串口,重定向后可使用printf函数
*/
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t) ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return (ch);
}
/**
* @brief 重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
*/
int fgetc(FILE *f)
{
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); // 等待串口输入数据
return (int)USART_ReceiveData(USART1);
}
- 在main.c中进行测试
注:测试该功能时,为保证系统稳定性,需要将接收中断失能。将usart.c文件中USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); 的第三个参数设置为DISABLE即可。
#include "stm32f4xx.h"
#include "usart.h"
int main(void)
{
char ch;
USART_Config();
while(1)
{
ch = getchar();
printf("接收到字符:%c\n", ch);
}
}