/*led_player服务器*/
/*实现对管道/tmp/led-control的监控*/
/*并实现对led的控制*/
#include <stdio.h> /*标准输入输出*/
#include <stdlib.h> /*标准库*/
#include <unistd.h> /* */
#include <sys/ioctl.h> /*输入输出控制*/
#include <sys/types.h> /*一些类型*/
#include <sys/stat.h> /*一些状态*/
#include <fcntl.h> /* */
#include <sys/select.h> /*select函数*/
#include <sys/time.h> /*时间方面的*/
/*包含的头文件越多,程序也就越复杂,实现的功能也就越强大*/
/*也就是说,头文件的多少,是判断一个软件规模的重要指标*/
/*我们希望尽量多包含一些头文件,但是不希望包含不必要的头文件*/
/*而且,一定要清楚所包含的头文件是干什么的!!!*/
/*对static的理解:编译时即做好内存的分配工作,只要整个程序开始运行,
则分配空间,一直到整个程序终止,才释放空间.*/
/*dynamic则不同,比如,子程序内部定义的一个局部变量,是在用到子程序,
并且到达创建该局部变量的语句时才创建,然后子程序执行完,就释放空间*/
static int led_fd; /*静态变量,led设备号*/
static int type = 1; /*类型,据此实现对led运作方式的控制*/
/*push_leds()函数*/
/*根据led的控制参数,实现对led的控制*/
/*事实上,很多子函数完成的才是核心的任务*/
/*而主函数做的工作则是用户界面,即人机交互方面的工作*/
/*也就是如何让用户来调用这些完成实际工作的子函数*/
/*GUI,图形用户界面则是人机交互的理想方式*/
static void push_leds(void);
/*主函数入口*/
int main(void)
{
int led_control_pipe; /*管道设备号,实现的是对管道的读取*/
int null_writer_fd; /*也是管道设备号,实现的是对管道的写入*/
/*似乎是没怎么用到它*/
double period = 0.5; /*间歇时间,led的控制参数之一*/
/*打开leds设备,其设备号赋给led_fd*/
/*leds设备的驱动函数事实上已经写好了*/
/*我们做的工作就是调用这些驱动函数*/
led_fd = open("/dev/leds",0);
/*对led_fd的处理*/
/*包括对leds设备错误打开的处理和正确打开的处理*/
/*通常对错误的处理即是要输出错误信息*/
/*而设备正确打开的处理,则要复杂得多*/
/*在这里,leds设备正确打开,则接下来要对leds设备进行控制*/
/*对leds设备进行控制,也不是一件轻易完成的事情*/
/*我们需要有控制信息吧?从哪里获取这些控制信息呢?*/
/*而且,这些控制信息本身已经很复杂了,所以要弄清楚控制信息的结构*/
/*然后要清楚*/
/*控制信息是谁产生的,从哪里获得控制信息,如何获得控制信息*/
/*从那获取控制信息之后,是不是要将那里清零呢?*/
/*获得控制信息之后,要依据控制信息,对leds设备进行相应的操作*/
/*下面我们开始吧*/
if(led_fd < 0) /*打开错误*/
{
perror("open device leds");
exit(1);
}
/*下面则是打开正确的处理*/
/*包括控制参数的获取和依据控制参数进行设备的控制*/
/*由于本程序是leds设备服务器程序,所以需要不断检测控制信息的变化*/
/*这就需要一个无限循环,在这个循环里完成检测任务,一旦检测到需要的东西*/
/*则跳出检测循环,依据检测到的信息去作相应的控制工作*/
/*而我们对leds的控制工作也不是简单的一蹴而就的事情*/
/*它事实上是一个无限循环的动态过程,涉及到时间的处理*/
/*如何改变该循环的模式以及如何跳出该循环是我们关心的重点*/
/*改变循环的模式当然还是要根据检测到的控制信息*/
/*下面开始*/
/*解开管道的连接*/
unlink("/tmp/led-control"); /*unlink()函数*/
mkfifo("/tmp/led-control",0666); /*mkfifio()函数,fifo方式的确立*/
/*打开管道*/
/*注意O_RDONLY和O_NONBLOCK的打开方式*/
/*管道号赋给led_control_pipe*/
led_control_pipe = open("/tmp/led-control",O_RDONLY | O_NONBLOCK);
/*led_control_pipe的处理*/
if(led_control_pipe < 0) /*打开错误*/
{
perror("open control pipe for read");
exit(1);
}
/*以O_WRONLY方式打开管道,并将管道号赋给null_writer_fd*/
/*其与前面的led_control_pipe管道号,构成了一读一写*/
null_writer_fd = open("/tmp/led-control",O_WRONLY | O_NONBLOCK);
/*对null_writer_fd的处理*/
if(null_writer_fd < 0) /*打开错误*/
{
perror("open control pipe for write");
exit(1);
}
/*for循环,对leds设备的动态控制*/
for(;;)
{
fd_set rds; /*结构体,文件描述符的集合*/
/*结合后面select()函数的使用*/
/*rds,就是read descriptor set的缩写*/
/*select()函数就用来检测rds中是否有文件可读*/
struct timeval step; /*时间的结构体*/
/*有秒和毫秒两个成员*/
/*要注意此处的step与子函数中的step*/
int ret; /*select()函数的返回值*/
FD_ZERO(&rds); /*宏FD_ZERO()初始化rds*/
FD_SET(led_control_pipe,&rds); /*宏FD_SET()*/
/*led_control_pipe文件符被放入rds*/
/*step结构体变量的赋值*/
step.tv_sec = period; /*period是在主函数刚开始定义的*/
/*秒的赋值*/
/*period作为控制参数之一,肯定会有所变化的*/
step.tv_usec = (period - step.tv_sec) * 1000000L;
/*毫秒的赋值,事实上被赋为0*/
/*select()函数的使用*/
/*select()函数能够监测需要监视的文件描述符的变化情况*/
/*此处也就是监测管道是否有新的消息,从而可读*/
/*这里,要注意,别的进程写控制消息到管道的时候*/
/*一定要对管道的可读标志位进行相关的处理才行*/
/*这样,我们的服务器才能检测到管道有新消息,并可读了*/
ret = select(led_control_pipe + 1,&rds,NULL,NULL,&step); /*此处step相当于延时处理,也就是leds设备的运作周期*/
/*led_control_pipe + 1 限定文件描述符的最大范围*/
/*&step监测的时间跨度*/
/*即花一定时间检测管道是否有新的控制消息*/
/*如果有,就读取*/
/*如果没有,则按以前的模式继续执行任务*/
/*对select()函数返回值ret的处理*/
if(ret < 0) /*表明select()出错*/
{
perror("select");
exit(1);
}
if(ret == 0) /*表明没有文件可读*/
/*也就是说没有新消息过来*/
{
/*那么就不用接收新消息*/
/*继续执行我们对leds设备的控制操作*/
/*即调用子函数push_leds()*/
push_leds(); /*注意,子函数中一定有能够接收消息的变量*/
}
else /*即ret大于0时,此时表示管道可读*/
/*即管道已经被写入了新的控制消息*/
/*那么我们接下来就是要读取消息了*/
if(FD_ISSET(led_control_pipe,&rds))
/*此处作判断有必要吗?*/
/*led_control_pipe本来就在rds里面*/
{
/*要读取消息,总要有缓冲区呗*/
static char buffer[200]; /*注意是static型的*/
/*读取是一个字符一个字符地读*/
/*这里用一个无限循环来做*/
/*跳出循环的时刻在于读到回车符*/
/*也要注意对空格符的处理*/
for(;;)
{
/*存放单个字符的变量*/
char c;
/*len很重要,指向buffer中消息的末尾*/
/*添加新的字符,就靠len来定位*/
/*因此,len也要不断地更新*/
int len = strlen(buffer);
/*strlen()函数*/
/*判断buffer是否已满*/
/*如果满了,则对buffer清空*/
/*只有清空了,才能继续盛放东西*/
/*并跳出循环*/
/*我觉得判断是否已满是没有必要的*/
/*因为buffer容量够大了,存放消息足够了*/
/*而消息一到来,我们接收之后,就一定会作清空处理的*/
/*所以不会出现满的情况*/
if(len >= sizeof(buffer) - 1)
/*注意这里-1的意义*/
{
memset(buffer,0,sizeof(buffer));
/*memset()函数*/
break;
}
/*从管道中读取字符,先放入c字符变量*/
/*之后放入buffer中*/
/*这才是控制信息获取的核心步骤*/
/*这实质上是管道设备的驱动程序完成的*/
/*通过read()函数完成*/
if(read(led_control_pipe,&c,1) != 1) /*读取异常*/
{
break;
}
/*本来紧接着就该把c里的字符放进buffer中*/
/*但是对c中的字符进行判断*/
/*因为并不是所有的字符都要放进缓冲区中*/
/*如/r,好像是空格,如/n,是回车*/
/*对这些特殊的字符有特别的处理,要非常小心*/
/*而对一般的控制信息的字符,则正常地放进buffer中就行了*/
/*对/r的处理,跳出本次循环,进入下次循环,continue语句*/
if(c == '/r') /*即是空格符就跳过去*/
{
continue;
}
/*对/n的处理*/
/*/n实质上是跳出整个消息获取循环的标志*/
/*回车符一到来,就证明消息结束了*/
/*之后我们要做的工作有两个*/
/*一个是将buffer中存储的消息传达给有关变量*/
/*之后,就需要清空buffer*/
/*至于对管道的清空处理,这里不是本服务器作的*/
/*有个疑问,在管道读取的过程中,是否可以继续写入呢?*/
if(c == '/n')
{
/*两个临时变量,存储空喊控制参数*/
int tmp_type; /*存储模式*/
double tmp_period; /*存储时间*/
/*标准输入函数sscanf()*/
/*我们放进buffer里的是字符*/
/*但我们从buffer里拿出来的时候却要是数字*/
/*这就是通过格式化输入完成的*/
if(sscanf(buffer,"%d%lf",&tmp_type,&tmp_period) == 2)
{
/*等于2则说明输入正确*/
/*则将临时变量的值输入正式变量里*/
/*这就是两个控制参数*/
type = tmp_type;
period = tmp_period;
}
/*输出提示信息*/
fprintf(stderr,"type is %d,period is %lf/n",type,period);
/*为什么要输到stderr中呢*/
memset(buffer,0,sizeof(buffer)); /*清空buffer*/
/*跳出循环*/
break;
}
/*正常的c的处理*/
buffer[len] = c; /*读取字符到buffer*/
}
}
}
/*关闭leds设备*/
close(led_fd); /*close()函数*/
/*主函数返回*/
return 0;
}
/*子函数*/
static void push_leds(void)
{
static unsigned step; /*此处的step与上层的step时间结构体不同*/
/*注意它是静态的,而且还会进行自增运算*/
/*以实现一个大的周期*/
/*比如type一直为1时,其运作周期为256*period*/
/*step最初没有初始化,所以会随机给定一个值*/
unsigned led_bitmap; /*led设备的最终控制参数就是它*/
int i; /*循环变量,以实现对4个led的逐个控制*/
/*switch语句,根据type来对led_bitmap赋值*/
switch(type)
{
case 0: /*模式0*/
if(step >= 6) /*6,7,8,...*/
step = 0; /*就意味着此计数周期不算大*/
/*才到6就要截止了*/
if(step < 3) /*0,1,2的处理*/
{
led_bitmap = 1 << step; /*<<是什么运算呢?*/
/*移位运算*/
/*若step为2,则其二进制为10*/
/*左移一位,则为100,就是4*/
/*赋值给led_bitmap,从而实现对led的控制*/
}
else /*3,4,5的处理*/
{
led_bitmap = 1 << (6 - step);
/*比如step为3时*/
/*则led_bitmap为110,即为6*/
}
break; /*跳出*/
case 1: /*模式1*/
if(step > 255) /*此循环还是比较大的*/
{
step = 0;
}
led_bitmap = step; /*对led_bitmap赋值*/
break; /*跳出,显然,模式1还是相对简单的*/
default:
led_bitmap = 0; /*其它模式,都是0,就是全灭*/
}
step ++; /*step的自增运算,跑马灯就要靠它来动态实现了*/
/*step每隔period变化一次,显示状态就会发生变化*/
/*注意说的是显示状态,而不是说显示模式*/
/*就是说,同一个显示模式下,状态也会不断变化*/
/*而不同模式,led的变化情况是不同的*/
/*下面依据led_bitmap对leds设备进行处理*/
for(i = 0; i < 4; i++) /*依此对0,1,2,3灯管进行处理*/
{
/*ioctl()函数*/
/*leds设备的核心驱动函数*/
ioctl(led_fd,led_bitmap & 1,i);
/*对i号灯进行控制*/
/*led_bitmap & 1,这一步非常考究*/
/*其结果要么为0,要么为1,就是开关*/
/*比如对0号灯进行开处理*/
/*1011&0001事实上就是屏蔽掉了前三位,而只留下一位*/
/* 当然可想而知,我们必须对led_bitmap不断右移*/
/*对led_bitmap进行右移,以实现控制位的对齐*/
led_bitmap >>= 1;
}
}
转载自:
http://www.dpj365.cn/bbs/viewthread.php?tid=203