开发环境

主机开发环境: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、测试现象

在这里插入图片描述

Logo

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

更多推荐