中断驱动学习与实例——定时器0中断实现led流水灯



中断驱动学习(1)

几个简单的中断API

 

【入门】

     在编写中断驱动之前先熟悉一下可能会用到的一些接口吧。

1、中断申请


点击(此处)折叠或打开

  1. int request_irq(
  2.       unsigned int irq,      //中断号
  3.       irq_handler_t handler, //中断处理函数
  4.       unsigned long flags,   //中断标志:触发方式[#define IRQF_*]
  5.          const char *name,   //中断名称,通常是设备驱动程序的名称
  6.                              //使用cat /proc/interrupts可以查看
  7.       void *dev_id);         //一般设置为设备结构体,或者不使用时为NULL


 
返回值:

      a)  返回0表示成功;

      b)  返回-EINVAL表示无效的参数,如果返回这个值,应该看看传递给

                 request_irq()的参数是否正确;

      c)  返回-EBUSY表示中断已经被占用且不能共享;

      d)  返回ENOMEM表示内存不足。嵌入式系统由于内存资源有限,经常

会发生这样的错误。

中断处理函数说明:[这是一个回调函数]

    

点击(此处)折叠或打开

  1. typedef irqreturn_t (*irq_handler_t)(int irq, void *dev_id);


2、中断注销


点击(此处)折叠或打开

  1. void free_irq(
  2.       unsigned int irq, //中断号
  3.       void *dev_id); //对应request_irq()参数中的dev


3、开启中断


点击(此处)折叠或打开

  1. void enable_irq(unsigned int irq); //中断号


4、关闭中断


点击(此处)折叠或打开

  1. void disable_irq(unsigned int irq); //中断号


    由于在disable_irq() 中会调用synchronize_irq() 函数等待中断返回,所以在中断处理程序中不能使用disable_irq,否则会导致cpu被synchronize_irq() 独占而发生系统崩溃.    

 

【扩展】

 

(1)请求中断(request_irq)与开启中断(enable_irq)的区别?

追根溯源:

       用source insight跟踪request_irq() 函数可以发现,实际上 request_irq() 调用的是 request_threaded_irq() 这个函数,而在 request_threaded_irq() 中调用的最后一个函数就是 enable_irq()。

       由此,我们可以做个比喻:

request_irq() 是一个戴着假面的温柔绅士,他在试图占用你(中断)之前会先询问你的意见(实际上是中断控制器管的),如果你不愿意,他就会默默离开,继续等待下一个目标;如果你愿意,他就露出 enable_irq() 的本性了。

再看 enable_irq() ,enable_irq() 是一个崇尚武力征服的蛮族(比如说:罗马、日本),如果他看中了你,不会屁颠屁颠地跑到你面前问你愿不愿意,而是直截了当地闯入你的领地,我们把 enable_irq() 这种强悍的仅供方式称为:单刀直入、直捣黄龙。  
    同理,free_irq()中也应该包含disable_irq(),不信?自己找找看吧。

 

【参考】

《Linux中断处理驱动程序编写 》

http://blog.csdn.net/gotosola/article/details/7422072


中断驱动学习(2)

基于Timer0中断控制的led流水灯驱动

 

实现平台:Ubuntu 14.04 + TQ2440

实现工具:arm-linux-gcc + SecureCRT + Samba

实现内容:

       用户空间                   内核空间                    实现结果

       open                         pwm_irq_open          pwm初始化和irq请求

       close                         pwm_irq_close          停止pwm输出

       ioctl                          pwm_led_ioctl           启动或关闭定时器

实现过程:

[1] 原材料:头文件


