当前位置: 首页 > article >正文

36. printf

1. printf

格式化函数说的是 printf、 sprintf 和 scanf 这样的函数,分为格式化输入和格式化输出两类函数。学习 C 语言的时候常常通过 printf 函数在屏幕上显示字符串,通过 scanf 函数从键盘获取输入。这样就有了输入和输出了,实现了最基本的人机交互。学习 STM32 的时候会将 printf 映射到串口上,这样即使没有屏幕,也可以通过串口来和开发板进行交互。在 I.MX6U-ALPHA 开发板上也可以使用此方法,将 printf 和 scanf 映射到串口上,这样就可以使用 SecureCRT 作为开发板的终端,完成与开发板的交互。也可以使用 printf 和 sprintf 来实现各种各样的格式化字符串,方便我们后续的开发。 文件夹 stdio 里面的文件就是我们要移植的源码文件。
在这里插入图片描述

图 22.3.2 就是 stdio 里面的所有文件, stdio 里面的文件其实是从 uboot 里面移植过来的。后面学习 uboot 以后大家有兴趣的话可以自行从 uboot 源码里面“扣”出相应的文件,完成格式化函数的移植。这里要注意一点, stdio 中并没有实现完全版的格式化函数,比如 printf 函数并不支持浮点数,但是基本够我们使用了。

2.代码

//bsp_uart.c

#include "bsp_uart.h"

/*
 * @description : 初始化串口1,波特率为115200
 * @param       : 无
 * @return      : 无
 */
void uart_init(void)
{
    /* 1、初始化串口IO            */
    uart_io_init();

    /* 2、初始化UART1           */
    uart_disable(UART1);    /* 先关闭UART1         */
    uart_softreset(UART1);  /* 软件复位UART1        */

    UART1->
UCR1 = 0;       /* 先清除UCR1寄存器 */
    
    /*
     * 设置UART的UCR2寄存器,设置内容包括字长,停止位,校验模式,关闭RTS硬件流控
     * bit14: 1 忽略RTS引脚
     * bit8: 0 关闭奇偶校验
     * bit6: 0 1位停止位
     * bit5: 1 8位数据位
     * bit2: 1 打开发送
     * bit1: 1 打开接收
     */
    UART1->UCR2 |= (1<<14) | (1<<5) | (1<<2) | (1<<1);

    /*
     * UART1的UCR3寄存器
     * bit2: 1 必须设置为1!参考IMX6ULL参考手册3642页
     */
    UART1->UCR3 |= 1<<2; 
       
    /*
     * 设置UART的UCR1寄存器,关闭自动波特率
     * bit14: 0 关闭自动波特率检测,我们自己设置波特率
     */
    UART1->UCR1 &= ~(1<<14);

    #if 0  //也可用
    /*
     * 设置波特率
     * 波特率计算公式:Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)) 
     * 如果要设置波特率为115200,那么可以使用如下参数:
     * Ref Freq = 80M 也就是寄存器UFCR的bit9:7=101, 表示1分频
     * UBMR = 3124
     * UBIR =  71
     * 因此波特率= 80000000/(16 * (3124+1)/(71+1))=80000000/(16 * 3125/72) = (80000000*72) / (16*3125) = 115200
     */
    UART1->UFCR = 5<<7; //ref freq等于ipg_clk/1=80Mhz
    UART1->UBIR = 71;
    UART1->UBMR = 3124;
    #endif

    uart_setbaudrate(UART1,115200,80000000);

    /* 使能串口 */
    uart_enable(UART1);
}

/*
 * @description : 初始化串口1所使用的IO引脚
 * @param       : 无
 * @return      : 无
 */
