led和蜂鸣器驱动广州龙芯中科1B开发板Word格式文档下载.docx
- 文档编号:3624023
- 上传时间:2023-05-02
- 格式:DOCX
- 页数:26
- 大小:336.97KB
led和蜂鸣器驱动广州龙芯中科1B开发板Word格式文档下载.docx
《led和蜂鸣器驱动广州龙芯中科1B开发板Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《led和蜂鸣器驱动广州龙芯中科1B开发板Word格式文档下载.docx(26页珍藏版)》请在冰点文库上搜索。
led7接GPIO40;
led8接GPIO41。
如原理图所示
注意:
这里有GPIO0,GPIO1,。
。
GPIO38,GPIO39。
到底表示什么意思啊?
个人认为第一列的GPIO1,GPIO2,……为原理图中的编号,而第三列的GPIO38,GPIO39为CPU引脚编号,可以再CPU手册中找到。
如前面的led9所示。
这几个引脚可以在源码中定义为宏,详细请见后面代码,这里只贴出相关部分。
LED原理分析
LED又叫发光二极管,有正负两个极,只要在正负两极之间接上合适的正电压,LED就导通,并发光。
这里只需要让CPU的GPIO引脚输出低电平,对应的LED就被点亮。
比如GPIO38输出低电平,即可点亮LED9。
蜂鸣器引脚
LED7接在CAN1_RX上,CAN1_RX经过电阻后接蜂鸣器,如下图所示
所以LED7和蜂鸣器共用一个引脚——GPIO40。
蜂鸣器原理分析
蜂鸣器通过NPN三极管提供所需的大电流,当GPIO40输出低电平时,NPN三极管截止,蜂鸣器不响;
当输出高电平时,NPN三极管导通,蜂鸣器响。
由于LED7和蜂鸣器共用同一个引脚,并且为了开机后蜂鸣器不响(想起来烦人,哈哈)。
所以引脚GPIO40必须输出低电平,恰好低电平使LED导通,所以LED7在开机后一直亮着。
GPIO相关分析
阅读CPU手册
首先看龙芯1B处理器的手册,其中对GPIO相关的寄存器有:
配置寄存器,输入使能寄存器,输入寄存器,配置输出寄存器,MUX寄存器。
根据经验,一般都是先配置GPIO为输入还是输出,然后读输入寄存器或者写输出寄存器实现输入输出功能。
V1.9版的手册中写得还不是很清楚,我们这里也只能猜了。
贴上手册中的截图
作为对比参考,我把其它CPU的截图也贴上
相比较而言,龙芯1B处理器手册写得太简单了,以至于没有说清楚。
Linux内核源码分析
现在我们来看一下linux内核中GPIO相关代码。
源码路径“linux内核根目录/arch/mips/loongson/ls1x/gpio.c”。
我们想实现的功能就是简单的在GPIO口输出高低电平。
源文件gpio.c中有个函数ls1b_gpio_direction_output(),从函数名字上看好像能实现这个功能。
具体分析一下。
/*
函数功能:
直接在某个GPIO输出高电平或者低电平
入参:
structgpio_chip*chip可以为空指针
unsignedgpioGPIO的序号
intlevel电平值。
1--高电平;
0--低电平
*/
intls1b_gpio_direction_output(structgpio_chip*chip,
unsignedgpio,intlevel)
{
u32temp;
u32mask;
//入参检查:
判断是否超过最大的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-32);
//配置GPIO引脚为GPIO功能
temp=LOONGSON_GPIOCFG1;
temp|=mask;
LOONGSON_GPIOCFG1=temp;
//配置GPIO引脚为输出
temp=LOONGSON_GPIOIE1;
temp&
=(~mask);
LOONGSON_GPIOIE1=temp;
//释放锁
spin_unlock(&
}else{
gpio;
temp=LOONGSON_GPIOCFG0;
LOONGSON_GPIOCFG0=temp;
temp=LOONGSON_GPIOIE0;
LOONGSON_GPIOIE0=temp;
}
return0;
}
/*
把高低电平值写到输出寄存器中
入参:
unsignedgpioGPIO编号
intstate高低电平。
voidgpio_set_value(unsignedgpio,intstate)
u32val;
//判断GPIO是否有效、合法
=STLS1B_N_GPIO){
__gpio_set_value(gpio,state);
return;
//对不同GPIO操作不同的寄存器
//获取锁
//把高低电平值写到输出寄存器中
val=LOONGSON_GPIOOUT1;
if(state)
val|=mask;
else
val&
LOONGSON_GPIOOUT1=val;
}else{
val=LOONGSON_GPIOOUT0;
if(state)
=~mask;
LOONGSON_GPIOOUT0=val;
经过以上分析,要让GPIO输出高低电平,首先写输出寄存器,然后把引脚配置为GPIO,最后设置为输出。
点亮一个led
源码
源码包含一个驱动源程序、一个应用源程序和一个Makefile。
[root@localhostled]#catMakefile
#ifKERNELRELEASEisdefined,we'
vebeeninvokedfromthe
#kernelbuildsystemandcanuseitslanguage.
ifneq($(KERNELRELEASE),)
obj-m:
=ls1b_led_driver.o
#otherwisewewerecalleddirectlyfromthecommand
#line;
invokethekernelbuildsystem.
else
KERNELDIR=/home/dev/develop/1b-linux-3.0-d8b47bb
PWD:
=$(shellpwd)
default:
$(MAKE)-C$(KERNELDIR)M=$(PWD)modules
cp./ls1b_led_driver.ko/nfsramdisk/LS1Brootfs/test
mipsel-linux-gccls1b_led_app.c-ols1b_led_app
cpls1b_led_app/nfsramdisk/LS1Brootfs/test
clean:
rm*.o*.mod.c*.order*.symvers*.ko
endif
这个Makefile是参考《linuxdevicedriver》修改的。
也是比较通用的,只需要修改一个linux内核路径和驱动模块文件名就可以了。
这里把应用程序的编译也放在了一起。
这样执行make时,驱动和应用一起编译。
[root@localhostled]#catls1b_led_driver.c
#include<
linux/module.h>
linux/fs.h>
linux/cdev.h>
linux/init.h>
linux/kernel.h>
linux/slab.h>
linux/errno.h>
linux/types.h>
linux/delay.h>
linux/device.h>
asm/uaccess.h>
asm/mach-loongson/gpio.h>
#defineLS1B_LED_1_ON(0)
#defineLS1B_LED_1_OFF
(1)
intls1b_led_major=0;
intls1b_led_minor=0;
structcdevls1b_led_devs;
intls1b_led_open(structinode*inode,structfile*filp)
printk(KERN_DEBUG"
%s:
open\n"
__FUNCTION__);
intls1b_led_release(structinode*inode,structfile*file)
close\n"
ssize_tls1b_led_set(structfile*filp,constchar__user*buff,size_tcount,loff_t*offp)
ls1b_gpio_direction_output(NULL,38,0);
led9on\n"
structfile_operationsls1b_led_ops={
.owner=THIS_MODULE,
.open=ls1b_led_open,
.release=ls1b_led_release,
.write=ls1b_led_set,
};
MODULE_AUTHOR("
caogoscaogos@"
);
MODULE_LICENSE("
DualBSD/GPL"
intls1b_led_init(void)
intresult;
dev_tdevno;
structcdev*dev=&
ls1b_led_devs;
structfile_operations*fops=&
ls1b_led_ops;
result=alloc_chrdev_region(&
devno,0,1,"
ls1b_led"
if(0>
result)
{
printk(KERN_WARNING"
ls1b_led:
unabletogetamjor\n"
returnresult;
ls1b_led_major=MAJOR(devno);
major=%d\n"
ls1b_led_major);
cdev_init(dev,fops);
dev->
ops=fops;
result=cdev_add(dev,devno,1);
if(result)
printk(KERN_NOTICE"
Error%daddingls1b_led"
result);
ls1b_leddeviceinstalled.withmajor%d\n"
voidls1b_led_exit(void)
cdev_del(&
ls1b_led_devs);
unregister_chrdev_region(MKDEV(ls1b_led_major,0),1);
printk("
ls1b_leddeviceuninstalled\n"
module_init(ls1b_led_init);
module_exit(ls1b_led_exit);
这个驱动程序必《linuxdevicedriver》中的HelloWorldModule要稍微复杂点。
不过已经是非常简单的字符设备驱动了。
这个驱动程序的核心就是函数ls1b_led_set()。
其中的ls1b_gpio_direction_output(NULL,38,0);
的功能是让GPIO38输出低电平。
然后把函数ls1b_led_set()作为该驱动structfile_operationsls1b_led_ops的写函数,即应用程序中调用write()函数,就会对应调用驱动的函数ls1b_led_set()。
函数ls1b_led_set()没有对入参进行分别处理,只要应用程序调用write()函数,不管write什么内容,都执行GPIO38输出低电平的操作。
[root@localhostled]#catls1b_led_app.c
stdio.h>
stdlib.h>
unistd.h>
fcntl.h>
string.h>
sys/types.h>
sys/stat.h>
intmain(void)
intdev_fd;
charbuff[2]={0};
dev_fd=open("
/dev/led"
O_RDWR|O_NONBLOCK);
if(-1==dev_fd)
printf("
Cann'
topenfile/dev/led\n"
return-1;
write(dev_fd,buff,1);
close(dev_fd);
这个应用程序简单来说就是打开设备文件,并执行写操作。
即调用驱动中的函数ls1b_led_set()点亮LED。
友情提示:
内核顶层配置中[*]Enableloadablemodulesupport--->
一定要选上。
运行结果
/test#ls
ls1b_led_appls1b_led_driver.ko
/test#echo8>
/proc/sys/kernel/printk
/test#insmodls1b_led_driver.ko
major=253
ls1b_leddeviceinstalled.withmajor253
/test#mknod/dev/ledc2530
/test#./ls1b_led_app
ls1b_led_open:
open
ls1b_led_set:
led9on
ls1b_led_release:
close
/test#
当然开发板上的LED9肯定被点亮。
经过前面分析可知,打印信息“ls1b_led_set:
led9on”为驱动程序打印出来的。
命令“echo8>
/proc/sys/kernel/printk”是将内核打印级别调到最低。
为了能把所有驱动中的printk打印信息打印出来。
命令“mknod/dev/ledc2530”新建设备节点。
否则,应用中open()会失败。
253为主设备号,根据前面的打印“ls1b_leddeviceinstalled.withmajor253”获得的。
手动指定那个led亮
在前面led驱动的基础上改进了一下,现在可以手动输入led序号,然后制定序号的led被点亮。
驱动程序源码ls1b_led_driver.c
//led正极接3.3v,负极接cpu引脚
//cpu输出低电平,led导通,亮
#defineLS1B_LED_ON(0)
//cpu输出高电平,led截止,灭
#defineLS1B_LED_OFF
(1)
//led引脚
#defineLS1B_PIN_LED_9(38)//gpio38
#defineLS1B_PIN_LED_6(39)//gpio39
#defineLS1B_PIN_LED_7(40)//gpio40
#defineLS1B_PIN_LED_8(41)//gpio41
//led主次设备号,动态申请
/***********************************************************
功能:
将led序号转换为对应的引脚
入参:
unsignedintled_indexled序号
出参:
无
返回值:
unsignedintled_gpioled引脚
***********************************************************/
unsignedintls1b_led_index_to_pin(unsignedintled_index)
switch(led_index)
case9:
returnLS1B_PIN_LED_9;
case6:
returnLS1B_PIN_LED_6;
case7:
returnLS1B_PIN_LED_7;
case8:
returnLS1B_PIN_LED_8;
default:
熄灭指定led
voidls1b_led_off(unsignedintled_index)
ls1b_gpio_direction_output(NULL,ls1b_led_index_to_pin(led_index),LS1B_LED_OFF);
点亮指定led
voidls1b_led_on(unsignedintled_index)
ls1b_gpio_direction_output(NULL,ls1b_led_index_to_pin(led_index),LS1B_LED_ON);
关闭所有led
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- led 蜂鸣器 驱动 广州 龙芯中科 开发