王吉豪,崔建明
(山東科技大學(xué) 電子通信與物理學(xué)院,青島 266590)
?
嵌入式Linux的時(shí)鐘語(yǔ)音芯片YF017驅(qū)動(dòng)設(shè)計(jì)※
王吉豪,崔建明
(山東科技大學(xué) 電子通信與物理學(xué)院,青島 266590)
摘要:將低成本的時(shí)鐘語(yǔ)音芯片融入到嵌入式Linux系統(tǒng)中,不但可以豐富嵌入式設(shè)備的功能,而且可以降低開發(fā)成本。本文以ARM架構(gòu)芯片S3C2440結(jié)合Linux2.6.22內(nèi)核作為實(shí)例,開發(fā)出語(yǔ)音報(bào)時(shí)芯片YF017的驅(qū)動(dòng)程序,并寫出該驅(qū)動(dòng)程序的上層應(yīng)用程序。測(cè)試結(jié)果表明,通過(guò)上層應(yīng)用程序可以準(zhǔn)確地調(diào)用驅(qū)動(dòng)程序,并使語(yǔ)音芯片發(fā)出相應(yīng)的語(yǔ)音。
關(guān)鍵詞:嵌入式Linux;驅(qū)動(dòng)程序;S3C2440;時(shí)鐘語(yǔ)音芯片;YF017
引言
在嵌入式Linux系統(tǒng)中加入語(yǔ)音功能,傳統(tǒng)方式是采用聲卡芯片,但是對(duì)于僅需簡(jiǎn)單語(yǔ)音的場(chǎng)合,使用聲卡芯片作為發(fā)音設(shè)備在功能上顯得多余,在成本上也大大提高。
YF017系列語(yǔ)音芯片是針對(duì)市場(chǎng)推出的一款具有PWM輸出的OTP語(yǔ)音標(biāo)準(zhǔn)芯片,共有3個(gè)I/O口,該芯片可以直接驅(qū)動(dòng)喇叭,無(wú)需再設(shè)計(jì)音頻放大電路,外圍最少僅需要一個(gè)0.1 μF電容就可以穩(wěn)定工作,把該芯片用到簡(jiǎn)單的語(yǔ)音場(chǎng)合可大大節(jié)約開發(fā)時(shí)間,降低開發(fā)成本。
在嵌入式Linux系統(tǒng)中實(shí)現(xiàn)簡(jiǎn)單的語(yǔ)音功能,YF017系列語(yǔ)音芯片成為首選之一。但是現(xiàn)成的Linux內(nèi)核中并沒有集成YF017系列語(yǔ)音芯片的驅(qū)動(dòng),因此,本文將著重實(shí)現(xiàn)YF017語(yǔ)音芯片的Linux驅(qū)動(dòng)程序,并實(shí)現(xiàn)上層應(yīng)用程序以驗(yàn)證驅(qū)動(dòng)程序的正確性。
1YF017芯片介紹
1.1芯片引腳介紹
圖1 YF017芯片引腳圖
YF017語(yǔ)音芯片有8個(gè)引腳,如圖1所示。其中:VDD、GND為電源引腳,工作電壓為2.2~6 V,適用范圍很寬。PWM1、PWM2為PWM輸出引腳,用于驅(qū)動(dòng)8~16 Ω范圍內(nèi)的任何喇叭(建議0.25~1 W)。Vreg為調(diào)節(jié)引腳,在外接電壓大于4.5 V時(shí),需要在Vreg和GND之間接0.1 μF電容,用于減少電源噪聲,外接電壓小于4.5 V時(shí)可不接電容。BSY、DAT、RST為數(shù)字I/O引腳,可以與控制芯片連接。
BSY:芯片播放聲音時(shí)輸出低電平,待機(jī)時(shí)保持高電平。
DAT:用于控制芯片內(nèi)播放指針,收到幾個(gè)上升沿脈沖,就播放第幾個(gè)地址的聲音內(nèi)容。
RST:用于復(fù)位芯片內(nèi)播放指針,當(dāng)該引腳收到一個(gè)上升沿脈沖時(shí),可以使芯片的播放指針歸零,同時(shí),芯片進(jìn)入待機(jī)狀態(tài)。
1.2芯片語(yǔ)音內(nèi)容
主控芯片通過(guò)發(fā)送相應(yīng)數(shù)量的上升沿脈沖到DAT引腳,語(yǔ)音芯片可以播放相應(yīng)的聲音,脈沖數(shù)量與播放聲音的對(duì)應(yīng)關(guān)系如表1所列。
表1 對(duì)應(yīng)關(guān)系
1.3芯片控制時(shí)序
主控芯片上電時(shí),需要先將語(yǔ)音芯片的DAT引腳和RST引腳分別置于低電平。如果需要播放第25段聲音,先將RST引腳拉高,過(guò)1 ms再拉低,使播放指針復(fù)位。接著連續(xù)發(fā)送25個(gè)上升沿脈沖到DATA引腳,芯片即可播放第25段的聲音。如果接下來(lái)需要播放第22段的聲音,主控芯片需要檢測(cè)語(yǔ)音芯片的BSY引腳是否為高電平,如果BSY為低電平,則表示語(yǔ)音芯片正忙,主控芯片需要等待,直到BSY引腳為高電平時(shí),可按照之前的步驟,先發(fā)送上升沿脈沖給RST,再發(fā)送22個(gè)上升沿脈沖給DAT引腳,這樣可播放語(yǔ)音芯片中的任意語(yǔ)音。RST和DAT脈沖寬度至少為0.2 ms,建議1 ms。
2YF017驅(qū)動(dòng)程序設(shè)計(jì)
2.1硬件原理圖
將語(yǔ)音芯片接入主控芯片為S3C2440的嵌入式Linux的開發(fā)板中,S3C2440芯片的GPIO引腳GPG5、GPG6、GPG7分別接語(yǔ)音芯片的RST、DAT、BSY引腳,如圖2所示。
2.2驅(qū)動(dòng)程序設(shè)計(jì)
2.2.1入口函數(shù)speak_init
入口函數(shù)speak_init使用register_chrdev函數(shù)向內(nèi)核申請(qǐng)主設(shè)備號(hào),把設(shè)備的操作函數(shù)結(jié)構(gòu)體speak_ops向內(nèi)核注冊(cè),并使用class_create函數(shù)和class_device_create函數(shù)在Linux文件系統(tǒng)的/sys/目錄下生成設(shè)備信息,調(diào)用mdev應(yīng)用程序。 mdev應(yīng)用程序根據(jù)/sys/目錄下生成的設(shè)備信息在/dev/目錄下創(chuàng)建設(shè)備節(jié)點(diǎn),該程序的設(shè)備節(jié)點(diǎn)為/dev/speak。
2.2.2出口函數(shù)speak_exit
圖2 硬件原理圖
出口函數(shù)speak_exit使用unregister_chrdev函數(shù)釋放之前向內(nèi)核申請(qǐng)的主設(shè)備號(hào),并使用class_device_destroy函數(shù)和class_destroy函數(shù)把之前在文件系統(tǒng)/sys/目錄下生成的設(shè)備信息刪除,并調(diào)用mdev應(yīng)用程序。 mdev應(yīng)用程序根據(jù)/sys/目錄下設(shè)備信息的變動(dòng),將/dev/目錄下創(chuàng)建的設(shè)備節(jié)點(diǎn)speak刪除。
2.2.3底層操作函數(shù)
把設(shè)備的底層操作函數(shù)都指向操作函數(shù)結(jié)構(gòu)體speak_ops,以便使用register_chrdev函數(shù)向內(nèi)核注冊(cè)。speak_ops結(jié)構(gòu)體定義如下:
static struct file_operations speak_ops = {
.owner = THIS_MODULE,
.open = speak_open,
.read = speak_read,
.write = speak_write,
};
(1) speak_open函數(shù)
該函數(shù)用于配置S3C3440的GPIO引腳,將GPG5、GPG6用作輸出,GPG7用作輸入,并將GPG5、GPG6初始化為低電平。當(dāng)上層應(yīng)用程序調(diào)用open(“/dev/speak”)函數(shù)時(shí),將調(diào)用到speak_open函數(shù)。實(shí)現(xiàn)程序代碼如下:
#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函數(shù)
該函數(shù)用于讀取語(yǔ)音芯片的BSY引腳并返回1字節(jié)狀態(tài),返回1代表BSY引腳為高電平,返回0代表BSY引腳為低電平。當(dāng)上層應(yīng)用程序調(diào)用read()函數(shù)時(shí)將調(diào)用到speak_read函數(shù)。實(shí)現(xiàn)代碼如下:
ssize_t speak_read (struct file *file, char __user *buf, size_t len, loff_t *pos){
unsigned char val;
/* 1.讀取BSY引腳的狀態(tài) */
val = (s3c2410_gpio_getpin(GPIO_BUSY)) ?1:0;
/* 2.將BSY的狀態(tài)從內(nèi)核空間復(fù)制到用戶空間 */
if(copy_to_user(buf, &val, 1)){
return -EFAULT;
}
return 1;
}
(3) speak_write函數(shù)
該函數(shù)把用戶空間發(fā)過(guò)來(lái)的語(yǔ)音地址指針序號(hào)轉(zhuǎn)化為控制語(yǔ)音芯片的脈沖。當(dāng)上層應(yīng)用程序調(diào)用write()函數(shù)時(shí)將調(diào)用到speak_write函數(shù)。實(shí)現(xiàn)代碼如下:
static ssize_t speak_write (struct file *file, const char __user *buf, size_t len, loff_t *pos){
int i;
unsigned char val;
/* 1. 把用戶數(shù)據(jù)從用戶空間復(fù)制到內(nèi)核空間 */
if(copy_from_user(&val, buf, 1)){
return -EFAULT;
}
/* 2.判斷語(yǔ)音地址指針是否越界,越界返回錯(cuò)誤*/
if (val > 32){
printk(KERN_NOTICE VERSION"out of band ! ");
printk(KERN_NOTICE VERSION"val = %d ", val);
return -ENXIO;
}
/*3.根據(jù)時(shí)序要求,先發(fā)復(fù)位脈沖信號(hào)到語(yǔ)音芯片 */
s3c2410_gpio_setpin(GPIO_RESET, 1);
udelay(1000);
s3c2410_gpio_setpin(GPIO_RESET, 0);
/* 4.根據(jù)用戶傳過(guò)來(lái)的數(shù)據(jù)設(shè)置語(yǔ)音地址指針,讓語(yǔ)音芯片發(fā)出相應(yīng)聲音 */
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驅(qū)動(dòng)程序編譯
寫Makefile并在程序所在的目錄執(zhí)行make命令,將編譯生成drv_speak.ko文件。
Makefile內(nèi)容如下:
#用于指定內(nèi)核源碼目錄
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
#指定驅(qū)動(dòng)源程序文件
obj-m += drv_speak.o
2.4驅(qū)動(dòng)程序安裝與卸載
將編譯生成的drv_speak.ko文件,復(fù)制到嵌入式Linux文件系統(tǒng)中,執(zhí)行命令insmod drv_speak.ko將把該驅(qū)動(dòng)模塊加載到內(nèi)核中。通過(guò)rmmod drv_speak命令可將驅(qū)動(dòng)卸載,insmod和rmmod命令的執(zhí)行將分別調(diào)用驅(qū)動(dòng)中的speak_init 和speak_exit函數(shù)。
可以通過(guò)命令 cat /proc/devices 查看speak設(shè)備的存在,也可以在/dev/目錄下查看speak設(shè)備節(jié)點(diǎn),在/sys/目錄下查看生成的設(shè)備信息。
3YF017應(yīng)用程序設(shè)計(jì)
3.1應(yīng)用程序設(shè)計(jì)
應(yīng)用程序通過(guò)讀取本地時(shí)間并控制/dev/speak發(fā)出相應(yīng)的聲音來(lái)驗(yàn)證驅(qū)動(dòng)程序的正確性。
3.1.1主函數(shù)main
int main(int argc, char* argv[]){
int fd;
time_t t;
struct tm* time_now;
/* 1.打開語(yǔ)音報(bào)時(shí)設(shè)備 */
fd = open("/dev/speak", O_RDWR);
if (fd < 0){
printf("can't open /dev/speak ");
return -1;
}
/* 2.獲取時(shí)間 */
time(&t);
time_now = localtime(&t);
/* 3.報(bào)時(shí) */
speak_time(fd, time_now);
return 0;
}
3.1.2報(bào)時(shí)函數(shù)speak_time
void speak_time(int fd, struct tm* t){
unsigned int month, day, week, hour, min;
/*1. 獲取日期和時(shí)間 */
month = t->tm_mon + 1;
……
min = t->tm_min;
/*2. 將報(bào)時(shí)分單字段 */
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單字段報(bào)音函數(shù)speak_segment
void speak_segment(int fd, unsigned int which, unsigned int val){
unsigned char addr;
/* 根據(jù)傳入的命令值which和val分別調(diào)用write_and_wait函數(shù) */
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寫等待函數(shù)write_and_wait
該函數(shù)將用戶傳來(lái)的指令寫到/dev/speak設(shè)備中,并用睡眠方式等待/dev/speak設(shè)備空閑。程序?qū)崿F(xiàn)代碼如下:
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應(yīng)用程序編譯
使用交叉編譯工具編譯,執(zhí)行命令“arm-Linux-gcc-o app_speak app_speak.c”將生成app_speak可執(zhí)行文件。
3.3應(yīng)用程序運(yùn)行
將可執(zhí)行文件app_speak復(fù)制到嵌入式Linux文件系統(tǒng)中,運(yùn)行前確保drv_speak.ko已經(jīng)安裝到內(nèi)核中。在app_speak所在目錄執(zhí)行./app_speak命令,將聽到語(yǔ)音芯片發(fā)出報(bào)時(shí)聲音。語(yǔ)音報(bào)時(shí)時(shí)間和執(zhí)行date命令顯示的時(shí)間一致。
結(jié)語(yǔ)
通過(guò)編寫對(duì)/dev/speak進(jìn)行控制的上層應(yīng)用程序并運(yùn)行,可以正確發(fā)出報(bào)時(shí)聲音,表明驅(qū)動(dòng)程序已成功加載進(jìn)內(nèi)核并能正常地工作。
參考文獻(xiàn)
[1] 宋寶華.Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解[M].北京:人民郵電出版社,2008.
[2] 倪繼利.Linux內(nèi)核分析及編程[M].北京:電子工業(yè)出版社,2007.
[3] 何永琪.嵌入式Linux系統(tǒng)實(shí)用開發(fā)[M].北京:電子工業(yè)出版社,2010.
[4] 孫瓊.嵌入式Linux應(yīng)用程序開發(fā)詳解[M].北京:人民郵電出版社,2006.
王吉豪(研究生),研究方向?yàn)榍度胧较到y(tǒng)及應(yīng)用;崔建明(副教授),從事集成電路設(shè)計(jì)教學(xué)與研究。
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
收稿日期:(責(zé)任編輯:楊迪娜2015-05-07)
中圖分類號(hào):TP311
文獻(xiàn)標(biāo)識(shí)碼:A