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=80MhzUART1->UBIR = 71;UART1->UBMR = 3124;#endifuart_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          ?= printfCC              := $(CROSS_COMPILE)gcc
LD              := $(CROSS_COMPILE)ld
OBJCOPY         := $(CROSS_COMPILE)objcopy
OBJDUMP         := $(CROSS_COMPILE)objdumpLIBPATH         := -lgcc -L /usr/local/arm/gcc-linaro-4.9.4-2017.01-x86_64_arm-linux-gnueabihf/lib/gcc/arm-linux-gnueabihf/4.9.4INCDIRS         := 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.mzph.cn/news/894265.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实验八 JSP访问数据库

实验八 JSP访问数据库 目的&#xff1a; 1、熟悉JDBC的数据库访问模式。 2、掌握使用My SQL数据库的使用 实验要求&#xff1a; 1、通过JDBC访问mysql数据&#xff0c;实现增删改查功能的实现 2、要求提交实验报告&#xff0c;将代码和实验结果页面截图放入报告中 实验过程&a…

python学opencv|读取图像(四十六)使用cv2.bitwise_or()函数实现图像按位或运算

【0】基础定义 按位与运算&#xff1a;全1取1&#xff0c;其余取0。按位或运算&#xff1a;全0取0&#xff0c;其余取1。 【1】引言 前序学习进程中&#xff0c;已经对图像按位与计算进行了详细探究&#xff0c;相关文章链接如下&#xff1a; python学opencv|读取图像&…

Flink (十二) :Table API SQL (一) 概览

Apache Flink 有两种关系型 API 来做流批统一处理&#xff1a;Table API 和 SQL。Table API 是用于 Scala 和 Java 语言的查询API&#xff0c;它可以用一种非常直观的方式来组合使用选取、过滤、join 等关系型算子。Flink SQL 是基于 Apache Calcite 来实现的标准 SQL。无论输入…

爬虫基础(六)代理简述

目录 一、什么是代理 二、基本原理 三、代理分类 一、什么是代理 爬虫一般是自动化的&#xff0c;当我们自动运行时 爬虫自动抓取数据&#xff0c;但一会就出现了错误&#xff1a; 如&#xff0c;您的访问频率过高&#xff01; 这是因为网站的反爬措施&#xff0c;如果频…

「 机器人 」利用数据驱动模型替代仿真器:加速策略训练并降低硬件依赖

前言 在强化学习(Reinforcement Learning, RL)中,策略训练需要大量的交互数据(状态、动作、奖励、下一状态),而这些数据通常来自仿真器或真实硬件。传统高保真仿真器虽然能在一定程度上模拟飞行器的动力学,但往往计算量大、开发成本高,且仍可能与真实环境存在差距。为此…

使用vhd虚拟磁盘安装两个win10系统

使用vhd虚拟磁盘安装两个win10系统 前言vhd虚拟磁盘技术简介准备工具开始动手实践1.winX选择磁盘管理2.选择“操作”--“创建VHD”3.自定义一个位置&#xff0c;输入虚拟磁盘大小4.右键初始化磁盘5.选择GPT分区表格式6.右键新建简单卷7.给卷起个名字&#xff0c;用于区分8.打开…

基于云计算、大数据与YOLO设计的火灾/火焰目标检测

摘要&#xff1a;本研究针对火灾早期预警检测需求&#xff0c;采用在Kaggle平台获取数据、采用云计算部署的方式&#xff0c;以YOLOv11构建模型&#xff0c;使用云计算服务器训练模型。经训练&#xff0c;box loss从约3.5降至1.0&#xff0c;cls loss从约4.0降至1.0&#xff0c…

计算机毕业设计Python+CNN卷积神经网络考研院校推荐系统 考研分数线预测 考研推荐系统 考研爬虫 考研大数据 Hadoop 大数据毕设 机器学习

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

为什么推荐将静态资源放在CDN上?

1. CDN 是什么&#xff1f; CDN&#xff08;Content Delivery Network&#xff09;是一种分布式网络&#xff0c;由地理上分散的服务器节点组成。其主要功能是将静态资源缓存到各地的边缘服务器上&#xff0c;从而将内容更快地传递给用户。当用户请求资源时&#xff0c;CDN 会…

Web-3.0(Solidity)ERC-20

