led和蜂鸣器驱动广州龙芯中科1B开发板_第1页
led和蜂鸣器驱动广州龙芯中科1B开发板_第2页
led和蜂鸣器驱动广州龙芯中科1B开发板_第3页
led和蜂鸣器驱动广州龙芯中科1B开发板_第4页
led和蜂鸣器驱动广州龙芯中科1B开发板_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、1. 前言32. 硬件电路32.1. LED引脚32.2. LED原理分析42.3. 蜂鸣器引脚52.4. 蜂鸣器原理分析53. GPIO相关分析63.1. 阅读CPU手册63.2. Linux内核源码分析64. 点亮一个led94.1. 源码94.2. 运行结果135. 手动指定那个led亮135.1. 源码135.2. 运行结果196. 蜂鸣器驱动206.1. 源码207. 参考资料20修订历史版本号更新日期更新内容V1.0创建1. 前言由于龙芯资料较少,现在又有点时间,写了两句,仅供初学者入门时参考,还望高手多多指教。2. 硬件电路2.1. LED引脚先把电路图贴出来我们选择LED9作为

2、本次实验的对象。LED9接到龙芯1B的引脚T12再查龙芯1B处理器的用户手册v1.9如下图即CAN0_RX为GPIO38.,同理可得Led6接GPIO39;led7接GPIO40;led8接GPIO41。如原理图所示注意:这里有GPIO0,GPIO1,。GPIO38,GPIO39。到底表示什么意思啊?个人认为第一列的GPIO1,GPIO2,为原理图中的编号,而第三列的GPIO38,GPIO39为CPU引脚编号,可以再CPU手册中找到。如前面的led9所示。这几个引脚可以在源码中定义为宏,详细请见后面代码,这里只贴出相关部分。2.2. LED原理分析LED又叫发光二极管,有正负两个极,只要在正负

3、两极之间接上合适的正电压,LED就导通,并发光。这里只需要让CPU的GPIO引脚输出低电平,对应的LED就被点亮。比如GPIO38输出低电平,即可点亮LED9。2.3. 蜂鸣器引脚LED7接在CAN1_RX上,CAN1_RX经过电阻后接蜂鸣器,如下图所示所以LED7和蜂鸣器共用一个引脚GPIO40。2.4. 蜂鸣器原理分析蜂鸣器通过NPN三极管提供所需的大电流,当GPIO40输出低电平时,NPN三极管截止,蜂鸣器不响;当输出高电平时,NPN三极管导通,蜂鸣器响。由于LED7和蜂鸣器共用同一个引脚,并且为了开机后蜂鸣器不响(想起来烦人,哈哈)。所以引脚GPIO40必须输出低电平,恰好低电平使LE

4、D导通,所以LED7在开机后一直亮着。3. GPIO相关分析3.1. 阅读CPU手册首先看龙芯1B处理器的手册,其中对GPIO相关的寄存器有:配置寄存器,输入使能寄存器,输入寄存器,配置输出寄存器,MUX寄存器。根据经验,一般都是先配置GPIO为输入还是输出,然后读输入寄存器或者写输出寄存器实现输入输出功能。V1.9版的手册中写得还不是很清楚,我们这里也只能猜了。贴上手册中的截图作为对比参考,我把其它CPU的截图也贴上相比较而言,龙芯1B处理器手册写得太简单了,以至于没有说清楚。3.2. Linux内核源码分析现在我们来看一下linux内核中GPIO相关代码。源码路径“linux内核根目录/a

5、rch/mips/loongson/ls1x/gpio.c”。我们想实现的功能就是简单的在GPIO口输出高低电平。源文件gpio.c中有个函数ls1b_gpio_direction_output(),从函数名字上看好像能实现这个功能。具体分析一下。/* 函数功能:直接在某个GPIO输出高电平或者低电平入参:struct gpio_chip *chip 可以为空指针 unsigned gpio GPIO的序号 int level 电平值。1-高电平;0-低电平*/int ls1b_gpio_direction_output(struct gpio_chip *chip,unsigned gpio

6、, int level)u32 temp;u32 mask;/ 入参检查:判断是否超过最大的GPIO个数,即GPIO的合法性检查if (gpio >= STLS1B_N_GPIO)return -EINVAL;/ 把高低电平值写到输出寄存器中gpio_set_value(gpio, level);/ 由于寄存器是32位的,一个寄存器最多可以控制32个GPIO/ 就比如:配置寄存器,就有配置寄存器0和配置寄存器1/ 所以这里分开处理if(gpio >= 32)/ 获取锁,执行原子操作spin_lock(&gpio_lock);mask = 1 << (gpio -

7、 32);/ 配置GPIO引脚为GPIO功能temp = LOONGSON_GPIOCFG1;temp |= mask;LOONGSON_GPIOCFG1 = temp;/ 配置GPIO引脚为输出temp = LOONGSON_GPIOIE1;temp &= (mask);LOONGSON_GPIOIE1 = temp;/ 释放锁spin_unlock(&gpio_lock);elsespin_lock(&gpio_lock);mask = 1 << gpio;temp = LOONGSON_GPIOCFG0;temp |= mask;LOONGSON_GP

