嵌入式驱动学习之PWM蜂鸣器驱动
·
开发环境
主机开发环境:ubuntu12.04
BootLoader:u-boot-1.1.6
kernel:linux-2.6.30.4
CPU:s3c2440
开发板:TQ2440
开发步骤
1、硬件分析
硬件电路图如下:
在驱动程序里面首先初始化该 PWM 所对应的管脚的功能,然后初始化定时器设置对 PWM 的定时,最后启动 PWM 功能。
2、蜂鸣器驱动代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <plat/regs-timer.h>
#include <mach/regs-irq.h>
#include <asm/mach/time.h>
#include <linux/clk.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#define DEVICE_NAME "Beep"
#define BEEP_MAJOR 233 /* 主设备号 */
/* 应用程序对设备文件/dev/Beep 执行 ioclt(...)时,
* 就会调用 le2440_beep_ioctl 函数
*/
static int le2440_beep_ioctl(
struct inode *inode,
struct file *file,
unsigned int cmd,
unsigned long arg)
{
unsigned long temp;
unsigned long gpbcon;
unsigned long tcfg0;
unsigned long tcfg1;
unsigned long tcntb;
unsigned long tcmpb;
unsigned long tcon;
if(cmd <= 0)
{
temp = __raw_readl(S3C2410_GPBCON); //GPBCON
temp &= ~3;
temp |= 1;
__raw_writel(temp, S3C2410_GPBCON);
temp = __raw_readl(S3C2410_GPBDAT); //GPBDAT
temp &= ~1;
__raw_writel(temp, S3C2410_GPBDAT);
}
else
{
gpbcon = __raw_readl(S3C2410_GPBCON); //GPBCON
gpbcon &= ~3;
gpbcon |= 2;
__raw_writel(gpbcon, S3C2410_GPBCON);
tcfg0 = __raw_readl(S3C2410_TCFG0); //TCFG0
tcfg0 &= ~0xff;
tcfg0 |= 15;
__raw_writel(tcfg0, S3C2410_TCFG0);
tcfg1 = __raw_readl(S3C2410_TCFG1); //TCFG1
tcfg1 &= ~0xf;
tcfg1 |= 2;
__raw_writel(tcfg1, S3C2410_TCFG1);
tcntb = (50000000/128)/cmd;
__raw_writel(tcntb, S3C2410_TCNTB(0));
tcmpb >>= 1;
__raw_writel(tcmpb, S3C2410_TCMPB(0));
tcon = __raw_readl(S3C2410_TCON); //TCON
tcon &= ~0x1f;
tcon |= 0xb;
__raw_writel(tcon, S3C2410_TCON);
tcon &= ~2;
__raw_writel(tcon, S3C2410_TCON);
}
return 0;
}
/* 这个结构是字符设备驱动程序的核心
* 当应用程序操作设备文件时所调用的 open、read、write、ioctl 等函数,
* 最终会调用这个结构中指定的对应函数
*/
static struct file_operations le2440_beep_fops = {
.owner = THIS_MODULE,
.ioctl = le2440_beep_ioctl,
};
static struct class *beep_class;
/*
* 执行“insmod le2440_beep.ko”命令时就会调用这个函数
*/
static int __init le2440_beep_init(void)
{
int ret;
/* 注册字符设备驱动程序
* 参数为主设备号、设备名字、file_operations 结构;
* 这样,主设备号就和具体的 file_operations 结构联系起来了,
* 操作主设备为 BEEP_MAJOR 的设备文件时,就会调用 le2440_beep_fops 中的相关成员函数
* BEEP_MAJOR 可以设为 0,表示由内核自动分配主设备号
*/
ret = register_chrdev(BEEP_MAJOR, DEVICE_NAME, &le2440_beep_fops);
if (ret < 0)
{
printk(DEVICE_NAME " can't register major number\n");
return ret;
}
//注册一个类,使 mdev 可以在"/dev/"目录下面建立设备节点
beep_class = class_create(THIS_MODULE, DEVICE_NAME);
if(IS_ERR(beep_class))
{
printk("Err: failed in le2440-Beep class. \n");
return -1;
}
//创建一个设备节点,节点名为 DEVICE_NAME
device_create(beep_class, NULL, MKDEV(BEEP_MAJOR, 0), NULL, DEVICE_NAME);
printk(DEVICE_NAME " initialized\n");
return 0;
}
/*
* 执行”rmmod le2440_beep.ko”命令时就会调用这个函数
*/
static void __exit le2440_beep_exit(void)
{
/* 卸载驱动程序 */
unregister_chrdev(BEEP_MAJOR, DEVICE_NAME);
device_destroy(beep_class, MKDEV(BEEP_MAJOR, 0)); //删掉设备节点
class_destroy(beep_class); //注销类
}
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(le2440_beep_init);
module_exit(le2440_beep_exit);
MODULE_LICENSE("GPL"); // 遵循的协议
Makefile如下:
#Makefile
ifeq ($(KERNELRELEASE),)
KERNELDIR ?= /home/linux/sky/ker/linux-2.6.30.4/
#KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD)
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
.PHONY: modules modules_install clean
else
obj-m := le2440_beep.o
endif
3、app控制程序
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
struct spihr
{
unsigned short status;
unsigned short dat;
};
int main(int argc, char **argv)
{
int fd;
unsigned long temp =0;
int i;
fd = open("/dev/Beep", 0);
if (fd < 0) {
perror("open device beep");
exit(1);
}
printf("Please enter the times number (0 is stop) :\n");
for(i=0;;i++)
{
scanf("%d",&temp);
printf("times = %d \n",temp);
if(temp == 0)
{
ioctl(fd, 0);
printf("Stop Control Beep!\n");
break;
}
else
{
ioctl(fd, 1, temp);
}
}
close(fd);
return 0;
}
Makefile如下:
CROSS=arm-linux-
all: beep
beep: beep.c
$(CROSS)gcc -o beep beep.c
$(CROSS)strip beep
clean:
@rm -vf beep *.o *~
4、测试现象

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)