S5PV210 buzzer驱动

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_33472414/article/details/102256036

我们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);
}

猜你喜欢

转载自blog.csdn.net/qq_33472414/article/details/102256036