浅谈MDK环境下使用stdio.h的问题

在使用MDK进行嵌入式开发调试串口时,使用C语言标准输入输出库函数,程序常无法正常运行。原因是标准库默认输出设备为显示器,且使用半主机模式。文章给出两种解决方法,一是使用微库,二是仍用标准库但重写相关函数,还介绍了半主机模式原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在使用MDK进行嵌入式开发,特别是调试串口的时候经常要用到C语言的标准输入输出库函数,如printf();。这样写出来的程序,通常编译和链接过程都不会报错,但是程序却无法正常运行.

原因分析如下:

标准库函数的默认输出设备是显示器,要实现在串口或LCD输出,必须重定义标准库函数里调用的与输出设备相关的函数. 

例如:printf输出到串口,需要将fputc里面的输出指向串口(重定向),方法如下: 只要自己添加一个int fputc(int ch, FILE *f)函数,能够输出字符就可以了

int fputc(int ch, FILE *f)
{      
    UART0_D = ch;
    while(!((UART0_S1)& 0x40));
    return ch;
}

因为printf()之类的函数,使用了半主机模式。使用标准库会导致程序无法运行,以下是解决方法:

方法1.使用微库,因为使用微库的话,不会使用半主机模式。

如果使用的是MDK,请在工程属性的“Target“-》”Code Generation“中勾选”Use MicroLIB“这样以后就可以使用printf,sprintf函数了 

方法2.仍然使用标准库,但在主程序中需要添加以下代码: 

 /*因为不使用半主机模式,所以标准C库stdio.h中有些使用半主机的函数需要重新写*/ 

#pragma import(__use_no_semihosting)  //确保没有从 C 库链接使用半主机的函数

 _sys_exit(int x)     //定义_sys_exit()以避免使用半主机模式 

{ x = x; }   

struct __FILE  //标准库需要的支持函数 

     int handle; 

}; 

/* FILE is typedef’ d in stdio.h. */ 

FILE __stdout;   

 

int fputc(int ch, FILE *f)
{      
    UART0_D = ch;                        //发送数据缓存区
    while(!((UART0_S1)& 0x40)); //串口发送中断标志位,等待发送完成

                                                    //如果中断标志位 = 0,则等待;while(!中断标志位)
    return ch;
}

 

在独立应用程序中,不太可能支持半主机操作。 因此,必须确保您的应用程序中没有链接 C 库半主机函数。 

为确保没有从 C 库链接使用半主机的函数,必须导入符号 __use_no_semihosting。可在工程的任何 C 或汇编语言源文件中执行此操作,如下所示: 

在 C 模块中,使用 #pragma 指令: 

#pragma import(__use_no_semihosting)  

在汇编语言模块中,使用 IMPORT 指令: 

IMPORT __use_no_semihosting 

如果仍然链接了使用半主机的函数,则链接器就会报告错误

下面是网上关于半主机模式的解释:

【半主机是用于 ARM 目标的一种机制,可将来自应用程序代码的输入/输出请求传送至运行调试器的主机。 例如,使用此机制可以启用 C 库中的函数,如 printf() 和 scanf(),来使用主机的屏幕和键盘,而不是在目标系统上配备屏幕和键盘。

这种机制很有用,因为开发时使用的硬件通常没有最终系统的所有输入和输出设备。 半主机可让主机来提供这些设备。

半主机是通过一组定义好的软件指令(如 SVC)来实现的,这些指令通过程序控制生成异常。 应用程序调用相应的半主机调用,然后调试代理处理该异常。 调试代理提供与主机之间的必需通信。

半主机接口对 ARM 公司提供的所有调试代理都是通用的。 在无需移植的情况下使用 RealView ARMulator ISS、指令集系统模型 (ISSM)、实时系统模型 (RTSM)、RealView ICE 或 RealMonitor 时,会执行半主机操作,请参阅Figure 8.1。