8、IOCFG0 = temp;temp = LOONGSON_GPIOIE0;temp &= (mask);LOONGSON_GPIOIE0 = temp;spin_unlock(&gpio_lock);return 0;/*函数功能:把高低电平值写到输出寄存器中入 参:unsigned gpio GPIO编号 int state 高低电平。1-高电平;0-低电平*/void gpio_set_value(unsigned gpio, int state)u32 val;u32 mask;/ 判断GPIO是否有效、合法if (gpio >= STLS1B_N_GPIO) _

9、gpio_set_value(gpio, state);return ;/ 对不同GPIO操作不同的寄存器if(gpio >= 32)mask = 1 << (gpio - 32);/ 获取锁spin_lock(&gpio_lock);/ 把高低电平值写到输出寄存器中val = LOONGSON_GPIOOUT1;if (state)val |= mask;elseval &= (mask);LOONGSON_GPIOOUT1 = val;/ 释放锁spin_unlock(&gpio_lock);elsemask = 1 << gpio;s

10、pin_lock(&gpio_lock);val = LOONGSON_GPIOOUT0;if(state)val |= mask;elseval &= mask;LOONGSON_GPIOOUT0 = val;spin_unlock(&gpio_lock);经过以上分析,要让GPIO输出高低电平,首先写输出寄存器,然后把引脚配置为GPIO,最后设置为输出。4. 点亮一个led4.1. 源码源码包含一个驱动源程序、一个应用源程序和一个Makefile。rootlocalhost led# cat Makefile # if KERNELRELEASE is define

11、d, we've been invoked from the# kernel build system and can use its language.ifneq ($(KERNELRELEASE),) obj-m := ls1b_led_driver.o# otherwise we were called directly from the command# line; invoke the kernel build system.else KERNELDIR = /home/dev/develop/1b-linux-3.0-d8b47bb PWD := $(shell pwd)d

12、efault: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules cp ./ls1b_led_driver.ko /nfsramdisk/LS1Brootfs/test mipsel-linux-gcc ls1b_led_app.c -o ls1b_led_app cp ls1b_led_app /nfsramdisk/LS1Brootfs/testclean: rm *.o *.mod.c *.order *.symvers *.koendif这个Makefile是参考linux device driver修改的。也是比较通用的,只需要修改一个linux内核路

13、径和驱动模块文件名就可以了。这里把应用程序的编译也放在了一起。这样执行make时,驱动和应用一起编译。rootlocalhost led# cat ls1b_led_driver.c #include <linux/module.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/errno.h>#i

14、nclude <linux/types.h>#include <linux/delay.h>#include <linux/device.h>#include <asm/uaccess.h>#include <asm/mach-loongson/gpio.h>#define LS1B_LED_1_ON (0)#define LS1B_LED_1_OFF (1)int ls1b_led_major = 0;int ls1b_led_minor = 0;struct cdev ls1b_led_devs;int ls1b_led_open

15、(struct inode *inode, struct file *filp) printk(KERN_DEBUG "%s: openn", _FUNCTION_); return 0;int ls1b_led_release(struct inode *inode, struct file *file) printk(KERN_DEBUG "%s: closen", _FUNCTION_); return 0;ssize_t ls1b_led_set(struct file *filp, const char _user *buff, size_t

16、count, loff_t *offp) ls1b_gpio_direction_output(NULL, 38, 0); printk(KERN_DEBUG "%s: led 9 onn", _FUNCTION_); return 0;struct file_operations ls1b_led_ops = .owner = THIS_MODULE, .open = ls1b_led_open, .release = ls1b_led_release, .write = ls1b_led_set,;MODULE_AUTHOR("caogos caogos&qu

17、ot;);MODULE_LICENSE("Dual BSD/GPL");int ls1b_led_init(void) int result; dev_t devno; struct cdev *dev = &ls1b_led_devs; struct file_operations *fops = &ls1b_led_ops; result = alloc_chrdev_region(&devno, 0, 1, "ls1b_led"); if (0 > result) printk(KERN_WARNING "l

18、s1b_led: unable to get amjorn"); return result; ls1b_led_major = MAJOR(devno); printk(KERN_DEBUG "ls1b_led: major = %dn", ls1b_led_major); cdev_init(dev, fops); dev->ops = fops; result = cdev_add(dev, devno, 1); if (result) printk(KERN_NOTICE "Error %d adding ls1b_led", r

19、esult); return result; printk(KERN_DEBUG "ls1b_led device installed. with major %dn", ls1b_led_major); return 0;void ls1b_led_exit(void) cdev_del(&ls1b_led_devs); unregister_chrdev_region(MKDEV(ls1b_led_major, 0), 1); printk("ls1b_led device uninstalledn");module_init(ls1b_le

20、d_init);module_exit(ls1b_led_exit);这个驱动程序必linux device driver中的Hello World Module要稍微复杂点。不过已经是非常简单的字符设备驱动了。这个驱动程序的核心就是函数ls1b_led_set()。其中的ls1b_gpio_direction_output(NULL, 38, 0);的功能是让GPIO38输出低电平。然后把函数ls1b_led_set()作为该驱动struct file_operations ls1b_led_ops的写函数,即应用程序中调用write()函数,就会对应调用驱动的函数ls1b_led_set(

21、)。注意:函数ls1b_led_set()没有对入参进行分别处理,只要应用程序调用write()函数,不管write什么内容,都执行GPIO38输出低电平的操作。rootlocalhost led# cat ls1b_led_app.c #include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>int

22、 main(void) int dev_fd; char buff2 = 0; dev_fd = open("/dev/led", O_RDWR | O_NONBLOCK); if (-1 = dev_fd) printf("Cann't open file /dev/ledn"); return -1; write(dev_fd, buff, 1); close(dev_fd); return 0;这个应用程序简单来说就是打开设备文件,并执行写操作。即调用驱动中的函数ls1b_led_set()点亮LED。友情提示:内核顶层配置中* Enabl

23、e loadable module support -> 一定要选上。4.2. 运行结果/test # lsls1b_led_app ls1b_led_driver.ko/test # echo 8 > /proc/sys/kernel/printk/test # insmod ls1b_led_driver.ko ls1b_led: major = 253ls1b_led device installed. with major 253/test # mknod /dev/led c 253 0/test # ./ls1b_led_app ls1b_led_open: openl

24、s1b_led_set: led 9 onls1b_led_release: close/test # 当然开发板上的LED9肯定被点亮。经过前面分析可知,打印信息“ls1b_led_set: led 9 on”为驱动程序打印出来的。命令“echo 8 > /proc/sys/kernel/printk”是将内核打印级别调到最低。为了能把所有驱动中的printk打印信息打印出来。命令“mknod /dev/led c 253 0”新建设备节点。否则,应用中open()会失败。253为主设备号,根据前面的打印“ls1b_led device installed. with major 25

25、3”获得的。5. 手动指定那个led亮5.1. 源码在前面led驱动的基础上改进了一下,现在可以手动输入led序号,然后制定序号的led被点亮。驱动程序源码ls1b_led_driver.c#include <linux/module.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/slab.h>#include <linux/errno.h>

26、;#include <linux/types.h>#include <linux/delay.h>#include <linux/device.h>#include <asm/uaccess.h>#include <asm/mach-loongson/gpio.h>/ led正极接3.3v,负极接cpu引脚/ cpu输出低电平,led导通,亮#define LS1B_LED_ON (0)/ cpu输出高电平,led截止,灭#define LS1B_LED_OFF (1)/ led引脚#define LS1B_PIN_LED_9 (38

27、) / gpio38#define LS1B_PIN_LED_6 (39) / gpio39#define LS1B_PIN_LED_7 (40) / gpio40#define LS1B_PIN_LED_8 (41) / gpio41/ led主次设备号,动态申请int ls1b_led_major = 0;int ls1b_led_minor = 0;struct cdev ls1b_led_devs;int ls1b_led_open(struct inode *inode, struct file *filp)printk(KERN_DEBUG "%s: openn"

