我是靠谱客的博主 娇气白猫,最近开发中收集的这篇文章主要介绍嵌入系统中标准输入输出扩展,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

        在嵌入式系统中串口可以说是用得最多的工具了,无论是串口通讯还是终端调试,串口的始终必不可少的。在很多时候我们学习单片机和keil时,已经window应用程序是老师都教我们单步调试,断点检查,或者直接一些调试工具比如jlink ,jtag之列的工具。但越来越多的程序开发过于庞大,这种方法都不能很好地跟踪代码的运行。这时候要是有一个能像操作系统一样直接使用printf函数就好了,通过打印信息跟踪代码非常方便。

下面是我曾经在msp430单片机上实现过的一个串口标准输入输出代码。

一、在嵌入式系统里除了实现功能之外,可移植性应该是另一个最应该考虑的哪怕他的功能很简单,所以先列出文件目录。

driver_uart.c       //硬件相关的函数
driver_uart.h
serial.c            //串口抽象后相关的函数
serial.h

对于driver_uart.c/h 主要存放着包括串口硬件模式的配置,波特率的配置寄存器配置文件的实现。

// file    driver_uart.c    硬件相关的函数就不实现了
// file    driver_uart.h 
struct uart_param
{
    unsigned char uart_numble;          //串口号
    unsigned int  bourd_rate;           //波特率
    unsinged char word_length;          //传输字长
    unsinged char stop_bit;             //停止位
    unsinged char uart_mode;            //串口模式
    BOOL odd_even_check;                //奇偶校验
};
    unsinged char uart_init(struct  uart_param * );  //uart初始化
    unsinged char uart_send_byte(unsigned char );    //发送一个字节
    unsinged char uatr_get_byte(void);               //获取一个字节   
    char * uart_send_string(char *,int num);         //发送字符串
    BOOL uart_start(void);                           //启动uart
    BOOL uart_stop(void);                            //关闭uart     

注意uart 驱动层是对串口最底层的操作,他本身不会对发送接受的字符传进行任何处理,所以在和其他设备使用串口通讯时应该使用这层的驱动程序。

二、在serial.h将uart进一步抽象出来,方便移植这里提供一些通用的接口给应用层使用,但这层只是为了串口显示或者支持ascii从机设备显示的可以仿照移植,在做串口通信时不能使用,因为他对发送和接收的字符进行了处理。

// file  serial.h
typedef struct serial_type
{
    struct uart_param *serial_param;                            //uart硬件相关的参数
    unsinged char (* serial_init)(struct  uart_param * );       //串口初始化
    unsinged char (* serial_putchar)(unsigned char );           //串口发一个字节
    unsinged char (* serial_getchar)(void);                     //串口接收一个字节
    BOOL (* serial _start)(void);                               //串口启动
    BOOL (* serial _stop)(void);                                //串口关闭
}SerialType;

BOOL serial0_init(void);
void serial0_puts(char *);
char serial0_getchar(void);
printf(char*,....);
scanf(char*);
关键的功能在serila.c中实现

//fileserial.c
#include"driver_uart.h"
#include<string.h>
struct uart_parm serial0_parm
{
    serial_numble = 0;
    baud_rate = 115200;
    word_length = 8 ;
    stop_bit = 1;
    uart_mode = RX | TX ;
    odd_even_check = 0;
};         //设置uart0 的硬件参数

