STM32
直播中

贾飞小

7年用户 1770经验值
私信 关注
[问答]

串口通信协议是什么?串口通信两端设备类型有哪些

串口通信协议是什么?串口通信两端设备类型有哪些?
原生串口通信到串口通讯的结构是怎样构成的呢?

回帖(2)

肖舒

2021-12-10 10:17:16
1 串口通讯
1.1 串口通信协议简介
1、分为两层
(1)物理层:规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。其实就是硬件部分。
(2)协议层:协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。其实就是软件部分。

1.2 串口通信两端设备类型
1、RS232标准
RS232标准串口通讯结构图





(1)RS232标准串口主要用于工业设备直接通信
(2)电平转换芯片一般有MAX3232,SP3232
(3)DB9 标准的公头及母头接法,如下图





(4)DB9串口线

(5)RS-232 与 TTL 电平区别





2、USB转串口
(1)USB转串口通讯结构图





(2)USB转串口主要用于设备跟电脑通信
(3)电平转换芯片一般有CH340、PL2303、CP2102、FT232
(4)使用的时候电脑端需要安装电平转换芯片的驱动

3、原生的串口到串口
(1)原生串口到串口通讯结构图





(2)原生的串口通信主要是控制器跟串口的设备或者传感器通信,不需要经过电平转换芯片来转换电平,直接就用TTL电平通信
(3)GPS模块、GSM模块、串口转WIFI模块、HC04蓝牙模块

1.3 串口数据包的基本组成





1、起始位
由一个逻辑 0 的数据位表示
2、结束位
由 0.5、 1、 1.5 或 2 个逻辑 1 的数据位表示
3、有效数据
在起始位后紧接着的就是有效数据,有效数据的长度常被约定为 5、 6、 7 或 8 位长
4、校验位:
可选,为的是数据的抗干扰性。校验方法分为:
(1)奇校验(odd)
有效数据和校验位中“ 1”的个数为奇数,比如一个 8 位长的有效数据为: 01101001,此时总共有 4 个“ 1”,为达到奇校验效果,校验位为“ 1”,最后传输的数据将是 8 位的有效数据加上 1 位的校验位总共 9 位
(2)偶校验(even)
有效数据和校验位中“ 1”的个数为偶数,比如一个 8 位长的有效数据为: 01101001,此时总共有 4 个“ 1”,为达到偶校验效果,校验位为“ 0”,最后传输的数据将是 8 位的有效数据加上 1 位的校验位总共 9 位
(3)0校验(space)
0 校验是不管有效数据中的内容是什么,校验位总为“ 0”
(4)1校验(mark)
1 校验是校验位总为“ 1”。
(5)无校验(noparity)
无校验就是数据包中不包含校验位。

1.4 串口功能框图
1-引脚  2-数据寄存器 3-控制器 4-波特率





1、功能引脚
TX:发送数据输出引脚。
RX:接收数据输入引脚。
SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚。
nRTS:请求以发送(Request To Send),n 表示低电平有效。如果使能 RTS 流控制,当USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时,nRTS 将被设置为高电平。该引脚只适用于硬件流控制。
nCTS:清除以发送(Clear To Send),n 表示低电平有效。如果使能 CTS 流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。
USART 引脚在 STM32F103ZET6 芯片具体分布见表





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。
3、控制器
USART 有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等等。使用USART之前需要向USART_CR1寄存器的UE位置1使能 USART,UE位用来开启供给给串口的时钟。发送或者接收数据字长可选8位或9位,由USART_CR1的M位控制。
4、发送器
当 USART_CR1 寄存器的发送使能位 TE置1时,启动数据发送,发送移位寄存器的数据会在 TX 引脚输出,低位在前,高位在后。如果是同步模式 SCLK 也输出时钟信号。一个字符帧发送需要三个部分:起始位+数据帧+停止位。起始位是一个位周期的低电平,位周期就是每一位占用的时间;数据帧就是我们要发送的 8 位或 9 位数据,数据是从最低位开始传输的;停止位是一定时间周期的高电平。停止位时间长短是可以通过 USART 控制寄存器2 (USART_CR2)的STOP[1:0]位控制,可选 0.5 个、1 个、1.5 个和 2 个停止位。默认使用 1 个停止位。2 个停止位适用于正常USART 模式、单线模式和调制解调器模式。0.5 个和 1.5 个停止位用于智能卡模式。当选择 8 位字长,使用 1 个停止位时,具体发送字符时序图见图





