linux驱动开发之路(三)——嵌入式 Linux 的蜂鸣器控制实验(misc设备)

实验内容介绍见《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) 原理图:

linux驱动开发之路(三)——嵌入式 Linux 的蜂鸣器控制实验(misc设备)_第1张图片


b) 系统框架

linux驱动开发之路(三)——嵌入式 Linux 的蜂鸣器控制实验(misc设备)_第2张图片

由该图可知,在选取了 PCLK 作为时钟源后,需经过 8BIT PRESCALER0 分频器,而后,经过 1/1 或 1/2 或 1/4 或 1/8 或 1/16 分频器后,送到逻辑控制电路(Control LogicX)后,被送到引脚输出。所以,对 PWM 的操作就是围绕设置时钟分频,得到合适的频率,然后,设置逻辑控制电路得到合适的脉宽。


c) 寄存器简介:


linux驱动开发之路(三)——嵌入式 Linux 的蜂鸣器控制实验(misc设备)_第3张图片

linux驱动开发之路(三)——嵌入式 Linux 的蜂鸣器控制实验(misc设备)_第4张图片

寄存器 TCFG0 用于配置 PWM 的预分频比,TCFG0[15:8]八位用于配置计时器 2、3、4 (timer2、timer3、timer4)的预分频值。TCFG0[7:0]八位用于配置计时器 0、1 的预分频值。
寄存器 TCFG1 用于配置 PWM 的分频比。

linux驱动开发之路(三)——嵌入式 Linux 的蜂鸣器控制实验(misc设备)_第5张图片

PWM 控制寄存器 TCON,设置启动 PWM,装载寄存器,输出反相及寄存器自动装载等功能。

linux驱动开发之路(三)——嵌入式 Linux 的蜂鸣器控制实验(misc设备)_第6张图片

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 

#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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define BUZZER_IOCTL_SET_FREQ	1
#define BUZZER_IOCTL_STOP	0
	
void Usage(char *args)
{
	printf("Usage: %s  \n",args);
	return ;
}
	
int main(int argc,char **argv)
{
	int buzzer_fd;
	unsigned long freq;
	char *endstr,*str;
	printf("Usage: %s  \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)	


你可能感兴趣的:(驱动程序)