void uart_io_init(void)
{
    /* 1、初始化IO复用 
     * UART1_RXD -> UART1_TX_DATA
     * UART1_TXD -> UART1_RX_DATA
     */
    IOMUXC_SetPinMux(IOMUXC_UART1_TX_DATA_UART1_TX,0);  /* 复用为UART1_TX */
    IOMUXC_SetPinMux(IOMUXC_UART1_RX_DATA_UART1_RX,0);  /* 复用为UART1_RX */

    /* 2、配置UART1_TX_DATA、UART1_RX_DATA的IO属性 
    *bit 16:0 HYS关闭
    *bit [15:14]: 00 默认100K下拉
    *bit [13]: 0 keeper功能
    *bit [12]: 1 pull/keeper使能
    *bit [11]: 0 关闭开路输出
    *bit [7:6]: 10 速度100Mhz
    *bit [5:3]: 110 驱动能力R0/6
    *bit [0]: 0 低转换率
    */
    IOMUXC_SetPinConfig(IOMUXC_UART1_TX_DATA_UART1_TX,0x10B0);
    IOMUXC_SetPinConfig(IOMUXC_UART1_RX_DATA_UART1_RX,0x10B0);
}

/*
 * @description         : 波特率计算公式,
 *                        可以用此函数计算出指定串口对应的UFCR,
 *                        UBIR和UBMR这三个寄存器的值
 * @param - base        : 要计算的串口。
 * @param - baudrate    : 要使用的波特率。
 * @param - srcclock_hz :串口时钟源频率,单位Hz
 * @return      : 无
 */
void uart_setbaudrate(UART_Type *base, unsigned int baudrate, unsigned int srcclock_hz)
{
    uint32_t numerator = 0u;        //分子
    uint32_t denominator = 0U;      //分母
    uint32_t divisor = 0U;
    uint32_t refFreqDiv = 0U;
    uint32_t divider = 1U;
    uint64_t baudDiff = 0U;
    uint64_t tempNumerator = 0U;
    uint32_t tempDenominator = 0u;

    /* get the approximately maximum divisor */
    numerator = srcclock_hz;
    denominator = baudrate << 4;
    divisor = 1;

    while (denominator != 0)
    {
        divisor = denominator;
        denominator = numerator % denominator;
        numerator = divisor;
    }

    numerator = srcclock_hz / divisor;
    denominator = (baudrate << 4) / divisor;

    /* numerator ranges from 1 ~ 7 * 64k */
    /* denominator ranges from 1 ~ 64k */
    if ((numerator > (UART_UBIR_INC_MASK * 7)) || (denominator > UART_UBIR_INC_MASK))
    {
        uint32_t m = (numerator - 1) / (UART_UBIR_INC_MASK * 7) + 1;
        uint32_t n = (denominator - 1) / UART_UBIR_INC_MASK + 1;
        uint32_t max = m > n ? m : n;
        numerator /= max;
        denominator /= max;
        if (0 == numerator)
        {
            numerator = 1;
        }
        if (0 == denominator)
        {
            denominator = 1;
        }
    }
    divider = (numerator - 1) / UART_UBIR_INC_MASK + 1;

    switch (divider)
    {
        case 1:
            refFreqDiv = 0x05;
            break;
        case 2:
            refFreqDiv = 0x04;
            break;
        case 3:
            refFreqDiv = 0x03;
            break;
        case 4:
            refFreqDiv = 0x02;
            break;
        case 5:
            refFreqDiv = 0x01;
            break;
        case 6:
            refFreqDiv = 0x00;
            break;
        case 7:
            refFreqDiv = 0x06;
            break;
        default:
            refFreqDiv = 0x05;
            break;
    }
    /* Compare the difference between baudRate_Bps and calculated baud rate.
     * Baud Rate = Ref Freq / (16 * (UBMR + 1)/(UBIR+1)).
     * baudDiff = (srcClock_Hz/divider)/( 16 * ((numerator / divider)/ denominator).
     */
    tempNumerator = srcclock_hz;
    tempDenominator = (numerator << 4);
    divisor = 1;
    /* get the approximately maximum divisor */
    while (tempDenominator != 0)
    {
        divisor = tempDenominator;
        tempDenominator = tempNumerator % tempDenominator;
        tempNumerator = divisor;
    }
    tempNumerator = srcclock_hz / divisor;
    tempDenominator = (numerator << 4) / divisor;
    baudDiff = (tempNumerator * denominator) / tempDenominator;
    baudDiff = (baudDiff >= baudrate) ? (baudDiff - baudrate) : (baudrate - baudDiff);

    if (baudDiff < (baudrate / 100) * 3)
    {
        base->UFCR &= ~UART_UFCR_RFDIV_MASK;
        base->UFCR |= UART_UFCR_RFDIV(refFreqDiv);
        base->UBIR = UART_UBIR_INC(denominator - 1); //要先写UBIR寄存器,然后在写UBMR寄存器,3592页 
        base->UBMR = UART_UBMR_MOD(numerator / divider - 1);
        //base->ONEMS = UART_ONEMS_ONEMS(srcclock_hz / (1000 * divider));
    }

}