当发送使能位 TE 置 1 之后,发送器开始会先发送一个空闲帧(一个数据帧长度的高电平),接下来就可以往 USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待 USART 状态寄存器(USART_SR)TC 位为 1,表示数据传输完成,如果USART_CR1 寄存器的 TCIE 位置 1,将产生中断。
在发送数据时,编程的时候有几个比较重要的标志位我们来总结下。


名称描述
TE发送使能
TXE发送寄存器为空,发送单个字节的时候使用
TC发送完成,发送多个字节数据的时候使用
TXIE发送完成中断使能

5、接收器
如果将 USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索起始位。在确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内。
接收完成后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置1,同时如果 USART_CR2 寄存器的 RXNEIE 置 1 的话可以产生中断。
在接收数据时,编程的时候有几个比较重要的标志位我们来总结下。


名称
描述
RE
接收使能
RXNE
读数据寄存器非空
RENEIE
接收完成中断使能

6、小数波特率生成
波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,单位为波特。比特率指单位时间内传输的比特数,单位 bit/s(bps)。对于USART波特率与比特率相等,以后不区分这两个概念。波特率越大,传输速率越快。USART 的发送器和接收器使用相同的波特率。计算公式如下:





其中,fPLCK 为 USART 时钟, USARTDIV 是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数。其中 DIV_Mantissa[11:0] 位定义USARTDIV的整数部分DIV_Fraction[3:0]位定义 USARTDIV 的小数部分。例如:DIV_Mantissa=24(0x18),DIV_Fraction=10(0x0A),此时USART_BRR值为0x18A;那么 USARTDIV 的小数位 10/16=0.625;整数位24,最终USARTDIV 的值为 24.625。 如果知道 USARTDIV 值为 27.68,那么 DIV_Fraction=16*0.68=10.88,最接近的正整数为11,所以DIV_Fraction[3:0]为0xB;DIV_Mantissa=整数(27.68)=27,即为0x1B。波特率的常用值有 2400、9600、19200、115200。下面以实例讲解如何设定寄存器值 得到波特率的值。
我们知道 USART1 使用 APB2 总线时钟,最高可达 72MHz,其他 USART 的最高频率为 36MHz。我们选取 USART1 作为实例讲解,即 fPLCK=72MHz。为得到 115200bps 的波特率,此时:





解得USARTDIV=39.0625,可算得 DIV_Fraction=0.0625*16=1=0x01 ,DIV_Mantissa=39=0x17,即应该设置 USART_BRR 的值为 0x171。
7、校验控制
STM32F103 系列控制器USART支持奇偶校验。当使用校验位时,串口传输的长度将是8 位的数据帧加上1位的校验位总共9位,此时 USART_CR1寄存器的M位需要设置为1,即9数据位。将USART_CR1寄存器的PCE位置1就可以启动奇偶校验控制,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,在发送数据帧时会自动添加校验位,接收数据时自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会见USART_SR寄存器的PE位置1,并可以产生奇偶校验中断。
使能了奇偶校验控制后,每个字符帧的格式将变成:起始位+数据帧+校验位+停止位
8、中断控制
USART 有多个中断请求事件,具体见表


中断事件
事件标志
使能控制位
发送数据寄存器为空
TXE
TXEIE
CTS标志
CTS
CTSIE
发送完成
TC
TCIE
准备好读取接收到的数据
RXNE
RXNEIE
检测到上溢错误
ORE

检测到空闲线路
IDLE
IELEIE
奇偶检验错误
PE
PEIE
断路标志
LBD
LBDIE
多缓冲通信中的噪声标志、上溢错误和帧错误
NF/ORE/FE
EIE

1.5 重要结构体及函数
HAL 库函数对每个外设都建立了一个初始化结构体,比如 USART_InitTypeDef,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如 USART_Init()调用,这些
设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。
初始化结构体和初始化库函数配合使用是 HAL 库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f1xx_hal_usart.h
文件中,初始化库函数定义在 stm32f1xx_hal_usart.c 文件中,编程时我们可以结合这两个文件内注释使用。
1.5.1 结构体
1、USART 初始化结构体

