對於做智能車的愛好者來說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;
}
}
}