单片机使用printf函数的两种办法

发布者:和谐相伴最新更新时间:2015-10-12 来源: eefocus关键字:单片机  printf函数 手机看文章 扫描二维码
随时随地手机看文章
今天就简单来说说怎么在51单片机中使用"printf"函数:包括使用自带的printf函数和自己动手写一个printf函数,再贴代码之前先介绍几个相关的知识,主要用于自己写printf函数。

①C语言函数:vsprintf,

其原型为int vsprintf(char *string, char *format, va_list param);,作用为将param按格式format写入字符串string中,因此他可以用于将任何格式数据转化为字符串数据,比如把整数97转化为ASCII码的97的程序就像下面这样vsprintf(string,"%d",97),当然还有一点需要注意:在keil里面使用sprintf需要包含stdio.h这个头文件(当然这里也可以使用sprintf,作用完全差不多,只需要稍加修改即可,不多介绍)。说到这里,再来说说另一个函数itoa吧,其实他比vsprintf更简单,其原型为char *itoa(int value, char *string, int radix),作用为将value按radix进制写入到string字符串中,使用时需要包含头文件stdlib.h,可是在keil里面却无法使用,头文件里面不包含itoa,即使把VC里面的复制进来貌似还是不行,呵呵,目前不知道原因。

②可变参数函数

具体来说就是stdarg.h里面的这几个函数va_start,va_arg,va_list,va_end,这几个参数用于开辟一段内存区域,可以配合vsprintf使用,但是对内存使用较大,需要单片机具备一定的RAM,否则程序就算能编译通过也是无法运行的,用法见这里http://blog.csdn.net/googlemi/article/details/8988567

③内部拓展RAM

定义:集成在单片机内部的数据存储器,在物理上是内部,但逻辑上是外部,访问时需要使用MOVX或者xdata访问,具体可以看STC8051手册,如下所示

从这里可以看出,STC12C5A60S2单片机内部含有1024byte=1Kbyte的内部拓展RAM,在访问的时候,使用C语言的时候,需要加上xdata才可以访问,使用汇编的时候需要用MOVX指令访问。

到这里我们先来算一算STC12C5A60S2单片机在不接外部RAM的情况下总的RAM大小,先看一下内部的RAM分布图

:内部RAM(256byte)包括三部分:低128byte,高128byte和特殊功能寄存器区,特别注意和51不同,他的特殊功能寄存器和高128byte地址重叠,但是物理上是分开的,所有内部可用的RAM有256byte,所以到此为止可以得到内部RAM大小总共有:256byte+1024byte=1280byte

说了这么多,下面不多说,直接上程序

《一》直接使用系统自带的printf函数:特别注意需要置位TI=1,否则是无法发送的,程序如下:

[cpp] view plaincopyprint?

01.//此程序主要用于uart发送(proteus终端不能显示汉字,但串口助手可以),输入换行符 换行

02.#include

03.//stdio.h,string.h用于printf函数原型

04.#include

05.

06.

07.void delay(unsigned int z);

08.void uart_init(void);//串行口初始化

09.

10.int main(void)

11.{

12. int a=99;

13. char *string="abde";

14. uart_init();

15. while(1)

16. {

17. printf("%d %x %c %s %p ",a,a,(char)a,string,string);

18. delay(1000);

19. }

20. return 0;

21.}

22.

23.void uart_init(void)

24.{

25. TMOD=0x20;//即0010 0000,定时器/计数器1,工作方式2

26. TH1=0xfd;//设置波特率为9600

27. TL1=0xfd;

28. TR1=1;//启动定时器/计数器1

29.

30. SCON=0x50; //0101 0000.串口工作方式1,允许串行控制

31. PCON=0x00;//设置SMOD=0

32. IE=0x90; //CPU允许中断,串行允许中断

33.

34. TI=1;//直接使用printf必须加入此句才能实现发送

35.}

36.

37.void delay(unsigned int z)

38.{

39. unsigned int x,y;

40. for(x=z;x>0;x--)

41. for(y=110;y>0;y--);

42.}

//此程序主要用于uart发送(proteus终端不能显示汉字,但串口助手可以),输入换行符 换行

#include

//stdio.h,string.h用于printf函数原型

#include

void delay(unsigned int z);

void uart_init(void);//串行口初始化

int main(void)

{

int a=99;

char *string="abde";

uart_init();

while(1)

{

printf("%d %x %c %s %p ",a,a,(char)a,string,string);

delay(1000);

}

return 0;

}