点击(此处)折叠或打开

  1. #ifndef _PWM_LED_
  2. #define _PWM_LED_
  3.  
  4. #include <linux/kernel.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/fs.h>
  8. #include <linux/types.h>
  9. #include <linux/cdev.h>
  10. #include <linux/uaccess.h>
  11. #include <mach/gpio-nrs.h>
  12. #include <mach/regs-gpio.h>
  13. #include <linux/clk.h>
  14. #include <linux/interrupt.h>
  15. #include <asm/irq.h>
  16. #include <mach/hardware.h>
  17. #include <plat/regs-timer.h>
  18. #include <mach/regs-irq.h>
  19. #include <asm/mach/time.h>
  20. #include <linux/irq.h>
  21. #include <mach/hardware.h>
  22. #include <linux/platform_device.h>
  23. #include <asm/io.h>
  24.  
  25. //#define DEVICE_MAJOR 250
  26. //#define DEVICE_MINOR 0
  27. #define DEVICE_NAME "PWM_LED"
  28. #define LED_OFF 0 //led灭
  29. #define LED_ON 1 //led亮
  30.  
  31. //led端口查询表
  32. static unsigned long led_table[] =
  33. {
  34.   S3C2410_GPB5,
  35.   S3C2410_GPB6,
  36.   S3C2410_GPB7,
  37.   S3C2410_GPB8,
  38. };
  39.  
  40. //led输出端口表
  41. static unsigned int led_cfg_out[] =
  42. {
  43.   S3C2410_GPB5_OUTP,
  44.   S3C2410_GPB6_OUTP,
  45.   S3C2410_GPB7_OUTP,
  46.   S3C2410_GPB8_OUTP,
  47. };
  48.  
  49. //定时器相关寄存器和参数
  50. volatile unsigned long *tcfg0 = NULL; //用来设置预分频
  51. volatile unsigned long *tcfg1 = NULL; //用来设置分频
  52. volatile unsigned long *tcon = NULL; //定时器控制器
  53. volatile unsigned long *tcntb0 = NULL; //计数缓冲寄存器
  54. volatile unsigned long *tcmpb0 = NULL; //比较缓冲寄存器
  55. volatile unsigned long *tcnto0 = NULL; //计数观察寄存器
  56.  
  57. struct clk *clk_p;
  58. unsigned long pclk;
  59.       
  60. //设备号和设备结构
  61. dev_t dev;
  62. int g_nMajor = 0;
  63. int g_nMinor = 0;
  64. struct cdev *pwm_cdev = NULL;
  65. static struct class *pwm_class;
  66.  
  67. //定时器中断描述
  68. struct pwm_irq_desc
  69. {
  70.        unsigned int irq_nb;
  71.        unsigned long irq_flag;
  72.        char *irq_name;
  73. };
  74.  
  75. //定时器中断描述符,为其他定时器预留,这里只测试TIMER0
  76. static struct pwm_irq_desc pwm_irqs[] =
  77. {
  78.        {IRQ_TIMER0, IRQF_TRIGGER_FALLING, "PWM_TIMER0" },
  79.        {IRQ_TIMER1, IRQF_TRIGGER_FALLING, "PWM_TIMER1"},
  80.        {IRQ_TIMER2, IRQF_TRIGGER_FALLING, "PWM_TIMER2"},
  81.        {IRQ_TIMER3, IRQF_TRIGGER_FALLING, "PWM_TIMER3"},
  82.        {IRQ_TIMER4, IRQF_TRIGGER_FALLING, "PWM_TIMER4"},
  83. };
  84. // IRQF_TRIGGER_FALLING 也可以改为定时器专用的 IRQF_TIMER
  85.  
  86. /********************************************************************
  87.  *函数接口声明
  88. ********************************************************************/
  89. static irqreturn_t irq_interrupt(int irq, void *dev_id);
  90. static int pwm_irq_close(struct inode *inode, struct file *file);
  91. static int pwm_irq_open(struct inode *inode, struct file *file);
  92. static int pwm_led_ioctl(struct inode *inode, struct file *file,
  93.                          unsigned int cmd, unsigned long arg);
  94.  
  95. /********************************************************************
  96.  *设备文件操作接口
  97. ********************************************************************/
  98. static struct file_operations dev_fops = {
  99.        .owner = THIS_MODULE,
  100.        .open = pwm_irq_open,
  101.        .release = pwm_irq_close,
  102.        .ioctl = pwm_led_ioctl,
  103. };
  104. #endif // _PWM_LED_