&#x1f680; 发行自己的加密货币&#xff08;ERC-20 代币&#xff09; 你可以使用 Solidity 编写 ERC-20 智能合约 来发行自己的加密货币&#xff0c;然后部署到 以太坊&#xff08;Ethereum&#xff09; 或 BNB/Polygon 等 EVM 兼容链。 &#x1f4cc; 1. ERC-20 代币是什么…

小程序-基础加强-自定义组件

前言 这次讲自定义组件 1. 准备今天要用到的项目 2. 初步创建并使用自定义组件 这样就成功在home中引入了test组件 在json中引用了这个组件才能用这个组件 现在我们来实现全局引用组件 在app.json这样使用就可以了 3. 自定义组件的样式 发现页面里面的文本和组件里面的文…

AI 的安全性与合规性:实践中的最佳安全策略

随着人工智能&#xff08;AI&#xff09;技术的不断进步&#xff0c;越来越多的企业将其应用于实际业务场景。然而&#xff0c;AI 系统的使用也伴随着安全性和合规性方面的挑战。特别是当 AI 模型处理敏感数据时&#xff0c;如何确保数据的安全、隐私保护、以及防止滥用成为企业…

docker安装emqx

emqx安装 拉取emqx镜像 docker pull emqx/emqx:v4.1.0 运行docker容器 docker run -tid --name emqx -p 1883:1883 -p 8083:8083 -p 8081:8081 -p 8883:8883 -p 8084:8084 -p 18083:18083 emqx/emqx:v4.1.0 放行端口 1、如果要是自己的虚拟机&#xff0c;并且关闭了防火墙&a…

在K8s中部署动态nfs存储provisioner

背景 之前&#xff0c;我已经在一台worker node上安装了local lvm 的provisioner来模拟需要本地高IOPS的数据库等stafeful应用的实现。 为了后续给虚拟机里的K8s集群安装可用的metrics和logs监控系统&#xff08;metrics和logs的时序数据库需要永久存储&#xff09;&#xff0…

【OpenGL】OpenGL游戏案例(二)

文章目录 特殊效果数据结构生成逻辑更新逻辑 文本渲染类结构构造函数加载函数渲染函数 特殊效果 为提高游戏的趣味性&#xff0c;在游戏中提供了六种特殊效果。 数据结构 PowerUp 类只存储存活数据&#xff0c;实际逻辑在游戏代码中通过Type字段来区分执行 class PowerUp …

OSCP:常见文件传输方法

在渗透测试过程中&#xff0c;文件传输是一个关键环节&#xff0c;涉及不同的协议和工具&#xff0c;本文整理了 Linux 和 Windows 系统下常见的文件传输方法&#xff0c;并提供相应的命令示例。 通用文件传输方式 Base64 编码传输 Base64 可用于跨平台传输文件&#xff0c;…

【4Day创客实践入门教程】Day4 迈向高手之路——进一步学习!

Day4 迈向高手之路——进一步学习&#xff01; 目录 Day4 迈向高手之路——进一步学习&#xff01;更多的开发板外壳制作 Day0 创想启程——课程与项目预览Day1 工具箱构建——开发环境的构建Day2 探秘微控制器——单片机与MicroPython初步Day3 实战演练——桌面迷你番茄钟Day4…

深度学习之“缺失数据处理”

缺失值检测 缺失数据就是我们没有的数据。如果数据集是由向量表示的特征组成&#xff0c;那么缺失值可能表现为某些样本的一个或多个特征因为某些原因而没有测量的值。通常情况下&#xff0c;缺失值由特殊的编码方式。如果正常值都是正数&#xff0c;那么缺失值可能被标记为-1…

history命令常见用法和设置

1、history命令的设置 1.1、history命令有关的环境变量 root:~$ env | grep "HIST*" HISTCONTROLignorespace:erasedups HISTSIZE20 HISTFILE/home/root/.bash_history HISTFILESIZE2000 HISTTIMEFORMAT"%F %T "使用env命令可能看不到上面这些变量&#…

日志收集Day007

1.配置ES集群TLS认证: (1)elk101节点生成证书文件 cd /usr/share/elasticsearch ./bin/elasticsearch-certutil cert -out config/elastic-certificates.p12 -pass "" --days 3650 (2)elk101节点为证书文件修改属主和属组 chown elasticsearch:elasticsearch con…