在很多情况下,半主机由库函数内的代码调用。 应用程序还可以直接调用半主机操作。 有关 ARM C 库中的半主机支持的详细信息,请参阅《库和浮点支持指南》中的第 2 章 C 和 C++ 库。】 

下面是参考文章的转载:

       https://blog.csdn.net/u013752202/article/details/49366577

      https://blog.csdn.net/yannanxiu/article/details/52438351

下面是51单片机通过printf()与串口结合发送数据到串口调试工具的代码如下:

#include<reg51.h>
#include<stdio.h>
#define uchar unsigned char
#define uint unsigned int
void uart_init();
uint i = 0;
//UART发送串口数据
void UART_SendData(char dat)
{
    ES=0;           //关串口中断
    SBUF=dat;           
    while(TI!=1);   //等待发送成功
    TI=0;           //清除发送中断标志
    ES=1;           //开串口中断
}

//UART 发送字符串
void UART_SendString(char *s)
{
    while(*s)//检测字符串结束符
    {
        UART_SendData(*s++);//发送当前字符
    }
}

//重写putchar函数
char putchar(char c)
{
    UART_SendData(c);
    return c;
}

void main()
{
	 uart_init();
	 
	 while(1)
	 {
	      UART_SendString("qqqqqqqqqqqqqqqqqqqqqq\r\n");
		  printf("i = %d\r\n",i++);
	 }
}
void uart_init()
{
         TMOD=0x20;			//定时器的工作方式2;
         TH1=0xfd;			//波特率9600
         TL1=0xfd;
         TR1=1;				//启动定时器1;
         SM0=0;				// 方式一:10位(8位数据位,1,一个起始位,1个停止位)
         SM1=1;
         REN=1;            //先允许串口接收
         EA=1;			   //全中断(总中断)
         ES=1;        	   // 串口中断
}

 

#include #include struct DATA { int ID; char name[4]; char sex[3]; int score; }; void paixu(int*,DATA*,int); int sishewuru(double); void func1(int*,int*,DATA*,int*,int,int,int,int);//统计男女比例 int func2(int*,int,DATA*);//查找考生序号 void print(); void main() { int length=0,i,yiben,erben,sanben,dazhuan,male[4],female[4]; int yi,er,san,si; char input; FILE* file=fopen("f1.txt","r"),*file1; if(file==NULL) { printf("No such file!\n"); return; } while(EOF!=fscanf(file,"%*[^\n]\n")) length++;//自动计算考生数罝ATA* data=(DATA*)malloc(length*sizeof(DATA)); int* pai=(int*)malloc(length*sizeof(int)); rewind(file); for(i=0;i='0'&&input<='4')) { printf("非法输入,请重新输入\n请输入:"); fflush(stdin); } else break; } getchar(); switch(input) { case '0': printf("\n一类本科招生线:%d\n二类本科招生线:%d\三类本科招生线:%d\\n高职高专招生线:%d\n",yi,er,san,si); printf("是否打印为文件?(y/n):"); if(getchar()=='y') { file1=fopen("各批次录取分数线.txt","w"); fprintf(file1,"一类本科招生线:%d\n二类本科招生线:%d\\n三类本科招生线:%d\n高职高专招生线:%d\n",yi,er,san,si); fclose(file1); } fflush(stdin); break; case '1': func1(male,female,data,pai,yiben,erben,sanben,dazhuan); printf("一类本科招生线男女比例:%d:%d\n",male[0],female[0]); printf("二类本科招生线男女比例:%d:%d\n",male[1],female[1]); printf("三类本科招生线男女比例:%d:%d\n",male[2],female[2]); printf("高职高专招生线招生线男女比例:%d:%d\n",male[3],female[3]); printf("是否打印为文件?(y/n):"); if(getchar()=='y') { file1=fopen("各批次录取男女比例.txt","w"); fprintf(file1,"一类本科招生线男女比例:%d:%d\n",male[0],female[0]);
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值