void uart_init(void)

{

TMOD=0x20;//即0010 0000,定时器/计数器1,工作方式2

TH1=0xfd;//设置波特率为9600

TL1=0xfd;

TR1=1;//启动定时器/计数器1

SCON=0x50; //0101 0000.串口工作方式1,允许串行控制

PCON=0x00;//设置SMOD=0

IE=0x90; //CPU允许中断,串行允许中断

TI=1;//直接使用printf必须加入此句才能实现发送

}

void delay(unsigned int z)

{

unsigned int x,y;

for(x=z;x>0;x--)

for(y=110;y>0;y--);

}

《二》自己动手写一个类似printf的函数:uart_printf

但是这种情况占用RAM较多,因为需要开辟大数组,所以需要使用拓展型的51单片机,普通的AT89C51和STC89C52之类的会造成内存不够用、堆栈溢出等等问题,所以以下程序都是基于STC12C5A60S2的,因为它含有内部拓展的1024byte的RAM,可以用来存储大数组

[cpp] view plaincopyprint?

01.//此程序主要用于uart发送(proteus不能仿真,但实际是可以运行的),输入换行符 换行

02.#include

03.//stdio.h,stdarg.h用于vsprintf函数原型

04.#include

05.#include

06.

07.void delay(unsigned int z);

08.void uart_init(void);//串行口初始化

09.void sendbyte(unsigned char c);

10.void sendstring(unsigned char *string);

11.void uart_printf(const char *fmt,...);

12.

13.int main(void)

14.{

15. int a=99;

16. uart_init();

17. while(1)

18. {

19. uart_printf("10进制:%d 16进制:%x 字符格式:%c ",a,a,a);

20. delay(1000);

21. }

22. return 0;

23.}

24.

25.void uart_init(void)

26.{

27. TMOD=0x20;//即0010 0000,定时器/计数器1,工作方式2

28. TH1=0xf3;//设置波特率为2400

29. TL1=0xf3;

30. TR1=1;//启动定时器/计数器1

31.

32. SCON=0x50; //0101 0000.串口工作方式1,允许串行控制

33. PCON=0x00;//设置SMOD=0[page]

34. IE=0x00; //由于是查询方式,故需要禁止中断,CPU不允许中断,串行不允许中断

35.

36.}

37.

38.void delay(unsigned int z)

39.{

40. unsigned int x,y;

41. for(x=z;x>0;x--)

42. for(y=110;y>0;y--);

43.}

44.

45.void sendbyte(unsigned char c)

46.{

47. if(c==' ')//如果遇到 就换行

48. {

49. //发送CR(carriage return)

50. SBUF=0x0D;

51. while(!TI);//等待发送完成

52. TI=0;

53.

54. //发送 LF(NL line feed,new line)

55. SBUF=0x0A;

56. while(!TI);//等待发送完成

57. TI=0;

58. }

59. else

60. {

61. SBUF=c;

62. while(!TI);//等待发送完成

63. TI=0;

64. }

65.}

66.

67.void sendstring(unsigned char *string)//此处*string相当于数组

68.{

69. while(*string!='')//判断是否到字符串末尾

70. {

71. sendbyte(*string);

72. string++;

73. }

74.}

75.

76.void uart_printf(const char *fmt,...)

77.{

78. va_list ap;

79. char xdata string[1024];//访问内部拓展RAM,非访问外部RAM,不能超过内部拓展RAM大小(此处为1024)

80.

81. va_start(ap,fmt);

82. vsprintf(string,fmt,ap);//此处也可以使用sprintf函数,用法差不多,稍加修改即可,此处略去

83. sendstring(string);

84. va_end(ap);

85.}

//此程序主要用于uart发送(proteus不能仿真,但实际是可以运行的),输入换行符 换行

#include

//stdio.h,stdarg.h用于vsprintf函数原型

#include

#include

void delay(unsigned int z);

void uart_init(void);//串行口初始化

void sendbyte(unsigned char c);

void sendstring(unsigned char *string);

void uart_printf(const char *fmt,...);

int main(void)

{

int a=99;

uart_init();

while(1)

{

uart_printf("10进制:%d 16进制:%x 字符格式:%c ",a,a,a);

delay(1000);

}

return 0;

}

void uart_init(void)

{

TMOD=0x20;//即0010 0000,定时器/计数器1,工作方式2

TH1=0xf3;//设置波特率为2400

TL1=0xf3;

TR1=1;//启动定时器/计数器1

SCON=0x50; //0101 0000.串口工作方式1,允许串行控制

PCON=0x00;//设置SMOD=0

IE=0x00; //由于是查询方式,故需要禁止中断,CPU不允许中断,串行不允许中断

}

