历史上的今天
返回首页

历史上的今天

今天是:2024年12月28日(星期六)

2021年12月28日 | STM32F103自定义的printf函数的实现

2021-12-28 来源:eefocus

  在单片机中使用最多的通信接口基本就是串口了,说起串口就不得不提串口中最常用的一个函数就是打印函数printf()函数,通常使用这个函数都是直接调用库函数来实现的,在单片机中如何要使用printf()函数一般都是在串口中进行重映射。如要在串口1中使用printf()函数,可以使用下面的代码进行重映射。


//加入以下代码,支持printf函数,而不需要选择use MicroLIB

#if 1

#pragma import(__use_no_semihosting)

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

struct __FILE

{

    int handle;

};

FILE __stdout;

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

_sys_exit(int x)

{

    x = x;

}

//重定义fputc函数

int fputc(int ch, FILE *f)

{

    while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕

    USART1->DR = (u8) ch;

    return ch;

}

#endif


  在串口1的c文件中,添加上面的代码后,使用printf()函数时,就可以通过串口1来打印了。那么不通过库函数的话,自己能不能实现printf()函数的功能呢?当然是可以的,下面就通过串口2来演示,如何在串口2上直接实现printf()函数的功能。


void UART2_Init(u32 bound)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    USART_InitTypeDef USART_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

    

    // 1、串口时钟使能 GPIO时钟使能

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    

    // 2、串口复位

    USART_DeInit(USART2);

    

    // 3、GPIO端口设置

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;                  //PA2 TX

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   //复用推挽输出

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStructure);


    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;                        //PA3 RX

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_Init(GPIOA, &GPIO_InitStructure);

    

    // 4、串口参数初始化

    USART_InitStructure.USART_BaudRate = bound;

    USART_InitStructure.USART_WordLength = USART_WordLength_8b;

    USART_InitStructure.USART_StopBits = USART_StopBits_1;

    USART_InitStructure.USART_Parity = USART_Parity_No;

    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART2, &USART_InitStructure);

    

    // 5、初始化NVIC

    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;

    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);

    

    // 6、开启中断

    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

    

    // 7、使能串口

    USART_Cmd(USART2, ENABLE);


}

void USART2_IRQHandler(void)

{

    u8 res;

    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)

    {

        res = USART_ReceiveData(USART2);

        USART_SendData(USART2, res); //把接收到的数据发送出去

    }

}


  首先初始化串口2,初始化方式和正常情况下一样,初始化完成之后开始自定义一个函数来实现printf()函数的功能。


//自定义串口2 的printf 函数

char UART2_TX_BUF[200];

void u2_printf(char* fmt, ...)    //无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表

{

    u16 i, j;

    va_list ap;          //va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。

    va_start(ap, fmt);   //va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束

    vsprintf((char*)UART2_TX_BUF, fmt, ap); // 把生成的格式化的字符串存放在这里

    va_end(ap);

    i = strlen((const char*)UART2_TX_BUF);              //此次发送数据的长度

    for(j = 0; j < i; j++)                                                    //循环发送数据

    {

        while((USART2->SR & 0X40) == 0);                    //循环发送,直到发送完毕

        USART2->DR = UART2_TX_BUF[j];

    }

}

  这个函数名定义为 :u2_printf(char* fmt, …) 使用省略号表示当前参数为可变参数。接下里就可以直接使用这个函数来打印数据了。在主函数中通过一段代码来测试串口1和串口2 printf函数的功能。


int main(void)

{

    u8 t;

    u8 len;

    u16 times = 0;

    delay_init();       //延时函数初始化

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    uart_init(115200);

    LED_Init();

    UART2_Init(115200);

    printf("USART1 AND USART2 TEST!!!");

    while(1)

    {

        if(USART_RX_STA & 0x8000)

        {

            LED1 = !LED1;

            len = USART_RX_STA & 0x3fff; //获取本次接收数据长度

            printf("rn您发送的消息为:rn");

            for(t = 0; t < len; t++)

            {

                USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据

                while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET); //等待发送完成

            }

            printf("rn");

            USART_RX_STA = 0;

        }

        else

        {

            times++;

            if(times % 5000 == 0)

            {

                printf("串口实验rn");

                u2_printf("串口实验rn"); //调用串口2 printf 函数

            }

            if(times % 300 == 0)

            {

                printf("请输入数据,以回车键结束rn");

                u2_printf("请输入数据,以回车键结束rn"); //调用串口2 printf 函数

            }

            if(times % 30 == 0)

                LED0 = !LED0;

            delay_ms(10);


        }


    }

}


  通过串口1和串口2输出同样的提示信息,然后用串口助手分别给串口1和串口2发送数据,当串口1和串口2接收到数据后就会通过串口打印出来。串口1和串口2输出的提示信息都是用printf函数输出的。

image.png?imageView2/2/w/550

  可以看出串口2的printf函数和功能和串口1的printf函数功能一样,可以正常输出字符串。也可以使用串口2的printf函数打印变量值,比如在代码中增加一句变量打印的功能。

image.png?imageView2/2/w/550

  每次输出提示信息的时候,顺便打印一下变量times的值。

image.png?imageView2/2/w/550

  可以看到变量times的值也可以正常打印。

推荐阅读

史海拾趣

启英泰伦(Chiplntelli)公司的发展小趣事

启英泰伦凭借其在人工智能芯片领域的硬科技实力,连续四年入选“成都硬科技扑克牌榜单”。这一荣誉不仅肯定了公司在技术创新和产品研发方面的成就,也提升了公司在行业内的知名度和影响力。作为一家专注于人工智能芯片硬科技领域的公司,启英泰伦一直致力于推动芯片技术的创新和突破,为客户提供最优质的产品和服务。

