嵌入式Linux字符设备LED驱动-基于树莓派

2019-07-13 02:39发布

个人原创,转载请注明原文出处
http://blog.csdn.net/u011897065/article/details/41307339
参考文章: http://blog.csdn.net/embbnux/article/details/17712547
首先先说下大致步骤: 1)、电路连接 2)、内核字符设备驱动编写 3)、Makefile和Kconfig添加 4)、编译内核 5)、复制内核到SD卡 6)、树莓派上编写相应的C程序调用系统API驱动LED
一、电路连接
首先先看树莓派B+GPIO口接口图

首先看GPIO_LED连接电路:我的Led分别连接到GPIO4,GPIO8,GPIO9,GPIO10,GPIO11,GPIO23,GPIO24,GPIO18


实物图: 、Rpi字符设备驱动编写 在树莓派Linux   /drivers/char路径下新建Led.c,编写程序。 /*
-------------------------------------------------------------------
*File name  : Led.c
*Author     : SSC_学行者
*Discription: 树莓派Led字符设备驱动文件。    
-------------------------------------------------------------------
*/
#include     
#include  
#include    
#include          
#include  
#include     
#include      
#include     
#include     
#include     
#include  
#include    
#include


#define DEVICE_NAME "Pi_Led"  
#define DRIVER_NAME "pi_led"  
 
//class声明内核模块驱动信息,是UDEV能够自动生成/dev下相应文件  
static dev_t pi_led_devno; //设备号  
static struct class *pi_led_class;  
static struct cdev pi_led_class_dev;  
 
struct gpio_chip *gpiochip; 

//Led  GPIO引脚定义 
#define led_1 (4u )                 
#define led_2 (8u )
#define led_3 (9u )
#define led_4 (10u)
#define led_5 (11u)
#define led_6 (23u)
#define led_7 (24u)
#define led_8 (18u)

static unsigned char pin_table[8]={led_1,led_2,led_3,led_4,led_5,led_6,led_7,led_8};

 
//这部分函数为内核调用后open的设备IO操作,和裸板程序一样  
int open_flag=0;  
static int pi_led_open(struct inode *inode, struct file *filp)    
{     
    printk("Open led ing! ");    
    if(open_flag ==0){  
       open_flag =1;  
       printk("Open led success! ");  
       return 0;  
    }  
    else{  
       printk("Led has opened! ");       
    }  
    return 0;    
}   
//这部分函数为内核调用后ioctl的设备IO操作,和裸板程序一样  
static long pi_led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)   
{    
    unsigned char i ;
    unsigned char data;
    for(i = 0 ; i < 8 ; i ++ ){
      data = (cmd&0x01);
      if(i>7)
        i = 7 ;
      gpiochip->set(gpiochip, pin_table[i], data);
      cmd >>= 1 ;
    }
    return 0;  
}   
 
static int pi_led_release(struct inode *inode,struct file *file){  
     printk("Led has release! ");  
     return 0;  
}  
 
//file_operations使系统的open,ioctl等函数指针指向我们所写的led_open等函数,  
//这样系统才能够调用  
static struct file_operations pi_led_dev_fops = {    
   .owner          =THIS_MODULE,    
   .open       =pi_led_open,   
   .unlocked_ioctl = pi_led_ioctl,   
   .release       = pi_led_release,  
};   
static int is_right_chip(struct gpio_chip *chip, void *data)  
{  
 
    if (strcmp(data, chip->label) == 0)  
        return 1;  
    return 0;  
}  
 