void delay(unsigned int z)

{

unsigned int x,y;

for(x=z;x>0;x--)

for(y=110;y>0;y--);

}

void sendbyte(unsigned char c)

{

if(c==' ')//如果遇到 就换行

{

//发送CR(carriage return)

SBUF=0x0D;

while(!TI);//等待发送完成

TI=0;

//发送 LF(NL line feed,new line)

SBUF=0x0A;

while(!TI);//等待发送完成

TI=0;

}

else

{

SBUF=c;

while(!TI);//等待发送完成

TI=0;

}

}

void sendstring(unsigned char *string)//此处*string相当于数组

{

while(*string!='')//判断是否到字符串末尾

{

sendbyte(*string);

string++;

}

}

void uart_printf(const char *fmt,...)

{

va_list ap;

char xdata string[1024];//访问内部拓展RAM,非访问外部RAM,不能超过内部拓展RAM大小(此处为1024)

va_start(ap,fmt);

vsprintf(string,fmt,ap);//此处也可以使用sprintf函数,用法差不多,稍加修改即可,此处略去

sendstring(string);

va_end(ap);

}

最后简单总结 :其中第一种方法用于中断方式工作会有问题,因为其底层是调用putchar函数实现的,工作方式为中断,但是比较简单,可以通过proteus仿真;第二种方法更完善,不过程序比较复杂,不能使用proteus仿真,但实际是可以工作的。​

注明:这篇文章是我在网上看到的,转载一下,自己做个备份,同时也希望更多的人看到它,学习到更多的东西。技术源于积累,以此共勉。

关键字:单片机  printf函数 引用地址:单片机使用printf函数的两种办法

上一篇:如何在单片机上使用printf函数
下一篇:复位电路的几种设计

推荐阅读最新更新时间:2024-03-16 14:35