SerialType serial0
{
    serial_param = &serial0_parm;
    serial_init =  serial0_init ;
    serial_putcar =  serial0_putchar ;
    serial_getchar =  serial0_getchar ;
    serial_puts =  serial0_puts ;
    serial_gets =  serial0_gets ;
    serial_start = uart_start ;
    serial_stop =  uart_stop ;
}
/**
**@parm : the char
**@function :  put an char to uart and echo
**@return :  char
**/  
char serial0_putchar(char ch)
{ 
    if(ch=='n')
    {
        uart_send_byte('r');
    } 
    uart_send_byte(ch);
    return ch;
}
/**
**@parm : none
**@function :  serial 0 get an char from uart
**@return : 
**/
char serial0_getchar(void)
{    
    char ch=uart_get_byte();    //从uart获得字符
    if(ch != 'n')
    {
        uart_send_byte(ch);     //回显得到的字符
    }
    uart_send_byte('r');       //回车
    uart_send_byte('n');       //换行
    return ch;   
}
/**
**@parm : the string buffer point
**@function :  get an string from uart 0 and echo
**@return :  the string buffer point
**/ 
char* serial0_gets(char*)
{
    char *str1=str;
    char ch;
    while( (ch = uart_get_byte())!='r')    //接收到键盘的0x0D
    {  
        if(c=='b')                         //判断是否为退格
        {   
            if((int)str1 <(int)str)
            {
                uart_send_string("b b");  //先退格再输出空格清除之前的位 再退格光标复位
                str--;         
            }   
        }
        else
        {
            *str++=c;
            uart_send_byte(c);               //回显
        }
    }
    *str++='';
    serial0_putchar("n");
    return str1;
}
/*
**@param int_num :the int num which you want convert;pchar:the string point
**@function:int convert to string
**@return : no 
*/
void IntToString(int int_num , char *pchar)
{
    char ch,*p=pchar;
    while(int_num!=0)
    {
        *pchar++ =(char)(int_num%10+'0');   //先转换成acsii值
        int_num/=10;
    }
    *pchar--='';                            //倒过来
    while((int)pchar>(int)p)
    {           
        ch =*p;
        *p++=*pchar;
        *pchar--=ch;
    }
}
/**
**@param:  s : new string ;format :输入字符串 ; arg : 可变参数地址
**@function: 将参数指针arg所指的不定参数按照输入字符串format中对应的格式转换成新的字符串格式存在地址s中
**@return: void
**/
void vs_printf(char *s,char *format,va_list arg)
{
    char *pchar;
    char *temp;
    for(pchar= format; *pchar;pchar++)
    {
        if(*pchar!='%')
        {
           *s++=*pchar;
           continue; 
        }
        switch(*++pchar)
        {
            case 'd':
                IntToString( va_arg(arg,int),s);   //获取当前可变参数整型值转换为字符串型
                while(*s++);
                *--s='0'; 
                break;
            case 's':
                temp=va_arg(arg,char *);          //直接添加可变参数
                while(*temp)
                { 
                    *s++=*temp++;
                }
                *--s='0';
                break;
            case 'c':
                *s++=va_arg(arg,char);      
                break;
            default :
                break;
        }
    }
    *s='';
}
/*****************************************
**name:printf
**function:实现格式化串口输出
**fmt 为格式字符串  ...为不定参数
******************************************/
void printf(char *fmt ,...)
{
    va_list v_arg;               //声明参数指针将
    char string[256];
    va_start(v_arg,fmt);         //初始化va_arg指向fmt
    vs_printf(string,fmt,v_arg); //将v_arg指向的不定参数按照fmt固定的格式转化成字符串存放在string中
    serial0_puts(string);
    va_end(v_arg);               //释放不定参数指针
}

暂时写到这里,实在太困了

scanf函数如果也按照格式化输入也可以按类似方法实现不难,只是实现的意义不大,我们在单片机系统中,获取终端输入的字符串用gets足够了,要是我们使用的单片机系统是32位的ARM完全可以简化程序,直接使用系统的库函数vsprintf函数这样可以和windows/linux下的printf函数一样了,但在单片及系统中这个方法是很不稳靠的,以为printf可以支持浮点等各种格式输出,然后他的底层实现接口较多,在资源有限的单片机系统中比较吃力的,所以我们在单片机系统尽量自己实现可以转整型,字符,字符串有时为了方便也可以将16进制输出加上。

三、移植注意

在移植的时候还是要强调下,这里一定要把uart输入输出和终端serial 显示区别开来,uart是数据的直接接收发送不管要发的数据是什么,而serila层是将接收数据按ascii码发送,接收到的也是从终端接收到的ascii码,这个很容混淆,有人会用printf去做串口通讯这样从机接收到的数据不是预期的。用这个概念来区分就很好理解了,我们在使用串口助手的时候有两个模式:

1、HEX  对应   uart层的实现

2、字符模式   对应 serial 层的实现

然后通过上面我们可以和window下相关函数来对应下更好理解

serial0_getchar()   -------------------------- getchar()

serial0_putchar()   -------------------------- putchar()

serial0_gets()       -------------------------- gets()

serial0_puts()        -------------------------- puts()

serial0_printf()       -------------------------- printf()

这样我们在serial接口的函数时就很轻松了,打印调试信息很方便__filename__ __line___可以跟踪代码,同时我们也可以将他应用到实际项目中比如我们要让LCD显示屏显示字符串,就可以putchar 修改为发送一个显示字符的底层代码。就可以轻松输出要显示的字符并且还带格式的。大学里我就做个LCD12864上的显示,真是非常方便,想显示数字或者字符可以随心所欲。

除了显示我们还可将他移植到那些是接收ascii码值的模块中,比如GSM模块,他的AT 指令都是ascii码值,我们通过printf函数发指令是不很方便啊。其他更多的应用期待你的发现。

以上都在实践中实验过欢迎转载http://blog.csdn.net/jundic/article/details/14047169


最后

以上就是娇气白猫为你收集整理的嵌入系统中标准输入输出扩展的全部内容,希望文章能够帮你解决嵌入系统中标准输入输出扩展所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(54)

评论列表共有 0 条评论

立即
投稿
返回
顶部