不知道做单片机开发的朋友们是不是还在用自定义的数据打印函数打印字串呢?如:
其实C标准输入输出库文件中已为我封装好了许多我们需要的函数。只要我们做个简单的移植便可以将它们用上。在库里,我们可以用:格式化输入输出函数。如:puts()、printf()、putchar()、getchar()、scanf()等等。
我们要做的莫非就两点:
但是,我在做STM32下的程序开发时。发现库并没有提供putchar()与getchar()这么简单的函数。而是:
其中,FILE结构体在STDIO.H中并没有定义。而只是开放地声名了有那么一个类型,具体是需要程序员自己根据自己的需要定义。
对于小型嵌入式系统的开发,也许你并没有想到“文件”这个层次上来。其实,这里有文件并非完全指的是存储数据的单元。它可以是设备,如显示器、键盘、串口输入输出等等。这些设备都可以被当成文件。我们只要通过FILE区分不同的文件,并对不同的FILE我们可以采集这个文件对应的输出输入方式。这么一来,FILE让我扩展的空间就大了。
这里,我主要是想用标准输入输出作为串口输入输出接口。那么,我们就可以将FILE定义成串口文件结构体。往串口文件里写数据就等于往串口发数据。
#define FILE_OPTION_CONSOLE 0x01
struct __FILE{
void (*output)(char); //输出函数
char (*input)(); //输入函数
OS_EVENT *semaphore; //数据接收信号量
slock_t slock; //串口锁
bool isPending; //读数据等待标识
INT16U time_out; //超时
INT8U option; //选项
INT8U errno; //错误码
};
typedef struct __FILE FILE;
如上FILE结构体的定义。我为每一个FILE定义一个output与input函数指针,用于告诉fputc与fgetc,输出数据与获得数据的方法。别的fputc与fgetc不关心,你只告许它什么做就是了。
所以,fputc函数的实现如下:
int fputc(int ch, FILE * file)
{
if( file && file->output ){ //校验file的合法性
file->output(ch);
}
return ch ;
}
但是,当我们用putchar('\n'),我们想要的是换行并回到行首。我们希望当输出'\n'之前,先输出'\r'。所以,我们将fputc()做修改:
int fputc(int ch, FILE * file)
{
if( file && file->output ){ //校验file的合法性
if( '\n' == ch){
file->output('\r');
}
file->output(ch);
}
return ch ;
}
我们不能全部这样,对于十六进制的数据输出,我们不需要做这样的处理。所以,我们在FILE结构中添加了option,当某位为1时表示具有这功能。否则不做任何处理。
int fputc(int ch, FILE * file)
{
if( file && file->output ){ //校验file的合法性
if( (FILE_OPTION_CONSOLE & file->option) && '\n' == ch){ //只允许在控制台下使用该功能
file->output('\r');
}
file->output(ch);
}
return ch ;
}
由于我们作了多任务操作系统。可能出现多个任务同时调用fputc()的问题。故,我们最好要加锁。
int fputc(int ch, FILE * file)
{
if( file && file->output ){ //校验file的合法性
Spin_Lock(&file->slock,1,0);
if( (FILE_OPTION_CONSOLE & file->option) && '\n' == ch){ //只允许在控制台下使用该功能
file->output('\r');
}
file->output(ch);
Spin_Unlock(&file->slock,1);
}
return ch ;
}
到了这一步,我们便可以方便地使用fputc(),fputs(),fprintf()等等。
那么接收怎么实现呢?