本文转载自:https://blog.csdn.net/zqixiao_09/article/details/50858776
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zqixiao_09/article/details/50858776
编写驱动的第一步仍是看原理图:
可以看到,该蜂鸣器由 GPD0_0 来控制 ,查手册可知该I/O口由Time0 来控制,找到相应的寄存器:
a -- I/O口寄存器及地址
GPD0CON 0x114000a0
b -- Time0 寄存器及地址
基地址为:TIMER_BASE 0x139D0000
这些物理寄存器地址都是相邻的,我们这里用偏移量来表示:
寄存器名 地址偏移量 所需配置
TCFG0 0x0000 [7-0] 0XFF
TCFG1 0x0004 [3-0] 0X2
TCON 0x0008 [3-0] 0X2 0X9 0X0
TCNTB0 0x000C 500
TCMPB0 0x0010 250
前面已经知道,驱动是无法直接操纵物理地址的,所以这里仍需物理地址向虚拟地址的转换,用到 ioremap() 函数、writel()函数、readl()函数:
1、地址映射操作
unsigned int *gpd0con;
void *timer_base;<span style="white-space:pre"> </span>//之所以是void类型,偏移量为4时,只是移动4个字节,方便理解
gpd0con = ioremap(GPD0CON,4);
timer_base = ioremap(TIMER_BASE , 0x14);
2、Time0初始化操作(这里使用的已经是虚拟地址)
这里现将数据从寄存器中读出,修改后再写回寄存器,具体寄存器操作可以移步Exynos4412裸机开发——PWM定时器:
writel((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);
writel ((readl(timer_base +TCFG0 )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);
writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );
3、装载数据,配置占空比
writel(500, timer_base +TCNTB0 );
writel(250, timer_base +TCMPB0 );
writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );
4、相关控制函数
void beep_on(void)
{
writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x9 <<0),timer_base +TCON );
}
void beep_off(void)
{
writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x0 <<0),timer_base +TCON );
}
下面是驱动程序,这里我们用到了 write() read() ioctl() 函数,具体解析移步:
驱动程序:beep.c
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/uaccess.h>
static int major = 250;
static int minor=0;
static dev_t devno;
static struct class *cls;
static struct device *test_device;
#define GPD0CON 0x114000a0
#define TIMER_BASE 0x139D0000
#define TCFG0 0x0000
#define TCFG1 0x0004
#define TCON 0x0008
#define TCNTB0 0x000C
#define TCMPB0 0x0010
static unsigned int *gpd0con;
static void *timer_base;
#define MAGIC_NUMBER 'k'
#define BEEP_ON _IO(MAGIC_NUMBER ,0)
#define BEEP_OFF _IO(MAGIC_NUMBER