我们buzzer的驱动,我们是基于杂项类设备驱动
杂项类设备驱动框架:misc.c文件,里面做了这些事:
1.class_create(THIS_MODULE, “misc”); 在/sys/class 下面创建了misc类
2.register_chrdev(MISC_MAJOR,“misc”,&misc_fops) 注册字符设备驱动,主设备号是10
3.提供杂项类设备的注册/注销方法misc_register/misc_deregister
buzzer驱动:
1.init_MUTEX(&lock); 初始化信号量
2.misc_register(&misc); 注册杂项类设备驱动,需要自己定义如下的结构体:
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.open = x210_pwm_open,
.release = x210_pwm_close,
.ioctl = x210_pwm_ioctl, };
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name= DEVICE_NAME,
.fops = &dev_fops, };
3.gpio_request(S5PV210_GPD0(2), “GPD0”); 申请与buzzer相连接的gpio的使用权限
4.申请到gpio的使用权限之后,对gpio进行相关的操作,GPIO相关操作:
<1>申请gpio,设置上拉
<2>设置输出模式,输出0
5.open函数和close函数里面用了一个信号量,保证这个设备节点只能被一个进程打开
6.ioctl函数里面实现了 pwm_set_freq 和 pwm_stop两个函数
7.pwm_stop 函数就是把gpio改为了input模式
pwm设置频率,这个比较重要:
8.pwm_set_freq
<1>首先把gpio设置为了 pwm输出模式
<2>设置PRESCALER和MUX都为1/16, 相当于用pclk/16/16,所以我们控制单元的时钟就是pclk/16/16, f = 1 / t, 那么t_con = 1 / (pclk/16/16)
<3>我们TCNT寄存器里面的值的减少,是以t_con为基准的,例如我们TCNT的值是100,那么TCNT减少到0就是一个周期,这个周期t = t_con x 100, 那么f = 1 / (t_con x 100)
<4>这里我们假设:TCNT = n, 那么这个n减少到0所用的时间 T=t_con x n, 那么频率
f = 1 / (t_con x n), 所以 n = 1 / (f x (1 / (pclk/16/16)) )
所以n x (1 / (pclk/16/16) = 1 / f
n = (pclk/16/16) / f, 所以这里我们设置频率为f_set的时候,TCNT就是这样算出来的
<5>TCNT的值是控制我们的周期的,TCMP的值是设置占空比的。TCNT寄存器只是设置cnt值的,
最后是TCNTB里面的值–。TCNTB里面的值是TCNT刷进去的。一般情况下当TCNTB的值大于 TCMP的时候,输出的信号是高电平,反之,则为低电平。
<6>打开自动刷新,关闭翻转(电平的翻转),手动刷新TCNT和TCMP寄存器到TCNTB和TCMPB中, 打开定时器
<7>关闭手动刷新,第一次需要手动刷新值到TCNTB和TCMPB寄存器中,之后就不需要了
static void PWM_Set_Freq( unsigned long freq )
{
unsigned long tcon;
unsigned long tcnt;
unsigned long tcfg1;
struct clk *clk_p;
unsigned long pclk;
//unsigned tmp;
//设置GPD0_2为PWM输出
s3c_gpio_cfgpin(S5PV210_GPD0(2), S3C_GPIO_SFN(2));
tcon = __raw_readl(S3C2410_TCON);
tcfg1 = __raw_readl(S3C2410_TCFG1);
//mux = 1/16
tcfg1 &= ~(0xf<<8);
tcfg1 |= (0x4<<8);
__raw_writel(tcfg1, S3C2410_TCFG1);
clk_p = clk_get(NULL, "pclk");
pclk = clk_get_rate(clk_p);
tcnt = (pclk/16/16)/freq;
__raw_writel(tcnt, S3C2410_TCNTB(2));
__raw_writel(tcnt/2, S3C2410_TCMPB(2));//占空比为50%
tcon &= ~(0xf<<12);
tcon |= (0xb<<12);
__raw_writel(tcon, S3C2410_TCON);
tcon &= ~(2<<12); //clear manual update bit
__raw_writel(tcon, S3C2410_TCON);
}