一、4412 xpwmTOUT1
这是4412的GPD0_1路,itop中被使用为LCD的背光电路的pwm功能。因此如果使用教程中的代码,同样操作GPD0_1是行不通的。
会出现错误,所以需要解除在内核中的占用
修改arch/arm/mach-exynos/mach-itop4412.c,找到并注释
samsung_bl_set(&smdk4x12_bl_gpio_info, &smdk4x12_bl_data);
在内核中取消相关的模块编译,(不确定)
Device Driver>>Graphics support>>Backlight & LCD device support(取消)
然后在平台文件中增加注册设备:
&s3c_device_timer[1],
然后是测试代码:
#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 <linux/gpio.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> #include <linux/ioctl.h> #include <asm-generic/uaccess.h> #define DEVICE_NAME "my_pwm3_dev" #define DRIVER_NAME "my_pwm3_drv" #define PWM_MAGIC 'p' #define PWM_MAX_NR 3 #define PWM_IOCTL_STOP _IO(PWM_MAGIC, 0) #define PWM_IOCTL_SET_FREQ _IO(PWM_MAGIC, 1) #define PWM_IOCTL_SET_DUTY _IO(PWM_MAGIC, 2) #define NS_IN_1HZ (1000000000UL) #define BUZZER_PWM_ID 0 #define PWM1_ID 1 #define PWM2_ID 2 #define PWM3_ID 3 #define BUZZER_PMW_GPIO EXYNOS4_GPD0(0) #define PWM1_GPIO EXYNOS4_GPD0(1) #define PWM2_GPIO EXYNOS4_GPD0(2) #define PWM3_GPIO EXYNOS4_GPD0(3) static struct pwm_device *pwm4buzzer; static struct semaphore lock; static int period_ns = 1000; static unsigned long freq = 50; static unsigned long duty = 2; static void pwm_set_freq(void) { //配置周期 period_ns = NS_IN_1HZ / freq; //配置占空比 if(duty < 1) duty = 1; pwm_disable(pwm4buzzer); pwm_config(pwm4buzzer, period_ns / duty, period_ns); pwm_enable(pwm4buzzer); //配置相应的GPIO,蜂鸣器IO配置成PWM输出模式 s3c_gpio_cfgpin(PWM3_GPIO, S3C_GPIO_SFN(2)); } static void pwm_stop(void) { s3c_gpio_cfgpin(PWM3_GPIO, S3C_GPIO_OUTPUT); pwm_config(pwm4buzzer, 0, NS_IN_1HZ / 100); pwm_disable(pwm4buzzer); } static int ops_pwm_open(struct inode *inode, struct file *file) { if(!down_trylock(&lock)) return 0; else return -EBUSY; } static int ops_pwm_close(struct inode *inode, struct file *file) { up(&lock); return 0; } static long ops_pwm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { if(_IOC_TYPE(cmd)!=PWM_MAGIC) return -EINVAL; if(_IOC_NR(cmd) > PWM_MAX_NR) return -EINVAL; switch(cmd) { case PWM_IOCTL_SET_FREQ: if(arg == 0) return -EINVAL; get_user(freq, (unsigned long __user *)arg); printk(KERN_EMERG "freq is %ld ", freq); pwm_set_freq(); break; case PWM_IOCTL_STOP: pwm_stop(); break; case PWM_IOCTL_SET_DUTY: get_user(duty, (unsigned long __user *)arg); printk(KERN_EMERG "duty is %ld ", duty); pwm_set_freq(); break; default: pwm_stop(); break; } return 0; } static struct file_operations pwm_ops = { .owner = THIS_MODULE, .open = ops_pwm_open, .release = ops_pwm_close, .unlocked_ioctl = ops_pwm_ioctl, }; static struct miscdevice pwm_misc_dev = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &pwm_ops, }; static int __init iTop4412_pwm_dev_init(void) { int ret; gpio_free(PWM3_GPIO); ret = gpio_request(PWM3_GPIO, DEVICE_NAME); if(ret) { printk(KERN_EMERG "request GPIO %d for pwm failed ", PWM3_GPIO); return ret; } gpio_set_value(PWM3_GPIO, 0); s3c_gpio_cfgpin(PWM3_GPIO, S3C_GPIO_OUTPUT); pwm4buzzer = pwm_request(PWM3_ID, DEVICE_NAME); if (IS_ERR(pwm4buzzer)) { printk(KERN_EMERG "request pwm %d for %s failed ", PWM3_ID, DEVICE_NAME); return -ENODEV; } pwm_stop(); sema_init(&lock, 1); ret = misc_register(&pwm_misc_dev); if(ret < 0) { gpio_free(BUZZER_PMW_GPIO); pwm_free(pwm4buzzer); misc_deregister(&pwm_misc_dev); } printk(KERN_EMERG " initialized "); return ret; } static void __exit iTop4412_pwm_dev_exit(void) { gpio_free(BUZZER_PMW_GPIO); pwm_free(pwm4buzzer); misc_deregister(&pwm_misc_dev); printk(KERN_EMERG " exit "); return; } module_init(iTop4412_pwm_dev_init); module_exit(iTop4412_pwm_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Chen Tuo"); MODULE_DESCRIPTION("Exynos4 PWM Driver");
然后是应用程序:
#include <stdio.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <stdlib.h> #include <sys/ioctl.h> #define PWM2_NAME "/dev/my_pwm3_dev" #define PWM_MAGIC 'p' #define PWM_MAX_NR 3 #define PWM_IOCTL_STOP _IO(PWM_MAGIC, 0) #define PWM_IOCTL_SET_FREQ _IO(PWM_MAGIC, 1) #define PWM_IOCTL_SET_DUTY _IO(PWM_MAGIC, 2) int fd = -1; void open_pwm1(void); void close_pwm1(void); void set_pwm_freq(int freq); void set_pwm_duty(int duty); void stop_pwm(void); void open_pwm1(void) { fd = open(PWM2_NAME, O_RDWR); if(fd < 0) { perror("open pwm1 device"); exit(1); } // atexit(close_pwm1); } void close_pwm1(void) { if(fd >= 0) { ioctl(fd, PWM_IOCTL_STOP); close(fd); fd = -1; } } void set_pwm_freq(int freq) { int ret = ioctl(fd, PWM_IOCTL_SET_FREQ, &freq); if(ret < 0) { perror("set the frequency of the buzzer"); exit(1); } } void set_pwm_duty(int duty) { int ret = ioctl(fd, PWM_IOCTL_SET_DUTY, &duty); if(ret < 0) { perror("set the duty of the pwm"); exit(1); } } void stop_pwm(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_pwm1(); stop_pwm(); if(argc < 3) { printf("usage:%s [cmd] [arg] ", argv[0]); printf(" stop:%s [0] [0] ",argv[0]); printf(" set freq:%s [1] [arg] ", argv[0]); printf(" set duty:%s [2] [arg] ", argv[0]); } switch(atoi(argv[1])) { case 2: set_pwm_duty(atoi(argv[2])); break; case 1: set_pwm_freq(atoi(argv[2])); break; case 0: default: stop_pwm(); return -1; } return 0; }
最后是Makefile:
TARGET_NAME = my_pwm3 #TARGET_NAME = example_pwm APP1_NAME = my_pwm3_app #APP2_NAME = open_atomic_int_two obj-m += $(TARGET_NAME).o KDIR := /home/topeet/chen/kernel-3.0/iTop4412_Kernel_3.0 PWD ?= $(shell pwd) all:app1 make -C $(KDIR) M=$(PWD) modules app1: arm-none-linux-gnueabi-gcc $(APP1_NAME).c -o $(APP1_NAME) -static #app2: # arm-none-linux-gnueabi-gcc $(APP2_NAME).c -o $(APP2_NAME) -static clean: rm -rf *.o *.ko *.mod.c *.symvers *.order .$(TARGET_NAME)* $(APP1_NAME) $(APP2_NAME)
二、4412 I2C_SDA7和I2C_SCL7
注释平台文件中的代码:
// s3c_i2c7_set_platdata(NULL); // i2c_register_board_info(7, i2c_devs7, ARRAY_SIZE(i2c_devs7));
和这个
// &s3c_device_i2c7,
然后make menuconfig
>>Device Drivers>>Input device support>>Touchscreens【】取消选择
三、尝试编写控制PWM波形的驱动和应用程序
my_pwm3.c(驱动)
#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 <linux/gpio.h> #include <mach/gpio.h> #include <plat/gpio-cfg.h> #include <linux/ioctl.h> #include <asm-generic/uaccess.h> #define DEVICE_NAME "my_pwm3_dev" #define DRIVER_NAME "my_pwm3_drv" #define PWM_MAGIC 'p' #define PWM_MAX_NR 3 #define PWM_IOCTL_STOP _IO(PWM_MAGIC, 0) #define PWM_IOCTL_SET_FREQ _IO(PWM_MAGIC, 1) #define PWM_IOCTL_SET_DUTY _IO(PWM_MAGIC, 2) #define NS_IN_1HZ (1000000000UL) #define BUZZER_PWM_ID 0 #define PWM1_ID 1 #define PWM2_ID 2 #define PWM3_ID 3 #define BUZZER_PMW_GPIO EXYNOS4_GPD0(0) #define PWM1_GPIO EXYNOS4_GPD0(1) #define PWM2_GPIO EXYNOS4_GPD0(2) #define PWM3_GPIO EXYNOS4_GPD0(3) static struct pwm_device *pwm4buzzer; static struct semaphore lock; static int period_ns = 1000; static unsigned long freq = 50; static unsigned long duty = 2; static void pwm_set_freq(void) { //配置周期 period_ns = NS_IN_1HZ / freq; //配置占空比 if(duty < 2) duty = 2; pwm_disable(pwm4buzzer); pwm_config(pwm4buzzer, period_ns / duty, period_ns); pwm_enable(pwm4buzzer); //配置相应的GPIO,蜂鸣器IO配置成PWM输出模式 s3c_gpio_cfgpin(PWM3_GPIO, S3C_GPIO_SFN(2)); } static void pwm_stop(void) { s3c_gpio_cfgpin(PWM3_GPIO, S3C_GPIO_OUTPUT); pwm_config(pwm4buzzer, 0, NS_IN_1HZ / 100); pwm_disable(pwm4buzzer); } static int ops_pwm_open(struct inode *inode, struct file *file) { if(!down_trylock(&lock)) return 0; else return -EBUSY; } static int ops_pwm_close(struct inode *inode, struct file *file) { up(&lock); return 0; } static long ops_pwm_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { if(_IOC_TYPE(cmd)!=PWM_MAGIC) return -EINVAL; if(_IOC_NR(cmd) > PWM_MAX_NR) return -EINVAL; switch(cmd) { case PWM_IOCTL_SET_FREQ: if(arg == 0) return -EINVAL; get_user(freq, (unsigned long __user *)arg); printk(KERN_EMERG "freq is %ld ", freq); pwm_set_freq(); break; case PWM_IOCTL_STOP: pwm_stop(); break; case PWM_IOCTL_SET_DUTY: get_user(duty, (unsigned long __user *)arg); printk(KERN_EMERG "duty is %ld ", duty); pwm_set_freq(); break; default: pwm_stop(); break; } return 0; } static struct file_operations pwm_ops = { .owner = THIS_MODULE, .open = ops_pwm_open, .release = ops_pwm_close, .unlocked_ioctl = ops_pwm_ioctl, }; static struct miscdevice pwm_misc_dev = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &pwm_ops, }; static int __init iTop4412_pwm_dev_init(void) { int ret; gpio_free(PWM3_GPIO); ret = gpio_request(PWM3_GPIO, DEVICE_NAME); if(ret) { printk(KERN_EMERG "request GPIO %d for pwm failed ", PWM3_GPIO); return ret; } gpio_set_value(PWM3_GPIO, 0); s3c_gpio_cfgpin(PWM3_GPIO, S3C_GPIO_OUTPUT); pwm4buzzer = pwm_request(PWM3_ID, DEVICE_NAME); if (IS_ERR(pwm4buzzer)) { printk(KERN_EMERG "request pwm %d for %s failed ", PWM3_ID, DEVICE_NAME); return -ENODEV; } pwm_stop(); sema_init(&lock, 1); ret = misc_register(&pwm_misc_dev); if(ret < 0) { gpio_free(BUZZER_PMW_GPIO); pwm_free(pwm4buzzer); misc_deregister(&pwm_misc_dev); } printk(KERN_EMERG " initialized "); return ret; } static void __exit iTop4412_pwm_dev_exit(void) { gpio_free(BUZZER_PMW_GPIO); pwm_free(pwm4buzzer); misc_deregister(&pwm_misc_dev); printk(KERN_EMERG " exit "); return; } module_init(iTop4412_pwm_dev_init); module_exit(iTop4412_pwm_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Chen Tuo"); MODULE_DESCRIPTION("Exynos4 PWM Driver");
my_pwm3_app.c
#include <stdio.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/types.h> #include <stdlib.h> #include <sys/ioctl.h> #define PWM2_NAME "/dev/my_pwm3_dev" #define PWM_MAGIC 'p' #define PWM_MAX_NR 3 #define PWM_IOCTL_STOP _IO(PWM_MAGIC, 0) #define PWM_IOCTL_SET_FREQ _IO(PWM_MAGIC, 1) #define PWM_IOCTL_SET_DUTY _IO(PWM_MAGIC, 2) int fd = -1; void open_pwm1(void); void close_pwm1(void); void set_pwm_freq(int freq); void set_pwm_duty(int duty); void stop_pwm(void); void open_pwm1(void) { fd = open(PWM2_NAME, O_RDWR); if(fd < 0) { perror("open pwm1 device"); exit(1); } // atexit(close_pwm1); } void close_pwm1(void) { if(fd >= 0) { ioctl(fd, PWM_IOCTL_STOP); close(fd); fd = -1; } } void set_pwm_freq(int freq) { int ret = ioctl(fd, PWM_IOCTL_SET_FREQ, &freq); if(ret < 0) { perror("set the frequency of the buzzer"); exit(1); } } void set_pwm_duty(int duty) { int ret = ioctl(fd, PWM_IOCTL_SET_DUTY, &duty); if(ret < 0) { perror("set the duty of the pwm"); exit(1); } } void stop_pwm(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_pwm1(); stop_pwm(); if(argc < 3) { printf("usage:%s [cmd] [arg] ", argv[0]); printf(" stop:%s [0] [0] ",argv[0]); printf(" set freq:%s [1] [arg] ", argv[0]); printf(" set duty:%s [2] [arg] ", argv[0]); } switch(atoi(argv[1])) { case 2: set_pwm_duty(atoi(argv[2])); break; case 1: set_pwm_freq(atoi(argv[2])); break; case 0: default: stop_pwm(); return -1; } return 0; }
Makefile
TARGET_NAME = my_pwm3 #TARGET_NAME = example_pwm APP1_NAME = my_pwm3_app #APP2_NAME = open_atomic_int_two obj-m += $(TARGET_NAME).o KDIR := /home/topeet/chen/kernel-3.0/iTop4412_Kernel_3.0 PWD ?= $(shell pwd) all:app1 make -C $(KDIR) M=$(PWD) modules app1: arm-none-linux-gnueabi-gcc $(APP1_NAME).c -o $(APP1_NAME) -static #app2: # arm-none-linux-gnueabi-gcc $(APP2_NAME).c -o $(APP2_NAME) -static clean: rm -rf *.o *.ko *.mod.c *.symvers *.order .$(TARGET_NAME)* $(APP1_NAME) $(APP2_NAME)
总结:内核接收ioctl的信息时需要使用特定的函数进行保护,防止被乱七八糟的传入数据。