在单片机的应用中,通讯是必不可少的,通过通讯能够收发数据,进而对数据进行处理,进而生成控制指令,或者单纯用来调试,也是非常不错的选择。

通信接口

  • 通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统

  • 通信协议:制定通信的规则,通信双方按照协议规则进行数据收发

image-20250205143915034

除上面指出的全双工和半双工之外,还有单工,指的是只能从将数据从一处传输至另一处,是单向的。

单端信号需要共地。

串口通信

  • 串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信

  • 单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力

电平标准

电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种:

  1. TTL电平:+3.3V+5V 表示 10V 表示 0

  2. RS232电平:-3 ~ -15V 表示 1+3 ~ +15V表示 0

  3. RS485电平:两线压差 +2 ~ +6V 表示 1-2 ~ -6V 表示 0(差分信号)

串口参数及时序

  • 波特率:串口通信的速率

  • 起始位:标志一个数据帧的开始,固定为低电平

  • 数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行

  • 校验位:用于数据验证,根据数据位计算得来

  • 停止位:用于数据帧间隔,固定为高电平

image-20250205192550058

USART简介

  1. USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器

  2. USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里

  3. 自带波特率发生器,最高达4.5Mbits/s

  4. 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)

  5. 可选校验位(无校验/奇校验/偶校验)

  6. 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN

  7. STM32F103C8T6 USART资源: USART1、 USART2、 USART3

USART框图

image-20250205192729415

注意:USART的TDR和RDR两个寄存器在程序上只表现为一个寄存器(DR),但在实际硬件电路中有两个,一个只写,一个只读。

USART基本结构

image-20250205192758875

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//串口接收+发送 程序代码

//初始化串口
void Serial_Init(void)
{
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

//GPIO配置 PA9
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

//USART初始化
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;//波特率
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用硬件流控制
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;//模式:发送和接收
USART_InitStructure.USART_Parity = USART_Parity_No;//校验位:无
USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位:1
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//长度:8
USART_Init(USART1, &USART_InitStructure);

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启USART1中断

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//NVIC配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);

USART_Cmd(USART1, ENABLE);//开启串口
}

//发送数据
void Serial_SendByte(uint8_t Byte)
{
USART_SendData(USART1, Byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

//发送数组
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++)
{
Serial_SendByte(Array[i]);
}
}

//发送字符串
void Serial_SendString(char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i ++)
{
Serial_SendByte(String[i]);
}
}

//次方函数
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y --)
{
Result *= X;
}
return Result;
}

//发送数字
void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i ++)
{
Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
}
}

//重定向printf函数,记得勾选MicroLIB
int fputc(int ch, FILE *f)
{
Serial_SendByte(ch);
return ch;
}

//对sprintf函数
void Serial_Printf(char *format, ...)
{
char String[100];
va_list arg;
va_start(arg, format);
vsprintf(String, format, arg);
va_end(arg);
Serial_SendString(String);
}

uint8_t Serial_GetRxFlag(void)
{
if (Serial_RxFlag == 1)
{
Serial_RxFlag = 0;
return 1;
}
return 0;
}

uint8_t Serial_GetRxData(void)
{
return Serial_RxData;
}

void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
Serial_RxData = USART_ReceiveData(USART1);
Serial_RxFlag = 1;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}

数据包

  1. HEX数据包
    • 固定包长
    • 可变包长
  2. 文本数据包
    • 固定包长
    • 可变包长

HEX数据包接收

image-20250206135407009

文本数据包接收

image-20250206135427856

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void USART1_IRQHandler(void)//中断函数内处理接收到的数据
{
//static关键字可以使得变量在函数运行完之后不被销毁,改变生命周期,不改变作用域
static uint8_t RxState = 0;//状态
static uint8_t pRxPacket = 0;//收集数据个数
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//RX非空:可读
{
uint8_t RxData = USART_ReceiveData(USART1);//读取数据

if (RxState == 0)
{
if (RxData == '@' && Serial_RxFlag == 0)
{
RxState = 1;
pRxPacket = 0;
}
}
else if (RxState == 1)
{
if (RxData == '\r')
{
RxState = 2;
}
else
{
Serial_RxPacket[pRxPacket] = RxData;
pRxPacket ++;
}
}
else if (RxState == 2)
{
if (RxData == '\n')
{
RxState = 0;
Serial_RxPacket[pRxPacket] = '\0';
Serial_RxFlag = 1;
}
}

USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除标志位
}
}

END