完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
【EVB-335X-II试用体验】之基于系统GPIO模型的蜂鸣器驱动 在上一篇的试用报告中,采用将GPIO的相关寄存器通过ioremap重映射,在LINUX内核空间直接操作虚拟过后的寄存器地址,从而实现对GPIO的高低电平输出控制。 采用自己的标准,基于直接操作寄存器实现外围设备驱动的开发方法,虽然具有较好的灵活性,不必去熟悉Linux规定好的复杂的驱动程序框架,但是这也给我们的驱动程序可移植性带来了较大麻烦。 为了使驱动程序有一个统一的标准,Linux对常用的硬件设备驱动做了规范化要求,这即减轻了驱动开发人员工作量,同时也减少了驱动代码在不同的CPU上移植的难度。 这一篇试用报告将主要介绍采用Linux系统标准的gpio操作接口,来实现对蜂鸣器驱动程序的编写。 1. 蜂鸣器的硬件电路 蜂鸣器的控制引脚分配,如图所示: 蜂鸣器的控制电路如图所示: 从原理图可以看出,蜂鸣器采用GPIO3_4控制,由于GPIO本身提供的电流很小,无法直接驱动蜂鸣器这种相对来说电流需求量较大的外围设备,所以采用了一个NPN型的三级管对输出电流进行放大。这里我们给EVB_335X_II开发板设计者点个赞,图中的R144起到了蜂鸣器误动作的作用,通常GPIO作为输出,其没有上拉、下拉电阻,在没有确定输出电平的情况下,引脚的输出是不确定的,通过把R144电阻并接在三极管的基极和发射极,起到了蜂鸣器稳定输出的作用。 当GPIO3_4输出高电平时,蜂鸣器发出哔哔的声响,当GPIO3_4输出低电平时,蜂鸣器停止发声。 2. 驱动程序编写 2.1 系统GPIO控制接口介绍 EVB_335X_II开发板的板载BSP已经实现了GPIO的底层函数接口,可以直接调用gpio_request,gpio_free,gpio_get_value,gpio_set_value等函数,实现对GPIO接口的申请、释放、设置输出值和获取输入值。由于BSP实现了标准的GPIO接口函数,我们编写的上层驱动无需关注实现的细节,可以直接调用,这样也大大提高了我们开发简单字符设备驱动程序的效率。 使用gpio标准函数接口,我们需要知道当前使用板卡的GPIO编号规则。对于AM335X系列的板卡,均采用ti提供的BSP包,其将GPIO分为4个组,GPIO0~GPIO3,每个组32个引脚,引脚编号从GPIO0依次递增,引脚编号算法为: GPIO_PORT_NUM = GPIO_GROUP_NUM*32 +GPIO_SUB_NUM 其中GPIO_GROUP_NUM为组号(0,1,2,3),GPIO_SUB_NUM为GPIO在这一组中的序号(0,1,2,...,31)。对于GPIO3_4来说,其对应的GPIO号为:3*32 + 4 =100. 2.2 IOCTL幻数 由于蜂鸣器只有开和关,没有复杂的数字读取、写入,所有只需设计ioctl函数即可。 为了去表ioctl的命令,保证命令的重复性达到最低,LINUX采用幻数的命令定义方法。 本例程设计了一个独立H头文件,定义命令,源码入下: #ifndef _BUZZER_DRV_H #define _BUZZER_DRV_H #define BUZZER_IOC_MAGIC 'B' #define BUZZER_ON _IO(BUZZER_IOC_MAGIC, 1) #define BUZZER_OFF _IO(BUZZER_IOC_MAGIC, 0) #define BUZZER_IOCTL_MAXNR 2 #endif /*_BUZZER_DRV_H*/ 2.3 驱动程序头文件 #include #include #include #include #include #include #include #include #include #include #include "buzzer_drv.h" 2.4 全局变量 static int major; static int minor; struct cdev *buzzer; /* cdev 数据结构 */ static dev_t devno; /* 设备编号 */ static struct class *buzzer_class; #define DEVICE_NAME "buzzer" #define GPIO_BUZZER_PIN_NUM (3*32 + 4) /*gpio 3_4 */ 2.5 open与release函数 static int buzzer_open(struct inode *inode,struct file *file ) { try_module_get(THIS_MODULE); gpio_direction_output(GPIO_BUZZER_PIN_NUM,1); return0; } static int buzzer_release(struct inode*inode, struct file *file ) { module_put(THIS_MODULE); gpio_direction_output(GPIO_BUZZER_PIN_NUM,1); return0; } 2.6 ioctl函数 根据不同的内核版本选择ioctl函数定义类型 #if LINUX_VERSION_CODE >=KERNEL_VERSION(2,6,36) int buzzer_ioctl(struct file *filp,unsigned int cmd, unsigned long arg) #else static int buzzer_ioctl(struct inode*inode, struct file *file, unsigned int cmd, unsigned long arg) #endif { if(_IOC_TYPE(cmd) != BUZZER_IOC_MAGIC) { return-ENOTTY; } if(_IOC_NR(cmd) > BUZZER_IOCTL_MAXNR) { return-ENOTTY; } switch(cmd){ caseBUZZER_ON: gpio_set_value(GPIO_BUZZER_PIN_NUM,1); break; caseBUZZER_OFF: gpio_set_value(GPIO_BUZZER_PIN_NUM,0); break; default: break; } return0; } 2.7 file_operations结构体 struct file_operations buzzer_fops = { .owner= THIS_MODULE, .open= buzzer_open, .release= buzzer_release, #ifLINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) .unlocked_ioctl= buzzer_ioctl #else .ioctl= buzzer_ioctl #endif }; 2.8 模块初始化函数 static int __init buzzer_init(void) { intret; gpio_free(GPIO_BUZZER_PIN_NUM); if(gpio_request(GPIO_BUZZER_PIN_NUM, "buzzer_bee")) { printk("request%s gpio faile n", "led_bee"); return-1; } ret= alloc_chrdev_region(&devno, minor, 1, "buzzer"); /* 从系统获取主设备号 */ major= MAJOR(devno); if(ret < 0) { printk(KERN_ERR"cannot get major %d n", major); return-1; } buzzer= cdev_alloc(); /* 分配 buzzer 结构 */ if(buzzer != NULL) { cdev_init(buzzer,&buzzer_fops); /* 初始化 buzzer 结构 */ buzzer->owner= THIS_MODULE; if(cdev_add(buzzer, devno, 1) != 0) { /* 增加 buzzer 到系统中 */ printk(KERN_ERR"add cdev error!n"); gotoerror; } }else { printk(KERN_ERR"cdev_alloc error!n"); return-1; } buzzer_class= class_create(THIS_MODULE, "buzzer_class"); if(IS_ERR(buzzer_class)) { printk(KERN_INFO"create class errorn"); return-1; } device_create(buzzer_class,NULL, devno, NULL, "buzzer"); return0; error: unregister_chrdev_region(devno,1); /* 释放已经获得的设备号 */ returnret; } 2.9 模块卸载函数 static void __exit led_exit(void) { cdev_del(buzzer);/* 移除字符设备 */ unregister_chrdev_region(devno,1); /* 释放设备号 */ device_destroy(buzzer_class,devno); class_destroy(buzzer_class); } 2.10 其他 module_init(buzzer_init); module_exit(buzzer_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zxl_zxl"); 3. Makefile KERN_DIR = /share/linux/linux-3.14.43-evb335x-ii all: make-C $(KERN_DIR) M=`pwd` modules clean: make-C $(KERN_DIR) M=`pwd` modules clean rm-rf modules.order obj-m +=buzzer_drv.o 上面红色部分需要根据自己的内核路径设置。 4. 编译驱动 切换到驱动源码所在的目录,执行如下命令: sudo make ARCH=ARM CROSS_COMPILE=/交叉编译器路径/bin/arm-linux-gnueabihf- 编译结果如下图所示: sudo cp buzzer_drv.ko /nfsshare 5. 测试 5.1 测试程序 编写用户空间的测试程序,让蜂鸣器以周期为2秒的鸣叫,程序源码如下: #include #include #include #include #include #include #include "/buzzer_drv.h" #define DEV_NAME "/dev/buzzer" int main(int argc, char *argv[]) { inti; intfd = 0; fd= open (DEV_NAME, O_RDONLY); if(fd < 0) { perror("Open"DEV_NAME" Failed!n"); exit(1); } while(1){ ioctl(fd,BUZZER_ON); sleep(1); ioctl(fd,BUZZER_OFF); sleep(1); } close(fd); return0; } 5.2 编译测试程序 执行命令: sudo /交叉编译器路径/bin/arm-linux-gnueabihf-gcc -o buzzer_testbuzzer_test.c 编译结果如下: |
|
相关推荐
2 个讨论
|
|
只有小组成员才能发言,加入小组>>
【盈鹏飞RK3399安卓主板 XPC-3399Pro免费试用】+烧写出厂固件
10264 浏览 0 评论
【盈鹏飞EVB-T335开发板试用体验】debian系统烧写
3289 浏览 1 评论
【盈鹏飞I.MX6UL工控开发板试用体验】linux can 测试
3188 浏览 0 评论
260浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-17 12:42 , Processed in 0.629175 second(s), Total 70, Slave 52 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号