历史上的今天
返回首页

历史上的今天

今天是:2025年01月03日(星期五)

正在发生

2020年01月03日 | Keil MDK 中利用串口及c标准库函数printf为cortex-m3做调试输出

2020-01-03 来源:eefocus

摘要:

c标准库的printf是输出给显示器的,将printf函数进行修改,使其输出重定向至串口,就能实现目的。printf函数调用fputc函数完成实质输出单一字符的工作,因此将fputc函数修改使之完成串口单字符发送工作即可。


注:

本文方法性内容主要来自《Keil MDK环境下使用printf函数的解决方法》与《STM32串口使用Printf()函数问题》。除使用c标准库外,还可以使用keil mdk提供的microLib,在STM32串口使用Printf()函数问题》一文有介绍,另外,该文同时也提到如果使用c标准库函数,则要避免链接使用半主机模式的函数,retarge.c文件中的#pragma import(__use_no_semihosting_swi) 和_sys_exit函数实现就是来确保不链接半主机模式函数的。


实现步骤:

1. keil MDK已经为我们提供了这样的接口文件:


文件位置:C:KeilARMStartup,(C:Keil为我的keil安装根目录)


文件名:Retarget.c


文件内容:


/******************************************************************************/

/* RETARGET.C: 'Retarget' layer for target-dependent low level functions      */

/******************************************************************************/

/* This file is part of the uVision/ARM development tools.                    */

/* Copyright (c) 2005 Keil Software. All rights reserved.                     */

/* This software may only be used under the terms of a valid, current,        */

/* end user licence from KEIL for a compatible version of KEIL software       */

/* development tools. Nothing else gives you the right to use this software.  */

/******************************************************************************/


#include

#include

#include


#pragma import(__use_no_semihosting_swi)



extern int  sendchar(int ch);  /* in Serial.c */

extern int  getkey(void);      /* in Serial.c */

extern long timeval;           /* in Time.c   */



struct __FILE { int handle; /* Add whatever you need here */ };

FILE __stdout;

FILE __stdin;



int fputc(int ch, FILE *f) {

  return (sendchar(ch));

}


int fgetc(FILE *f) {

  return (sendchar(getkey()));

}



int ferror(FILE *f) {

  /* Your implementation of ferror */

  return EOF;

}



void _ttywrch(int ch) {

  sendchar (ch);

}



void _sys_exit(int return_code) {

  while (1);    /* endless loop */

}


因此我们的工作就是:


(1)将Retarget.c文件加入自己的工程


(2)提供Serial.c文件,在该文件中实现sendchar和getkey()


sendchar即为串口发送单字符函数。


2. Serial.c文件实现(lpc1788芯片)


这里使用lpc1788的uart 0口实现rs232功能


 

#include "lpc177x_8x.h"


// SET32BIT宏将32位变量x的l位至h位部分置为数val,

// 例如:

// x = 0x03; 二进制 00000000000000000000000000000011

// SET32BIT(x,1,3,6);

// 则x变为0x0D ,二进制 00000000000000000000000000001101

#define SET32BIT(x,l,h,val)        {(x) &= (~((~(0xfffffffful<<(h-l+1)))<

void uart0_init()

