C编程实现键盘LED灯闪烁

在《Shell脚本实现键盘LED灯闪烁》一文中,我们已感受到了控制的乐趣,一步步向硬件逼近,这次我们在Linux下使用C语言进行系统调用来实现该功能。这里面会涉及到应用层定时器和ioctl系统调用来控制键盘LED灯状态。
关于应用层定时器需要涉及到信号机制,其包含有alarm闹钟和timer定时器两种,其与信号机制分别说明如下(这两种均是自动循环的,即不需要处理函数里再设置一遍定时器超时设置):
1.alarm
运行man alarm命令后有关于该函数的使用说明,其函数原型如下:
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
当设定的时间到了后会发出SIGALRM信号,需要对应的信号处理函数配套处理。
2.timer
运行man setitimer命令后有相关说明,本次会用到的函数原型如下:
#include <sys/timer.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
其中which是设置的定时器类型,分别对应如下:
  • ITIMER_REAL:实时定时器,发SIGALRM信号;
  • ITIMER_VIRTUAL:应用进程执行时间定时器,发SIGVTALRM信号;
  • ITIMER_PROF:应用进程执行和内核交互时间定时器,发SIGPROF信号。
从上面可以看出,同一时间只能有3个定时器存在,分别对应3种类型。那么上面的信号要如何处理呢?执行man signal命令有相关的函数原型如下:
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
这signal函数除了与定时器配套外,还可以捕获其他信号,执行man 7 signal命令可以查看到。 好了,关于定时器部分就说到这。
而关于键盘LED灯控制,我们需要用到TTY设备的ioctl系统调用函数,该函数是标准的字符设备接口(可以不实现),其接口原型(执行man ioctl)如下:
#include <sys/ioctl.h>
int ioctl(int d, int request, ...);
其中d为相应的设备描述符号,request是命令码,…是命令码对应的参数值。对于Keyboard的LED,我们使用到KDSETLED这个命令码,而其对应的设备是/dev/console,这里的d就对应open这个设备的返回值,而…对应传入的值,恢复未定义状态是0xff,而键盘右上角3个灯(Number lock,Caps Lock,Scroll Lock)全亮对应0x07。
下面是实现C编程控制PC键盘LED灯闪烁的源码:
#include <stdio.h>
#include <sys/time.h>
#include <signal.h>
#include <linux/kd.h>
#include <sys/ioctl.h>

void slam_alarm_handler(int a)
{
        int tty = open("/dev/console", 0), led;

        if(tty<3)
        {
                perror("open:");
                exit(0);
        }
        led = 0x07;
        if(ioctl(tty,KDSETLED,led)>0)
                perror("ioctl led on:");
        sleep(1);
        led = 0xff;
        if(ioctl(tty,KDSETLED,led)>0)
                perror("ioctl led off:");
        close(tty);
}

int main(void)
{
        /* The struct itimerval
         * it_interval:means interval everytime after first time
         * it_value:means the first time interval
         * The ITIMER_REAL timer come with SIGALRM signal
         */
        struct itimerval t;
        t.it_interval.tv_usec = 0;
        t.it_interval.tv_sec = 2;
        t.it_value.tv_usec = 0;
        t.it_value.tv_sec = 2;
        if(setitimer(ITIMER_REAL, &t, NULL) < 0)
        {
                printf("Setitimer failed.\n");
                exit(-1);
        }
        signal(SIGALRM, slam_alarm_handler);

        while(1)
        {
                sleep(2);
        }

        exit(0);
}
相应的Makefile文件内容如下:
all:
        gcc -o timer_keyboard_led_flash timer_keyboard_led_flash.c

clean:
        rm -rf timer_keyboard_led_flash
对应的源码文件目录树如下:
/home/xinu/xinu/c_cpp/timer_keyboard_led_flash/
├── Makefile
└── timer_keyboard_led_flash.c
编译生成的文件后,执行时需要root用户权限,在ubuntu下需sudo ./timer_keyboard_led_flash命令去执行。
参考网址:
http://www.cppblog.com/jerryma/archive/2012/01/31/164704.html
http://jpkc.zju.edu.cn/k/505/pdf/nihe4.pdf
http://blog.csdn.net/fanwenbo/article/details/2645362
http://falldog7.blogspot.com/2008/05/linux-timer.html
http://ubuntuforums.org/showthread.php?t=1372521

评论

此博客中的热门博文

I/O映射之I/O端口

通过Netlink检测网线插拔

使用seq_file