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
#include "stm32f10x.h" // Device header

#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

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
#include "stm32f10x.h" // Device header

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
#include <stdio.h>

int fputc(int ch, FILE*){
Serial_SendByte(ch);
return ch;
}

就……可以愉快地printf了……(啊这……)
不过,为了让其它串口也用上printf,我们可以……
1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include <stdarg.h>
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
2
printf("中文测试\r\n");
Serial_Printf("苍山如海,残阳似血\r\n");

接收

直接用中断的了。也可以循环检测,到时候去USART那里扒拉一下有关的读取函数就好。

1
2
3
4
5
6
void 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
2
3
4
5
6
7
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
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);

就是通过RXNE去判断是否发过来了一个字节。

作者

勇敢梧桐树

发布于

2022-12-28

更新于

2023-01-04

许可协议

评论

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×