typedef struct {
uint32_t BaudRate;     //波特率
uint32_t WordLength;   //字长
uint32_t StopBits;     //停止位
uint32_t Parity;       //校验位
uint32_t Mode;         //UART 模式
uint32_t HwFlowCtl;    //硬件流控制
uint32_t OverSampling; // 过采样模式
uint32_t CLKLastBit;   // 最尾位时钟脉冲
} USART_InitTypeDef;
1) BaudRate:波特率设置。一般设置为 2400、9600、19200、115200。
2) WordLength:数据帧字长,可选8位或9位。它设定UART_CR1寄存器的M位的值。如果没有使能奇偶校验控制,一般使用8数据位;如果使能了奇偶校验则一般设置为9数据位。
3) StopBits:停止位设置,可选 0.5 个、1 个、1.5 个和 2 个停止位,它设定USART_CR2 寄存器的 STOP[1:0]位的值,一般我们选择 1 个停止位。
4) Parity :奇偶校验控制选择,可选USART_PARITY_NONE ( 无校验 ) 、USART_PARITY_EVEN (偶校验)以及 USART_PARITY_ODD (奇校验),它设定 UART_CR1 寄存器的 PCE 位和 PS 位的值。
5) Mode:UART 模式选择,有 USART_MODE_RX 和 USART_MODE_TX,允许使用逻辑或运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。
2、同步时钟初始化结构体


1 typedef struct
2 {
3   uint16_t USART_Clock;      // 同步时钟 CR2_CLKEN
4   uint16_t USART_CPOL;       // 极性 CR2_CPOL
5   uint16_t USART_CPHA;       // 相位 CR2_CPHA
6   uint16_t USART_LastBit;    //最后一个位的时钟脉冲 CR2_LBC
7 } USART_ClockInitTypeDef;


1.5.2 函数
1-串口初始化函数
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct) 2-中断配置函数
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState)
3-串口使能函数
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)
4-数据发送函数
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
5-数据接收函数
uint16_t USART_ReceiveData(USART_TypeDef* USARTx)
6-中断状态位获取函数
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)  
1.6 UART1收发通信实验
USART 只需两根信号线即可完成双向通信,对硬件要求低,使得很多模块都预留USART 接口来实现与其他模块或者控制器进行数据传输,比如 GSM 模块,WIFI 模块、蓝牙模块等等。在硬件设计时,注意还需要一根“共地线”。
我们不仅仅可以将数据发送到串口调试助手,我们还可以在串口调试助手发送数据给控制器,控制器程序根据接收到的数据进行下一步工作。首先,我们来编写一个程序实现开发板与电脑通信,在开发板上电时通过 USART发送一串字符串给电脑,然后开发板进入中断接收等待状态,如果电脑有发送数据过来,开发板就会产生中断,我们在中断服务函数接收数据,并马上把数据返回发送给电脑。
1.6.1 硬件设计
为利用 USART 实现开发板与电脑通信,需要用到一个 USB 转 USART 的 IC,我们选择CH340G芯片来实现这个功能,CH340G 是一个 USB 总线的转接芯片,实现 USB 转USART、USB 转 lrDA 红外或者 USB 转打印机接口,我们使用其 USB 转 USART 功能。具体电路设计如下图,我们将 CH340G 的 TXD 引脚与 USART1 的 RX 引脚连接,CH340G的RXD引脚与USART1的TX引脚连接。CH340G 芯片集成在开发板上,其地线(GND)已与控制器的GND连通。





1.6.2 软件设计
创建了两个文件:bsp_usart.c和bsp _usart.h文件用来存放 USART 驱动程序及相关宏定义。
1. 编程要点
1) 使能 RX 和 TX 引脚 GPIO 时钟和 USART 时钟;
2) 初始化 GPIO,并将 GPIO 复用到 USART 上;
3) 配置 USART 参数;
4) 配置中断控制器并使能 USART 接收中断;
5) 使能 USART;
6) 在 USART 接收中断服务函数实现数据接收和发送。
举报

苏旭

2021-12-10 10:17:19
2. 代码实现
(1)创建bsp_usart.h


1 #ifndef _BSP_USART_H_
2 #define _BSP_USART_H_
3  
4 // 串口1-USART1
5 #define  DEBUG_USARTx                   USART1
6 #define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
7 #define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
8 #define  DEBUG_USART_BAUDRATE           115200
9 // USART GPIO 引脚定义
10 #define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
11 #define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
12     
13 #define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
14 #define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
15 #define  DEBUG_USART_RX_GPIO_PORT       GPIOA
16 #define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10
17 #define  DEBUG_USART_IRQ                USART1_IRQn
18 #define  DEBUG_USART_IRQHandler         USART1_IRQHandler
19
20 void USART_Config(void);
21 void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data);
22 void Usart_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data);
23 void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num);
24 void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str);
25
26 #endif /* _BSP_USART_H_ */

