0%

在51和STM32单片机上分别实现任意串口的类似printf操作

说明

这个是在本科的时候就弄好了,最近整理本科的一些文件的时候又看到了这些,如是准备做一些小结,也是给自己回忆一下。在51和STM32两款单片机实现C语言自带的printf操作,打印在串口中断,或者用于串口的通讯,下面是具体的操作。

关于串行和并行

在计算机和终端之间的数据传输通常是靠电缆或信道上的电流或电压变化实现的,通信电压或者电流的变化实现通信。如果一组数据的各数据位在多条线上同时被传输,这种传输方式称为并行通信。串行接口是一种可以将接受来自CPU的并行数据字符转换为连续的串行数据流发送出去。简单来说就是串行线材少,信息连续发送,并行信号同时发送,可以同时发送较多的信息,下面介绍单片机的串口通信。

串口通信(Serial Communication), 是指外设和计算机间,通过数据信号线 、地线、控制线等,按位进行传输数据的一种通讯方式。这种通信方式使用的数据线少,在通信中虽然传输速度比并行传输低,但是由于咸菜数量少,可以节约通信成本,而且在链接过程中非常方便,因此广泛使用。关于串口的相关知识点,比如:波特率, 数据为, 停止位, 奇偶检验位等等的具体信息王珊又很多的内容,这里就不具体介绍了,下面具体说明常用的两款单片机:51/STM32,他们是怎么配置的;

51单片机操作

首先是串口的初始化:

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
//串口初始化
void UartInit(void)
{
SCON = 0x50; // SCON: 模式 1, 8-bit UART, 使能接收
TMOD |= 0x20; // TMOD: timer 1, mode 2, 8-bit 重装
TH1 = 0xFD; // TH1: 重装值 9600 波特率 晶振 11.0592MHz
TR1 = 1; // TR1: timer 1 打开
EA = 1; //打开总中断
// ES = 1; //打开串口中断
}

//串口中断程序
void UART_SER (void) interrupt 4 //串行中断服务程序
{
unsigned char Temp; //定义临时变量
if(RI) //判断是接收中断产生
{
RI=0; //标志位清零
Temp = SBUF; //读入缓冲区的值
SBUF = Temp; //把接收到的值再发回电脑端
}
if(TI) //如果是发送标志位,清零
{
TI=0;
}
}

其次是发送单个字节:

1
2
3
4
5
6
7
//发送一个字节
void SendByte(unsigned char dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}

这里做一个说明,如果使用默认的库中的printf函数是可以的,他向下调用了“char puchar(char c);”这个在”stdio.h”中的函数,因此只需要给“putchar(char c)”给重定向即可使用printf了,使用如下:

1
2
3
4
5
6
//重定向
char puchar(char c)
{
SendByte(c);
return c;
}

重定向之后就可以尽情使用printf了!但是也有一些列的缺点,因为printf函数在库中就会占用1k左右的代码量,因此在小容量的51单片机中还是谨慎使用,下面给出一个更好的方法,就是sprintf函数,他与printf非常像素,但是他将内容映射到一个字符串,然后在将字符串按照字符一个一个发送出去,因此字符串长度可控,占用空间更少,效率也更高!具体如下:

1
2
3
4
5
6
7
8
9
//发送字符串
void SendStr(unsigned char *s)
{
while(*s!='\0') // \0 表示字符串结束标志
{
SendByte(*s);
s++;
}
}

这样就可以发送任意的字符串了,下面操作的时候先用sprintf转化一下即可,比如我想发送一个可变长度的helloworld,示例如下:

1
2
3
4
5
6
for(int i = 8; i < 12; i++>)
{
char Temp_S[15];
sprintf(Temp_S, "Hello World %d", i);
SendStr(Temp_S);
}

关于51单片机的说明就到这里,节约一点空间来讲,使用 sprintf + SendStr 这样的肯定是最好的。

STM32单片机操作

STM32的程序我是基于正点原子的来改的,也去他的论学习了很多知识,关于串口的这个也是参考了这个里面很多。原子哥文件里面默认是支持printf的,但是默认是一个,因为printf也是需要一个重定向,在usart.c文件中有说明,重定向那个串口就能用那个串口使用printf函数,具体函数如下:

1
2
3
4
5
6
7
//重定义fputc函数 
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}

但是但是但是,如果想使用多个串口通信而且想用printf这样方便的函数输出信息怎么办呢?当然有办法,那就是自定义一个,过程的话注释写的很清楚,具体如下:

1
2
3
4
5
6
7
8
9
10
11
//用串口写一个printf函数
void uprintf(USART_TypeDef *USARTx, const char *fmt, ...)
{
va_list ap; //typedef char *va_list; va_list是char型的指针
char *s_string = malloc(300); //申请缓冲区
va_start(ap, fmt); //找第一个可变形参的地址,并把地址赋给ap
vsprintf(s_string, fmt, ap); //类似sprintf函数
USART_String(USARTx, s_string); //发送和整个字符串
va_end(ap); //结束
free(s_string);
}

下面是完整的文件:

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
#include "uart_x.h"
#include "stdlib.h"
#include "stdarg.h"
//发送一个字节
void SendByte(USART_TypeDef *USARTx, unsigned char dat)
{
while((USARTx->SR & 0X40) == 0) {}; //循环发送,直到发送完毕
USARTx->DR = (u8) dat;
}
//发送一个字符串
void USART_String(USART_TypeDef *USARTx, char *s)
{
while(*s != '\0') // '\0' 表示字符串结束标志
{
SendByte(USARTx, *s);
s++;
}
}
//用串口写一个printf函数
void uprintf(USART_TypeDef *USARTx, const char *fmt, ...)
{
va_list ap; //typedef char *va_list; va_list是char型的指针
char *s_string = malloc(300); //申请空间
va_start(ap, fmt); /找第一个可变形参的地址,并把地址赋给ap
vsprintf(s_string, fmt, ap); //类似sprintf函数
USART_String(USARTx, s_string); //发送和整个字符串
va_end(ap); //结束
free(s_string);
}

上面的已经可以基本完成想要的内容了,可以多个串口同时使用printf,但是有一个问题,如果发送的字符串长度大于缓冲区的长度就会内存溢出,因此最好的办法就是在家一个参数,从而申请合适的大小空间。

1
2
3
4
5
6
7
8
9
10
11
//用串口写一个printf函数
void uprintf_s(USART_TypeDef *USARTx, uint32_t BuffSize,const char *fmt, ...)
{
va_list ap; //typedef char *va_list; va_list是char型的指针
char *s_string = malloc(BuffSize); //申请空间
va_start(ap, fmt); //找第一个可变形参的地址,并把地址赋给ap
vsprintf_s(s_string,BuffSize, fmt, ap); //类似sprintf_s函数
USART_String(USARTx, s_string); //发送和整个字符串
va_end(ap); //结束
free(s_string);
}

结语

总的来说,printf是很好用,可以映射很多东西,比如LCD,OLED的驱动程序,只要配置好了并且设置了重定向都能用,可以说用途非常广泛,但是毕竟是系统库,占用内容还是比较多的,因此51单片机的话还是不用吧,STM32的话也是尽量用sprintf的形式,因为这样效率更高,内容占用也少。

-------------本文结束感谢您的阅读-------------