您的位置:首页 > 其它

嵌入系统中标准输入输出扩展

2013-11-03 01:25 316 查看
        在嵌入式系统中串口可以说是用得最多的工具了,无论是串口通讯还是终端调试,串口的始终必不可少的。在很多时候我们学习单片机和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++='\0';
    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--='\0';                            //倒过来
    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='\0';
}
/*****************************************
**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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