1 串口通讯
1、分为两层
(1)物理层:规定通信系统具有机电功能部分的特点,保证物理媒体传输原始数据。其实是硬件部分。
(2)协议层:协议层主要规定通信逻辑,统一收发双方数据包装和解包标准。其实是软件部分。
RS232标准串口通信结构图
(1)RS232标准串主要用于工业设备直接通信
(2)电平转换芯片一般有MAX3232,SP3232
(3)DB9标准的公头和母头连接方法如下图所示
(4)DB9串口线
(5)RS-232与TTL电平区别
(1)USB串口通信结构图
(2)USB主要用于设备和计算机通信
(3)电平转换芯片一般有CH340、PL2303、CP2102、FT232
(4)使用的时候电脑端需要安装电平转换芯片的驱动
(1)从原串口到串口通信结构图
(2)原串口通信主要是控制器与串口设备或传感器通信,不需要通过电平转换芯片转换电平,直接使用TTL电平通信
(3)GPS模块、GSM转动模块和串口WIFI模块、HC04蓝牙模块
1、
由一个数据位表示
2、
由0.5、1、1.5或2个数据位表示
3、
有效数据紧随其后,有效数据的长度通常被约定为5、6、7或8位长
4、
数据的抗干扰性是可选的。验证方法分为:
(1)奇校验(odd)
有效数据和校验位一个数字是奇数,比如一个8位长的有效数据为:0110101101010014个“为达到奇怪的校验效果,校验位为1”,最终传输的数据将是8加上位置的有效数据1总共校准位9位
(2)偶校验(even)
有效数据和校验位一个数字是偶数,比如一个8位长的有效数据如下:0110101101010014个“为达到偶校验效果,校验位为0”,最终传输的数据将是8加上位置的有效数据1总共校准位9位
(3)0校验(space)
0无论有效数据中的内容是什么,验证位总是0”
(4)1校验(mark)
1验证总是验证1”。
(5)无校验(noparity)
无校验是指数据包中不包含校验位。
1-引脚2-数据寄存器3-控制器4-波特率
TX:发送数据输出引脚。
RX:接收数据输入引脚。
SW_RX:数据接收引脚,仅用于单线和智能卡模式,属于内引脚,无具体外引脚。
nRTS:请求以发送(Request To Send),n 表示低电平有效。如果使能。 RTS 流控制,当USART 当接收器准备好接收新数据时,它将被接收 nRTS 当接收寄存器满时,变成低电平;nRTS 将其设置为高电平。该引脚仅适用于硬件流控制。
nCTS:清除以发送(Clear To Send),n 表示低电平有效。如果使能 CTS 在发送下一帧数据之前,发送器将检测流量控制 nCTS 引脚,如果是低电平,则表示可以发送数据,如果是高电平,则在发送当前数据帧后停止发送。引脚仅适用于硬件流控制。
SCLK:发送器时钟输出引脚。该引脚仅适用于同步模式。
USART引脚在STM32F103ZET6 芯片具体分布见表
USART 数据寄存器(USART_DR)只有低 9 位置有效,第 9 取决于位数据是否有效USART 控制寄存器 1(USART_CR1)的 M 位设置,当,当M位1表示9位数据字长时,我们通常使用它。USART_DR 它包含已发送或接收的数据。当进行时,往 USART_DR 写入数据会自动存储内;读取操作时,向 USART_DR自动提取读取数据数据。TDR 和 RDR 它们都介于系统总线和移位寄存器之间。串行通信是一位一位传输的,发送时会传输 TDR 内容转移到发送移位寄存器,然后发送每个移位寄存器数据,将接收到的每个顺序保存在接收移位寄存器中,然后转移到 RDR。
USART 有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等等。,UE用于打开供应串口的时钟。发送或接收数据字长可选8或9位,由USART_CR1的M位控制。
当 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 位字长,
当发送使能位时 TE 置 1 之后发送器会先发送一个空闲帧(一个数据帧长度的高电平),然后就可以了 USART_DR 将要发送的数据写入寄存器。,如果USART_CR1 寄存器的 TCIE 位置 1,将产生断。
在发送数据时,编程的时候有几个比较重要的标志位我们来总结下。
名称 | 描述 |
TE | 发送使能 |
TXE | 发送寄存器为空,发送单个字节的时候使用 |
TC | 发送完成,发送多个字节数据的时候使用 |
TXIE | 发送完成中断使能 |
如果将 USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索起始位。在确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内。
接收完成后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置1,同时如果 USART_CR2 寄存器的 RXNEIE 置 1 的话可以产生中断。
在接收数据时,编程的时候有几个比较重要的标志位我们来总结下。
名称 |
描述 |
RE |
接收使能 |
RXNE |
读数据寄存器非空 |
RENEIE |
接收完成中断使能 |
指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,单位为波特。指单位时间内传输的比特数,单位 bit/s(bps)。波特率越大,传输速率越快。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。
STM32F103 系列控制器USART支持奇偶校验。当使用校验位时,串口传输的长度将是8 位的数据帧加上1位的校验位总共9位,此时 USART_CR1寄存器的M位需要设置为1,即9数据位。将USART_CR1寄存器的就可以启动控制,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,在发送数据帧时会自动添加校验位,接收数据时自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会见USART_SR寄存器的PE位置1,并可以产生奇偶校验中断。
使能了奇偶校验控制后,每个字符帧的格式将变成:。
USART 有多个中断请求事件,具体见表
发送数据寄存器为空 |
TXE |
TXEIE |
CTS标志 |
CTS |
CTSIE |
发送完成 |
TC |
TCIE |
准备好读取接收到的数据 |
RXNE |
RXNEIE |
检测到上溢错误 |
ORE |
|
检测到空闲线路 |
IDLE |
IELEIE |
奇偶检验错误 |
PE |
PEIE |
断路标志 |
LBD |
LBDIE |
多缓冲通信中的噪声标志、上溢错误和帧错误 |
NF/ORE/FE |
EIE |
HAL 库函数对每个外设都建立了一个初始化结构体,比如 USART_InitTypeDef,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如 USART_Init()调用,这些
设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。
初始化结构体和初始化库函数配合使用是 HAL 库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f1xx_hal_usart.h
文件中,初始化库函数定义在 stm32f1xx_hal_usart.c 文件中,编程时我们可以结合这两个文件内注释使用。
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-串口初始化函数
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)
USART 只需两根信号线即可完成双向通信,对硬件要求低,使得很多模块都预留USART 接口来实现与其他模块或者控制器进行数据传输,比如 GSM 模块,WIFI 模块、蓝牙模块等等。在硬件设计时,注意还需要一根“共地线”。
我们不仅仅可以将数据发送到串口调试助手,我们还可以在串口调试助手发送数据给控制器,控制器程序根据接收到的数据进行下一步工作。首先,我们来编写一个程序实现开发板与电脑通信,在开发板上电时通过 USART发送一串字符串给电脑,然后开发板进入中断接收等待状态,如果电脑有发送数据过来,开发板就会产生中断,我们在中断服务函数接收数据,并马上把数据返回发送给电脑。
为利用 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连通。
创建了两个文件: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 接收中断服务函数实现数据接收和发送。
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<num; i++ )
95 {
96 Usart_SendByte(pUSARTx, array[i]);
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) != '\0');
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 }