printf 和scanf函数是C语言中最常用的输入出函数,从学习C语言开始,就开始使用这两个函数,然而当写用C语言写单片机程序时却不能使用这两个函数,总觉得单片机的C语言和一般的C语言差别很大,写起来不大方便;其实,单片机的C语言也是标准C语言上扩展或是改动的,都支持格式化输入输出函数(printf 和scanf);事实上,printf,scanf只负责格式化输入输出的字符,至于从哪儿输入,输出到哪儿,他们分别依靠getchar和putchar函数,只要实现单片机上的getchar函数和putchar函数,即可正常使用printf函数和scanf函数,这可以给我们单片机的信息交互带来很多方便。下面我们就来实现他们的移置。


硬件介绍:

硬件部分只需字符型输入输出设备:scanf从输入字符型设备读取字符,printf输出到字符型输出设备。在这里,我选用的字符型输入设备是超级终端,通过串口与单片机连接,输入字符;输出设备是超级终端和12864的液晶。scanf从串口读入字符,printf输出字符到串口和液晶。


有关串口的预提信息参考:MSP430程序库<二>UART异步串口。


有关液晶的具体信息参考:MSP430程序库<三>12864液晶程序库。


scanf还可以从按键读取信息,可以参考移置方法自行移置。


程序实现:

printf

单片机在调用printf时,printf是负责将数据解析成ASCII码流,通过调用putchar函数依次将字符发出。如果在putchar内编写从串口发送一字节数据,则printf的结果将从单片机串口发送出;如果putchar是向液晶写字符,让液晶显示一个字符,则printf的结果将显示在液晶上。本程序实现putchar同时向串口和液晶同时发送一个字符(液晶是显示一个字符)。


putchar函数如下:


int putchar(int ch)

{

    putchar2Com(ch);

    putchar2Lcd(ch);

    return (ch);

}

程序先向串口发送一个字符,然后像向晶发送字符。


其中:putchar2Com,向串口发送一个字符,代码如下:


int putchar2Com(int ch)

{

    if (ch == 'n')           //  'n'(回车)扩展成 'n''r' (回车+换行) 

    {

        UartWriteChar('r') ;   //0x0d 换行

    }

    UartWriteChar(ch);        //从串口发出数据  

    return (ch);

}

代码仅仅调用向串口写字符的函数UartWriteChar(ch)(详见Uart.c,在<二>中有介绍),当要输出换行时,需先输出’n’将光标移至本行首位置,还需要’r’(换行)才能将光标置于下一行起始位置,即将’n’扩展为’r’,’n’两个字节依次发出。


purchar2Lcd函数比较复杂,因为我所使用的12864液晶是中文字库的液晶,每行8个地址,可以显示8个中文字符或16个英文字符,而putchar只发出一个字节,需要判断每个地址的前半字还是后半字(因为每个字可以显示中文,如果中文的两个字节在相邻的两个地址上,将不会显示,或是显示乱码)。


上代码:


int putchar2Lcd(int ch)

{

    char addr,dat;

    

    if (ch == 'n')           //  'n'(回车),换行

    {

        ChangeNextRow();

    }

    else

    {

        addr = LcdReadAddr();

        if(ch < 0x80)

        {

            LcdWriteData(ch);

        }

        else

        {

            LcdWriteData(0x20);     //写入一个空字符,根据地址判断是否为前半字

            if(addr == LcdReadAddr())   //前半字 从新写入ch字符

            {

                LcdWriteComm(addr);

                LcdWriteData(ch);

            }

            else

            {

                LcdWriteComm(addr);

                dat = LcdReadData();

                if(dat < 0x80)           //前一个字符是英文字符

                {

                 LcdWriteData(0x20);                 //空格

              }

                LcdWriteData(ch);

            }

        }

    }

    if((addr != LcdReadAddr()) &&               //写入的是行最后位的后半字则换行

       (addr==0x87 || addr==0x97 || addr==0x8F || addr==0x9F))

    {

        ChangeNextRow();

    }

    return (ch);

}

这个函数首先判断换行;然后处理其他一般字符,如果是英文字符,不用考虑前后半字,只需正常写入液晶即可;如果是中文字符,在判断是否是前半字,前半字则直接写入,后半字则判断之前写入的前半字是否是中文,是则直接写入,不是则把英文字符移入后半字,然后写入;最后判断是否到行尾,是则换行。


程序更新为:更新日期:20110821 18:51

目的是修复原来,行尾前半字为英文,再输入中文会显示乱码。

int putchar2Lcd(int ch)