/*
 * @description : 关闭指定的UART
 * @param - base: 要关闭的UART
 * @return      : 无
 */
void uart_disable(UART_Type *base)
{
    base->UCR1 &= ~(1<<0);  
}

/*
 * @description : 打开指定的UART
 * @param - base: 要打开的UART
 * @return      : 无
 */
void uart_enable(UART_Type *base)
{
    base->UCR1 |= (1<<0);   
}

/*
 * @description : 复位指定的UART
 * @param - base: 要复位的UART
 * @return      : 无
 */
void uart_softreset(UART_Type *base)
{
    base->UCR2 &= ~(1<<0);          /* UCR2的bit0为0,复位UART       */
    while((base->UCR2 & 0x1) == 0); /* 等待复位完成                   */
}

/*
 * @description : 发送一个字符
 * @param - c   : 要发送的字符
 * @return      : 无
 */
void putc(unsigned char c)
{
    while(((UART1->USR2 >> 3) &0X01) == 0);/* 等待上一次发送完成 */
    UART1->UTXD = c & 0XFF;                 /* 发送数据 */
}

/*
 * @description : 发送一个字符串
 * @param - str : 要发送的字符串
 * @return      : 无
 */
void puts(char *str)
{
    char *p = str;

    while(*p)
        putc(*p++);
}

/*
 * @description : 接收一个字符
 * @param       : 无
 * @return      : 接收到的字符
 */
unsigned char getc(void)
{
    while((UART1->USR2 & 0x1) == 0);/* 等待接收完成 */
    return UART1->URXD;             /* 返回接收到的数据 */
}

/*
 * @description : 防止编译器报错
 * @param       : 无
 * @return      : 无
 */
void raise(int sig_nr) 
{

}

//main.c

#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "stdio.h"

/*
 * @description : main函数
 * @param       : 无
 * @return      : 无
 */
int main(void)
{
    unsigned char state = OFF;
    int a , b;

    int_init();                 /* 初始化中断(一定要最先调用!) */
    imx6u_clkinit();            /* 初始化系统时钟          */
    delay_init();               /* 初始化延时            */
    clk_enable();               /* 使能所有的时钟          */
    led_init();                 /* 初始化led           */
    beep_init();                /* 初始化beep          */
    uart_init();                /* 初始化串口,波特率115200 */
    
    while(1)                    
    {   
        printf("输入两个整数,使用空格隔开:");
        scanf("%d %d", &a, &b);                         /* 输入两个整数 */
        printf("\r\n数据%d + %d = %d\r\n\r\n", a, b, a+b);    /* 输出两个数相加的和 */

        state = !state;
        led_switch(LED0,state);
    }

    return 0;
}

//Makefile
修改 Makefile 中的 TARGET 为 printf,在 INCDIRS 中加入“stdio/include”,在 SRCDIRS中加入“stdio/lib”,修改后的 Makefile 如下:

CROSS_COMPILE   ?= arm-linux-gnueabihf-
TARGET          ?= printf

CC              := $(CROSS_COMPILE)gcc
LD              := $(CROSS_COMPILE)ld
OBJCOPY         := $(CROSS_COMPILE)objcopy
OBJDUMP         := $(CROSS_COMPILE)objdump

LIBPATH         := -lgcc -L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4

INCDIRS         := imx6ul \
                   stdio/include \
                   bsp/clk \
                   bsp/led \
                   bsp/delay  \
                   bsp/beep \
                   bsp/gpio \
                   bsp/key \
                   bsp/exit \
                   bsp/int \
                   bsp/epittimer \
                   bsp/keyfilter \
                   bsp/uart 
                               