[2] 模块框架


点击(此处)折叠或打开

  1. static int __init pwm_module_init(void)
  2. {
  3. ;初始化led
  4. ;注册设备{这部分在《基于TQ2440的led字符设备驱动》中分解的很详细了}
  5. }
  6.  
  7. static void __init pwm_module_exit(void)
  8. {
  9. ;注销设备{这部分在《基于TQ2440的led字符设备驱动》中分解的很详细了}
  10. }
  11.  
  12. module_init(pwm_module_init);
  13. module_exit(pwm_module_exit);
  14.  
  15. MODULE_LICENSE("Dual BSD/GPL");
  16. MODULE_DESCRIPTION("PWM Led Test");
  17. MODULE_AUTHOR("Reyn-2014.05.27");

[3] 操作接口

由于我们实际上要的效果是流水灯,所以只要实现两个或者三个接口即可。而open和close肯定是需要设计的,那么如果你嫌麻烦,可以直接在open中实现流水灯;如果不怕麻烦,就增加一个ioctl也不为过吧。

本文加上了ioctl,总共三个操作接口,在头文件中的dev_fops中有声明。
     下面逐一介绍。

 

[4] 逐个介绍

       [+] pwm_open() 主要的工作是请求定时器0中断,在中断中实现流水灯,流水灯我们在接触ARM阶段的时候应该都有写过,就不需要特别说明了,现在感觉写驱动好像也不那么难吧?笔者倒是觉得,比起ARM阶段把工作手册DataSheet翻来翻去找寄存器的时候更加难受。申请完irq之后,还要一个操作,那就是初始化pwm。所以,这个过程除了对寄存器的操作有点技巧之外,并不难实现,下面贴出代码:

