读书人

MINI2440开发板PWM驱动与测试程序简明

发布时间: 2013-01-27 13:56:15 作者: rapoo

MINI2440开发板PWM驱动与测试程序简要分析

先看下电路原理图

MINI2440开发板PWM驱动与测试程序简明分析

#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>

#include <asm/uaccess.h>

#include <mach/regs-gpio.h>

#include <mach/hardware.h>

#include <plat/regs-timer.h>

#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_FREQ 1 //设置pwm的频率

#define PWM_IOCTL_STOP 0 //停止pwm

static struct semaphore lock;

//定义信号量,此处的信号量是一个互斥信号量,用于PWM设备之多只能被一个进程打开

/* 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;

unsigned long tcfg0;

struct clk *clk_p;

unsigned long pclk;

//set GPB0 as tout0, pwm output

s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);//功能配置

tcon = __raw_readl(S3C2410_TCON); //获得定时器控制寄存器的数值

tcfg1 = __raw_readl(S3C2410_TCFG1); //获得定时器配置寄存器0的值

tcfg0 = __raw_readl(S3C2410_TCFG0); //获得定时器配置寄存器1的值

//prescaler = 50

tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;

tcfg0 |= (50 - 1);

//mux = 1/16

tcfg1 &= ~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)

{

if (!down_trylock(&lock))//判断是否设备已经打开,已打开返回EBUSY

return 0;

else

return -EBUSY;

}

static int s3c24xx_pwm_close(struct inode *inode, struct file *file)

{

PWM_Stop();//停止PWM

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");

分析,这个程序的结构比较简单,只实现了open(),ioctl(),close()功能,其中最主要的就是ioctl()功能,在ioctl()中最重要的核心函数为static void PWM_Set_Freq( unsigned long freq );所以说只要分析懂了这个函数,这个驱动也都全懂了。要是做过这个裸机程序的就更简单了。里面出现的一些没分析的,在本博客另一篇LED驱动分析中基本都说过了,不清楚的可以去看那个。

首先分析static void PWM_Set_Freq( unsigned long freq );

static void PWM_Set_Freq( unsigned long freq )

{

unsigned long tcon;

unsigned long tcnt;

unsigned long tcfg1;

unsigned long tcfg0;

struct clk *clk_p;

unsigned long pclk;

//set GPB0 as tout0, pwm output

s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0);//功能配置

tcon = __raw_readl(S3C2410_TCON); //获得定时器控制寄存器的数值

tcfg1 = __raw_readl(S3C2410_TCFG1); //获得定时器配置寄存器0的值

tcfg0 = __raw_readl(S3C2410_TCFG0); //获得定时器配置寄存器1的值

//prescaler = 50

tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK;

tcfg0 |= (50 - 1);

//上面的两句是设置预分频器

//mux = 1/16

tcfg1 &= ~S3C2410_TCFG1_MUX0_MASK;

tcfg1 |= S3C2410_TCFG1_MUX0_DIV16;

//上面两句设置MUX0

__raw_writel(tcfg1, S3C2410_TCFG1);

__raw_writel(tcfg0, S3C2410_TCFG0);

//上面两句将配置好的数值写入到配置寄存器

clk_p = clk_get(NULL, "pclk");

pclk = clk_get_rate(clk_p);

//上面两句用于获取PCLK

tcnt = (pclk/50/16)/freq;

//上面这个数值决定了频率,

__raw_writel(tcnt, S3C2410_TCNTB(0));

__raw_writel(tcnt/2, S3C2410_TCMPB(0));

//上面两句将tcnt寄存器写入到TCNTB0,同时设置S3C2410_TCMPB0,也就是说占空比为50%

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);

}

看几个宏定义:

#define S3C2410_TCFG_PRESCALER0_MASK (255<<0) //0xff<<0

#define S3C2410_TCFG1_MUX0_MASK (15<<0) //0xf<<0

#define S3C2410_TCFG1_MUX0_DIV16 (3<<0)

分析:static void PWM_Stop(void)

static void PWM_Stop(void)

{

s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT);//配置管脚为输出状态

s3c2410_gpio_setpin(S3C2410_GPB(0), 0); //设置管脚为低电平输出

}

以下是测试程序

#include <stdio.h>

#include <termios.h>

#include <unistd.h>

#include <stdlib.h>

#define PWM_IOCTL_SET_FREQ 1

#define PWM_IOCTL_STOP 0

#define ESC_KEY 0x1b

static 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 setting

if(tcgetattr(STDIN_FILENO, &oldt) < 0) {

perror("save the terminal setting");

exit(1);

}

// set terminal as need

newt = oldt;

newt.c_lflag &= ~( ICANON | ECHO );

if(tcsetattr(STDIN_FILENO,TCSANOW, &newt) < 0) {

perror("set terminal");

exit(1);

}

ch = getchar();

// restore termial setting

if(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 buzzer

atexit(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 frequency

int 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;

}

}

}

通过调用ioctl函数实现控制PWM的频率以及关闭PWM.

1楼hshhduuduj4天前 22:05
这里面好像没有实现占空比的改变,我现在想实现占空比改变,怎么搞?请教
Re: ssdsafsdsd4天前 22:11
回复hshhduudujn __raw_writel(tcnt/2, S3C2410_TCMPB(0));n这个是控制占空比的,根据你的需要仿照上面的设置频率的函数写个调整占空比的函数,不难。。。

读书人网 >移动开发

热点推荐