下面这个程序时现在ARM开发板常用的一个LED应用程序实例。我参考资料详细做了分析。这段程序用管道来传递LED控制参数。但是我只看到了读管道,没看到写管道。希望哪位高手能指点一下。
/*************************************
NAME:ledplayer.c
COPYRIGHT:www.dpj365.cn
*************************************/
#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>
#include <sys/time.h>
static int led_fd;//静态变量,LED设备号
static int type = 1;//LED的点亮方式
static void push_leds(void)
{
static unsigned step;//静态变量,进行自增运算,实现一个完整的LED点亮周期
unsigned led_bitmap;
int i;
switch(type) {
//方式1:led依次点亮第1个灯-第2个灯-第3个灯-第4个灯-第3个灯-第2个灯-第1个灯,
//每次点亮一个灯,不断循环
case 0:
if (step >= 6) {
step = 0;
}
if (step < 3) {
led_bitmap = 1 << step;//这里是1左移step位,不是有些帖子里面解释的step左移1位
} else {
led_bitmap = 1 << (6 - step);
}
break;
//方式2:led用亮表示1,灭表示0,显示0-15(因为只有4个LED,不能显示到255)
case 1:
if (step > 255) {
step = 0;
}
led_bitmap = step;
break;
//默认方式:led全灭
default:
led_bitmap = 0;
}
step++;
for (i = 0; i < 4; i++) {
//leds设备的核心驱动函数
//对i号灯进行控制,led_bitmap & 1的结果要么为1,要么为0
ioctl(led_fd, led_bitmap & 1, i);
//led_bitmap低4位每一位对应一个LED状态,左移一位,与for循环同步
led_bitmap >>= 1;
}
}
int main(void)
{
int led_control_pipe;//管道设备号
int null_writer_fd; // for read endpoint not blocking when control process exit
double period = 0.5;
//“/dev/TQ2440_leds”设备节点,在驱动程序中创建,和主设备号,设备名对应
led_fd = open("/dev/TQ2440_leds", 0);// 以只读方式打开,O_RDONLY为0
//open函数失败返回-1,成功返回文件描述符
if (led_fd < 0) {
perror("open device leds");
exit(1);
}
unlink("/tmp/led-control");//先删除有名管道,可能不是必须的
mkfifo("/tmp/led-control", 0666);//创建一个可读写不可执行的有名管道
//以非阻塞读方式打开有名管道
led_control_pipe = open("/tmp/led-control", O_RDONLY | O_NONBLOCK);
if (led_control_pipe < 0) {
perror("open control pipe for read");
exit(1);
}
//以非阻塞写方式打开有名管道
null_writer_fd = open("/tmp/led-control", O_WRONLY | O_NONBLOCK);
if (null_writer_fd < 0) {
perror("open control pipe for write");
exit(1);
}
for (;;) {
//结构体,文件描述符的集合,配合系统调用select()函数使用
fd_set rds;
/*timeval的结构定义如下:
struct timeval{
long tv_sec; //表示几秒
long tv_usec; //表示几微妙
}
timeout取不同的值,该调用就表现不同的性质:
1.timeout为0,调用立即返回;
2.timeout为NULL,select()调用就阻塞,直到知道有文件描述符就绪;
3.timeout为正整数,就是一般的定时器。*/
struct timeval step;
/*select调用返回时,除了那些已经就绪的描述符外,select将清除readfds、writefds和exceptfds中的所有没有就绪的描述符。select的返回值有如下情况:
1.正常情况下返回就绪的文件描述符个数;
2.经过了timeout时长后仍无设备准备好,返回值为0;
3.如果select被某个信号中断,它将返回-1并设置errno为EINTR。
4.如果出错,返回-1并设置相应的errno。*/
int ret;
//宏FD_ZERO清除文件描述符集fdset中的所有位(既把所有位都设置为0)。
FD_ZERO(&rds);
//宏FD_SET设置文件描述符集fdset中对应于文件描述符fd的位(设置为1)
//文件描述符集fd_set相关知识请看 http://www.dpj365.cn/bbs/viewthread.php?tid=369&extra=page%3D1
FD_SET(led_control_pipe, &rds);
step.tv_sec = period;//tv_sec = 0
step.tv_usec = (period - step.tv_sec) * 1000000L;//tv_usec=500 000
ret = select(led_control_pipe + 1, &rds, NULL, NULL, &step);
if (ret < 0) {
perror("select");
exit(1);
}
if (ret == 0) {
push_leds();//没有文件可读,继续执行先前对LED设备的控制操作
}
//在调用select后使用FD_ISSET来检测文件描述符集fdset中对应于文件描述符fd的位是否被设置。
else if (FD_ISSET(led_control_pipe, &rds)) {
static char buffer[200];
for (;;) {
char c;
int len = strlen(buffer);//计算字符个数,不包括'\0'
//如果len=199,sizeof(buffer)=200, 那么buffer已经满了
if (len >= sizeof(buffer) - 1) {
memset(buffer, 0, sizeof buffer);//将buffer清零
break;
}
//从管道读一个字符
if (read(led_control_pipe, &c, 1) != 1) {
break;
}
/*\r 是回车,return
\n 是换行,newline
我们在平时使用电脑时,已经习惯了回车和换行一次搞定,敲一个回车键,即是回车,又是换行,但在早期的打字机上,要另起一行打字需要两个步骤,首先要发送命令"\r”将打字头复位,即回车,然后再发送命令"\n”让打字机走纸移到下一行,所以这个历史遗留问题导致了如今我们在DOS-Windows的系统里需要区分"\r\n”和“\n”,但在Unix中只有"\n”。
用UltraEdit打开文本查看,会看到换行处显示的是0x0D0A,0x0D即"\r”,0x0A即"\n”。 */
if (c == '\r') {
continue;//如果是‘\r’,跳过读下一个字节
}
if (c == '\n') {
/*接收到'\n' ,证明消息结束了,接着要将buffer中存储的消息传给相关变量,最后清空buffer*/
int tmp_type;
double tmp_period;
if (sscanf(buffer,"%d%lf", &tmp_type, &tmp_period) == 2) {
type = tmp_type;
period = tmp_period;
}
fprintf(stderr, "type is %d, period is %lf\n", type, period);
memset(buffer, 0, sizeof buffer);
break;
}
buffer[len] = c;//这里没有len++,len通过前面的len = strlen(buffer)得到新值,相当于加1
}
}
}
close(led_fd);//关闭LED设备
return 0;
}
文章评论(0条评论)
登录后参与讨论