{

    // 配置P0.2, P0.3为UART接口(即使用UART0)

    SET32BIT(LPC_IOCON->P0_2,0,2,1); // TXD0

    SET32BIT(LPC_IOCON->P0_3,0,2,1); // RXD0

    // RXD0口设置为内部上拉。在232通信协议、电平为TTL电平时,除逻辑1外,空闲状态也用高电平表示,因此接个上拉不影响通信,

    // 同时还会在空闲时钳制电平,不至于引入干扰信号。当然这都是自己的理解而已。。。

    SET32BIT(LPC_IOCON->P0_3,3,4,2); 


    // 给UART0模块供电(实际上UART0默认为单片机上电时供电,但像UART2、3是不供电的,需要像这里一样手动置位供电)

    SET32BIT(LPC_SC->PCONP,0,2,1);


    // 配置NVIC 中断使能寄存器,使能UART0模块的中断能力(这是对内核对中断的第一层配置管理)

    NVIC->ISER[0] |= 0x01<<5;


    // 配置UART0 FCR FIFO控制寄存器

    // 使能FIFO,如果不使能,那么...我认为必须使能啊,如果不使能,串口没有缓存队列,岂不是残疾了。

    LPC_UART0->FCR |= 0x01<<0;

    // 清空缓存,每次清缓存只需要对下列位置位

    LPC_UART0->FCR |= 0x01<<1;     // 清接收缓存

    LPC_UART0->FCR |= 0x01<<2;     // 清发送缓存

    // 设定    接收中断触发事件

    SET32BIT(LPC_UART0->FCR,6,7,1);      // 设置触发点1事件(默认为接收缓存中有不少于4个字节时触发接收中断)


    // 配置UART0 LCR线控制寄存器

    SET32BIT(LPC_UART0->LCR,0,1,3);   // 8bit 数据位

    SET32BIT(LPC_UART0->LCR,2,2,0);   // 1bit 停止位

    SET32BIT(LPC_UART0->LCR,3,3,0);      // 无校验


    // 配置UART0 中断使能寄存器IER,使能UART0中断响应(这是对中断的第二层管理

    // 在LPC1788上,一般各模块或IO的中断都需要这样两级控制,包括使能与禁能

    // IER必须是在分频除数寄存器LSB与MSB被锁定、即访问被禁止的情况下才能进行读写操作,上电后的默认值是锁定的(对应LCR的DLAB位为0)

    // 锁定通过LCR控制寄存器中的DLAB位被置位来实现,上电时的默认值为0,表示禁止访问LSB与MSB。

    SET32BIT(LPC_UART0->LCR,7,7,0);      // DLAB赋0,禁止访问LSB与MSB

    LPC_UART0->IER |= 0x1<<0;  // 使能UART0接收事件中断,前面FCR设置的事件发生时触发中断


    // lpc1788在上电后默认使用片内12MHz晶振,通过SystemInit函数设置后的PCLK时钟频率为60MHz

    // 要根据想要的波特率和PCLK时钟频率得到UART0分频除数寄存器的配置值,需要先开放LSB与MSB的访问

    LPC_UART0->LCR |= 0x1<<7;  // DLAB赋1,允许访问LSB与MSB

    // 对于9600波特率与60Mhz(60000000)的PCLK,按照lpc1788手册提供的算法计算得到以下各寄存器的值

    // 这些寄存器的值确定出的波特率为9615,与9600有0.15%的误差,

    SET32BIT(LPC_UART0->FDR,0,3,1);  // 小数分频寄存器DIVADDVAL赋1

    SET32BIT(LPC_UART0->FDR,4,7,2);  // 小数分频寄存器MULVAL赋2

    SET32BIT(LPC_UART0->DLL,0,7,4);   // LSB寄存器赋4

    SET32BIT(LPC_UART0->DLM,0,7,1);    // MSB寄存器赋1

    SET32BIT(LPC_UART0->LCR,7,7,0);        //  LSB和MSB配置结束后,要禁止访问LSB与MSB,否则UART的一些功能不能使用

}


int sendchar(int ch)

{

    LPC_UART0->THR = (uint8_t)ch;

    while((LPC_UART0->LSR&0x40)==0){};

    return ch;

}


int getkey(void)

{

    while((LPC_UART0->LSR&0x1)==0){};

    return LPC_UART0->RBR;

}


3. 将Serial.c加入工程,就可以使用printf了



#include "lpc177x_8x.h"

#include


void uart0_init(void);


int main()

{

    uart0_init(void);

    printf("hello worldn");

    return 0;

}


推荐阅读

史海拾趣

Hama公司的发展小趣事

为了提升产品的市场竞争力,H&D Wireless积极寻求技术合作。20XX年,公司与全球领先的微控制器解决方案提供商爱特梅尔(Atmel)携手,共同推出了基于AVR®微控制器的IEEE802.11b+g Wi-Fi解决方案。这一合作不仅提升了H&D Wireless产品的功耗效率和连接稳定性,还显著缩短了客户的产品开发周期。通过不断优化产品性能,H&D Wireless的Wi-Fi解决方案在市场上获得了广泛认可。

DuPont公司的发展小趣事

随着全球环保意识的提高,对于环保型电子材料的需求也日益增长。DuPont公司积极响应这一趋势,致力于环保型电子材料的研发和生产。公司采用先进的环保技术和生产工艺,开发出了一系列低污染、低能耗的电子材料。这些材料不仅具有良好的性能,而且对环境友好,符合可持续发展的要求。DuPont公司的这一举措,不仅提升了公司的竞争力,也为电子行业的可持续发展做出了积极贡献。

