王吉豪,崔建明
(山东科技大学 电子通信与物理学院,青岛 266590)
嵌入式Linux的时钟语音芯片YF017驱动设计※
王吉豪,崔建明
(山东科技大学 电子通信与物理学院,青岛 266590)
摘要:将低成本的时钟语音芯片融入到嵌入式Linux系统中,不但可以丰富嵌入式设备的功能,而且可以降低开发成本。本文以ARM架构芯片S3C2440结合Linux2.6.22内核作为实例,开发出语音报时芯片YF017的驱动程序,并写出该驱动程序的上层应用程序。测试结果表明,通过上层应用程序可以准确地调用驱动程序,并使语音芯片发出相应的语音。
关键词:嵌入式Linux;驱动程序;S3C2440;时钟语音芯片;YF017
引言
在嵌入式Linux系统中加入语音功能,传统方式是采用声卡芯片,但是对于仅需简单语音的场合,使用声卡芯片作为发音设备在功能上显得多余,在成本上也大大提高。
YF017系列语音芯片是针对市场推出的一款具有PWM输出的OTP语音标准芯片,共有3个I/O口,该芯片可以直接驱动喇叭,无需再设计音频放大电路,外围最少仅需要一个0.1 μF电容就可以稳定工作,把该芯片用到简单的语音场合可大大节约开发时间,降低开发成本。
在嵌入式Linux系统中实现简单的语音功能,YF017系列语音芯片成为首选之一。但是现成的Linux内核中并没有集成YF017系列语音芯片的驱动,因此,本文将着重实现YF017语音芯片的Linux驱动程序,并实现上层应用程序以验证驱动程序的正确性。
1YF017芯片介绍
1.1芯片引脚介绍
图1 YF017芯片引脚图
YF017语音芯片有8个引脚,如图1所示。其中:VDD、GND为电源引脚,工作电压为2.2~6 V,适用范围很宽。PWM1、PWM2为PWM输出引脚,用于驱动8~16 Ω范围内的任何喇叭(建议0.25~1 W)。Vreg为调节引脚,在外接电压大于4.5 V时,需要在Vreg和GND之间接0.1 μF电容,用于减少电源噪声,外接电压小于4.5 V时可不接电容。BSY、DAT、RST为数字I/O引脚,可以与控制芯片连接。
BSY:芯片播放声音时输出低电平,待机时保持高电平。
DAT:用于控制芯片内播放指针,收到几个上升沿脉冲,就播放第几个地址的声音内容。
RST:用于复位芯片内播放指针,当该引脚收到一个上升沿脉冲时,可以使芯片的播放指针归零,同时,芯片进入待机状态。
1.2芯片语音内容
主控芯片通过发送相应数量的上升沿脉冲到DAT引脚,语音芯片可以播放相应的声音,脉冲数量与播放声音的对应关系如表1所列。
表1 对应关系
1.3芯片控制时序
主控芯片上电时,需要先将语音芯片的DAT引脚和RST引脚分别置于低电平。如果需要播放第25段声音,先将RST引脚拉高,过1 ms再拉低,使播放指针复位。接着连续发送25个上升沿脉冲到DATA引脚,芯片即可播放第25段的声音。如果接下来需要播放第22段的声音,主控芯片需要检测语音芯片的BSY引脚是否为高电平,如果BSY为低电平,则表示语音芯片正忙,主控芯片需要等待,直到BSY引脚为高电平时,可按照之前的步骤,先发送上升沿脉冲给RST,再发送22个上升沿脉冲给DAT引脚,这样可播放语音芯片中的任意语音。RST和DAT脉冲宽度至少为0.2 ms,建议1 ms。
2YF017驱动程序设计
2.1硬件原理图
将语音芯片接入主控芯片为S3C2440的嵌入式Linux的开发板中,S3C2440芯片的GPIO引脚GPG5、GPG6、GPG7分别接语音芯片的RST、DAT、BSY引脚,如图2所示。
2.2驱动程序设计
2.2.1入口函数speak_init
入口函数speak_init使用register_chrdev函数向内核申请主设备号,把设备的操作函数结构体speak_ops向内核注册,并使用class_create函数和class_device_create函数在Linux文件系统的/sys/目录下生成设备信息,调用mdev应用程序。 mdev应用程序根据/sys/目录下生成的设备信息在/dev/目录下创建设备节点,该程序的设备节点为/dev/speak。
2.2.2出口函数speak_exit
图2 硬件原理图
出口函数speak_exit使用unregister_chrdev函数释放之前向内核申请的主设备号,并使用class_device_destroy函数和class_destroy函数把之前在文件系统/sys/目录下生成的设备信息删除,并调用mdev应用程序。 mdev应用程序根据/sys/目录下设备信息的变动,将/dev/目录下创建的设备节点speak删除。
2.2.3底层操作函数
把设备的底层操作函数都指向操作函数结构体speak_ops,以便使用register_chrdev函数向内核注册。speak_ops结构体定义如下:
static struct file_operations speak_ops = {
.owner = THIS_MODULE,
.open = speak_open,
.read = speak_read,
.write = speak_write,
};
(1) speak_open函数
该函数用于配置S3C3440的GPIO引脚,将GPG5、GPG6用作输出,GPG7用作输入,并将GPG5、GPG6初始化为低电平。当上层应用程序调用open(“/dev/speak”)函数时,将调用到speak_open函数。实现程序代码如下:
#define GPIO_RESETS3C2410_GPG5
#define GPIO_DATA S3C2410_GPG6
#define GPIO_BUSY S3C2410_GPG7
static int speak_open(struct inode *inode, struct file *file){
/* 1. 将GPG5、GPG6用作输出,GPG7用作输入*/
s3c2410_gpio_cfgpin(GPIO_RESET, S3C2410_GPIO_OUTPUT);
s3c2410_gpio_cfgpin(GPIO_DATA, S3C2410_GPIO_OUTPUT);
s3c2410_gpio_cfgpin(GPIO_BUSY, S3C2410_GPIO_INPUT);
/* 2.将GPG5、GPG6初始化为低电平 */
s3c2410_gpio_setpin(GPIO_RESET, 0);
s3c2410_gpio_setpin(GPIO_DATA, 0);
return 0;
}
(2) speak_read函数
该函数用于读取语音芯片的BSY引脚并返回1字节状态,返回1代表BSY引脚为高电平,返回0代表BSY引脚为低电平。当上层应用程序调用read()函数时将调用到speak_read函数。实现代码如下:
ssize_t speak_read (struct file *file, char __user *buf, size_t len, loff_t *pos){
unsigned char val;
/* 1.读取BSY引脚的状态 */
val = (s3c2410_gpio_getpin(GPIO_BUSY)) ?1:0;
/* 2.将BSY的状态从内核空间复制到用户空间 */
if(copy_to_user(buf, &val, 1)){
return -EFAULT;
}
return 1;
}
(3) speak_write函数
该函数把用户空间发过来的语音地址指针序号转化为控制语音芯片的脉冲。当上层应用程序调用write()函数时将调用到speak_write函数。实现代码如下:
static ssize_t speak_write (struct file *file, const char __user *buf, size_t len, loff_t *pos){
int i;
unsigned char val;
/* 1. 把用户数据从用户空间复制到内核空间 */
if(copy_from_user(&val, buf, 1)){
return -EFAULT;
}
/* 2.判断语音地址指针是否越界,越界返回错误*/
if (val > 32){
printk(KERN_NOTICE VERSION"out of band ! ");
printk(KERN_NOTICE VERSION"val = %d ", val);
return -ENXIO;
}
/*3.根据时序要求,先发复位脉冲信号到语音芯片 */
s3c2410_gpio_setpin(GPIO_RESET, 1);
udelay(1000);
s3c2410_gpio_setpin(GPIO_RESET, 0);
/* 4.根据用户传过来的数据设置语音地址指针,让语音芯片发出相应声音 */
for (i = 0; i < val; i++){
s3c2410_gpio_setpin(GPIO_DATA, 1);
udelay(1000);
s3c2410_gpio_setpin(GPIO_DATA, 0);
udelay(1000);
}
return 0;
}
2.3驱动程序编译
写Makefile并在程序所在的目录执行make命令,将编译生成drv_speak.ko文件。
Makefile内容如下:
#用于指定内核源码目录
KERN_DIR = /work/system/Linux-2.6.22.6
all:
make-C $(KERN_DIR) M=`pwd` modules
clean:
make-C $(KERN_DIR) M=`pwd` modules clean
rm-rf Module.symvers
#指定驱动源程序文件
obj-m += drv_speak.o
2.4驱动程序安装与卸载
将编译生成的drv_speak.ko文件,复制到嵌入式Linux文件系统中,执行命令insmod drv_speak.ko将把该驱动模块加载到内核中。通过rmmod drv_speak命令可将驱动卸载,insmod和rmmod命令的执行将分别调用驱动中的speak_init 和speak_exit函数。
可以通过命令 cat /proc/devices 查看speak设备的存在,也可以在/dev/目录下查看speak设备节点,在/sys/目录下查看生成的设备信息。
3YF017应用程序设计
3.1应用程序设计
应用程序通过读取本地时间并控制/dev/speak发出相应的声音来验证驱动程序的正确性。
3.1.1主函数main
int main(int argc, char* argv[]){
int fd;
time_t t;
struct tm* time_now;
/* 1.打开语音报时设备 */
fd = open("/dev/speak", O_RDWR);
if (fd < 0){
printf("can't open /dev/speak ");
return -1;
}
/* 2.获取时间 */
time(&t);
time_now = localtime(&t);
/* 3.报时 */
speak_time(fd, time_now);
return 0;
}
3.1.2报时函数speak_time
void speak_time(int fd, struct tm* t){
unsigned int month, day, week, hour, min;
/*1. 获取日期和时间 */
month = t->tm_mon + 1;
……
min = t->tm_min;
/*2. 将报时分单字段 */
speak_segment(fd, SPK_NUM_2, month);
speak_segment(fd, SPK_MONTH, 0);
speak_segment(fd, SPK_NUM_2, day);
speak_segment(fd, SPK_DAY, 0);
……
}
3.1.3单字段报音函数speak_segment
void speak_segment(int fd, unsigned int which, unsigned int val){
unsigned char addr;
/* 根据传入的命令值which和val分别调用write_and_wait函数 */
switch (which){
case SPK_MONTH:
case SPK_DAY:
……
case SPK_HOUR:
case SPK_MIN:
case SPK_TIDY:
addr = which;
write_and_wait(fd, &addr, 1);
break;
……
}
}
3.1.4写等待函数write_and_wait
该函数将用户传来的指令写到/dev/speak设备中,并用睡眠方式等待/dev/speak设备空闲。程序实现代码如下:
void write_and_wait(int fd, void* buf, int cnt){
unsigned char busy;
write(fd, buf, cnt);
do {
read(fd, &busy, 1);
usleep(1000);
}while(!busy);
}
3.2应用程序编译
使用交叉编译工具编译,执行命令“arm-Linux-gcc-o app_speak app_speak.c”将生成app_speak可执行文件。
3.3应用程序运行
将可执行文件app_speak复制到嵌入式Linux文件系统中,运行前确保drv_speak.ko已经安装到内核中。在app_speak所在目录执行./app_speak命令,将听到语音芯片发出报时声音。语音报时时间和执行date命令显示的时间一致。
结语
通过编写对/dev/speak进行控制的上层应用程序并运行,可以正确发出报时声音,表明驱动程序已成功加载进内核并能正常地工作。
参考文献
[1] 宋宝华.Linux设备驱动开发详解[M].北京:人民邮电出版社,2008.
[2] 倪继利.Linux内核分析及编程[M].北京:电子工业出版社,2007.
[3] 何永琪.嵌入式Linux系统实用开发[M].北京:电子工业出版社,2010.
[4] 孙琼.嵌入式Linux应用程序开发详解[M].北京:人民邮电出版社,2006.
王吉豪(研究生),研究方向为嵌入式系统及应用;崔建明(副教授),从事集成电路设计教学与研究。
Voice Chip YF017 Driver Design Based on Linux※
Wang Jihao,Cui Jianming
(College of Electronic,Communication and Physics,Shandong University of Science and Technology,Qingdao 266590,China)
Abstract:Taking the low cost voice chip into the embedded Linux system,it is not only can enrich the function of embedded system,but also can reduce the cost of development.This paper develops the voice chip YF107 driver and the upper application program,using ARM chip S3C2440 with Linux2.6.22 kernel as an example.The test results shows that the program can accurately call the driver through the upper application,and the voice chip can send out the corresponding voice.
Key words:embedded Linux;driver;S3C2440;voice chip;YF017
收稿日期:(责任编辑:杨迪娜2015-05-07)
中图分类号:TP311
文献标识码:A