嵌入系统中标准输入输出扩展
2013-11-03 01:25
316 查看
在嵌入式系统中串口可以说是用得最多的工具了,无论是串口通讯还是终端调试,串口的始终必不可少的。在很多时候我们学习单片机和keil时,已经window应用程序是老师都教我们单步调试,断点检查,或者直接一些调试工具比如jlink ,jtag之列的工具。但越来越多的程序开发过于庞大,这种方法都不能很好地跟踪代码的运行。这时候要是有一个能像操作系统一样直接使用printf函数就好了,通过打印信息跟踪代码非常方便。
下面是我曾经在msp430单片机上实现过的一个串口标准输入输出代码。
一、在嵌入式系统里除了实现功能之外,可移植性应该是另一个最应该考虑的哪怕他的功能很简单,所以先列出文件目录。
对于driver_uart.c/h 主要存放着包括串口硬件模式的配置,波特率的配置寄存器配置文件的实现。
注意uart 驱动层是对串口最底层的操作,他本身不会对发送接受的字符传进行任何处理,所以在和其他设备使用串口通讯时应该使用这层的驱动程序。
二、在serial.h将uart进一步抽象出来,方便移植这里提供一些通用的接口给应用层使用,但这层只是为了串口显示或者支持ascii从机设备显示的可以仿照移植,在做串口通信时不能使用,因为他对发送和接收的字符进行了处理。
关键的功能在serila.c中实现
暂时写到这里,实在太困了
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
下面是我曾经在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
相关文章推荐
- 在程序开始运行时,系统自动打开3个标准文件:标准输入、标准输出、标准出错输出
- Linux系统教程 标准输入/输出和重定向
- 在程序开始运行时,系统自动打开3个标准文件:标准输入、标准输出、标准出错输出
- 在程序开始运行时,系统自动打开3个标准文件:标准输入、标准输出、标准出错输出
- 系统的标准输入、输出和错误
- java--IO流-LineNumberReader,读取键盘录入,字符字节流转换,改变标准输入输出设备,异常的日志信息,系统信息
- Linux下C编程-----文件操作(1) 通过系统调用简单操作标准输入、标准输出、标准错误
- UNIX系统调用_标准输入输出
- Linux系统教程 标准输入/输出和重定向
- 50.黑马程序员-改变标准输入输出设备、日志、系统信息
- 编写程序从标准输入读取几行输入。每行输入都打印在标准输出上,且前面加上行号
- 以下通讯电路,输入是标准的RS232信号TTL电平,输出什么信号呀?是标准的422信号吗?硬件高手请进做客!
- 编写一个程序,验证从标准输入获得的信用卡号是否校验通过, 校验通过,则输出”成功“,校验未通过则输出”失败“。
- 2. 编写一个程序,从标准输入读取几行输入。每行输入都要打印到标准输出上,前面加上行号。在编写这个程序的时候要使用让这个程序能够处理的输入行的长度没有限制
- 标准输入与输出(Shell命令)
- Java标准输入和输出
- 从标准输入读取几行输入,每行输入都要打印到标准输出上,前面加上行号。
- java线上编程挑战系统的输入和输出详细说明
- 1.3 用标准I/O将标准输入复制到标准输出
- IO之转换流与重定向标准输入/输出