×
单片机 > 单片机程序设计 > 详情

STM32的UART读写及printf打印

发布时间:2020-08-25 发布时间:
|

0.摘要

本文以STM32F1x系列单片机为例,主要介绍了串口的初始化、串口中断、接收/发送、串口调试等内容,也顺带讲到中断分组、半主机模式以及微库MicroLIB。


1.串口初始化

串口初始化主要包括对IO、USART和中断的初始化。根据STM32F1x手册RM0008的P166,USART在全双工模式下,发送口TX要配置成复用推挽输出,接收口RX要配置成浮空输入或上拉输入。此外,本文不使用USART的硬件流控制,所谓硬件流控制就是通过加入额外的引脚(RTS和CTS)来控制数据的收发过程,在数据传输之前确认收发双方均准备好才进行通信,用于防止接收缓冲区满而导致的数据丢失问题。


{

printf("buff[%d] = 0x%02hhx\r\n", i, buff[i]); //输出十六进制,保留最低两位,不够补0

}

n = 0;

}

}

 


3.串口发送与printf打印

前面说的的DR寄存器是一个可读写寄存器(实际上是由两个寄存器组成),串口的发送和接收都要围着它转,收到的数据从它里面读,而发送的数据要往它里面扔。串口的发送操作非常简单,一条语句就能搞定,就是往DR寄存器写入要发送的数据:


USART1->DR = data;

或者使用库函数:


USART_SendData(USART1, data);

串口在嵌入式领域不仅是一个通讯接口,还是一种调试工具,其好用程度不亚于硬件仿真。学过C语言的朋友应该都知道标准库函数printf()和scanf(),前者用于打印信息到控制台上,后者实现从键盘读入字符到程序。Keil、IAR等集成开发环境均支持标准库函数,如果在单片机的程序里调用printf()打印内容,最终会在哪里显示呢?答案是不可知的,因为单片机没有控制台这种东西,但我们可以利用它的外设来实现printf(),比如LCD或串口(串口再接到电脑上显示打印信息)。串口基本上大多数单片机都有,而LCD就不一定了,所以我们通常用串口来打印内容。


那么只要是有串口的单片机,调用一下printf()就可以打印信息了吗?还没那么简单,单片机并不能猜透你的意图,你需要告诉它往哪里printf,通过下面的fputc()函数来实现。fputc()是printf()的底层函数,需要把它改装一番,让它把要打印的数据发送到串口上去。


/*****************************************************

*function: 写字符文件函数

*param1: 输出的字符

*param2: 文件指针

*return: 输出字符的ASCII码

******************************************************/

int fputc(int ch, FILE *f)

{

while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); //等待上次发送结束

USART_SendData(USART1, (unsigned char)ch); //发送数据到串口

return ch;

}

除此之外,我们还要再做一点配置工作——禁用半主机模式,禁用了半主机模式才能使用标准库函数printf()打印信息到串口,在程序中加入以下代码即可。那么什么是半主机模式?为什么不用它?半主机模式是ARM单片机的一种调试机制,跟串口调试不一样的是,它需要通过仿真器来连接电脑和ARM单片机,并调用相应的指令来实现单片机向电脑显示器打印信息(或者从电脑键盘读取输入)。简而言之,这种方法比串口调试更复杂(需要进行更多的配置操作),也更不灵活(一定要用仿真器)。


/********** 禁用半主机模式 **********/

#pragma import(__use_no_semihosting)

 

struct __FILE

{

int a;

};

 

FILE __stdout;

 

void _sys_exit(int x)

{

}

 


上面的配置似乎有点麻烦,要加入这么一堆难懂的代码,难道没有更简便点的方法吗?有,但不推荐。方法是使用微库(MicroLIB),只要在Keil的“Options for Target -> Target ->Use MicroLIB”上打钩,即可使用串口打印(fputc()函数还是要改,但上述代码不用加)。微库是区别于C标准库的另一个库,当使用微库时,就默认关闭了半主机模式,也就不用添加上面的代码。这样虽然方便,但个人建议能不用就不用,原因:第一,微库是为小内存嵌入式设备而设计的,使用它可以减少代码所占空间,但对现在STM32等单片机来说,内存一般都够用,微库并非必需;第二,微库相对于C标准库而言,支持的功能更少,主要体现在对操作系统的支持上。总的来说,标准的东西总是相对更可靠,所以不必要的掉坑,还是用C标准库,不用微库。


4.最后

我们还需要一个USB转TTL模块和一台装有串口调试软件的电脑,就可以看到单片机打印到串口上的内容了。从此,如果我们想看某个变量的值,可以打印一下,想看程序跑到哪个地方,也可以打印一下,想让单片机向世界say个hello,还是可以打印一下。妈妈再也不用担心我的调试!


主函数:


int main()

{

USART1_Init(115200);

NVIC_Config();

printf("Hello, world!\r\n");

printf("Please enter any character:\r\n");

while(1);

}

运行效果:


参考:


[1] RM0008 (STM32F1x用户手册)


[2] UM0427 (STM32F101x/STM32F103x固件库手册)


[3] Keil官方对半主机模式semihosting的介绍


[4] Keil官方对微库MicroLib的介绍




『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
8051单片机的函数发生器的设计