linux驱动开发之路(三)——嵌入式 Linux 的蜂鸣器控制实验(misc设备)
2014-09-03 10:09
686 查看
实验内容介绍见《GEC210嵌入式系统开发教材20131120(更新)》第102页--“5.4 嵌入式 Linux 的蜂鸣器控制实验”
5.4 嵌入式 Linux 的蜂鸣器控制实验
实验目的:
1、 掌握基本的字符设备的驱动程序设计。
2、 掌握基本的文件操作。
3、 掌握在操作系统下的 PWM 驱动程序的原理。
实验内容:
1、 阅读 S5PV210 数据手册,熟悉 PWM 的原理。
2、 编写 PWM 输出的应用程序。
3、 编写 makefile 文件
4、 下载并调试 PWM 输出的应用程序。
预备知识:
1、 C 语言基础知识。
2、 Linux 下常用命令的使用及 vim 编辑器的使用
3、 程序调试的基础知识和方法。
4、 ARM 应用程序的基本架构。
实验设备及工具:
1、 硬件:GEC210 物联网开发板
2、 软件:PC 操作系统 Ubuntu10.04 、minicom、arm-linux-交叉编译环
基础知识:
1、 硬件原理:
a) 原理图:
b) 系统框架
由该图可知,在选取了 PCLK 作为时钟源后,需经过 8BIT PRESCALER0 分频器,而后,经过 1/1 或 1/2 或 1/4 或 1/8 或 1/16 分频器后,送到逻辑控制电路(Control LogicX)后,被送到引脚输出。所以,对 PWM 的操作就是围绕设置时钟分频,得到合适的频率,然后,设置逻辑控制电路得到合适的脉宽。
c) 寄存器简介:
寄存器 TCFG0 用于配置 PWM 的预分频比,TCFG0[15:8]八位用于配置计时器 2、3、4 (timer2、timer3、timer4)的预分频值。TCFG0[7:0]八位用于配置计时器 0、1 的预分频值。
寄存器 TCFG1 用于配置 PWM 的分频比。
PWM 控制寄存器 TCON,设置启动 PWM,装载寄存器,输出反相及寄存器自动装载等功能。
TCNTB0 是 timer0 的定时器计数寄存器。
TCMPB0 是 timer0 的定时器比较寄存器。
TCNTO0 是 timer0 的定时器计数寄寄存器,可以从中读出当前计数寄存器中的值。
实验原理:
1、 PWM 输出的原理:PWM 输出脚,默认为低电平,PWM 计数器 TCNTn 的初始值等于 TCNTBn,当 TCNTn 的值递减到等于 TCMPBn 的值时,PWM 输出高电平,当 PWM 计数器的递减到 0 时,输出又变为低电平,如此周而复始。
2、 驱动原理:
该驱动实现为一个字符设备,通过 ioctl 函数来设置相关寄存器的值,以此来实现 PWM波形的输出与禁止
3、 驱动代码分析:
(详细代码请查看附件)
驱动测试程序:
5.4 嵌入式 Linux 的蜂鸣器控制实验
实验目的:
1、 掌握基本的字符设备的驱动程序设计。
2、 掌握基本的文件操作。
3、 掌握在操作系统下的 PWM 驱动程序的原理。
实验内容:
1、 阅读 S5PV210 数据手册,熟悉 PWM 的原理。
2、 编写 PWM 输出的应用程序。
3、 编写 makefile 文件
4、 下载并调试 PWM 输出的应用程序。
预备知识:
1、 C 语言基础知识。
2、 Linux 下常用命令的使用及 vim 编辑器的使用
3、 程序调试的基础知识和方法。
4、 ARM 应用程序的基本架构。
实验设备及工具:
1、 硬件:GEC210 物联网开发板
2、 软件:PC 操作系统 Ubuntu10.04 、minicom、arm-linux-交叉编译环
基础知识:
1、 硬件原理:
a) 原理图:
b) 系统框架
由该图可知,在选取了 PCLK 作为时钟源后,需经过 8BIT PRESCALER0 分频器,而后,经过 1/1 或 1/2 或 1/4 或 1/8 或 1/16 分频器后,送到逻辑控制电路(Control LogicX)后,被送到引脚输出。所以,对 PWM 的操作就是围绕设置时钟分频,得到合适的频率,然后,设置逻辑控制电路得到合适的脉宽。
c) 寄存器简介:
寄存器 TCFG0 用于配置 PWM 的预分频比,TCFG0[15:8]八位用于配置计时器 2、3、4 (timer2、timer3、timer4)的预分频值。TCFG0[7:0]八位用于配置计时器 0、1 的预分频值。
寄存器 TCFG1 用于配置 PWM 的分频比。
PWM 控制寄存器 TCON,设置启动 PWM,装载寄存器,输出反相及寄存器自动装载等功能。
TCNTB0 是 timer0 的定时器计数寄存器。
TCMPB0 是 timer0 的定时器比较寄存器。
TCNTO0 是 timer0 的定时器计数寄寄存器,可以从中读出当前计数寄存器中的值。
实验原理:
1、 PWM 输出的原理:PWM 输出脚,默认为低电平,PWM 计数器 TCNTn 的初始值等于 TCNTBn,当 TCNTn 的值递减到等于 TCMPBn 的值时,PWM 输出高电平,当 PWM 计数器的递减到 0 时,输出又变为低电平,如此周而复始。
2、 驱动原理:
该驱动实现为一个字符设备,通过 ioctl 函数来设置相关寄存器的值,以此来实现 PWM波形的输出与禁止
3、 驱动代码分析:
(详细代码请查看附件)
< driver / beep_driver.c > /* ** **This is a beep driver. **The author is Alan. ** ** */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/platform_device.h> #include <linux/fd.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 //设置PWM的输出频率 #define PWM_IOCTL_STOP 0 //停止PWM #define NS_IN_1HZ (1000000000UL)//用于设置计数器值来获取相应频率 #define BUZZER_PWM_ID 0 #define BUZZER_PWM_GPIO S5PV210_GPD0(0)//蜂鸣器的输出端口 static struct pwm_device *pwm4buzzer;//pwm设备结构 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);//使能pwm } static void pwm_stop(void) { pwm_config( pwm4buzzer,0,NS_IN_1HZ / 100 ); pwm_disable(pwm4buzzer); } static int gec210_pwm_open(struct inode *inode ,struct file *file) { if(!down_trylock(&lock))//获取信号锁 return 0; else return -EBUSY; } static int gec210_pwm_close(struct inode *inode,struct file *file) { up(&lock);//p操作,即释放信号锁 return 0; } static long gec210_pwm_ioctl(struct file *filp , 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: break; } return 0; } static struct file_operations gec210_pwm_ops = {//pwm操作接口 .owner = THIS_MODULE, .open = gec210_pwm_open, .release = gec210_pwm_close, .unlocked_ioctl = gec210_pwm_ioctl, }; static struct miscdevice gec210_misc_dev = {//misc设备 .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &gec210_pwm_ops, }; static int __init gec210_pwm_dev_init(void) { int ret; ret = gpio_request(BUZZER_PWM_GPIO, DEVICE_NAME); if(ret) { printk("request GPIO %d for pwm failed\n",BUZZER_PWM_GPIO); return ret; } gpio_set_value(BUZZER_PWM_GPIO,0); s3c_gpio_cfgpin(BUZZER_PWM_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(); s3c_gpio_cfgpin(BUZZER_PWM_GPIO,S3C_GPIO_SFN(2)); gpio_free(BUZZER_PWM_GPIO); init_MUTEX(&lock);//初始化信号锁 ret = misc_register(&gec210_misc_dev);//注册misc设备 printk(DEVICE_NAME "\tinitiazed\n"); return ret; } static void __exit gec210_pwm_dev_exit(void) { pwm_stop(); //停止pwm misc_deregister(&gec210_misc_dev); //接触misc设备 } module_init(gec210_pwm_dev_init); module_exit(gec210_pwm_dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("ALAN"); MODULE_DESCRIPTION("This is a pwm_beep driver!");
< driver / Makefile > ifneq ($(KERNELRELEASE),) obj-m :=beep_driver.o else module-objs :=beep_driver.o KERNELDIR :=/home/gec/linux_kernel/linux2.6.35.7/ PWD :=$(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean: $(RM) *.ko *.mod.c *.mod.o *.o *.order *.symvers *.cmd
驱动测试程序:
< app / beep_test.c > #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <sys/time.h> #include <errno.h> #include <limits.h> #define BUZZER_IOCTL_SET_FREQ 1 #define BUZZER_IOCTL_STOP 0 void Usage(char *args) { printf("Usage: %s <on/off> <freq>\n",args); return ; } int main(int argc,char **argv) { int buzzer_fd; unsigned long freq; char *endstr,*str; printf("Usage: %s <on/off> <freq>\n","./beep_test"); if( argc==3 ) { //例: ./beep_test on 100 或者 ./beep_test off 100 buzzer_fd = open( "/dev/pwm",O_RDWR );//打开设备 if(buzzer_fd<0) { perror("open device:"); exit(1); } str = argv[2]; errno = 0; freq = strtol(str , &endstr,0); if((errno == ERANGE &&(freq == LONG_MAX || freq == LONG_MIN))||(errno != 0 && freq == 0)) { perror("freq :"); exit(EXIT_FAILURE); } if(endstr == str) { fprintf(stderr,"Please input a digits for freq\n"); exit(EXIT_FAILURE); } if(!strncmp(argv[1],"on",2))//判断是否为打开指令 { ioctl(buzzer_fd,BUZZER_IOCTL_SET_FREQ,freq); } else if (!strncmp(argv[1],"off",3))//判断是否为关闭指令 { ioctl(buzzer_fd,BUZZER_IOCTL_STOP,freq); } else { close(buzzer_fd);//出错处理 exit(EXIT_FAILURE); } } else if (argc == 2) //例: ./beep_test off { buzzer_fd = open("/dev/pwm",O_RDWR); if(buzzer_fd<0) { perror("open device:"); exit(1); } if(!strncmp(argv[1],"off",3)) //判断是否为关闭指令 { ioctl(buzzer_fd,BUZZER_IOCTL_STOP,freq); } else { close(buzzer_fd); exit(EXIT_FAILURE); } } else //输入的参数不等于 3 或者 2 { Usage(argv[0]); exit(EXIT_FAILURE); } close(buzzer_fd); return 0; }
< app / Makefile > SRC =${wildcard *.c} OBJ =${patsubst %.c,%.o,$(SRC)} TARGET = beep-test CROSS = arm-linux- CC =$(CROSS)gcc .phony:clean echo $(TARGET): $(OBJ) $(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBM) $(LDLIBS) $(LIBGCC) -lm clean: $(RM) $(OBJ) $(TARGET) *.elf *.gdb echo: @echo $(OBJ) $(TARGET)
相关文章推荐
- 实验题目:实现嵌入式Linux系统下的字符设备驱动程序(报告)
- 嵌入式Linux应用学习(一)------QT控制LED设备硬件
- 向嵌入式Linux移植实时设备驱动程序
- 嵌入式设备上的 Linux 系统开发(2)(转)
- 嵌入式设备上的 Linux 系统开发
- [转]嵌入式设备上的 Linux 系统开发(2)
- [转]嵌入式设备上的 Linux 系统开发(1)
- 异军突起:Linux抢占嵌入式设备市场
- ARM的嵌入式Linux移植体验之设备驱动
- 构建嵌入式Linux系统-存储设备管理
- 嵌入式设备上的 Linux 系统开发
- 如何在嵌入式LINUX中增加自己的设备驱动程序
- 〔转贴〕如何在嵌入式LINUX中增加自己的设备驱动程序
- 基于ARM的嵌入式Linux移植真实体验(4)――设备驱动
- 嵌入式设备上的 Linux 系统开发
- 嵌入式设备上的 Linux 系统开发
- 嵌入式设备上的 Linux 系统开发(1)(转)
- 嵌入式设备上的 Linux 系统开发
- 如何在嵌入式LINUX中添加设备驱动程序
- ARM的嵌入式Linux移植体验之设备驱动