{

    char addr,dat;

    char changeRowFlag = 0;

    

    if (ch == 'n')         //  'n'(回车),换行

    {

        ChangeNextRow();

        changeRowFlag = 1;

    }

    else if (ch == 'b')    // 'b' (退格)

    {

        BackSpace();

    }

    else

    {

        addr = LcdReadAddr();

        if(ch < 0x80)

        {

            LcdWriteData(ch);

        }

        else

        {

            LcdWriteData(0x20);     //写入一个空字符,根据地址判断是否为前半字

            if(addr == LcdReadAddr())   //前半字 从新写入ch字符

            {

                LcdWriteComm(addr);

                LcdWriteData(ch);

            }

            else

            {

                LcdWriteComm(addr);

                dat = LcdReadData();

                if(dat < 0x80)           //前一个字符是英文字符

                {

                    LcdWriteData(0x20);                 //空格

                }

                if((addr != LcdReadAddr()) &&               //写入的是行最后位的后半字则换行

                   (addr==0x87 || addr==0x97 || addr==0x8F || addr==0x9F))

                {

                    ChangeNextRow();

                    changeRowFlag = 1;

                }

                LcdWriteData(ch);

            }

        }

    }

    if((addr != LcdReadAddr()) &&   //写入的是行最后位的后半字则换行,且未换过行

       (changeRowFlag == 0) &&   

       (addr==0x87 || addr==0x97 || addr==0x8F || addr==0x9F))

    {

        ChangeNextRow();

    }

    return (ch);

}

前后半字判断方法如下:读液晶地址,向液晶写入一个空格,再读地址,两地址相同则是前半字,不同则是后半字。读地址函数在Lcd12864.c中,新加入函数,代码如下:


char LcdReadAddr()

{

    char ch;

    

    WaitForEnable();

    

    CLR_RS;

    SET_RW;

    

    DATA_DIR_IN;

    

    SET_EN;

    _NOP();

    

    ch = DATA_IN;    //读数据

    CLR_EN;

    DATA_DIR_OUT;

    

    return (ch|0x80);

}

这个是读地址,ch|0x80是因为写入液晶地址首位应为1.。


液晶中新加入两个函数,一个是上边的读地址,另外一个是读数据;作用是读取液晶当前地址处的数据,从而判断之前半字是否是中文。代码如下:


char LcdReadData()

{

    char ch;

    

    WaitForEnable();

    

    SET_RS;

    SET_RW;

    

    DATA_DIR_IN;

    

    SET_EN;

    _NOP();

    

    ch = DATA_IN;    //读数据

    CLR_EN;

    DATA_DIR_OUT;

    

    return ch;

}

另外 putchar还调用了换行——ChangeNextRow函数,完成液晶输出换至下一行。


代码如下:


void ChangeNextRow()

{

    char addr;

    

    addr = LcdReadAddr();       //当前地址

    if(addr <= 0x88)

    {

        LcdWriteComm(0x90);

    }

    else if(addr <= 0x90)

    {

        LcdWriteComm(0x98);

    }

    else if(addr <= 0x98)

    {

        LcdWriteComm(0x88);

    }

    else

    {

        AddNewline();           //添加行,同时向上滚动

        LcdWriteComm(0x98);

    }

}

读取当前地址,判断在哪一行,然后写入下一行首地址;如果是最后一行,则所有安徽那个向上移,写入最后一行首地址。


AddNewLine函数完成所有行向上滚动一行,然后地址定位至最后一行。


代码如下:


void AddNewline()

{

    char str[17];

    str[16] = 0;

    

    //第二行 移至第一行

    LcdWriteComm(0x90);

    LcdReadData();              //空读取

    for(int i = 0;i<16;i++)

    {

        str[i] = LcdReadData();

    }

    LcdWriteString(0x80,str);

    

    //第三行 移至第二行

    LcdWriteComm(0x88);

    LcdReadData();

    for(int i = 0;i<16;i++)

    {

        str[i] = LcdReadData();

    }

    LcdWriteString(0x90,str);

    

    //第四行 移至第三行

    LcdWriteComm(0x98);

    LcdReadData();

    for(int i = 0;i<16;i++)

    {

        str[i] = LcdReadData();

    }

    LcdWriteString(0x88,str);

    

    //第四行 空白

    LcdWriteString(0x98,"                ");    //十六个空格

}

读出下一行数据,写入上一行,最后一行写入空格即可。


到此putchar函数全部完成,printf移植的程序部分完成,使用方法详见使用示例。


scanf

scanf和printf类似,其只负责格式化输入的字符,字符来源是从getchar函数获取;同样,在使用scanf函数之前,要针对字符输入源自行编写getchar函数


最简getchar:


int getchar()

{

    return (putchar(UartReadChar()));

}

这是最简单的getchar函数,直接调用读取字符函数,输出并返回。