28、;, _FUNCTION_);return 0;int ls1b_led_release(struct inode *inode, struct file *file)printk(KERN_DEBUG "%s: closen", _FUNCTION_);return 0;/*功 能: 将led序号转换为对应的引脚入 参: unsigned int led_index led序号出 参: 无返 回 值: unsigned int led_gpio led引脚*/ unsigned int ls1b_led_index_to_pin(unsigned int led_inde

29、x) switch (led_index) case 9: return LS1B_PIN_LED_9; case 6: return LS1B_PIN_LED_6; case 7: return LS1B_PIN_LED_7; case 8: return LS1B_PIN_LED_8; default: return 0; /*功 能: 熄灭指定led入 参: unsigned int led_index led序号出 参: 无返 回 值: 无*/ void ls1b_led_off(unsigned int led_index) ls1b_gpio_direction_output(NU

30、LL, ls1b_led_index_to_pin(led_index), LS1B_LED_OFF);/*功 能: 点亮指定led入 参: unsigned int led_index led序号出 参: 无返 回 值: 无*/ void ls1b_led_on(unsigned int led_index) ls1b_gpio_direction_output(NULL, ls1b_led_index_to_pin(led_index), LS1B_LED_ON);/*功 能: 关闭所有led入 参: 无出 参: 无返 回 值: 无*/ void ls1b_led_off_all() ls

31、1b_led_off(9); / 关闭led9 ls1b_led_off(6); ls1b_led_off(7); ls1b_led_off(8);ssize_t ls1b_led_set(struct file *filp, const char _user *buff, size_t count, loff_t *offp) char inputbuff10 = 0; unsigned int led_index = 0; unsigned int tmp = 0; / 关闭所有led ls1b_led_off_all(); / 打开指定led copy_from_user(inputbu

32、ff, buff, 1); / 一次只处理一个led led_index = (unsigned int)(inputbuff0); ls1b_led_on(led_index); printk(KERN_DEBUG "%s: led %d onn", _FUNCTION_, led_index); return 0;struct file_operations ls1b_led_ops = .owner = THIS_MODULE,.open= ls1b_led_open,.release = ls1b_led_release,.write= ls1b_led_set,;

33、MODULE_AUTHOR("caogos caogos");MODULE_LICENSE("Dual BSD/GPL");int ls1b_led_init(void)int result;dev_t devno;struct cdev *dev = &ls1b_led_devs;struct file_operations *fops = &ls1b_led_ops;result = alloc_chrdev_region(&devno, 0, 1, "ls1b_led");if (0 > resul

34、t)printk(KERN_WARNING "ls1b_led: unable to get amjorn");return result;ls1b_led_major = MAJOR(devno);printk(KERN_DEBUG "ls1b_led: major = %dn", ls1b_led_major);cdev_init(dev, fops);dev->ops = fops;result = cdev_add(dev, devno, 1);if (result)printk(KERN_NOTICE "Error %d add

35、ing ls1b_led", result);return result;printk(KERN_DEBUG "ls1b_led device installed. with major %dn", ls1b_led_major);return 0;void ls1b_led_exit(void)cdev_del(&ls1b_led_devs);unregister_chrdev_region(MKDEV(ls1b_led_major, 0), 1);printk("ls1b_led device uninstalledn");modu

36、le_init(ls1b_led_init);module_exit(ls1b_led_exit);应用程序源码#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>int main(void)int dev_fd;char buff2 = 0; unsigned int tmp = 0;dev_

37、fd = open("/dev/led", O_RDWR | O_NONBLOCK);if (-1 = dev_fd)printf("Cann't open file /dev/ledn");return -1; / 根据用户输入,点亮指定的led buff0 = getchar()-'0'write(dev_fd, buff, 1); / 由于led7和蜂鸣器接在同一个引脚上 / 避免蜂鸣器一直响,最后把蜂鸣器关了 sleep(1); buff0 = 7;write(dev_fd, buff, 1); close(dev_fd)

38、;return 0;Makefilerootlocalhost led# cat Makefile # if KERNELRELEASE is defined, we've been invoked from the# kernel build system and can use its language.ifneq ($(KERNELRELEASE),) obj-m := ls1b_led_driver.o# otherwise we were called directly from the command# line; invoke the kernel build syste

39、m.else KERNELDIR = /home/dev/develop/1b-linux-3.0-d8b47bb PWD := $(shell pwd)default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules cp ./ls1b_led_driver.ko /nfsramdisk/LS1Brootfs/test mipsel-linux-gcc ls1b_led_app.c -o ls1b_led_app cp ls1b_led_app /nfsramdisk/LS1Brootfs/test/clean: rm *.o *.mod.c *.order

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论