(2)创建bsp_usart.c文件
  1 #include "bsp_usart.h"
  2 static void NVIC_Configuration(void)
  3 {
  4   NVIC_InitTypeDef NVIC_InitStructure;
  5   
  6   /* 嵌套向量中断控制器组选择 */
  7   NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  8   
  9   /* 配置USART为中断源 */
10   NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;     // USART1_IRQn
11   /* 抢断优先级*/
12   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
13   /* 子优先级 */
14   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
15   /* 使能中断 */
16   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
17   /* 初始化配置NVIC */
18   NVIC_Init(&NVIC_InitStructure);
19 }
20
21 void USART_Config(void)
22 {
23     GPIO_InitTypeDef GPIO_InitStructure;
24     USART_InitTypeDef USART_InitStructure;
25     // (1)打开串口GPIO时钟
26     DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
27     
28     // (2)打开串口外设时钟
29    DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
30
31     // (3)将USART Tx的GPIO配置为推挽复用模式
32     GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN; //GPIO_Pin_9
33     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
34     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
35    GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
36
37     // (4)将USART Rx的GPIO配置为浮空输入模式
38     GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;//GPIO_Pin_10
39     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
40     GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
41     
42     //  (5)配置串口工作参数
43     // 配置波特率
44     USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE; //115200
45     // 配置帧数据字长
46     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
47     // 配置停止位
48     USART_InitStructure.USART_StopBits = USART_StopBits_1;
49     //配置校验位
50     USART_InitStructure.USART_Parity = USART_Parity_No ;
51     // 配置硬件控制流
52     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
53     // 配置工作模式,收发一起
54     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
55     // 完成串口初始化配置
56     USART_Init(DEBUG_USARTx, &USART_InitStructure);
57     
58     // (6)串口中断优先级配置
59     NVIC_Configuration();
60     
61     // (7)使能接收中断
62     USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);   
63     
64     // (8)使能串口
65     USART_Cmd(DEBUG_USARTx, ENABLE);        
66 }
67
68 /* 发送一个字节的数据 */
69 void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
70 {
71     USART_SendData(pUSARTx, data);
72     while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );
73 }
74
75 /* 发送两个字节的数据 */
76 void Usart_SendHalfWord(USART_TypeDef* pUSARTx, uint16_t data)
77 {
78     uint8_t temp_h,temp_l;
79     
80     temp_h = (data&0xff00) >> 8 ;
81     temp_l = data&0xff;
82     
83     USART_SendData(pUSARTx, temp_h);
84     while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );
85     
86     USART_SendData(pUSARTx, temp_l);
87     while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET );
88 }
89
90 /* 发送8为数据的数组 */
91 void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array,uint8_t num)
92 {
93     uint8_t i;
94     for( i=0; i 95   {
96         Usart_SendByte(pUSARTx, array);
97     }
98     while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET );
99 }
100
101 /* 发送字符串 */
102 void Usart_SendStr(USART_TypeDef* pUSARTx, uint8_t *str)
103 {
104     uint8_t i=0;
105     do
106   {
107         Usart_SendByte(pUSARTx, *(str+i));
108         i++;
109     }while(*(str+i) != '');
110     while( USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) == RESET );
111 }
112
113 // 重定向C库函数printf到串口,重定向后可使用printf函数
114 int fputc(int ch, FILE *f)
115 {
116         /* 发送一个字节数据到串口 */
117         USART_SendData(DEBUG_USARTx, (uint8_t) ch);
118         
119         /* 等待发送完毕 */
120         while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);     
121     
122         return (ch);
123 }
124
125 // 重定向C库函数scanf到串口,重定向后可使用printf、getchar等函数
126 int fgetc(FILE *f)
127 {
128         /* 等待串口输入数据 */
129         while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);
130
131         return (int)USART_ReceiveData(DEBUG_USARTx);
132 }

(3)创建main.c


1 int main(void)
2 {   
3   uint8_t a[10]={88,2,3,4,5,6,7,8,9,10};
4     USART_Config();
5     
6 //  Usart_SendByte(DEBUG_USARTx,'a');
7 //  Usart_SendHalfWord(DEBUG_USARTx, 0xff56);
8 //  Usart_SendArray(DEBUG_USARTx, a,10);
9 //  Usart_SendStr(DEBUG_USARTx, "发送字符串测试 n");
10     
11     printf( "串口printf函数测试n" );
12     
13     while (1)
14     {  
15     }
16 }
举报

更多回帖

发帖
×
20
完善资料,
赚取积分