mini2440之PWM混杂驱动分析

来源:互联网 发布:杭州友谦网络 编辑:程序博客网 时间:2024/05/21 11:10
在其Linux源码中,和这个平台相关的代码主要在arch/arm/mach-s3c2410和include/asm-arm/arch-s3c2410

Linux对I/O的操作都定义在asm/io.h中,相应的在arm平台下,就在asm-arm/io.h中。

#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/poll.h>#include <linux/interrupt.h>#include <linux/gpio.h>#include <asm/irq.h>#include <asm/io.h>//__raw_readl#include <asm/uaccess.h>#include <mach/regs-gpio.h>#include <mach/hardware.h>#include <plat/regs-timer.h>//s3c2440定时器所有寄存器定义#include <mach/regs-irq.h>#include <asm/mach/time.h>#include <linux/clk.h>//为系统提供时钟频率#include <linux/cdev.h>#include <linux/device.h>#include <linux/miscdevice.h>#define DEVICE_NAME   "pwm"//设备名#define PWM_IOCTL_SET_FREQ1#define PWM_IOCTL_STOP0//保护临界区,只有获得信号量,才能访问临界资源static struct semaphore lock;//定义信号量,当无法获取,进入休眠//定时器输入时钟频率=pclk/prescale+1/divider
//tcnt计数值,通过这两个,可以算出多长时间发生定时中断/* freq:  pclk/50/16/65536 ~ pclk/50/16   * if pclk = 50MHz, freq is 1Hz to 62500Hz  * human ear : 20Hz~ 20000Hz  */static void PWM_Set_Freq( unsigned long freq ){unsigned long tcon;//定时控制寄存器,设置,自动重装,死区允许,自动更新,启动定时器,反相器等unsigned long tcnt;//定时计数器unsigned long tcfg1;//定时配置寄存器,获得dividerunsigned long tcfg0;//定时配置寄存器,获得prescalestruct clk *clk_p;unsigned long pclk;//set GPB0 as tout0, pwm output,设置GPB0为PWM0输出端口s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);tcon = __raw_readl(S3C2410_TCON);//定时器控制寄存器,__raw_readl是I/O映射,是内核访问硬件的一种方式,还有一种是内存映射tcfg1 = __raw_readl(S3C2410_TCFG1);//定时器配置寄存器tcfg0 = __raw_readl(S3C2410_TCFG0);//取出原始的寄存器的值//prescaler = 50tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;//输出低电平,清除掉tcfg0之前的寄存器配置
//S3C2410_TCFG_PRESCALER0_MASK=0xff,定义在arch/arm/plat-s3c/include/plat/regs-timer.htcfg0 |= (50 - 1); //mux = 1/16tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;tcfg1 |= S3C2410_TCFG1_MUX0_DIV16;__raw_writel(tcfg1, S3C2410_TCFG1);//把新配置的寄存器值写入进去__raw_writel(tcfg0, S3C2410_TCFG0);clk_p = clk_get(NULL, "pclk");//为系统提供时钟频率pclk  = clk_get_rate(clk_p);tcnt  = (pclk/50/16)/freq;__raw_writel(tcnt, S3C2410_TCNTB(0));__raw_writel(tcnt/2, S3C2410_TCMPB(0));//把新配置的寄存器值写入进去//配置定时器tcon &= ~0x1f;tcon |= 0xb;//disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0__raw_writel(tcon, S3C2410_TCON);tcon &= ~2;//clear manual update bit__raw_writel(tcon, S3C2410_TCON);}static void PWM_Stop(void){s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT);s3c2410_gpio_setpin(S3C2410_GPB(0), 0);}static int s3c24xx_pwm_open(struct inode *inode, struct file *file){//尝试获得信号量,如果能立即获得,获得信号量并返回0,否则返回非0,//不会导致调用者睡眠,可以在中断上下文使用。if (!down_trylock(&lock))return 0;//成功elsereturn -EBUSY;//设备忙}static int s3c24xx_pwm_close(struct inode *inode, struct file *file){PWM_Stop();up(&lock);//释放信号量,唤醒等待者    return 0;}static int s3c24xx_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){//printk("ioctl pwm: %x %lx\n", cmd, arg);switch (cmd) {case PWM_IOCTL_SET_FREQ:if (arg == 0)return -EINVAL;PWM_Set_Freq(arg);break;case PWM_IOCTL_STOP:PWM_Stop();break;}return 0;}static struct file_operations dev_fops = {    .owner   =   THIS_MODULE,    .open    =   s3c24xx_pwm_open,    .release =   s3c24xx_pwm_close,     .ioctl   =   s3c24xx_pwm_ioctl,};static struct miscdevice misc = {.minor = MISC_DYNAMIC_MINOR,.name = DEVICE_NAME,.fops = &dev_fops,};static int __init dev_init(void){int ret;init_MUTEX(&lock);//初始化互斥信号量ret = misc_register(&misc);//注册设备号printk (DEVICE_NAME"\tinitialized\n");    return ret;}static void __exit dev_exit(void){misc_deregister(&misc);}module_init(dev_init);module_exit(dev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("FriendlyARM Inc.");MODULE_DESCRIPTION("S3C2410/S3C2440 PWM Driver");

测试程序

#include <stdio.h>#include <termios.h>#include <unistd.h>#include <stdlib.h>#define PWM_IOCTL_SET_FREQ1#define PWM_IOCTL_STOP0#defineESC_KEY0x1bstatic int getch(void){struct termios oldt,newt;int ch;if (!isatty(STDIN_FILENO)) {fprintf(stderr, "this problem should be run at a terminal\n");exit(1);}// save terminal settingif(tcgetattr(STDIN_FILENO, &oldt) < 0) {perror("save the terminal setting");exit(1);}// set terminal as neednewt = oldt;newt.c_lflag &= ~( ICANON | ECHO );if(tcsetattr(STDIN_FILENO,TCSANOW, &newt) < 0) {perror("set terminal");exit(1);}ch = getchar();// restore termial settingif(tcsetattr(STDIN_FILENO,TCSANOW,&oldt) < 0) {perror("restore the termial setting");exit(1);}return ch;}static int fd = -1;static void close_buzzer(void);static void open_buzzer(void){fd = open("/dev/pwm", 0);if (fd < 0) {perror("open pwm_buzzer device");exit(1);}// any function exit call will stop the buzzeratexit(close_buzzer);}static void close_buzzer(void){if (fd >= 0) {ioctl(fd, PWM_IOCTL_STOP);close(fd);fd = -1;}}static void set_buzzer_freq(int freq){// this IOCTL command is the key to set frequencyint ret = ioctl(fd, PWM_IOCTL_SET_FREQ, freq);if(ret < 0) {perror("set the frequency of the buzzer");exit(1);}}static void stop_buzzer(void){int ret = ioctl(fd, PWM_IOCTL_STOP);if(ret < 0) {perror("stop the buzzer");exit(1);}}int main(int argc, char **argv){int freq = 1000 ;open_buzzer();printf( "\nBUZZER TEST ( PWM Control )\n" );printf( "Press +/- to increase/reduce the frequency of the BUZZER\n" ) ;printf( "Press 'ESC' key to Exit this program\n\n" );while( 1 ){int key;set_buzzer_freq(freq);printf( "\tFreq = %d\n", freq );key = getch();switch(key) {case '+':if( freq < 20000 )freq += 10;break;case '-':if( freq > 11 )freq -= 10 ;break;case ESC_KEY:case EOF:stop_buzzer();exit(0);default:break;}}}