Custom Connector Corporation公司的发展小趣事

CCC深知产品质量对于企业生存和发展的重要性。因此,公司始终将质量管理放在首位,建立了严格的质量检测体系和售后服务体系。从原材料采购到产品生产、再到售后服务,CCC都严格按照质量标准进行操作,确保每一件产品都符合客户的要求。这种对质量的执着追求让CCC赢得了客户的信赖和口碑相传。

Herrmann Kg公司的发展小趣事

随着全球市场的不断扩展,Herrmann Kg积极实施全球化发展战略。公司在德国和匈牙利设有生产厂房,并在欧洲、美洲和亚洲等多个国家设有分支机构。通过全球化布局,Herrmann Kg能够更好地服务全球客户,同时利用各地的资源和优势进行技术创新和市场拓展。这种全球化战略不仅提升了公司的市场影响力,还为其未来的持续发展奠定了坚实的基础。

请注意,以上故事是基于Herrmann Kg在超声波焊接技术领域的整体发展情况进行构想的,并非直接来源于特定事件的报道或官方声明。因此,在描述过程中可能存在一定的想象和推测成分。然而,这些故事旨在展示Herrmann Kg在电子行业及相关领域中的发展成就和趋势。

Greenwich Instruments Ltd公司的发展小趣事

随着全球对环境保护和可持续发展的重视,Greenconn Corp积极响应号召,将绿色理念融入产品设计和生产全过程。公司致力于开发环保型连接器产品,采用可回收材料、减少能源消耗和废弃物排放等措施,努力实现生产过程的绿色化。同时,Greenconn Corp还积极参与行业内的绿色标准制定和认证工作,为推动整个电子行业的绿色发展贡献了自己的力量。

昆泰芯微电子(CONNTEK)公司的发展小趣事

为了加快产品国产化进程,昆泰芯微电子与世强先进(深圳)科技股份有限公司签订了授权代理合作协议。通过这一战略合作,昆泰芯微电子成功将旗下高端传感器产品触达至终端市场,实现了市场的快速扩张。同时,这一合作也进一步提升了昆泰芯微电子在行业中的知名度和影响力。

Engineered Components Co公司的发展小趣事

为了确保产品质量和客户满意度,ECC建立了完善的质量管理体系。他们从原材料采购到生产、检测、包装等各个环节都制定了严格的质量标准。ECC还引入了先进的质量检测设备和方法,确保每一件产品都符合高标准的质量要求。此外,ECC还定期对员工进行质量培训,提高员工的质量意识和操作技能。这些措施使得ECC的产品在市场上赢得了良好的口碑和信誉。

问答坊 | AI 解惑

监视电视系统防雷防护技术方案简介

一、 闭路监视电视系统简介:     CCTV系统结构:     电视监控系统(Closed Circuit Television,简称CCTV),一般由以下三部分组成:     前端部分:     主要由黑白(彩色)摄像机、镜头、云台、防 ...…

查看全部问答>

高人可以帮我看下我的这个拨码开关程序老有点小我问题,,十万火急

process(clk_a) VARIABLE cnt_a : std_logic_vector(7 downto 0):=\"11111111\"; begin   if (clk_a\'event and clk_a=\'1\') then    if cnt_a=\"11111111\" then     cnt_a:=d;     full_a…

查看全部问答>

Altium Designer 使用

Altium Designer 使用 —— 快速制作原理图封装当制作引脚数比较多的器件的原理图封装时,可以使用AD提供的“smart grid insert”功能快速制作原理图封装库。本文以制作K9F1G08为例,进行简单说明。K9F1G08的引脚分布如图1所示:1. 打开 excel ,按 ...…

查看全部问答>

新建一个wince 的mfc exe,怎么在对话框中添加一个ie控件,浏览一个url(本地页面),thanks

新建的时候,选择,WCE MFC appWizard[exe] ,然后再选择Dialog base, 怎么样在该Dialog上添加一个ie控件,然后去浏览一个本地url? thanks…

查看全部问答>

最新cadence(allegro)视频教程

本人从朋友那里得到一份cadence(allegro)视频教程15.5板本,是一个培训班的内部视频讲义,清晰度很高,很具体详细,是自学的好资料。如果您没时间看书或者想很快学会allegro的话就请联系:13783696474   或者QQ:20247125    ...…

查看全部问答>

新人报道

我是刚刚来到的新人,刚刚接触WINCE。现在跟老师做着一个项目,需要将一个PC上的一个写好的软件移植到wince6.0下面,平台在mini的6410上搭建,还需要搞好一个ccd摄像头的驱动(有可二次开发的开发包),想问问,如果要入门,那么我应该先看拿些书籍 ...…

查看全部问答>

f2812 SCI RS485 调试

1.最近我用sci调试RS485的时候遇到一个怪现象: 有时候(不是全部,重新加载程序的时候一般都是)cpu明明有数据发出来,SCIRX,SCITX波形正常,可是485驱动芯片出来的两线信号发不出来,示波器只能看到负半波.只要我把与其通信的人机界面的通讯口拔出来 ...…

查看全部问答>

proteus仿真出现问题

proteus仿真出现问题,如下图,不知怎么回事?…

查看全部问答>

时钟的触发时序执行时间?

最近在学习FPGA方面知识,对时钟触发有了了解,但有好多问题有些不明白。希望大虾解释 如: always(posedge CLK) begin //执行任务块 end 假如时钟的周期为50nS,占空比50%;所执行的任务块比较耗时间,超过50nS,而此时任务块尚未完全执行完 ...…

查看全部问答>

在PCB中设计WIFI时候注意事项

各位大神,在PCB中设计WIFI时候注意事项有哪些?你们有什么经验?…

查看全部问答>