您的位置:首页 > 运维架构 > Linux

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、 驱动代码分析:

(详细代码请查看附件)

< 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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: