基於tiny210v2的linux-3.9.6內核驅動移植4:PWM移植

 對於做智能車的愛好者來說PWM可說是很重要的,那麼如果要用arm開發板來控制pwm,首先還是要移植pwm驅動

       開發板:tiny210v2,S5PV210核心。

        環境:    ubuntu13.04 +arm-linux-gcc

        內核:    linux-3.9

        參考友善自帶的linux-3.0.8內核代碼

        轉載請註明:

               blog.csdn.net/canyue102/article/details/9526551

一、硬件信息

         對於tiny210v2開發板上有一個PWM0對應一個峯鳴器,引腳爲GPD0-0

二、驅動源碼

         在linux-3.9.6/drivers/char文件夾下新建smdkv210_pwm.c

 

/*
 * linux/drivers/char/smdkv210_pwm.c
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/slab.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>

#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>


#define DEVICE_NAME				"pwm"

#define PWM_IOCTL_SET_FREQ		1
#define PWM_IOCTL_STOP			0

#define NS_IN_1HZ				(1000000000UL)


#define BUZZER_PWM_ID			0
#define BUZZER_PMW_GPIO			S5PV210_GPD0(0)

static struct pwm_device *pwm4buzzer;

static struct semaphore lock;


static void pwm_set_freq(unsigned long freq) {
	int period_ns = NS_IN_1HZ / freq;

	pwm_config(pwm4buzzer, period_ns / 2, period_ns);
	pwm_enable(pwm4buzzer);

	s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_SFN(2));
}

static void pwm_stop(void) {
	s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_OUTPUT);

	pwm_config(pwm4buzzer, 0, NS_IN_1HZ / 100);
	pwm_disable(pwm4buzzer);
}


static int smdkv210_pwm_open(struct inode *inode, struct file *file) {
	if (!down_trylock(&lock))
		return 0;
	else
		return -EBUSY;
}

static int smdkv210_pwm_close(struct inode *inode, struct file *file) {
	up(&lock);
	return 0;
}

static long smdkv210_pwm_ioctl(struct file *filep, unsigned int cmd,
		unsigned long arg)
{
	switch (cmd) {
		case PWM_IOCTL_SET_FREQ:
			if (arg == 0)
				return -EINVAL;
			pwm_set_freq(arg);
			break;

		case PWM_IOCTL_STOP:
		default:
			pwm_stop();
			break;
	}

	return 0;
}


static struct file_operations smdkv210_pwm_ops = {
	.owner			= THIS_MODULE,
	.open			= smdkv210_pwm_open,
	.release		= smdkv210_pwm_close, 
	.unlocked_ioctl	= smdkv210_pwm_ioctl,
};

static struct miscdevice smdkv210_misc_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = DEVICE_NAME,
	.fops = &smdkv210_pwm_ops,
};

static int __init smdkv210_pwm_dev_init(void) {
	int ret;

	ret = gpio_request(BUZZER_PMW_GPIO, DEVICE_NAME);
	if (ret) {
		printk("request GPIO %d for pwm failed\n", BUZZER_PMW_GPIO);
		return ret;
	}

	gpio_set_value(BUZZER_PMW_GPIO, 0);
	s3c_gpio_cfgpin(BUZZER_PMW_GPIO, S3C_GPIO_OUTPUT);

	pwm4buzzer = pwm_request(BUZZER_PWM_ID, DEVICE_NAME);
	if (IS_ERR(pwm4buzzer)) {
		printk("request pwm %d for %s failed\n", BUZZER_PWM_ID, DEVICE_NAME);
		return -ENODEV;
	}

	pwm_stop();

	sema_init(&lock, 1);
	ret = misc_register(&smdkv210_misc_dev);

	printk(DEVICE_NAME "\tinitialized\n");

	return ret;
}

static void __exit smdkv210_pwm_dev_exit(void) {
	pwm_stop();

	misc_deregister(&smdkv210_misc_dev);
	gpio_free(BUZZER_PMW_GPIO);
}

module_init(smdkv210_pwm_dev_init);
module_exit(smdkv210_pwm_dev_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");
MODULE_DESCRIPTION("S5PV210 PWM Driver");

二、設置config文件

    在該目錄下的Kconfig文件中添加:

config SMDKV210_BUZZER
    tristate "Buzzer driver for FriendlyARM TINY210 development boards"
    depends on MACH_SMDKV210
    default y
    help
      this is buzzer driver for FriendlyARM TINY210 development boards
三、修改Makefile

    在該目錄下的Makefile文件中添加:

    obj-$(CONFIG_SMDKV210_BUZZER)    += smdkv210_pwm.o

四、make測試

    編譯通過

五、測試程序

  

#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#define PWM_IOCTL_SET_FREQ
#define PWM_IOCTL_STOP
#define ESC_KEY
1
2
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;
}
}
}


       

             


發佈了35 篇原創文章 · 獲贊 19 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章