//内核加载后的初始化函数.  
static int __init pi_led_init(void)  
{  
   struct device *dev;  
   int major; //自动分配主设备号  
   major = alloc_chrdev_region(&pi_led_devno,0,1,DRIVER_NAME);  
   //register_chrdev 注册字符设备使系统知道有LED这个模块在.  
     
   cdev_init(&pi_led_class_dev, &pi_led_dev_fops);  
   major = cdev_add(&pi_led_class_dev,pi_led_devno,1);  
   //注册class  
   pi_led_class = class_create(THIS_MODULE,DRIVER_NAME);  
     
   dev = device_create(pi_led_class ,NULL,pi_led_devno,NULL,DRIVER_NAME);  
     
   gpiochip = gpiochip_find("bcm2708_gpio", is_right_chip);  
   gpiochip->direction_output(gpiochip, led_1, 1);  
   gpiochip->direction_output(gpiochip, led_2, 1);
   gpiochip->direction_output(gpiochip, led_3, 1);
   gpiochip->direction_output(gpiochip, led_4, 1);
   gpiochip->direction_output(gpiochip, led_5, 1);
   gpiochip->direction_output(gpiochip, led_6, 1);
   gpiochip->direction_output(gpiochip, led_7, 1);
   gpiochip->direction_output(gpiochip, led_8, 1);
   gpiochip->set(gpiochip, led_1, 1);  
   gpiochip->set(gpiochip, led_2, 1);
   gpiochip->set(gpiochip, led_3, 1);
   gpiochip->set(gpiochip, led_4, 1);
   gpiochip->set(gpiochip, led_5, 1);
   gpiochip->set(gpiochip, led_6, 1);
   gpiochip->set(gpiochip, led_7, 1);
   gpiochip->set(gpiochip, led_8, 1);
   printk("pi led init ok! ");  
   return 0;  
}  
//内核卸载后的销毁函数.  
void pi_led_exit(void)  
{  
   gpio_free(led_1);  
   gpio_free(led_2);  
   gpio_free(led_3);  
   gpio_free(led_4);  
   gpio_free(led_5);  
   gpio_free(led_6);
   gpio_free(led_7);
   gpio_free(led_8);
   device_destroy(pi_led_class,pi_led_devno);  
   class_destroy(pi_led_class);  
   cdev_del(&pi_led_class_dev);  
   unregister_chrdev_region(pi_led_devno, 1);  
   printk("pi led exit ok! ");  
     
}  
 
module_init(pi_led_init);  
module_exit(pi_led_exit);  
 
MODULE_DESCRIPTION("Rasp Led Driver");  
MODULE_AUTHOR("SSC_学行者");  
MODULE_LICENSE("GPL");
三、Maklefile和Kconfig内容添加 在 /drivers/char目录下的Makefile中添加一下语句:
obj-$(CONFIG_FB_LED)          += Led.o
在相同目录下的Kconfig中添加一下语句:
config FB_LED  
    tristate "LED connected to Raspberry Pi GPIO support"  
    depends on FB  
    select FB_SYS_FILLRECT  
    select FB_SYS_COPYAREA  
    select FB_SYS_IMAGEBLIT  
    select FB_SYS_FOPS  
    select FB_DEFERRED_IO  
    help  
      This driver is for Leds.
      SSC_学行者. 
四、内核编译 使用menuconfig来编译 在Linux文件夹下: 选择编译选项:
make ARCH=arm CROSS_COMPILE=../RpiTools/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin/arm-bcm2708hardfp-linux-gnueabi- menuconfig 开始编译: make ARCH=arm CROSS_COMPILE=../RpiTools/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin/arm-bcm2708hardfp-linux-gnueabi- -j3 转换内核格式: cd ../RpiTools/mkimage/
./imagetool-uncompressed.py ../../RpiLinux/arch/arm/boot/zImage

拷贝内核到SD卡上 首先先备份SD卡中的kernel.img(也可以直接删除),将mkimage目录下的内核(kernel.img)拷贝到SD中。 在树莓派上编写C程序测试
 #include
 #include
 #include
 #include
 #include
//左移函数
unsigned char corr(unsigned char data) {     unsigned char _data;    _data = data ;    _data <<= 1;    data &=0x80;    if(data)      _data += 1 ;    return _data ;
}

 int main(void)
 {
    int data=0xfe;
    int led_no;
    int fd;
    int i;

    fd = open("/dev/pi_led", 0);
    if (fd < 0) {
       fd = open("/dev/pi_led", 0);
    }
    if (fd < 0) {
      perror("open device led");
      exit(1);
    }
  for(i=0;i<=7;i++){
    ioctl(fd,data, led_no);
     corr(data);
    sleep(1);
  }
  close(fd);
  return 0;
}