linux pwm实现蜂鸣器

要点:

  1.本驱动基于信号量的并发控制。相关的API: 

     static struct semaphore lock;

     void init_MUTEX(&lock);  //初始化信号量

     int down_trylock(&lock);尝试获取信号量,如果有效则立即获得,同时返回0,否则返回非0,此函数不会导致休眠,可以用在中断上下

     void up(&lock)  //释放信号量

  2.蜂蜜器采用的是GPB0口,在设置频率的时候注意要将该口设置成TOUT0模式,

      s3c2410_gpio_cfgpin(S3C2410_GPB0,S3C2410_GPB0_TOUT0);

    在static void pwm_stop()中只需要将GPB0设置成低电平,所以要配置成输出

      s3c2410_gpio_cfgpin(S3C2410_GPB0,S3C2410_GPB0_OUTP);

  3.在linux的驱动中不要直接去操作寄存器地址,系统提供了API:

     __raw_readl(寄存器)//

    tcon = __raw_readl(S3C2410_TCON);

    __raw_writel (value,寄存器) //写value 到寄存器中   

   __raw_writel(tcon, S3C2410_TCON);

 

 

下面是驱动程序:

#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 <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.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 SET_FREQ 1  //设置频率
#define STOP   2 //停止
#define START  3 //开始


static struct semaphore lock;  //定义一个信号量


static void pwm_stop()
{
	s3c2410_gpio_cfgpin(S3C2410_GPB0,S3C2410_GPB0_OUTP);
	s3c2410_gpio_setpin(S3C2410_GPB0,0);   //低电平,停止

}


static void pwm_set_freq(unsigned long freq)
{ 
	unsigned long tcon;
	unsigned long tcfg0;
	unsigned long tcnt;
	unsigned long tcfg1;

	struct clk *clk_p;
	unsigned long pclk;
	
	s3c2410_gpio_cfgpin(S3C2410_GPB0,S3C2410_GPB0_TOUT0);
	tcon = __raw_readl(S3C2410_TCON);
	tcfg0 = __raw_readl(S3C2410_TCFG0);
	tcfg1 = __raw_readl(S3C2410_TCFG1);

	tcfg0 &=~S3C2410_TCFG_PRESCALER0_MASK;
	tcfg0 |=49;

	//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 |= (1<<3) |(1<<1) |(1<<0) ;
	//printk("tcon =%x\n",tcon);
	//tcon |= 0xb;

	__raw_writel(tcon, S3C2410_TCON);
	tcon &= ~2;    //清除手动更新,注意这个时必须的见NOTE
	
	__raw_writel(tcon, S3C2410_TCON);   
	
}

static int s3c24xx_pwm_open(struct inode *inode, struct file *file)
{
	if(!down_trylock(&lock))   //获取信号量成功则返回0
	   return 0;
	else 
		return -EBUSY;
		
}


static void s3c24xx_pwm_close(struct inode *inode, struct file *file)
{
   pwm_stop();
   up(&lock);
   
}

static int s3c24xx_pwm_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	switch(cmd) {
		case SET_FREQ:
			if(arg==0)  //如果参数为0,表示传递的参数不正确
			  return -EINVAL;
		    pwm_set_freq(arg);   //设置频率
		    break;
		case 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("Huang renjun"


测试程序:

include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>

#define PWM_IOCTL_SET_FREQ		1
#define PWM_IOCTL_STOP			2

#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();
		
		printf( "\tkey = %c\n", key );
		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;
		}
	}
}


 

  

 

   

你可能感兴趣的:(linux,struct,Semaphore,Module,File,Terminal)