点击(此处)折叠或打开

  1. /********************************************************************
  2.  *函数名称:    pwm_irq_open
  3.  *函数描述:    申请定时器中断
  4.  *返回值 :    request_irq的处理结果
  5. ********************************************************************/
  6. static int pwm_irq_open(struct inode *inode, struct file *file)
  7. {
  8.     int nRet = 0;
  9.     printk(KERN_INFO "[Kernel] try pwm_irq_open, we'll request irq.\n");
  10.     printk(KERN_INFO "[Kernel] irq_nb[%d],irq_flag[%d],irq_name[%s].\n",
  11.             IRQ_TIMER0, IRQ_TYPE_EDGE_FALLING/*IRQF_TIMER*/, "PWM_LED"); 
  12.    //请求Timer0中断        
  13.     nRet = request_irq(IRQ_TIMER0, irq_interrupt, IRQ_TYPE_EDGE_FALLING,
  14.                         "PWM_LED", NULL);                        
  15.     printk(KERN_INFO "[Kernel] request irq and returns %d.\n",nRet);          
  16.     //如果请求失败,则禁止并释放该中断
  17.     if(nRet < 0)
  18.     {
  19.         disable_irq(IRQ_TIMER0);
  20.         free_irq(IRQ_TIMER0, NULL);
  21.         printk(KERN_INFO "[Error] request_irq fail.\n");
  22.         return -1;    
  23.     }    

  24.     //初始化pwm
  25.     pwm_init();

  26.     return nRet;
  27. }

  28.     然后是中断注册函数:
  29. /********************************************************************
  30.  *函数名称:    irq_interrupt
  31.  *函数描述:    定时器中断执行函数
  32.  *返回值 :    IRQ_NONE / IRQ_HANDLED / IRQ_WAKE_THREAD
  33. ********************************************************************/
  34. static irqreturn_t irq_interrupt(int irq, void *dev_id)
  35. {
  36.     static int nLedNb = 0;
  37.     static int irq_count = 0;
  38.     
  39.     switch(nLedNb%4)
  40.     {
  41.         case 0:
  42.         {
  43.             s3c2410_gpio_setpin(S3C2410_GPB5, ~(LED_ON));
  44.             s3c2410_gpio_setpin(S3C2410_GPB6, ~(LED_OFF));
  45.             s3c2410_gpio_setpin(S3C2410_GPB7, ~(LED_OFF));
  46.             s3c2410_gpio_setpin(S3C2410_GPB8, ~(LED_OFF));
  47.             printk(KERN_INFO "[Kernel] led1 on.    ");
  48.             nLedNb++;
  49.         }
  50.         break;
  51.         
  52.         case 1:
  53.         {
  54.             s3c2410_gpio_setpin(S3C2410_GPB5, ~(LED_OFF));
  55.             s3c2410_gpio_setpin(S3C2410_GPB6, ~(LED_ON));
  56.             s3c2410_gpio_setpin(S3C2410_GPB7, ~(LED_OFF));
  57.             s3c2410_gpio_setpin(S3C2410_GPB8, ~(LED_OFF));
  58.             printk(KERN_INFO "[Kernel] led2 on.    ");    
  59.             nLedNb++;            
  60.         }
  61.         break;
  62.         
  63.         case 2:
  64.         {
  65.             s3c2410_gpio_setpin(S3C2410_GPB5, ~(LED_OFF));
  66.             s3c2410_gpio_setpin(S3C2410_GPB6, ~(LED_OFF));
  67.             s3c2410_gpio_setpin(S3C2410_GPB7, ~(LED_ON));
  68.             s3c2410_gpio_setpin(S3C2410_GPB8, ~(LED_OFF));    
  69.             printk(KERN_INFO "[Kernel] led3 on.    ");    
  70.             nLedNb++;        
  71.         }
  72.         break;
  73.         
  74.         case 3:
  75.         {
  76.             s3c2410_gpio_setpin(S3C2410_GPB5, ~(LED_OFF));
  77.             s3c2410_gpio_setpin(S3C2410_GPB6, ~(LED_OFF));
  78.             s3c2410_gpio_setpin(S3C2410_GPB7, ~(LED_OFF));
  79.             s3c2410_gpio_setpin(S3C2410_GPB8, ~(LED_ON));    
  80.             printk(KERN_INFO "[Kernel] led4 on.    ");
  81.             nLedNb++;
  82.         }
  83.         break;
  84.         
  85.         default:
  86.             printk(KERN_INFO "[Kernel] unrecognize led number.    ");
  87.             break;
  88.     }
  89.     
  90.     irq_count++;
  91.     printk(KERN_INFO "[IRQ] counts %d.\n", irq_count);
  92.     
  93.     return IRQ_RETVAL(IRQ_HANDLED);
  94. }

  95. 最后是初始化pwm的代码:
  96. /********************************************************************
  97.  *函数名称:    pwm_init
  98.  *函数描述:    定时器初始化
  99.  *返回值 :    void
  100. ********************************************************************/
  101. static void pwm_init(void)
  102. {    
  103.     //对寄存器的操作可以使用io端口映射——ioremap
  104.     //实际上是因为笔者在内核目录下找不到针对tcntb0/tcnto0的宏操作函数
  105.     //如果你们找到了,麻烦请通知我一声
  106.     tcfg0 = (volatile unsigned long *)ioremap(0x51000000, 4);
  107.     tcfg1 = (volatile unsigned long *)ioremap(0x51000004, 4);
  108.     tcon = (volatile unsigned long *)ioremap(0x51000008, 4);
  109.     tcntb0 = (volatile unsigned long *)ioremap(0x5100000c, 4);
  110.     tcnto0 = (volatile unsigned long *)ioremap(0x51000014, 4);
  111.     
  112.     printk(KERN_INFO "[Kernel] pwm_init.\n");
  113.     //定时器时钟频率=pclk /( 预分频器的值+1) / 分频器
  114.     *tcfg0 &= ~0xff; //定时器0预分配清零
  115.     *tcfg0 |=(157-1); //预分频
  116.     *tcfg1 &= ~0xf; //定时器0 mux 输入分频清零
  117.     *tcfg1 |=3;     //mux 分频 16

  118.     clk_p = clk_get(NULL, "pclk");
  119.     pclk = clk_get_rate(clk_p); //get pclk,实际得到的pclk=50000000
  120.     printk("pclk = %ld\n",pclk);

  121.     *tcntb0 &=0x0000; //计数缓冲器,先清零
  122.     *tcntb0 |=20003;
  123.     printk("tcntb0=%ld\n", *tcntb0);
  124. }


    [+] pwm_irq_close () ,顾名思义,就是清理中断了,另外还要停止pwm输出,代码实现起来也是很简单的:


点击(此处)折叠或打开

  1. /********************************************************************
  2.  *函数名称:    pwm_irq_close
  3.  *函数描述:    注销定时器中断
  4.  *返回值 :    成功返回0,失败返回其他值
  5. ********************************************************************/
  6. static int pwm_irq_close(struct inode *inode, struct file *file)
  7. {
  8.     pwm_stop();
  9.     free_irq(IRQ_TIMER0, NULL);        
  10.     printk(KERN_INFO "[Kernel] free irq done.\n");
  11.     return 0;
  12. }

  13.     pwm_stop()的实现如下:
  14. /********************************************************************
  15.  *函数名称:    pwm_stop
  16.  *函数描述:    停止定时器
  17.  *返回值 :    void
  18. ********************************************************************/
  19. static void pwm_stop(void)
  20. {
  21.     *tcon &= ~1;
  22.     //__raw_writel(tcon, S3C2410_TCON);
  23. //对寄存器的写操作也可以使用__raw_writel
  24.     printk(KERN_INFO "[Kernel] pwm stop now!\n");    
  25. }

       [+] pwm_led_ioctl(),就是控制定时器的开启和关闭了,这里的实现略微人性化一点,用户空间向内核空间发送指令(开启和关闭定时器),若符合,则进行相应操作;若不符合,则点亮第一盏led等作为错误表示,详细见代码:


点击(此处)折叠或打开

  1. /********************************************************************
  2.  *函数名称:    pwm_led_ioctl
  3.  *函数描述:    定时器初始化
  4. *                cmd:    0代表开启定时器,1代表停止定时器
  5. *                        若cmd不符合,则点亮led1以示错误
  6.  *返回值 :    成功返回0,失败返回其他值
  7. ********************************************************************/
  8. static int pwm_led_ioctl(struct inode *inode, struct file *file,
  9.                         unsigned int cmd, unsigned long arg)
  10. {
  11.     printk(KERN_INFO "[Kernel] do operations with pwm_led_ioctl.\n");
  12.     if(cmd < 0 || cmd > 1 )
  13.     {
  14.           s3c2410_gpio_setpin(led_table[0], ~(LED_ON));
  15.           s3c2410_gpio_setpin(led_table[1], ~(LED_OFF));
  16.           s3c2410_gpio_setpin(led_table[2], ~(LED_OFF));
  17.           s3c2410_gpio_setpin(led_table[3], ~(LED_OFF));
  18. //对GPIO的操作也可以使用ioremap
  19.         printk(KERN_INFO "[Error] unrecognize cmd, led1 on.\n");
  20.         return -1;
  21.     }

  22.     switch(cmd)
  23.     {
  24.         case 0: pwm_start();break;
  25.         case 1: pwm_stop();break;
  26.         default:break;
  27.     }
  28.     return 0;
  29. }
  30. /********************************************************************
  31.  *函数名称:    pwm_start
  32.  *函数描述:    启动定时器
  33.  *返回值 :    void
  34. ********************************************************************/
  35. static void pwm_start(void)
  36. {    
  37.     *tcon &= ~0x1f;
  38.     *tcon |= 0xb;        //start timer
  39.     //__raw_writel(tcon, S3C2410_TCON);

  40.     *tcon &= ~2;
  41.     //__raw_writel(tcon, S3C2410_TCON);    
  42.     
  43.     printk(KERN_INFO "[Kernel] pwm start now.\n");
  44. }

[5] 测试运行

