4412——Linux驱动入门03-寄存器操作pwm波形控制蜂鸣器

前言

说到寄存器这么底层的控制,想必都会说厂家会提供相关的api函数,不需要我们寄存器操作这比驱动层还靠底层。4412中确实已经被三星公司封装了,而且封装的确实好,但是目前公司使用的芯片,对方没有给提供使用的平台,因此就不存在如gpio的封装操作。更别提PWM的事情了,因此我用4412来操作寄存器,脱离平台控制硬件设备外设。

开始我的表演

通过硬件原理图了解到接入的蜂鸣器接口时GPD0_0
在这里插入图片描述
1:在6.2.2.31中:然后了解到io配置为0x2的时候时time0 的输出,配置好io为pwm后开始配置pwm的相关的设置。(io的配置采取的时虚拟地址映射第一节中有讲过的)
2:24.5.1中有4个pwm的寄存器控制内容,看着寄存器好多,但是真正用到的就只有TCNTB0系列,对着手册看着源码理解会快些,上代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <mach/regs-gpio.h>
#include <asm/io.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
struct
{
	unsigned int TCFG0;
	unsigned int TCFG1;
	unsigned int TCON;
	unsigned int TCNTB0;
	unsigned int TCMPB0;
	unsigned int TCNTO0;
	unsigned int TCNTB1;
	unsigned int TCMPB1;
	unsigned int TCNTO1;
	unsigned int TCNTB2;
	unsigned int TCMPB2;
	unsigned int TCNTO2;
	unsigned int TCNTB3;
	unsigned int TCMPB3;
	unsigned int TCNTO3;
	unsigned int TCNTB4;
	unsigned int TCNTO4;
	unsigned int TINT_CSTAT;
// 一个结构体解决了所有的pwm设置问题,但是我们只用到了0
} * PWM;
volatile unsigned long virt_addr, virt_addr_gpio, phys_addr, phys_addr_gpio; //用于存放虚拟地址和物理地址
volatile unsigned long *GPD0CON, *GPD0PUD;
void addr_init(void)
//虚拟地址的映射,在操作
{
	phys_addr = 0x139D0000;
	virt_addr = (unsigned long)ioremap(phys_addr, 0x32);
	PWM = (unsigned long *)(virt_addr + 0x00);
	phys_addr_gpio = 0x11400000 + 0xA0;
	virt_addr_gpio = (unsigned long)ioremap(phys_addr_gpio, 0x10);
	GPD0CON = (unsigned long *)(virt_addr_gpio + 0x00);
	GPD0PUD = (unsigned long *)(virt_addr_gpio + 0x00A8 - 0x00A0);
}
void pwm_init(void)
{
	addr_init();
	*GPD0CON = *GPD0CON & (~(0xf)) | 0x2;
	*GPD0PUD = *GPD0PUD & (~(0xf));
	//预分频1-254 + 1
	// (*PWM).TCFG0 = (*PWM).TCFG0 & (~(0xff)) | 0xf9; // f9 249分频
	(*PWM).TCFG0 = (*PWM).TCFG0 & (~(0xff)) | 0x7f; //127
	// (*PWM).TCFG0 = (*PWM).TCFG0 & (~(0xff)) | 0x1; //1
	// (*PWM).TCFG0 = (*PWM).TCFG0 & (~(0xff)) | 0x00;
	// (*PWM).TCFG0 = (*PWM).TCFG0 & (~(0xff)) | 0x18;
	//分频1.2.4.8.16
	(*PWM).TCFG1 = (*PWM).TCFG1 & (~(0xf)) | 0x4; // 16 fenpin de
	// (*PWM).TCFG1 = (*PWM).TCFG1 & (~(0xf)) | 0x0;
	//设置占空比  pwm
	(*PWM).TCMPB0 = 30;
	(*PWM).TCNTB0 = 100;
	//设置手动加载,开启定时器
	(*PWM).TCON = (*PWM).TCON & (~(0xf)) | 0x1 | 0x2;
}
static void beep_on(void)
{
	//开启自动重载
	(*PWM).TCON = (*PWM).TCON & (~(0xf)) | 0x1 | 0x8;
}
static int iTop4412_PWM_init(void)
{
	pwm_init();
	beep_on();
	return 0;
}
static void beep_off(void)
{
	(*PWM).TCON = (*PWM).TCON & (~(0xf)) | 0x0;
	//定时器结束之后,输出的是高电平还是低电平?
	*GPD0CON = *GPD0CON & (~(0xf)) | 0x0;
}
static void iTop4412_PWM_exit(void)
{
	beep_off();
}
module_init(iTop4412_PWM_init);
module_exit(iTop4412_PWM_exit);
MODULE_LICENSE("GPL");

频率的计算公式如下:这个时候我们就需要知道PCLK具体是多少
在这里插入图片描述
手册中给了这个表格:搞得好像PCLK=66M。
在这里插入图片描述
测试方法:当make以后,加载驱动的时候就会输出波形的,无需应用程序来调用的。别和我说字符设备或者是杂项设备,我只是测试寄存器的操作,波形是否成功而已。

当PWM的分辨率设置为2的时候,确实,PCLK=66M,然后最大PWM频率33M,通过示波器测试得到的。
但是当设置的分辨率不是2的时候比如100的时候,占空比是50%的时候,就会出现一个,PCLK=100M的结果算,如100 000 000 /(249+1)/4 == 1000HZ,如果这个时候你采用了PCLK=66M就无法解释正确的结果了,但是这个时候你又在想,如果是100MHZ的话,那么分辨率设置为2,分频系数都设置为1,就是不分频,那么得到的最大的pwm的频率理论上应该是100M/2=50M对吧,但是根据实际测得到的数值就是33M,所以得出结论是PCLK=66M的。但是这种测试得到的结论只有在pwm的分辨率是2的情况下。其他情况都是PCLK=100MHZ。
是不是结果很绕,和别人解释,别人听不懂。所以发个微博,看到的兄弟,知道原因的话,告诉小弟下。pwm的分辨率好像是有设置的范围,对于单片机来说,假如10M的时钟我是不是可以设置分辨率为10000呢?甚至是100000呢,好像是不行的,原因呢,我不懂了。。。。。。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值