十一、串口打印信息
1. 配置流程
一般我们使用串口,都需要有以下几个步骤:
- 开启时钟(包括串口时钟和GPIO时钟)
- 配置GPIO复用模式
- 配置GPIO的模式
- 配置GPIO的输出
- 配置串口(配置一些参数)
- 使能串口(串口使能和发送使能)
1.1 开启时钟
使用串口1的话就是PA9和PA10引脚。那第一步就是先开启端口A的时钟,在库函数点灯那一章节给大家介绍了使能时钟的函数RCC_APB2PeriphClockCmd
,只需要传入对应的参数即可。使能端口A的时钟就把GPIOA的时钟当做参数传入。第二步就是开启串口的时钟,把对应的串口1的时钟RCC_APB2Periph_USART1
传入即可。
// 开启时钟:GPIO复用时钟、GPIOA时钟和USART1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOA,ENABLE);
2
1.2 配置GPIO
配置GPIO的模式还是使用GPIO_Init
这个函数,不同的是第二个参数要配置为复用功能而不是输出功能,第三个参数要配置为上拉,转化为代码如下:
GPIO_InitTypeDef GPIO_InitStructure;
// 初始化GPIO结构体,并配置TX引脚
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //TX引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 重新初始化GPIO结构体,并配置RX引脚
GPIO_StructInit(&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //RX引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
2
3
4
5
6
7
8
9
10
11
12
13
14
- GPIO_Mode_AF_PP:表示引脚被配置为复用推挽输出,适用于USART的TX引脚。
- GPIO_Mode_IN_FLOATING:表示引脚被配置为浮空输入,适用于USART的RX引脚。
1.3 配置串口
我们依然使用结构体的方式进行配置串口数据,里面有些参数是必须要配置的:
- 波特率:我们使用形参__Baud可以自由调整波特率
- 字节长度:8位
- 停止位:1位停止位
- 校验位:不需要校验位
- 收发模式:收发
- 流控选择:不流控
相关的代码:
// 配置UART
USART_InitTypeDef USART_InitStructure; //定义配置串口的结构体变量
// 开启USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// 将USART1寄存器重置为默认值
USART_DeInit(USART1);
// 初始化USART结构体
USART_StructInit(&USART_InitStructure);
USART_InitStructure.USART_BaudRate = __Baud; //设置波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字节长度为8bit
USART_InitStructure.USART_StopBits = USART_StopBits_1; //1个停止位
USART_InitStructure.USART_Parity = USART_Parity_No ; //没有校验位
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
USART_Init(USART1, &USART_InitStructure); //初始化USART1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1.4 使能串口
串口配置好之后并不能开始工作,还需要去使能,就是相当于有一个开关可以打开关闭。要使用串口,首先要打开总开关。
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
转化为代码为:
// 使能USART1
USART_Cmd(USART1, ENABLE);
2
2. 串口发送数据
配置好串口之后,下一步的操作就是要发送数据。
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
发送数据
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
获取状态寄存器的标志
从上图可以了解到当将要发送的数据写入USART_DATA时,此位被清0,当数据发送完成之后,此位置1。所以当检测到此位为1时就表明当前数据缓冲区为空,可以继续发送数据。
关于串口发送数据可以封装为一个函数如下:
void usart_send_data(uint8_t ucch)
{
USART_SendData(BSP_USART, (uint8_t)ucch);
// 等待发送数据缓冲区标志置位
while( RESET == USART_GetFlagStatus(BSP_USART, USART_FLAG_TXE) ){}
}
2
3
4
5
6
7
当我们使用以下代码时:
usart_send_data('h');
usart_send_data('e');
usart_send_data('l');
usart_send_data('l');
usart_send_data('o');
2
3
4
5
将会在串口助手打印 hello :
这样一个字符一个字符的打印输出是不是很麻烦,没关系,我们可以再进行封装一层,一次发送一个字符串。
void usart_send_String(uint8_t *ucstr)
{
while(ucstr && *ucstr) // 地址为空或者值为空跳出
{
usart_send_data(*ucstr++);
}
}
2
3
4
5
6
7
通过调用下面的代码:
usart_send_String("hello\r\n");
就可以打印出 hello。
3. 串口重定向
上面封装的发送字符串的方式打印信息看似方便,但如果我们想要打印数字,小数,该怎么打印呢?大家是否习惯使用了printf
这个函数,可以通过%d
,%f
打印整形和小数,这一小节就教大家怎么把串口重定向到printf
函数。
3.1 串口重定向介绍
C语言中的printf
函数默认输出设备是显示器,如果要在串口显示,必须重新定义标准库函数里调用的与输出设备相关的函数。需要注意的是,在keil中使用printf
一定要勾选“微库”选项。
3.2 printf重定向
首先c语言的printf函数中不断循环调用fputc
函数,所以需要重写fputc
函数,这个函数的功能就是打印输出一个字符,这不正和我们编写的usart_send_data函数功能一样。fputc
函数可写为:
#if !defined(__MICROLIB)
//不使用微库的话就需要添加下面的函数
#if (__ARMCLIB_VERSION <= 6000000)
//如果编译器是AC5 就定义下面这个结构体
struct __FILE
{
int handle;
};
#endif
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
#endif
/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
USART_SendData(BSP_USART, (uint8_t)ch);
while( RESET == USART_GetFlagStatus(BSP_USART, USART_FLAG_TXE) ){}
return ch;
}
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
注意
从第1行到第18行的代码,会在我们不使用微库的时候生效,我们这样设置就能正常使用printf
函数了,而且兼容性会更高。
编写好fputc
函数之后就可以使用printf
函数输出信息了。
printf("hello\r\n");
4. 实验现象
关于这一章节的代码百度网盘下载,在立创·STM32F103C8T6开发板资料/第03章软件资料/代码例程/005串口打印信息。
烧写代码之后,打开串口调试助手,每隔1s会打印一次信息,信息分别为整数每隔1s+1和小数每隔1s+0.11,打印信息如下图所示。