但是人的输入过程会偶尔犯错误的,为了支持退格键等,需要开辟一个缓存区。


详细代码如下:


#define LINE_LENGTH 80          //行缓冲区大小,决定每行最多输入的字符数

[1] [2] [3]
关键字:MSP430  程序库  printf  scanf  函数移植 引用地址:MSP430程序库<四>printf和scanf函数移植

上一篇:基于MSP430F413水果电池供电的低功耗时钟
下一篇:MSP430程序库<三>12864液晶程序库

推荐阅读

   近日,江苏省印发了《智慧江苏建设三年行动计划(2018-2020年)》的通知。行动计划指出“加快发展数字经济”是主要任务之一,要做到数字产业化与产业数字化。在数字产业化方面,将加快物联网、云计算、大数据、人工智能等新一代信息技术的研发应用和融合创新,在软件和信息服务、集成电路、智能硬件、工业控制、智能终端等关键领域实现重点突破。在空...
日前,国能电动汽车瑞典有限公司(NEVS)正在开发一辆名为“城市出行仓”的车辆,轮胎带有360度转向功能。恒大集团目前持有国能汽车51%的股权,并在2019年6月收购了Protean公司。据悉,此次的“城市出行仓”就由Protean公司设计完成。不止是电动车,随着国能汽车的不断布局,在自动驾驶领域,这家车企的战略布局也在慢慢浮出水面。一般来讲,电动车底盘的...
由隧道股份上海隧道自主研发制造的超大直径泥水气平衡盾构“骐跃号”在位于上海浦东的国产盾构研发制造基地成功调试下线。“骐跃”盾构是首台“上海制造”的超大直径盾构,也是首台服务于上海轨道交通市域线机场联络线工程的盾构,将应用于上海轨道交通市域线机场联络线工程(西段)JCX5G-3 标,担任区间隧道掘进任务。“骐跃”盾构的研发制造,正是针对...
美国贸易代表戴琪10月4日表示,美计划与中方就中美第一阶段经贸协议落实情况、产业政策等问题展开坦率对话。她称,美方无意“激化”与中国的贸易紧张局势。戴琪当天在美国智库战略与国际研究中心发表演讲,首次阐述拜登政府对华贸易政策愿景。她说,中美经贸关系影响深远,作为全球最大的两个经济体,中美两国如何相处不仅影响两国本身,也影响全世界。戴...

史海拾趣

问答坊 | AI 解惑

咨询个事玩车的高手进来指点一下!

这个是EVO第几代?北京有卖的吗?大概多少钱?…

查看全部问答∨

如何给文件在磁盘上定位

如果一个文件的大小是已知的,且硬盘有足够的连续空间容纳该文件。   现在的问题是:有没有方法将文件放在硬盘的某一个固定的连续的位置上?…

查看全部问答∨

4种实时操作系统实时性的分析对比

4种实时操作系统实时性的分析对比,与大家分享   喜欢就顶一下…

查看全部问答∨

频谱仪

本帖最后由 paulhyde 于 2014-9-15 03:32 编辑 搜集的频谱仪资料 !!!!!!!!!!!!!!!!!!!!!!!! [ 本帖最后由 yushiqian 于 2009-8-15 23:33 编辑 ]  …

查看全部问答∨

X86学习之第一章无线连接

本帖最后由 paulhyde 于 2014-9-15 08:56 编辑 1.2.2 Wireless The WiFi settings are configured in the file /etc/config/wireless (currently supported on Broadcom, Atheros and mac80211). When booting the router for the first time it s ...…

查看全部问答∨

求教三星6410中otg相关问题!

6410+wince6.0,我用的是华恒的bsp,发现otg驱动中,只有作为device的驱动,而没有作为host的驱动,请教各位的otg驱动也是如此嘛? 参照6410的spec,发现otg phy可以通过配置寄存器给usb host用,这点我已经实现,otg的口可以正常用于u盘,usb鼠标 ...…

查看全部问答∨

弄电源管理遇到奇怪的问题,求救。

今天,看了一下电源管理,下午就动起手来,后来启动后无论怎么搞,都会不断的调用OEMIDLE函数,我在里面加的打印信息,不停的打印。 我就做了下面的修改而已 首先修改bootloader ;Check if the boot is caused by the wake-up from SLEEP mode. ...…

查看全部问答∨

DLL入口DLLMain两次被系统用DLL_PROCESS_DETACH调用

CE5下.自己写了个motola手机的USB驱动程序. 现在加载OK. 串口出来的信息如下: USBInstallDriver++ load USBD.DLL sucess! USBInstallDriver-- DLL_PROCESS_DETACH Device Attached! Looking for Stream Interfaces 2 2 There are 2 i ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved