1、linux驱动程序设计方案实例封面作者:PanHongliang仅供个人学习AT91SAM9G20驱动程序设计开发环境:Vmware + ubuntu10.04硬件平台:AT91SAM9G20Linux版本:linux2.6.27一:led驱动说明:因为设计的开发板上没有led灯,便通过PC0来演示,通过示波器来观察引脚端的电平变化。1.驱动程序:my_led.c#include #include #include #include #include #include #include #define MY_LED_MAJOR 250 /定义主设备号#define LED_ON 0#defin
2、e LED_OFF 1struct global_dev struct cdev cdev。 /定义设备结构体struct global_dev *global_devp。 /定义一个指向设备结构体的指针static int my_led_open(struct inode *inode, struct file *filp) filp-private_data = global_devp。 return 0。static int my_led_release(struct inode *inode, struct file *file) return 0。static int my_led_
3、ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long data) switch(cmd) case LED_ON: at91_set_gpio_value(AT91_PIN_PC0, 0)。 /将PC0引脚置低 break。 case LED_OFF: at91_set_gpio_value(AT91_PIN_PC0, 1)。 /将PC1引脚置高 break。 default: printk(no valid cmd input!n)。 break。 return 0。struct file_
4、operations my_led_ctl_ops = .owner = THIS_MODULE, .open = my_led_open, .release = my_led_release, .ioctl = my_led_ioctl,。/*初始化设备结构体*/static void my_led_setup(struct global_dev *dev, int index) int err。 int devno = MKDEV(MY_LED_MAJOR, index)。 cdev_init(&dev-cdev, &my_led_ctl_ops)。 dev-cdev.owner = TH
5、IS_MODULE。 dev-cdev.ops = &my_led_ctl_ops。 err = cdev_add(&dev-cdev, devno, 1)。 if(err) printk(add my led setup failed!n)。static int my_led_init(void) int ret。 dev_t devno = MKDEV(MY_LED_MAJOR, 0)。 /创建设备号 printk(my first driver-led!n)。 at91_set_GPIO_periph(AT91_PIN_PC0, 1)。 at91_set_gpio_output(AT91
6、_PIN_PC0, 1)。 /对PC0引脚的初始化 ret = register_chrdev_region(devno, 1, my_led)。 /申请设备号 if( ret cdev)。 /删除设备 kfree(global_devp)。 /释放内存 unregister_chrdev_region(MKDEV(MY_LED_MAJOR, 0), 1)。 /释放设备号MODULE_LICENSE(MYGPL)。MODULE_AUTHOR(FANY)。module_init(my_led_init)。 /注册设备module_exit(my_led_cleanup)。 /卸载设备2:如何将驱
7、动驱动程序编译成模块在drivers目录下新建led目录,并在该目录下添加Kconfig,Makefile文件。Kconfig:Menu My driver supportConfig Trisate led driver!Help Led driverEndmenu:Makefile:Obj-$(CONFIG_MY_LED) += my_led.o修改linux/drivers目录下的Kconfig,Makefile文件Kconfig:Source drivers/led/KconfigMakefile:Obj-y += my_led/修改体系结构目录arch/arm目录下的Kconfig文
8、件,否则在配置菜单中将无法看到led的配置选项。(如果是在drivers目录下新建一文件夹,并在其中添加驱动程序,必须相应的体系结构目录下添加配置选项)。Kconfig:Source driver/led/Kconfig3.测试程序:my_led_test.c#include #include #include #include #include #define DEVICE_NAME /dev/my_led#define LED_ON 0#define LED_OFF 1int main(void) int fd。 int ret。 int i。 printf(my_led_driver t
9、est!n)。 fd = open(DEVICE_NAME, O_RDONLY)。 if(fd = -1) printf(open device %s error!n, DEVICE_NAME)。 for(i = 0。 i 50。 i+) ioctl(fd, LED_OFF)。 sleep(1)。 ioctl(fd, LED_ON)。 sleep(1)。 ret = close(fd)。 printf(ret = %dn, ret)。 printf(close my_led_driver!n)。 return 0。将测试程序编译成目标平台的可执行文件,并下载到开发板GCC=/home/zzq/
10、9G20/arm-2007q1/bin/arm-none-linux-gnueabi-gcc #交叉编译器的路径My_led_test:my_led_test.c $(GCC) -o my_led_test my_led_test.cclean: rm -f my_led_test学习总结:熟悉驱动程序的架构,如何将驱动程序添加到内核即如何写测试程序。二:按键驱动设计1.硬件部分:PC4接按键。2.驱动程序:#include #include #include #include #include #include #include #include #include #include #inc
11、lude #include #include #include #include #include #include #include #define BUTTON_MAJOR 245#define DEVICE_NAME /dev/buttonstatic volatile int ev_press = 0。static struct cdev button_cdev。static void button_do_tasklet(unsigned long n)。DECLARE_TASKLET(button_tasklet, button_do_tasklet, 0)。/定义tasklet并与
12、处理函数关联起来static DECLARE_WAIT_QUEUE_HEAD(button_waitq)。/静态的初始化一个等待队列struct button_irq_desc int irq。 int irq_type。 int pin。 int number。 char *name。static struct button_irq_desc button_irq1 = AT91_PIN_PB22, AT91_AIC_SRCTYPE_LOW, AT91_PIN_PB22, 0, KEY0 。static int key_values1=0。/中断处理底半部static void button
13、_do_tasklet(unsigned long n) wake_up_interruptible(&button_waitq)。 /唤醒队列 printk(button press!n)。 /中断处理顶半部static irqreturn_t button_interrupt(int irq, void *dev_id, struct pt_regs *regs) int up。 static int press_down。 up = gpio_get_value(button_irq0.pin)。 printk(irqn)。 /*按键消抖*/ if(up) press_down = 1。
14、 /当按键没有按下,置标志位为1. if(!up & (press_down = 1) press_down = 0。 /当按键按下,置标志位为0. ev_press = 1。 at91_set_gpio_value(button_irq0.pin, 1)。 key_valuesbutton_irq0.number = !up。tasklet_schedule(&button_tasklet)。 return IRQ_RETVAL(IRQ_HANDLED)。static int button_open(struct inode *inode, struct file *filp) return
15、 0。static int button_release(struct inode *inode, struct file *filp) return 0。static int button_read(struct file *filp, char _user *buff, size_t count, loff_t *offp) int ret。 if(!ev_press) /当按键没有按下时,读进程挂起,知道按键按下。 wait_event_interruptible(button_waitq, ev_press)。 ev_press = 0。 ret = copy_to_user(buff
16、, (const void *)key_values, min(sizeof(key_values), count)。 memset(void _user *)key_values, 0, sizeof(key_values)。 return ret ? -EFAULT:min(sizeof(key_values), count)。 static struct file_operations button_fops = .owner = THIS_MODULE, .open = button_open, .release = button_release, .read = button_rea
17、d,。static int irq_init(void) int err。 at91_set_gpio_input(button_irq0.pin, 1)。 at91_set_deglitch(button_irq0.pin, 1)。/将PC0设置为中断功能 set_irq_type(button_irq0.irq, button_irq0.irq_type)。/设置中断类型 at91_set_gpio_value(button_irq0.pin, 1)。 err = request_irq( button_irq0.irq, button_interrupt, IRQF_DISABLED,
18、button_irq0.name, (void *)&button_irq0)。/申请中断 if ( err ) disable_irq(button_irq0.irq)。 free_irq(button_irq0.irq, (void *)&button_irq0)。 return -EBUSY。 return 0。static int _init button_init(void) int ret, err。 ret = register_chrdev_region(MKDEV(BUTTON_MAJOR, 0), 1, DEVICE_NAME)。 if(ret 0) printk(butt
19、on init failed with %dn, ret)。 return ret。 cdev_init(&button_cdev, &button_fops)。 button_cdev.owner = THIS_MODULE。 button_cdev.ops = &button_fops。 err = cdev_add(&button_cdev, MKDEV(BUTTON_MAJOR, 0), 1)。 if( err0 ) printk(key add failedn)。 return err。 irq_init()。 printk(key driver add success!n)。 re
20、turn 0。static void _exit button_exit(void) cdev_del(&button_cdev)。 unregister_chrdev_region(MKDEV(BUTTON_MAJOR, 0), 1)。 disable_irq(button_irq0.irq)。 free_irq(button_irq0.irq, (void *)&button_irq0)。 printk(unregister key driver!n)。module_init(button_init)。module_exit(button_exit)。MODULE_AUTHOR(fany)
21、。MODULE_DESCRIPTION(Atmel9g20 key Driver)。MODULE_LICENSE(GPL)。3.测试程序:#include #include #include #include #include #include #define DEVICE_NAME /dev/buttonint main(void) int fd,i。 int ret。 int key_value1。 printf(key test!n)。 fd = open(DEVICE_NAME, O_RDWR)。 if(fd 0) printf(open device %s error!n, DEVI
22、CE_NAME)。 else printf(open device success!n)。 while(1) ret = read(fd, key_value, 1)。 if( !ret ) printf(button not press!n)。 else printf(button press!n)。 printf(key_value %dn, key_value)。 close(fd)。 printf(close key driver!n)。 return 0。4.学习总结:在linux设备驱动程序中,中断处理程序通常分为两部分:上半部和下半部。上半部处理比较紧急的的硬件操作,比如简单的读
23、取寄存器中的状态并清除中断标志后,进行登记中断的工作。剩下的工作就由下半部来实现。对阻塞与非阻塞进程的理解,阻塞:在执行设备操作时,若不能获取设备资源则挂起,直到满足可操作的条件后再进行操作。非阻塞操作:在执行设备操作时,若不能获取设备资源则立即返回。三:总线驱动AT91SAM9G20存储器映射图(截取部分),片选4接外设,利用总线对外设进行访问。但是在linux驱动,不能对物理地址进行操作,可通过内存访问的机制实现对物理地址的访问。将一段物理地址空间映射到虚拟地址空间中,然后对虚拟地址的操作即是对物理地址的操作。3.1:总线驱动#include #include #include #incl
24、ude #include #include #include #include #include #include #include #include #include #include #include #include atmel9g20_liu.hstruct gr_liu_info void _iomem *virtbase。 void _iomem *regbase。 struct resource *res。 u32 flags。static struct gr_liu_info liu_info。static struct cdev atmel9g20_liu_cdev。unsi
25、gned short liu_read(unsigned addr) addr &= ATMEL9G20_LIU_MASK。 addr = addr 1。 addr += (unsigned long)liu_info.regbase。 printk(read the virtual addr is 0x%xn, addr)。 return readw(addr) & 0xff。 /读IO内存。EXPORT_SYMBOL(liu_read)。int liu_write(unsigned addr, unsigned val) addr &= ATMEL9G20_LIU_MASK。 addr =
26、 addr 1。 addr += (unsigned long)liu_info.regbase。 printk(write the virtual addr is 0x%xn, addr)。 writew(val&0xff, addr)。 /写IO内存 return 0。EXPORT_SYMBOL(liu_write)。static int atmel9g20_liu_open(struct inode *inode, struct file *filp) return 0。static int atmel9g20_liu_release(struct inode *inode, struct file *filp) return 0。
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1