Advanced Semiconductor, Inc.公司的发展小趣事

为了进一步扩大市场份额,ASI积极寻求与全球各大电子企业的合作。通过与这些企业的战略合作,ASI不仅获得了更多的订单和市场份额,还进一步提升了自身的技术水平和创新能力。同时,ASI也积极参与国际半导体行业的交流和合作,与全球同行共同推动半导体技术的发展。

GaN Systems公司的发展小趣事

GAIA Converter Inc成立于1993年,总部位于法国波尔多附近的航空航天工业城,这里聚集了众多高科技企业和研究机构。公司自创立之初便专注于高可靠性和工业模块化电源解决方案的研发与生产。在成立初期,GAIA盖亚电源凭借对电力转换技术的深入理解,迅速开发出了一系列适用于航空、军事及高端工业应用的DC/DC转换器模块。这些产品凭借其卓越的性能和稳定的质量,在市场上赢得了初步认可。

AUK Contractors Co Ltd公司的发展小趣事

随着公司业务的不断扩展,AUK Contractors Co Ltd意识到单一市场已无法满足其增长需求。于是,公司积极寻求国际合作,与多个国家的电子企业建立了战略伙伴关系。通过技术交流和资源共享,公司成功打开了新的市场,实现了业务的快速增长。

Bel Power Solutions公司的发展小趣事

随着全球环保意识的日益增强,Bel Power Solutions积极响应号召,将环保理念融入产品设计和生产中。公司推出了多款环保型电源产品,如高效能、低能耗的DC-DC转换器和电源供应器,帮助客户降低能耗、减少碳排放。同时,公司还致力于研发和推广可再生能源应用技术,为可持续发展做出贡献。

问答坊 | AI 解惑

很想知道学会了单片机研发,工资一般能拿多少啊?能达到6000吗?

很想知道学会了单片机研发,工资一般能拿多少啊?能达到6000吗?…

查看全部问答>

18B20传感器

18B20温度传感器中文资料…

查看全部问答>

电路设计技术与技巧(第二版)国外牛人Tim Williams著

国际电子大侠Tim Williams(蒂姆·威廉斯)著 内容简介 本书较全面和系统地讲述了在实际电子电路设计中常见问题和容易忽视的方方面面, 涵盖了设计产品所需是的全面知识,包括印制电路板布线和接地、有源和无源器件、模拟和数字集成电路、电源、电 ...…

查看全部问答>

【FPGA 代码】

持续赋值方式定义的2 选1 多路选择器module MUX21_1(out,a,b,sel);input a,b,sel;output out;assign out=(sel==0)?a:b;//持续赋值,如果sel 为0,则out=a ;否则out=bendmodule阻塞赋值方式定义的2 选1 多路选择器module MUX21_2(out,a,b,sel);inpu ...…

查看全部问答>

问一个笨笨的问题

请问如何在EVC build后执行某个PC上的EXE文件. 比如,我要在build aa.exe后自动调用bb.exe. 我在post-build里设置,总是不成功.…

查看全部问答>

串口通信

我现在用RS232进行PC机和单片机的通信,在用串口调试助手的时候,发现单片机发送回来的数据是乱码,请问这是什么原因?…

查看全部问答>

地址卷绕

想问下,地址卷绕是一种什么概念啊!好像发生在运算结果上溢出和下溢出时候的!…

查看全部问答>

WINCE6.0操作系统在三星6410开发板上完美展现!--基于立宇泰ARMSYS6410开发板

精彩测评实例图片说明:1,WINCE6.0系统启动画面(进度条动态显示内核加载进程,LOGO图片可以根据客户需要免费更换);2,WINCE6.0桌面效果;3,800x480液晶屏全屏播放H.264视频流文件(采用硬件解码)4,800x480液晶屏全屏播放MPEG4(avi后缀)音视 ...…

查看全部问答>

STM32用到的TFT屏幕资料及例程.欢迎补充

既然有人需要TFT屏 资料.放假在家也没什么事.整理一下自己手头有的吧.做个抛砖引玉.希望大家能吧自己方便分享的资料都上传上传~…

查看全部问答>

Ecan通信的中断问题

       本人在一个项目中运用Ecan进行通信,在通信过程中会碰到中断故障(具体原因说不上来)。我是将Ecan设置成中断接收的(邮箱0-15设置成接收邮箱,16邮箱设置成发送邮箱),若接收到一个合理的读写命令,则返回 ...…

查看全部问答>