最近在搞讯为开发板的驱动,终于将讯为的蜂鸣器驱动搞定,现在进行一个总结。
编写流程:
(1)分析硬件原理图
要控制beep ----> gpd0_0 --->输出高:响;输出低:不响。
(2)分析该硬件所需配置的寄存器
物理地址:
con:0x114000a0 [0-3] 0x01(output)
dat:0x114000a4 [0] 1响 0灭
(3)搭建驱动框架
1)模块三要素
2)完成字符设备相关的操作
3)相关硬件的操作
3.1 完成映射
3.2 配置寄存器
驱动源码:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/io.h>
#define NAME "beep_demo"
#define GPD0CON 0x114000a0
#define GPD0DAT 0x114000a4
void __iomem *gpd0con;
void __iomem *gpd0dat;
int major = 0;
int led_open(struct inode *inode, struct file *file)
{
writel((0x1<<0),gpd0dat);
printk("%s,%d\n",__func__,__LINE__);
return 0;
}
int led_release(struct inode *inode, struct file *file)
{
writel(~(0x1<<0),gpd0dat);
printk("%s,%d\n",__func__,__LINE__);
return 0;
}
struct file_operations f_ops = {
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
};
int beep_init(void)
{
int ret = 0;
major = register_chrdev(major,NAME,&f_ops);
if(major < 0){
printk("%s,%d register_chrdev fail...\n",__func__,__LINE__);
return -EINVAL;
}
gpd0con = ioremap(GPD0CON,4);
if(gpd0con == NULL){
printk("%s,%d ioremap gpd0con fail...\n",__func__,__LINE__);
goto ERR_STEP1;
}
gpd0dat = ioremap(GPD0DAT,4);
if(gpd0dat == NULL){
printk("%s,%d ioremap gpd0dat fail...\n",__func__,__LINE__);
goto ERR_STEP2;
}
writel((readl(gpd0con)&~(0xf<<0))|(0x1<<0),gpd0con);
printk("%s,%d\n",__func__,__LINE__);
return 0;
ERR_STEP2:
iounmap(gpd0con);
ERR_STEP1:
unregister_chrdev(major,NAME);
return ret;
}
void beep_exit(void)
{
iounmap(gpd0con);
iounmap(gpd0dat);
unregister_chrdev(major,NAME);
printk("%s,%d\n",__func__,__LINE__);
}
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
编写Makefile
#!/bin/bash
$(warning KERNELRELEASE = $(KERNELRELEASE))
ifeq ($(KERNELRELEASE),)
#内核的源码路径, ?= 条件赋值, uname -r 得到内核版本号
KERNELDIR ?= /home/skyfall/iTOP4412/kernel/iTop4412_Kernel_3.0
# := 立即赋值, 得到当前的绝对路径
PWD := $(shell pwd)
# -C 切换工作路径, $(MAKE) = make
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: modules clean
else
# 生成模块
obj-m := beep_demo.o
endif
交叉编译,生成驱动文件:
(3)编写上层的应用程序,实现的是响3s后停止。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{
int fd;
fd = open("/dev/beep" , O_RDWR);
if(fd < 0){
perror("open");
}
sleep(3);
return 0;
}
交叉编译,生成可执行文件:
arm-none-linux-gnueabi-gcc test.c -o test
将驱动文件与应用层文件拷贝至开板,首先加载驱动。
insmod beep_demo.ko
查看生成的设备号
创建设备节点
mknod /dev/beep c 248 0
应用层调用:
发现蜂鸣器响3s后停止。
最后卸载驱动模块:
整个蜂鸣器的驱动全部完成。