对于做智能车的爱好者来说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");
在该目录下的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; } } }