又到了Makefile时间,其实对于驱动的Makefile而言,基本上一个Makefile就是一个框架,写完一个,就基本可以无限套用了。还是贴出来吧:


点击(此处)折叠或打开

  1. # PWM_LED Module Makefile
  2.  
  3. OBJ := pwm_led.o
  4. KERNDIR := /opt/EmbedSky/linux-2.6.30.4/
  5. INSTALL := /home/reyn/share/out/
  6. CC := arm-linux-gcc
  7.  
  8. obj-m := $(OBJ)
  9.  
  10. all:
  11.        $(MAKE) -C $(KERNDIR) M=$(PWD) modules
  12.        cp *.ko $(INSTALL)
  13.  
  14. modules:
  15.        $(MAKE) -C $(KERNDIR) M=$(PWD) modules
  16.  
  17. install:
  18.        cp *.ko $(INSTALL)
  19.  
  20. .PHONY:modules clean
  21. clean:
  22.        rm *.mod.c *.o *.order *.ko *.symvers *~
  23.        rm $(INSTALL)*.ko
  24.  

  25. 终端输入make编译:

  26. reyn@ubuntu:pwm_led$ make
  27. make -C /opt/EmbedSky/linux-2.6.30.4/ M=/home/reyn/share/drivers/pwm_led modules
  28. make[1]: 正在进入目录 `/opt/EmbedSky/linux-2.6.30.4'
  29.   CC [M] /home/reyn/share/drivers/pwm_led/pwm_led.o
  30. /home/reyn/share/drivers/pwm_led/pwm_led.h:76: warning: 'pwm_irqs' defined but not used
  31.   Building modules, stage 2.
  32.   MODPOST 1 modules
  33.   LD [M] /home/reyn/share/drivers/pwm_led/pwm_led.ko
  34. make[1]:正在离开目录 `/opt/EmbedSky/linux-2.6.30.4'
  35. cp *.ko /home/reyn/share/out/

未发现任何错误,继续,写个测试程序验证一下效果:

点击(此处)折叠或打开

  1. reyn@ubuntu:test$ gedit test_pwm.c &
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. #include <sys/types.h>
  6. #include <sys/stat.h>
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. #include <malloc.h>
  10. #include <sys/ioctl.h>

  11. int main(int argc, char **argv)
  12. {
  13.     int nFd = 0;
  14.     int ch = 0;
  15.     int cmd = 0;
  16.     unsigned long arg = 0;
  17.     
  18.     printf("[User] open pwm, it will request irq.\n");
  19.     nFd = open("/dev/PWM_LED", O_RDWR);
  20.     if(nFd < 0)
  21.     {
  22.         printf("[User] Can't open %s\n", "/dev/PWM_LED");
  23.         return -1;
  24.     }

  25. ioctl:    
  26.     printf("[User] Ready for ioctl.\n");
  27.     while((ch = getchar()) != '\n');
  28.     ioctl(nFd, cmd);
  29.     printf("[User] ioctl%ld cmd:%d arg:%ld.\n", arg, cmd, arg);
  30.     arg++;
  31.     while((ch = getchar()) != '\n');
  32.     cmd = !(cmd);//令cmd反转,可以实现控制定时器开关的效果,真棒!
  33.     goto ioctl;    
  34.     
  35.     close(nFd);    
  36. }

测试程序的编译,咱就不说了吧。

 

废话不说,上板测试:

[$] 第一步,加载模块:


点击(此处)折叠或打开

  1. [root@EmbedSky /]# insmod pwm_led.ko
  2. [Kernel] led init start.
  3. [Kernel] led init done, all should be off.
  4. [Kernel] request memory for pwm_cdev now.
  5. [Kernel] request dev number now.
  6. [Kernel] [Device:PWM_LED][major:252][minor:0].
  7. [Kernel] add operation interfaces to device.
  8. [Kernel] auto create device node.
  9. [Kernel] succedd to insert PWM_LED module.

[$] 第二步:运行测试程序:

首先修改权限为777:

chmod 777 test_pwm

然后运行之:


点击(此处)折叠或打开

  1. [root@EmbedSky /]# ./test_pwm
  2. [User] open pwm, it will request irq.
  3. [Kernel] try pwm_irq_open, we'll request irq.
  4. [Kernel] irq_nb[26],irq_flag[2],irq_name[PWM_LED].
  5. [Kernel] request irq and returns 0.
  6. [Kernel] pwm_init.
  7. pclk = 50000000
  8. tcntb0=20003
  9. [User] Ready for ioctl.
  10. |    //这里在等待用户输入,以进行下一步操作,当输入回车之后,定时器开启
  11. [Kernel] do operations with pwm_led_ioctl.
  12. [Kernel] pwm start now.
  13. [User] ioctl0 cmd:0 arg:0.
  14. [Kernel] led1 on. <6>[IRQ] counts 1.    //进入irq中断,开始中断次数计数
  15. [Kenerl] led2 on. <6>[IRQ] counts 2.
  16. [Kernel] led3 on. <6>[IRQ] counts 3.
  17. [Kernel] led4 on. <6>[IRQ] counts 4.
  18. [Kernel] led1 on. <6>[IRQ] counts 5.
  19. [Kenerl] led2 on. <6>[IRQ] counts 6.
  20. [Kernel] led3 on. <6>[IRQ] counts 7.
  21. [Kernel] led4 on. <6>[IRQ] counts 8.
  22. [Kernel] led1 on. <6>[IRQ] counts 9.
  23. [Kenerl] led2 on. <6>[IRQ] counts 10.
  24.     //再按回车,等待用户输入,这时pwm并没有暂停,因此仍然会继续输出
  25. [User] Ready for ioctl.
  26. [Kernel] led1 on. <6>[IRQ] counts 61.
  27. [Kenerl] led2 on. <6>[IRQ] counts 62.
  28. [Kernel] led3 on. <6>[IRQ] counts 63.
  29. [Kernel] led4 on. <6>[IRQ] counts 64.
  30. [Kernel] led1 on. <6>[IRQ] counts 65.
  31. [Kenerl] led2 on. <6>[IRQ] counts 66.
  32. [Kernel] led3 on. <6>[IRQ] counts 67.
  33.     //再按一次,pwm暂停了,led停止,irq计数也停止了
  34. [Kernel] do operations with pwm_led_ioctl.
  35. [Kernel] pwm stop
  36. [User] ioctl1 cmd:1 arg:1.
  37. //多按两次,又开始运行了
  38. [User] Ready for ioctl.

  39. [Kernel] do operations with pwm_led_ioctl.
  40. [Kernel] pwm start now.
  41. [User] ioctl2 cmd:0 arg:2.
  42. [Kernel] led4 on. <6>[IRQ] counts 68.
  43. [Kernel] led1 on. <6>[IRQ] counts 69.
  44. [Kenerl] led2 on. <6>[IRQ] counts 70.
  45. [Kernel] led3 on. <6>[IRQ] counts 71.
  46. [Kernel] led4 on. <6>[IRQ] counts 72.
  47. [Kernel] led1 on. <6>[IRQ] counts 73.
  48.     //发送终止进程信号,调用close接口
  49. ^C[Kernel] pwm stop
  50. [Kernel] free irq done.
  51.     //进程退出了,可以看到流水灯也随之停止,说明测试成功

[6] 总结陈词

       《基于TQ2440的led字符设备驱动》 描述了字符设备驱动编写的整体框架,其中包括一些常用接口的实现,如:open / close / read / write / ioctl ,本文在其基础上增加了定时器0中断,相比内容的丰富度,远不如上一篇,但是在笔者写代码的过程中,也掌握了很多技巧,比如ioremap的使用,goto用来调试也是不错的。所以,套用一句许多高大上的程序猿经常挂在嘴边的一句话(类似的话):代码究竟是人写出来的,难的并非代码本身,而是你能否打开自己的思想!



<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>
阅读(467) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值