前言
说到寄存器这么底层的控制,想必都会说厂家会提供相关的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呢,好像是不行的,原因呢,我不懂了。。。。。。