SRCDIRS         := project \
                   stdio/lib \
                   bsp/clk \
                   bsp/led \
                   bsp/delay \
                   bsp/beep \
                   bsp/gpio \
                   bsp/key \
                   bsp/exit \
                   bsp/int \
                   bsp/epittimer \
                   bsp/keyfilter \
                   bsp/uart 
                   
                   
INCLUDE         := $(patsubst %, -I %, $(INCDIRS))

SFILES          := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S))
CFILES          := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))

SFILENDIR       := $(notdir  $(SFILES))
CFILENDIR       := $(notdir  $(CFILES))

SOBJS           := $(patsubst %, obj/%, $(SFILENDIR:.S=.o))
COBJS           := $(patsubst %, obj/%, $(CFILENDIR:.c=.o))
OBJS            := $(SOBJS) $(COBJS)

VPATH           := $(SRCDIRS)

.PHONY: clean
    
$(TARGET).bin : $(OBJS)
    $(LD) -Timx6ul.lds -o $(TARGET).elf $^ $(LIBPATH)
    $(OBJCOPY) -O binary -S $(TARGET).elf $@
    $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis

$(SOBJS) : obj/%.o : %.S
    $(CC) -Wall -nostdlib -fno-builtin -c -O2  $(INCLUDE) -o $@ $<

$(COBJS) : obj/%.o : %.c
    $(CC) -Wall -Wa,-mimplicit-it=thumb -nostdlib -fno-builtin -c -O2  $(INCLUDE) -o $@ $<
    
clean:
    rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

第 2 行修改变量 TARGET 为“printf”,也就是目标名称为“printf”。
第 7 行在变量 INCDIRS 中添加 stdio 相关头文件(.h)路径。第 28 行在变量 SRCDIRS 中添加 stdio 相关文件(.c)路径。
第 37 行在编译 C 文件的时候添加了选项“-Wa,-mimplicit-it=thumb”,否则的话会有如下类似的错误提示:
thumb conditional instruction should be in IT block – `addcs r5,r5,#65536’链接脚本保持不变。

在这里插入图片描述

3. 效果

在这里插入图片描述


http://www.kler.cn/a/527672.html

相关文章:

  • DeepSeek大模型技术深度解析:揭开Transformer架构的神秘面纱
  • 服务器虚拟化实战:架构、技术与最佳实践
  • three.js+WebGL踩坑经验合集(4.1):THREE.Line2的射线检测问题(注意本篇说的是Line2,同样也不是阈值方面的问题)
  • vue3中el-input无法获得焦点的问题
  • python 语音识别
  • 安卓(android)订餐菜单【Android移动开发基础案例教程(第2版)黑马程序员】
  • 团体程序设计天梯赛-练习集——L1-029 是不是太胖了
  • 大模型高频知识汇总:查漏补缺参考大全
  • 【Redis】set 和 zset 类型的介绍和常用命令
  • oracl:多表查询>>表连接[内连接,外连接,交叉连接,自连接,自然连接,等值连接和不等值连接]
  • Docker小游戏 | 使用Docker部署跳一跳经典小游戏
  • 23.Word:小王-制作公司战略规划文档❗【5】
  • Python3 + Qt5:实现AJAX异步更新UI
  • EtherCAT主站IGH-- 25 -- IGH之fsm_slave_scan.h/c文件解析
  • DeepSeek 使用初体验
  • Git的安装步骤详解(复杂的安装界面该如何勾选?)
  • 在线知识库创建与维护提升企业效率与知识共享能力
  • 【Unity3D】实现横版2D游戏角色二段跳、蹬墙跳、扶墙下滑
  • Linux Vim编辑器:快捷键与高效编辑技巧
  • C语言指针专题一 -- 指针基础原理
  • 【Linux】使用管道实现一个简易版本的进程池
  • Pandas 常用函数
  • 【PLL】杂散生成和调制
  • (动态规划基础 打家劫舍)leetcode 198
  • 简要介绍C++中的 max 和 min 函数以及返回值
  • TensorFlow 简单的二分类神经网络的训练和应用流程