一、 串口通信协议简介
1-1 概念
- 物理层:规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。其实就是硬件部分。
- 协议层:协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。其实就是软件部分。
简单来说物理层规定我们用嘴巴还是用肢体来交流,协议层则规定我们用中文还是英文来交流。
1-2 常用标准
①.RS232标准
RS232标准串口通讯结构图
- RS232标准串口主要用于工业设备直接通信
- 电平转换芯片一般有MAX3232,SP3232
DB9 标准的公头及母头接法
RS-232 与 TTL 电平区别(232趋向高容错)
②.USB转串口(USB2TTL)
USB转串口通讯结构图
- USB转串口主要用于设备跟电脑通信
- 电平转换芯片一般有CH340、PL2303、CP2102、FT232
- 使用的时候电脑端需要安装电平转换芯片的驱动
③.原生的串口到串口 (TTL2TTL)
原生的串口到串口结构图
- 原生的串口通信主要是控制器跟串口的设备或者传感器通信,不需要经过电平转换芯片来转换电平,直接就用TTL电平通信
- GPS模块、GSM模块、串口转WIFI模块、HC04蓝牙模块
1-3 串口数据包的基本组成
- 起始位:由1个逻辑 0 的数据位表示
- 结束位:由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示
- 有效数据:在起始位后紧接着的就是有效数据,有效数据的长度常被约定为 5、 6、 7 或 8 、9位长
- 校验位:可选,为的是数据的抗干扰性。
校验方法分为:
1-奇校验(odd)、 2-偶校验(even)
3-0 校验(space)、 4-校验(mark)
5-无校验(noparity)
- 奇校验 (odd) :有效数据和校验位中“ 1”的个数为奇数
比如一个 8 位长的有效数据为: 01101001,此时总共有 4 个“ 1”,为达到奇校验效果,校验位为“ 1”,最后传输的数据将是 8 位的有效数据加上 1 位的校验位总共 9 位
- 偶校验 (even) :有效数据和校验位中“ 1”的个数为偶数
比如一个 8 位长的有效数据为: 01101001,此时总共有 4 个“ 1”,为达到偶校验效果,校验位为“ 0”,最后传输的数据将是 8 位的有效数据加上 1 位的校验位总共 9 位
- 0 校验:不管有效数据中的内容是什么,校验位总为“ 0”。
- 1 校验: 校验位总为“ 1”。
- 无校验: 数据包中不包含校验位。
二、 STM32串口功能框图讲解
2-1 引脚
TX:数据发送
RX:数据接收
SCLK :时钟,仅同步通信时使用
nRTS :请求发送(Request To Send)
nCTS :允许发送(Clear To Send)
2-2 数据寄存器
- 数据寄存器 —USART_DR :9位有效,包含一个发送数据寄存器TDR和一个接收数据寄存器RDR。一个地址对应了两个物理内存。
- 字长配置:USART_CR1:M,0:8bit,1:9bit
- 停止位配置:USART_CR2:STOP
- 奇偶验证位:USART_CR1:PCE(使能校验控制)、PS(奇偶校验选择)、PEIE(PE中断使能)
- USART_SR :PE(奇偶校验反映位)
2-2-1 如何通过串口实现发送接收?
1、 USART_CR1:UE、TE、RE均配置为开启
2、 以发送为例,发送过程涉及2个寄存器的值改变(发送数据寄存器----TDR)
2、 TDR发送数据后状态寄存器(USART_SR)中的TXE(empty)位置1,若TXEIE=1则产生中断,不代表数据发送成功,此时TC位置1才表示发送成功,若此时TCIE=1则产生中断(相应的读取时UE、RE使能情况下,读取数据进入接收移位寄存器后传到RDR,RXNE置1,用户可从DR寄存器中读数据,若RXNEIE=1,则产生中断)、
- TXEIE/TCIE/RXNEIE均位于CR1寄存器
USART_SR:TXE,Transmit data register empty
2-3 控制器
2-4 波特率
- 每秒钟要发送多少数据(多少个二进制位)
- 配置单位:USART_BRR:波特率寄存器
- 计算公式:
例:**USART:**USART1,时钟为72M **波特率:**115200
最小精度:1/16(2^4)
三、 代码
3-1 固件库相关配置结构体
USART初始化结构体:USART_InitTypeDef
typedef struct
{
uint32_t USART_BaudRate; //波特率 BRR
uint16_t USART_WordLength; //字长 CR1_M
uint16_t USART_StopBits; //停止位 CR2_STOP
uint16_t USART_Parity; //校验控制 CR1_PCE、CR1_PS
uint16_t USART_Mode; //模式选择CR1_TE、CR1_RE
// 硬件流选择 CR3_CTSE、CR3_RTSE
uint16_t USART_HardwareFlowControl;
} USART_InitTypeDef;
USART同步时钟初始化结构体:USART_ClockInitTypeDef
typedef struct
{
uint16_t USART_Clock; // 同步时钟 CR2_CLKEN
uint16_t USART_CPOL; // 极性 CR2_CPOL
uint16_t USART_CPHA; // 相位 CR2_CPHA
uint16_t USART_LastBit; //最后一个位的时钟脉冲 CR2_LBC
} USART_ClockInitTypeDef;
3-2 固件库相关配置函数
串口初始化函数:
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
1
中断配置函数:
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
1
串口使能函数:
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
1
数据发送函数:
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
1
数据接收函数:
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
1
中断状态位获取函数:
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
1
3-3 固件库编程
编程要点
1-初始化串口需要用到的GPIO
2-初始化串口,USART_InitTypeDef
3-中断配置(接收中断,中断优先级)
4-使能串口
5-编写发送和接收函数
6-编写中断服务函数
实验1:中断接收和发送
需求:单片机给电脑发送数据,电脑上位机把数据打印出来;电脑上位机给单片机发数据,单片机接收到数据之后立马发回给电脑,并打印出来。
‘bsp-usart.c’
#include "bsp-usart.h"
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
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(DEBUG_USARTx, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
/* 发送一个字节 */
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
USART_SendData(pUSARTx, data);
while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );
}
/* 发送两个字节的数据 */
void Usart_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data)
{
uint8_t temp_h,temp_l;
temp_h = (data&0xff00) >> 8 ;
temp_l = data&0xff;
USART_SendData(pUSARTx, temp_h);
while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );
USART_SendData(pUSARTx, temp_l);
while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );
}
/* 发送8位数据的数组 */
void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num)
{
uint8_t i;
for( i=0; i
{
Usart_SendByte(pUSARTx, array
);
}
while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET );
}
/* 发送字符串 */
void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str)
{
uint8_t i=0;
do
{
Usart_SendByte(pUSARTx, *(str+i));
i++;
}while(*(str+i) != '