汽车电子MCU的抗EMI设计
随着集成电路集成度的提高,越来越多的元件集成到芯片上,电路功能变得复杂,工作电压也在降低。当一个或多个电路里产生的信号或噪声与同一个芯片内另一个电路的运行彼此干扰时,就产生了芯片内的EMC问题,最为常见的就是SSN(Simultaneous Switch Noise,同时开关噪声)和Crosstalk(串音),它们都会给芯片正常工作带来影响。由于集成电路通过高速脉冲数字信号进行工作,工作频率越高产生的电磁干扰频谱越宽,越容易引起对外辐射的电磁兼容方面问题。基于以上情况,集成电路本身的电磁干扰(EMI)与抗扰度(EMS)问题已成为集成电路设计与制造关注的课题。 集成电路电磁兼容不仅涉及集成电路电磁干扰与抗扰度的设计和测试方法,而且
[嵌入式]
基于采用AT89S51单片机和LM35温度传感器的温度采集显示系统设计
随着电子和传感技术的快速发展,温度的测量和控制在民用、工业以及航空航天技术等领域,等到了广泛应用。小型的、低功耗的、廉价的、可靠性高的温度传感器引起了人们的广泛关注。在实际生产、生活等领域中,温度是环境因素不可或缺的一部分,对温度进行及时精确的控制和检测显得尤为重要。本文基于AT89S51单片机,采用 LM35温度传感器,设计了一种灵敏度较高,抗干扰能力强,工作稳定可靠的温度采集显示系统。 1、系统结构及工作原理 温度采集显示系统电路由温度采集模块、A/D转换模块、单片机控制模块、数码管显示模块和下载模块组成。电路工作原理是:首先由LM35温度传感器采集外界环境的温度,经LM358放大10倍后以电压形式输入到A/D采样电路,由
[单片机]
基于采用AT89S51<font color='red'>单片机</font>和LM35温度传感器的温度采集显示系统设计
stm32l01系列单片机通过串口编程(ISP)
官方给出的ISP软件(串口烧录软件)FlashLoader Demonstrator不支持stm32l01系列的单片机那么我们怎么使用串口给这个型号的单片机通过串口烧录程序呢? 最近做了一个项目有这方面的需求,所以研究了一下这个方法,在此分享出来给有需要的人。准备工具只需mcuisp(也称flymcu)软件,串口助手 给懒人提供mcuisp软件mcuisp 经过我的试验mcuisp可以给这个单片机编程但是不能擦除这个芯片,后来我发现这个芯片的擦除指令不一样有兴趣的同学可以读一读这个文档AN3155 (USART protocol used in the STM32 bootloader)去度娘搜。 所以我们现在的任务就是想
[单片机]
51单片机读端口与读引脚彻底研究(个人浅见)
要弄清读端口、读引脚问题,思考以下问题: (1)引脚的结构图清楚吗? 以上是P0口一位的结构。以上结构说明:读引脚之前一定要对端口先置1,使mosfet V2截止,以免信号被拉低而读错,这一点很容易理解。 (2)什么指令读端口?什么指令读引脚? 一般的教科书上都会强调:读端口的指令为端口内容取反这样的 读-修改-写 指令。而读引脚之前,先置1,然后用Mov A,Px之类的指令即可。 以下是从web( http://www.dzsc.com/dzbbs/20061112/200765184756359377.html )上搜集的资料: 资料称: 这样的指令才有 Read-Modify-Write 功效 ANL
[单片机]
基于MSP430单片机的LCD1602显示
首先我们先安装IAR for MSP430,这里注意不要下载错了IAR for ARM版本的就不能用。 贴上下载链接:https://pan.baidu.com/s/1P7qAhhjYFYnpz6gWZQ6_Pg 提取码:0j24 IAR的安装见:https://blog.csdn.net/qq_41860526/article/details/103411695 安装完成后我们就开始实验啦 先说仿真吧,不细说了,直接贴图,一切从简 嗯这时候开始码代码吧 也直接贴上吧,工程创建参考:https://blog.csdn.net/klaus_x/article/details/80957189 我把我的例
[单片机]
基于MSP430<font color='red'>单片机</font>的LCD1602显示
聚焦IoT领域 鸿博微以“MCU+”策略推动国产替代进程
近年来,受物联网快速发展带来的联网节点数量增长、汽车电子的渗透率提升以及工业4.0对自动化设备的旺盛需求等因素的影响,MCU在汽车电子、物联网、消费电子等下游应用领域的使用大幅增加,全球MCU市场规模快速增长。 面对如此诱人的市场蛋糕,包括兆易创新、芯海科技、广州鸿博微电子技术有限公司(下称“鸿博微”)等国内MCU厂商也纷纷加码布局。其中,鸿博微作为国内“造芯”的新势力,其设立之初便制定“MCU+解决方案”的产品策略,并在一年内量产三款MCU产品,具备较高的产品迭代速度和研发能力。 据了解,鸿博微拥有高水平的技术团队,在模拟芯片和数字芯片方面都有丰富的经验积累。同时,该公司在产品布局、知识产权、市场开发以及团队建设方面均做了清晰规
[手机便携]
51单片机-电位器控制舵机
1.题目要求 为了使ADC的知识能配合其他模块实现不同功能,这里我们打算通过扭动电位器(滑动变阻器)来控制舵机桨的位置,如果我们顺时针扭一圈电位器,那么舵机桨也会跟着从一端旋转到另一端。 2.main.c测试代码 #include reg52.h #include function.h //详见第六章第8讲 #include timer.h //详见第八章第11讲 #include lcd.h //详见第十一章第3讲 #include adc.h //详见第十四章 sbit PWMOUT=P1^7; //舵机信号线接口 u8 flag20ms = 0; u8 PWM_VAL = 0; void m
[单片机]
51<font color='red'>单片机</font>-电位器控制舵机
单片机抢答器的设计资料详细说明
在知识比赛中, 特别是做抢答题目的时候, 在抢答过程中,为了知道哪一组或哪一位选手先答题,必须要设计一个系统来完成这个任务。如果在抢答中,靠视觉是很难判断出哪组先答题。利用单片机系统来设计抢答器,使以上问题得以解决,即使两组的抢答时间相差几微秒,也可分辨出哪组优先答题。本文主要介绍了单片机抢答器设计及工作原理,以及它的实际用途。 ! 系统工作原理本系统采用8051单片机作为核心。控制系统的四个模块分别为:存储模块、显示模块、语音模块、抢答开关模块。该抢答器系统通过开关电路四个按键输入抢答信号;利用语音芯片ISD1420 完成语音的录放功能; 利用存储程序; 利用一个数码管来完成显示功能。工作时,用按键通过开关电路输入各路的抢答信号
[单片机]
<font color='red'>单片机</font>抢答器的设计资料详细说明
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
热门活动
换一批
更多
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

更多精选电路图
换一换 更多 相关热搜器件
更多每日新闻
随便看看
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved