STM32USART
我的新冠终于快要好啦!!!
本小节需要用到串口助手
观察实验现象。
USART串口协议
名称 | 引脚 | 双工 | 时钟 | 电平 | 设备 |
---|---|---|---|---|---|
USART | TX(D)、RX(D) | 全双工 | 异步 | 单端 | 点对点 |
I2C | SCL、SDA | 半双工 | 同步 | 单端 | 多设备 |
SPI | SCLK、MOSI、MISO、CS | 全双工 | 同步 | 单端 | 多设备 |
CAN | CAN_H、CAN_L | 半双工 | 异步 | 差分 | 多设备 |
USB | DP(D+)、DM(D-) | 半双工 | 异步 | 差分 | 点对点 |
同步:有着一个单独的时钟线,例如I2C和SPI。
差分信号可以提高通信的抗干扰能力。
USART应该共地,以抗干扰。TX是发送,RX是接收,两个设备互连时TX与RX交叉连接。但是选项的数据传出时,可以只接一根通信线。
串口参数
波特率:串口通信的速率(一般来讲,在单片机领域,波特率=比特率)
起始位:标志着一个数据帧的开始,固定为低电平
数据位:数据帧的有效载荷,1为高电平,0位低电平,低位先行
校验位:用于数据验证,根据数据位计算得来
停止位:用于数据帧间隔,固定为高电平
奇偶校验:“1的个数”。
CRC校验更好些。
串口时序
起始位下降沿
停止位上升沿
可带校验可以不带。
TX输出定时翻转的高低电平,RX依次读取。
USART
USART 通用同步/异步收发器。有时候称UART,即异步收发器
停止位长度:帧间隔。0.5/1/1.5/2,通常用1
“硬件流控制”(略)
USART1是APB2上的,USART2、3是APB1上的(好怪的分配)
应该根据引脚定义决定使用哪些引脚作为USART的引脚。
TXE标志位:发送数据寄存器已经空了
RXNE标志位:接收数据寄存器已得到数据。可以引发中断。
示例
USART串口发送信息
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void Init()
{
OLED_Init();
Serial_Init();
}
int main()
{
Init();
Serial_SendArray("Hello World!", 12);
while (1){
OLED_Refresh();
}
return 0;
}Serial.c
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
void Serial_Init(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_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 *arr, uint32_t Length){
while (Length--){
Serial_SendByte(*arr);
arr++;
}
}
printf移植
printf的基础输出是fputc(int, FILE*)(感觉这印证了linux的“老话”:“一切对象皆文件”)1
2
3
4
5
6
int fputc(int ch, FILE*){
Serial_SendByte(ch);
return ch;
}
就……可以愉快地printf了……(啊这……)
不过,为了让其它串口也用上printf,我们可以……1
2
3
4
5
6
7
8
9
10
void Serial_Printf(char *format, ...){
char buffer[128];
va_list arg;
va_start(arg, format);
vsprintf(buffer, format, arg);
va_end(arg);
Serial_SendStr(buffer);
}
虽然一致觉得C语言的可变参数比较粗糙,不过在单片机上还是很好用的。
测试代码:1
2printf("中文测试\r\n");
Serial_Printf("苍山如海,残阳似血\r\n");
接收
直接用中断的了。也可以循环检测,到时候去USART那里扒拉一下有关的读取函数就好。1
2
3
4
5
6void USART1_IRQHandler(void){
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET){
printf("Received: %x\r\n", USART_ReceiveData(USART1));
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
}
1 | NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); |
就是通过RXNE去判断是否发过来了一个字节。