原创 arm开发板LED应用程序详细分析

2010-2-2 16:07 3150 5 5 分类: MCU/ 嵌入式

下面这个程序时现在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;
}

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
5
关闭 站长推荐上一条 /3 下一条