led驱动实例

编程入门 行业动态 更新时间:2024-10-08 04:28:33

led驱动<a href=https://www.elefans.com/category/jswz/34/1771375.html style=实例"/>

led驱动实例

前面分析了fl2440的led驱动代码,现在就基于之前的LED驱动代码完成应用程序(跑马灯)的实现,并且在linux系统下手动创建设备节点,运行跑马灯程序。


1.跑马灯程序

首先来看 用户空间下跑马灯程序的实现

#include <stdio.h>  
#include <sys/ioctl.h>  
#include <sys/stat.h>  
#include <unistd.h>  
#include <stdarg.h>  
#include <errno.h>  
#include <sys/types.h>  
#include <fcntl.h>  
#include <string.h>  #define LED_NUM         4  
#define DEVNAME_LEN     10  #define PLATDRV_MAGIC             0x60  
#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18)  
#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19)  int main(int argc, char **argv)  
{  int fd[LED_NUM];  int     i;  int     j;  char devname[DEVNAME_LEN] = {0};  for(i = 0; i < LED_NUM; i++)  {  snprintf(devname, sizeof(devname), "/dev/led%d",i);  fd[i] = open(devname, O_RDWR,755);  if(fd[i] < 0)  {  printf("Can not open %s: %s", devname, strerror(errno));  for(j = 0; j < i; j++)  //如果某一设备不能打开,则要关掉之前已经打开的设备{  if(fd[j] > 0)  close(fd[j]);  }  return -1;  }  }  while(1)  {  for(i = 0; i < LED_NUM; i++)  {  ioctl(fd[i], LED_ON);  //打开led灯sleep(1);  //延时1sioctl(fd[i], LED_OFF);//关闭led灯  }  }  for(i = 0; i < LED_NUM; i++)  {  close(fd[i]);  //关掉设备文件}  return 0;  
}  

在 linux 下编写完跑马灯程序之后,我们必须使用交叉编译器对该程序进行编译。

[lwn@localhost s3c-led]$ /opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-gcc ledtest.c 
/opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-gcc是我交叉编译器的目录下的交叉编译工具, 编译之后生成a.out可执行文件


2.led驱动程序:

/**********************************************************************************      Copyright:  (C) 2011 Guo Wenxue<guowenxue@gmail>  *                  All rights reserved.**       Filename:  plat_button.c*    Description:  This is the common button driver runs on S3C2440*                 *        Version:  1.0.0(4/27/2017~)*         Author:  Li Wanneng <liwjng@gmail>*      ChangeLog:  1, Release initial version on "4/27/2017 11:39:10 AM"*                 ********************************************************************************/#include <linux/module.h>   /* Every Linux kernel module must include this head */
#include <linux/init.h>     /* Every Linux kernel module must include this head */
#include <linux/kernel.h>   /* printk() */
#include <linux/fs.h>       /* struct fops */
#include <linux/errno.h>    /* error codes */
#include <linux/cdev.h>     /* cdev_alloc()  */
#include <asm/io.h>         /* ioremap()  */
#include <linux/ioport.h>   /* request_mem_region() */#include <asm/ioctl.h>      /* Linux kernel space head file for macro _IO() to generate ioctl command  */
#ifndef __KERNEL__
#include <sys/ioctl.h>      /* User space head file for macro _IO() to generate ioctl command */
#endif
//#include <linux/printk.h> /* Define log level KERN_DEBUG, no need include here */#define DRV_AUTHOR                "Li Wanneng <liwjng@gmail>"
#define DRV_DESC                  "S3C24XX LED driver"#define DEV_NAME                  "led"
#define LED_NUM                   4/* Set the LED dev major number */
//#define LED_MAJOR                 79
#ifndef LED_MAJOR
#define LED_MAJOR                 0
#endif#define DRV_MAJOR_VER             1
#define DRV_MINOR_VER             0
#define DRV_REVER_VER             0#define DISABLE                   0
#define ENABLE                    1#define GPIO_INPUT                0x00
#define GPIO_OUTPUT               0x01/*防止ioctl传入的命令与其他参数冲突*/
#define PLATDRV_MAGIC             0x60 //魔术字
#define LED_OFF                   _IO (PLATDRV_MAGIC, 0x18)
#define LED_ON                    _IO (PLATDRV_MAGIC, 0x19)/*GPB有3个寄存器,GPBCON, GPBDATA和GPBUP寄存器,下面是其对应的物理地址*/
#define S3C_GPB_BASE              0x56000010//s3c2440GPB寄存器的基地址
#define GPBCON_OFFSET             0         //s3c2440GPBCON寄存器偏移地址0x56000010
#define GPBDAT_OFFSET             4         //s3c2440GPBDATA寄存器偏移地址 0x56000014
#define GPBUP_OFFSET              8         //s3c2440GPBUP寄存器的偏移地址 0x56000018
#define S3C_GPB_LEN               0x10        /* 0x56000010~0x56000020  */int led[LED_NUM] = {5,6,8,10};  /* Four LEDs use GPB5,GPB6,GPB8,GPB10 */static void __iomem *s3c_gpb_membase; //__iomem表示指针s3c_gpb_membase是指向一个I/O的内存空间// #define _raw_readl(reg) *(volatile unsigned int *)(reg)
#define s3c_gpio_write(val, reg) __raw_writel((val), (reg)+s3c_gpb_membase) //虚拟地址与物理地址的映射(写)
#define s3c_gpio_read(reg)       __raw_readl((reg)+s3c_gpb_membase)    //虚拟地址与物理地址的映射(读)int dev_count = ARRAY_SIZE(led); //设备个数
int dev_major = LED_MAJOR; //定义主设备号
int dev_minor = 0; //定义次设备号
int debug = DISABLE; //debug = 0,上面宏定义DISABLE为0/*struct cdev 是表示字符设备的内核内部结构,当innod指向一个字符设备文件时,该字段包含了指向struct cdev 结构的指针*/
static struct cdev      *led_cdev;  //定义cdev结构体指针,led内核结构static int s3c_hw_init(void)//硬件初始化,设置相应GPIO口为output模式
{int          i;volatile unsigned long  gpb_con, gpb_dat, gpb_up;/*为s3c2440_led申请一片物理地址,其起始地址为S3C_GPB_BASE,大小为s3c_GPB_LEN;如果申请失败,返回-EBUSY */if(!request_mem_region(S3C_GPB_BASE, S3C_GPB_LEN, "s3c2440 led")){return -EBUSY;}/*如果物理地址申请成功,将其映射到相应的虚拟地址。以后操作寄存器一律操作虚拟地址即s3c_gpb_membase的地址*/if( !(s3c_gpb_membase=ioremap(S3C_GPB_BASE, S3C_GPB_LEN)) )//ioremap函数作用为将物理地址映射到虚拟地址{release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);//如果映射失败,一定要将物理地址释放return -ENOMEM;//申请失败返回错误信息-ENOMEM}/*1,设置相应gpio端口为输出模式2,disable上拉寄存器哦3,设置gpio数据寄存器为1,即使相应GPIO端口输出高电平,默认关闭LED灯*/for(i=0; i<dev_count; i++)//dev_count为设备数量 {/* Set GPBCON register, set correspond GPIO port as input or output mode  */gpb_con = s3c_gpio_read(GPBCON_OFFSET);//获取GPBCON寄存器的值gpb_con &= ~(0x3<<(2*led[i]));   /* Clear the currespond LED GPIO configure register */gpb_con |= GPIO_OUTPUT<<(2*led[i]); /* Set the currespond LED GPIO as output mode */s3c_gpio_write(gpb_con, GPBCON_OFFSET);//将gpb_con的值写到寄存器GPBCON寄存器/* Set GPBUP register, set correspond GPIO port pull up resister as enable or disable  */gpb_up = s3c_gpio_read(GPBUP_OFFSET);gpb_up |= (0x1<<led[i]);  /* Disable pull up resister */s3c_gpio_write(gpb_up, GPBUP_OFFSET);/* Set GPBDAT register, set correspond GPIO port power level as high level or low level */gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);//gpb_dat &= ~(0x1<<led[i]); /* This port set to low level, then turn LED on */gpb_dat |= (0x1<<led[i]);  /* This port set to high level, then turn LED off */s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);//1:46}return 0;
}static void turn_led(int which, unsigned int cmd)//操作led
{volatile unsigned long  gpb_dat;gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);if(LED_ON == cmd){gpb_dat &= ~(0x1<<led[which]); /*  Turn LED On */}else if(LED_OFF == cmd){gpb_dat |= (0x1<<led[which]);  /*  Turn LED off */}s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);
}static void s3c_hw_term(void)//清除led操作
{int                     i;volatile unsigned long  gpb_dat;for(i=0; i<dev_count; i++){gpb_dat = s3c_gpio_read(GPBDAT_OFFSET);gpb_dat |= (0x1<<led[i]);  /* Turn LED off */s3c_gpio_write(gpb_dat, GPBDAT_OFFSET);}release_mem_region(S3C_GPB_BASE, S3C_GPB_LEN);//释放已申请的物理内存iounmap(s3c_gpb_membase);//取消物理地址到虚拟地址的映射关系
}static int led_open(struct inode *inode, struct file *file)//"打开led"函数
{int minor = iminor(inode);//获取次设备号file->private_data = (void *)minor;//在file结构体中,private_data是一个空类型指针printk(KERN_DEBUG "/dev/led%d opened.\n", minor);return 0;
}static int led_release(struct inode *inode, struct file *file)//"关闭led"函数
{printk(KERN_DEBUG "/dev/led%d closed.\n", iminor(inode));return 0;
}static void print_help(void)//打印帮助信息
{printk("Follow is the ioctl() commands for %s driver:\n", DEV_NAME);//printk("Enable Driver debug command: %u\n", SET_DRV_DEBUG);printk("Turn LED on command  : %u\n", LED_ON);printk("Turn LED off command : %u\n", LED_OFF);return;
}//ioctl(fd, LED_ON, 0)
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg)//ioctl()函数会用到,其中参数为用户程序空间传过来的参数
{int which = (int)file->private_data;//获取次设备号switch (cmd)//判断并执行相应的命令{case LED_ON:turn_led(which, LED_ON);break;case LED_OFF:turn_led(which, LED_OFF);break;default:printk(KERN_ERR "%s driver don't support ioctl command=%d\n", DEV_NAME, cmd);print_help();break;}return 0;
}/*面向对象的思想*/
static struct file_operations led_fops = //定义fop结构体,针对该驱动提供的系统调用和操作
{.owner = THIS_MODULE,//.owener的值一般为THIS_MODULE.open = led_open,//open为指向led_open()的函数指针,调用open()函数时会用到.release = led_release,.unlocked_ioctl = led_ioctl,
};static int __init s3c_led_init(void)//led初始化函数
{int                    result;dev_t                  devno;/*如果硬件初始化失败,打印如下信息并返回-ENODEV*/if( 0 != s3c_hw_init() ){printk(KERN_ERR "s3c2440 LED hardware initialize failure.\n");return -ENODEV;}/* 如果硬件初始化成功,分配主次设备号 */if (0 != dev_major) /* 如果已经有了设备号 静态获取主次设备号 */{devno = MKDEV(dev_major, 0);result = register_chrdev_region (devno, dev_count, DEV_NAME);}else//动态获取主次设备号{result = alloc_chrdev_region(&devno, dev_minor, dev_count, DEV_NAME);dev_major = MAJOR(devno);}/*  如果设备号申请失败,打印如下信息并返回-ENODEV */if (result < 0){printk(KERN_ERR "S3C %s driver can't use major %d\n", DEV_NAME, dev_major);return -ENODEV;} printk(KERN_DEBUG "S3C %s driver use major %d\n", DEV_NAME, dev_major);if(NULL == (led_cdev=cdev_alloc()) )//分配cdev结构体,如果分配失败,打印如下信息	{printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);unregister_chrdev_region(devno, dev_count);return -ENOMEM;}led_cdev->owner = THIS_MODULE;//如果分配成功,将cdev结构体添加进内核 cdev_init(led_cdev, &led_fops);//连接led_cdev和led_fops/*只有当cdev_add()函数执行之后,才能在 /dev/目录下看到设备节点*/result = cdev_add(led_cdev, devno, dev_count);//将其添加进内核,字符设备注册的最后一步if (0 != result){   printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result); goto ERROR;}printk(KERN_ERR "S3C %s driver[major=%d] version %d.%d.%d installed successfully!\n", DEV_NAME, dev_major, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);return 0;ERROR:printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME);cdev_del(led_cdev);unregister_chrdev_region(devno, dev_count);return result;
}static void __exit s3c_led_exit(void)//led卸载函数
{dev_t devno = MKDEV(dev_major, dev_minor);s3c_hw_term();//释放。。。cdev_del(led_cdev);//删掉cdevunregister_chrdev_region(devno, dev_count);//释放设备节点号printk(KERN_ERR "S3C %s driver version %d.%d.%d removed!\n", DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);return ;
}/* These two functions defined in <linux/init.h> */
module_init(s3c_led_init);//Led驱动首先应该找到module_init函数
module_exit(s3c_led_exit);module_param(debug, int, S_IRUGO);//使能驱动的debug功能,debug为全局变量
module_param(dev_major, int, S_IRUGO);//函数传参,insmod时通过参数动态修改主设备号MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");


3.Makefile

驱动程序通过Makefile编译,Makefile主要内容如下

[lwn@localhost s3c-led]vim MakefileMakefile                                                                                                                            1 LINUX_SRC?=../../kernel/linux-lwn-3.0.12 CROSS_COMPILE=/opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-3 4 obj-m := s3c_led.o5 6 modules:7     @make -C $(LINUX_SRC) M=`pwd` modules8     @make clean9 10 clean:11     rm -f  *.ko.* *.o *mod.c *.order *.symvers12     


编写完Makefile之后使用make命令生成 sec_led.ko二进制文件

[lwn@localhost s3c-led]$ ls
a.out  ledtest.c  Makefile  s3c_led.c  s3c_led.ko

4.开发板上面运行程序

在主机上面生成了a.out和s3c_led.ko之后,使用 tftp 工具将其下载到开发板。


现在开发板上只是有了s3c_led.ko文件,但是如果要使用驱动,还需要安装驱动,创建设备节点,运行应用程序。

使用insmod命令安装驱动

下面是在开发板上的操作


执行a.out之后在开发板上可以看到led已经跑起来了。到此,整个LED驱动和用户程序已经成功执行。



更多推荐

led驱动实例

本文发布于:2024-02-13 14:14:38,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1759203.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:实例   led

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!