基于Arduino的LD3320语音识别模块设计详解
文章目录
- 基于Arduino的LD3320语音识别模块设计详解
- 前言
- 一、LD3320驱动编写
- step 1.0 使用Arduino的SPI库,通过硬件SPI和LD3320通讯,读写寄存器
- setp 1.1 访问LD3320的三个指定寄存器,检查硬件连接的可靠性
- setp 1.2 进行驱动程序的编写,驱动LD3320进行语音识别
- 二、第二部分 用模拟SPI编写LD3320的驱动程序
- 模拟SPI通讯程序例程
- 三、IIC修改命令词
前言
本文章为记录本人的学习过程,最终目的是设计一款IIC通讯方式的语音识别模块,该模块的主要功能,就是识别程序中设定的指令词,并返回识别结果,指令词和对应的返回编号可在程序中任意修改,不需要去给语音识别模块烧录固件,支持命令词的动态编辑。
模块硬件包含一块单片机芯片,一块LD3320芯片,以及外围电路。PCB工程已开源,网上到处都有。
基础功能:
1.动态编辑命令词,不需要烧录模块的固件
2.识别成功后返回对应结果
文章将会记录我从零开始的调试步骤以及遇到的问题,还有解决问题的详细思路。
接下来我们开始。
工程已上传(PCB工程+模块固件+Demo),有需要的可以先下载,链接挂在末尾
一、LD3320驱动编写
根据ICroute官方文档所写,想要驱动LD3320进行语音识别,可以使用软硬并口通讯方式对LD3320的寄存器进行读写从而驱动LD3320,也可以使用软硬SPI串行通讯方式对LD3320的寄存器进行读写,也可以驱动LD3320。需要注意的是,模拟并口驱动方式官方并不推荐,不仅速度慢,也会造成通讯不稳定等情况。推荐使用硬件SPI进行驱动,如果MCU没有SPI的硬件接口,也可以使用普通I/O口模拟SPI进行通讯,较为稳定。
回到正题,我手头正好有一块网上买的LD3320模块,是不带单片机的那种,板子上只有LD3320的那种。
直接先拿Arduino UNO开发板和这款LD3320进行原理测试,Let’s go。
直接用杜邦线,按如下方式连接:
5V – VCC
GND – GND
MISO – D12
MOSI – D11
SCK – D13
NSS – D4
RST – D9
IRQ – D2
WR – GND
官方文档所说,LD3320的电压为3V3,通讯引脚耐压也是3V3,超过3V3会使模块使用不稳定,但是在使用Arduino的实际测试中,未使用电压转换模块,反应良好。千万别学我,最好还是用3V3该转压还是得转请注意,引脚电平可以超过3v3,但是!电源只能是3v3
本部分我分为三小步来完成:
step 1.0 使用Arduino的SPI库,通过硬件SPI和LD3320通讯,读写寄存器
setp 1.1 访问LD3320的三个指定寄存器,检查硬件连接的可靠性
setp 1.2 进行驱动程序的编写,驱动LD3320进行语音识别
step 1.0 使用Arduino的SPI库,通过硬件SPI和LD3320通讯,读写寄存器
这一步的主要目的,是确定和LD3320能否进行正常的通讯,众所周知,SPI是一种串行通讯方式,需要至少连接五根线,为什么是五而不是四,因为要公地。
【SCK】 》》同步主机产生的数据传输的时钟脉冲。
【MOSI】 》》用于向外设发送数据的线,主机从这个引脚发送,从机从这个引脚接收。
【MISO】《《用于向主设备发送数据的线,主机从这个引脚接收,从机从这个引脚发送。
【CS】 》》片选信号,主机控制这个引脚来开启和禁用从机,低电平使能。
【GND】 祖传公地。
好了,SPI相关的知识我们就了解这么多,我们只需要知道他是全双工串行通讯,还有这些引脚是干啥的就可以了。继续下一步。
我们要和LD3320进行通讯,首先要让LD3320工作,我们需要先复位芯片,激活内部DSP。
只需要将LD3320芯片的47脚【RSTB*】发一个低电平,就可以复位LD3320到初始状态。然后需要激活DSP,就需要对片选【CS】做一次拉低再拉高的操作。
复位LD3320激活内部dsp的子函数
void LD_reset()//对LD3320复位 对47脚发送低电平 然后反转片选一次 激活DSP:
{
digitalWrite(RSTB, HIGH);
delay(1);
digitalWrite(RSTB, LOW);
delay(1);
digitalWrite(RSTB, HIGH);
delay(1);
cSLow();
delay(1);
cSHigh();
delay(1);
//writeReg(0xb9, 0x00); //寄存器 0xB9 当前添加识别语句的字符串长度 初始化时写入0x00
}
激活DSP之后,就可以进行LD3320的寄存器读写了,我们使用的是SPI串行方式,根据官方的文档描述,SPI的通讯参数需要设置如下:
SPI没有官方标准,所以要好好看芯片的datasheet
在Arduino的setup里对SPI参数进行设置
void setup() {
......
SPI.setClockDivider(SPI_CLOCK_DIV16); //16分频
SPI.setDataMode(SPI_MODE2); //时钟极性CPOL 1 时钟相位CPHA 0
SPI.setBitOrder(MSBFIRST); //高位在前
......
}
接下来写一个用于读寄存器的程序,根据官方文档描述,要读寄存器,需要先发送0x05,然后再发送十六进制的寄存器地址,然后再读取芯片返回过来的十六进制数据。
读寄存器的子函数
unsigned char LD_readReg(unsigned char address)
{
unsigned char result;//局部变量 保存读取的数据
cSLow(); //片选使能 低电平有效
delay(10);
SPI.transfer(0x05);
SPI.transfer(address); //发送 寄存器地址
result = SPI.transfer(0x00); //读取数据
cSHigh(); //片选去使能
return (result); //返回数据
}
接下来写一个用于写寄存器的程序,根据官方文档描述,要写取寄存器,需要先发送0x04,然后再发送十六进制的寄存器地址,最后发送十六进制数据。
写寄存器的子函数
void LD_writeReg(unsigned char address, unsigned char value)
{
cSLow();//片选使能
delay(10);
SPI.transfer(0x04);//发送0x04 是写入模式
SPI.transfer(address); //发送 寄存器地址
SPI.transfer(value); //发送 数据
cSHigh();//片选去使能
}
接下来,进行对LD3320寄存器的读写测试。
如果,我们先向可读写的寄存器写入某个数值,再读出来,用来检查寄存器读写是否正常。
每次先向一个寄存器写,再读出来,内容是完全正确,但是在接下来的语音识别操作中,发现LD3320芯片并不会鸟你,这是为什么呢,因为可能你的数据存在SPI的总线上,但是没有Touch到寄存器里面
所以我建议读写寄存器的序列如下:
对寄存器写入再读取的操作顺序
void setup() {
......
LD_reset();//先复位激活LD3320
delay(1);
LD_readReg(0x06);//读取一次0X06寄存器
delay(1);
LD_writeReg(0x35, 0x33);//对寄存器0x35写入0x33
LD_writeReg(0x1b, 0x55);//对寄存器0x1b写入0x55
LD_writeReg(0xb3, 0xaa);//对寄存器0xb3写入0xaa
Serial.print(LD_readReg(0x35),HEX); Serial.print(" ");//读取并打印寄存器0x35的值
Serial.print(LD_readReg(0x1b),HEX); Serial.print(" ");//读取并打印寄存器0x1b的值
Serial.print(LD_readReg(0xb3),HEX); Serial.println(" ");//读取并打印寄存器0xb3的值
}
就是向 3 个寄存器先依次写数据,再依次读数据出来。作为比较。
这样可以有效地验证读写寄存器是否正常。
如果结果是 33 55 aa 那么恭喜你,读写寄存器正常,可以进行下一步操作了。这一步基本不会出错,如果出错了,请详细比对SPI的参数设置
step1.0 完整代码,在工程文档中,有需要的直接下载即可
setp 1.1 访问LD3320的三个指定寄存器,检查硬件连接的可靠性
既然已经可以正常读写LD3320的寄存器了,那为了保证LD3320稳定工作,再来检查一下硬件之间连接的可靠性。
我们在复位LD3320之后,读取寄存器0x06两次,再读取寄存器0x35和0xb3,将值打印出来。
访问三个指定寄存器的操作
void setup() {
......
LD_reset();//先复位激活LD3320
delay(1);
Serial.print(LD_readReg(0x06),HEX); Serial.print(" "); //读取并打印寄存器0x06的值
Serial.print(LD_readReg(0x06),HEX); Serial.print(" "); //读取并打印寄存器0x06的值
Serial.print(LD_readReg(0x35),HEX); Serial.print(" "); //读取并打印寄存器0x35的值
Serial.print(LD_readReg(0xb3),HEX); Serial.println(" ");//读取并打印寄存器0xb3的值
}
如果打印出来的值,是 87 87 80 FF 或者 00 87 80 FF 则正常。不要把LD3320断电,复位Arduino UNO ,多读取几次,看看值是否稳定,如果稳定,则连接非常稳定。就可以进行语音识别的操作了。
setp 1.2 进行驱动程序的编写,驱动LD3320进行语音识别
现在我们进行完整驱动的编写。首先,先介绍驱动LD3320进行语音识别的流程。
ASR初始化→写入识别列表→开始识别→准备好中断函数→打开中断
然后每次识别到语音,无论是否识别到命令词,就会触发中断,运行你准备好的中断函数,正常情况是只运行一次就结束了,但是你可以在中断函数中添加ASR初始化→开始识别,继续进行下一轮识别,达到循环识别的目的。
接下来,我们按照流程来编写驱动程序。先贴两张寄存器地址的图表。
通用初始化函数,直接抄作业就可以了,都是官方例程,唯一需要注意的,就是晶振频率相关的四个变量
void LD_Init_Common()//通用初始化
{
LD_readReg(0x06);
LD_writeReg(0x17, 0x35);
delay(10);
LD_readReg(0x06);
LD_writeReg(0x89, 0x03);
delay(5);
LD_writeReg(0xcf, 0x43);
delay(5);
LD_writeReg(0xcb, 0x02);
LD_writeReg(0x11, PLL_11);//和晶振频率有关,注意调整
LD_writeReg(0x1e, 0x00);
LD_writeReg(0x19, PLL_ASR_19);//和晶振频率有关,注意调整
LD_writeReg(0x1b, PLL_ASR_1B);//和晶振频率有关,注意调整
LD_writeReg(0x1d, PLL_ASR_1D);//和晶振频率有关,注意调整
delay(10);
LD_writeReg(0xcd, 0x04);
LD_writeReg(0x17, 0x4c);
delay(5);
LD_writeReg(0xb9, 0x00);
LD_writeReg(0xcf, 0x4f);
LD_writeReg(0x6f, 0xff);
}
ASR初始化函数,一样的,抄作业就完事了,没什么需要注意的
void LD_Init_ASR() /**语音识别初始化**/
{
LD_Init_Common();
LD_writeReg(0xbd, 0x00);
LD_writeReg(0x17, 0x48);
delay(10);
LD_writeReg(0x3c, 0x80);
LD_writeReg(0x3e, 0x07);
LD_writeReg(0x38, 0xff);
LD_writeReg(0x3a, 0x07);
LD_writeReg(0x40, 0);
LD_writeReg(0x42, 8);
LD_writeReg(0x44, 0);
LD_writeReg(0x46, 8);
delay(1);
}
接下来,写入识别列表,也就是把自定义的命令词写入到LD3320里面。需要注意的是,我们在写入识别列表之前,要先读取0xB2寄存器的值,查看芯片的状态是否正常,如果不正常,则需要先复位一次芯片,再重新初始化,再写入。为什么会出现不正常的情况,主要原因,是电压和电流不稳定造成的。
检查0xB2寄存器的函数,返回1则正常,返回0失败
int LD_Check_ASRbusyFlag_b2()/**检查芯片状态**/
{
for (int j = 0; j < 10; j++)
{
if (LD_readReg(0xb2) == 0x21)//寄存器值0x21为空闲
{
return 1;
}
delay(10);
}
return 0;
}
如果检查寄存器结果是正常,那么就可以写入识别词了。
写入识别列表的函数,char *pass也可以用string pass代替
int LD_ASRAddFixed(char *pass, int num) /**添加命令词**/
{
int i; //局部变量 用来循环写入识别字的数据,并记录字符的长度
int flag;//返回添加成功或者失败
flag = 1;
for (int j = 0; j < 5; j++) {//这个循环没有任何意义
if (LD_Check_ASRbusyFlag_b2() == 0) {
flag = 0;
break;//如果检查到不支持,则退出,并返回添加失败
}
LD_writeReg(0xc1, num);//添加命令的编号
LD_writeReg(0xc3, 0);//识别字添加时写入0x00
LD_writeReg(0x08, 0x04);//清除数据缓存器内容
delay(1);
LD_writeReg(0x08, 0x00);//清除后再次写入0x00
delay(1);
for (i = 0; i <80; i++)//为什么i<80,因为单个命令词最多支持80字节,最好只写入79字节
{
if (pass[i] == 0)break;//如果读取到空字符则退出循环写入
LD_writeReg(0x5, pass[i]);///将识别字写入寄存器0x05
}
LD_writeReg(0xb9, i);//在0xb9寄存器写入当前字符长度
LD_writeReg(0xb2, 0xff);
LD_writeReg(0x37, 0x04);//在0x37寄存器写入 0x04通知DSP 我要写入一条识别词
break;//写入完成,退出
}
return flag;//返回添加成功或者失败
}
接下来,就要启动识别了。
启动识别函数,照抄就行,这些变量都在程序头部进行宏定义
int LD_AsrRun()/**启动ASR**/
{
LD_writeReg(0x35, MIC_VOL);//在0x35寄存器 写入识别灵敏度
LD_writeReg(0xb3, speech_endpoint);//在0xb3寄存器 写入语音端点检测功能的灵敏度 0关闭 数值越小越灵敏
LD_writeReg(0xb4, speech_start_time);//在0xb4寄存器 写入判断语句开始的时间
LD_writeReg(0xb5, speech_end_time);//在0xb5寄存器 写入判断语句结束的时间
LD_writeReg(0xb6, voice_max_length);//在0xb6寄存器 写入识别语句的最长长度
LD_writeReg(0xb7, noise_time);//在0xb7寄存器 写入初始忽略掉的底噪时间
LD_writeReg(0x1c, 0x09);//保留命令字
LD_writeReg(0xbd, 0x20);//保留命令字
LD_writeReg(0x08, 0x01);//清除缓存器
delay( 1);
LD_writeReg(0x08, 0x00);//再次写入00
delay( 1);
if (LD_Check_ASRbusyFlag_b2() == 0) //查看寄存器0xb2的值是否正常
{
return 0;//不正常返回0 ,启动失败
}
LD_writeReg(0xb2, 0xff);//更新寄存器0xb2的值
LD_writeReg(0x37, 0x06);//通知dsp开始语音识别
delay( 5 );
LD_writeReg(0x1c, 0x0b);//选择声音输入方式,因为我用双极电容麦,直接写入0x0B,
LD_writeReg(0x29, 0x10);//中断开启
LD_writeReg(0xbd, 0x00);//启动ASR模块
return 1;//成功
}
启动之后,我们准备好中断函数,用来响应LD3320发送的中断信号,查询返回值并启动下一轮识别。
中断响应函数
void LD_ASRget()/**中断执行程序**/
{
/****以下程序对LD3320的运算结果进行分析,取其运算结果****/
uint8_t Asr_Count = 0;
LD_writeReg(0x29, 0) ;
LD_writeReg(0x02, 0) ;
if ((LD_readReg(0x2b) & 0x10) && LD_readReg(0xb2) == 0x21 && LD_readReg(0xbf) == 0x35)
{
Asr_Count = LD_readReg(0xba); //读取有几个候选值
if (Asr_Count > 0 && Asr_Count < 4)
{
readnum = LD_readReg(0xc5); //得到最佳选项 如果你需要其他选项,可以去另外三个寄存器掏
readflag = 1;
}
else {
Serial.println("运算结果是无法识别");
}
LD_writeReg(0x2b, 0);
LD_writeReg(0x1C, 0);
}
else {
Serial.println("运算结果是无法识别");
}
/****以下程序对LD3320进行初始化,预备进行下一次识别****/
LD_readReg(0x06);
delay(10);
LD_readReg(0x06);
LD_writeReg(0x89, 0x03);
delay(5);
LD_writeReg(0xcf, 0x43);
delay(5);
LD_writeReg(0xcb, 0x02);
LD_writeReg(0x11, PLL_11);
LD_writeReg(0x1e, 0x00);
LD_writeReg(0x19, PLL_ASR_19);
LD_writeReg(0x1b, PLL_ASR_1B);
LD_writeReg(0x1d, PLL_ASR_1D);
delay(10);
LD_writeReg(0xcd, 0x04);
LD_writeReg(0x17, 0x4c);
delay(5);
LD_writeReg(0xcf, 0x4f);
LD_writeReg(0xbd, 0x00);
LD_writeReg(0x17, 0x48);
delay(10);
LD_writeReg(0x3c, 0x80);
LD_writeReg(0x3e, 0x07);
LD_writeReg(0x38, 0xff);
LD_writeReg(0x3a, 0x07);
LD_writeReg(0x40, 0);
LD_writeReg(0x42, 8);
LD_writeReg(0x44, 0);
LD_writeReg(0x46, 8);
delay(1);
LD_writeReg(0x1c, 0x09);
LD_writeReg(0xbd, 0x20);
LD_writeReg(0x08, 0x01);
delay( 1);
LD_writeReg(0x08, 0x00);
delay( 1);
LD_writeReg(0xb2, 0xff);
LD_writeReg(0x37, 0x06);
delay( 5 );
LD_writeReg(0x1c, 0x0b);
LD_writeReg(0x29, 0x10);
LD_writeReg(0xbd, 0x00);
}
把这个丢在LOOP里面循环运行
int LD_Read()
{
if (readflag == 1)
{
readflag = 0;
return readnum;
}
return -1;
}
所有的子函数都准备完毕了,接下来,开始识别!
Let’s Go!!!
再来个运行语音识别的子函数,我保证是最后一个了
int RunASR()
{
int asrflag = 0;
for (int z = 0; z < 5; z++) {//循环尝试五轮
LD_Init_ASR();//进行ASR初始化
delay(100);//延时时间自己控制,可以改5ms
if (LD_ASRAddFixed("kai deng", 1) == 0) {//添加命令词kai deng,编号是1,注意,两个字的拼音用空格隔开
LD_reset(); //如果失败了,复位芯片
delay(100);
continue; //进行下一轮尝试
}
if (LD_ASRAddFixed("guan deng", 2) == 0) {//添加命令词guan deng,编号是2
LD_reset();//如果失败了,复位芯片
delay(100);
continue;//进行下一轮尝试
}
if (LD_AsrRun() == 0) {//启动识别
LD_reset();//如果失败了,复位芯片
delay(100);
continue;//进行下一轮尝试
}
asrflag = 1;//运行到这里了,说明上面的步骤都成功了
break;//退出循环
}
return asrflag;//返回状态,成功或者失败
}
接下来调用RunASR()函数就可以启动语音识别了,当然你也可以这样写
if(RunASR()==0){
Serial.println("ASR启动失败");
}
else
Serial.println("ASR启动成功");
然后就可以愉快得尝试了。
二、第二部分 用模拟SPI编写LD3320的驱动程序
模拟SPI通讯程序例程
时隔不知多少日,我又来更新了~~~~~~~~~~
这部分内容来介绍一下模拟SPI通讯,为什么要这样做呢,因为部分的单片机没有硬件SPI的支持,只能通过普通引脚来模拟SPI,达到通讯的目的,不过这样实现的通讯,往往速度较硬件SPI慢的不止一点点,也不够稳定,只能说,勉强够用。
先附上时序图,其实和硬件SPI的时序一毛一样。
和常规的 SPI通讯一样,查看时序图可知这四个引脚的状态。CS引脚(片选引脚SCS),低电平有效,也就是说在开始通讯之前,要先将CS引脚置为低电平,结束通讯之后,将CS引脚置为高电平。CLK引脚(时钟信号引脚SDCK),在开始通讯之后,要送出稳定的时钟信号,下降沿有效,在CLK送出下降沿的时候,MOSI(SDI)或者MISO(SDO)线上的电平信号才被记录为一个位数据。
写寄存器的操作和之前一样,要先写入一个字节0x04(写入寄存器的指令,读出寄存器为0x05),再写入一个字节(地址的值),最后写入数据。如图所示的SDI线上的时序,分为了三个字节位置,分别是指令字节、地址字节、数据字节,每个四节各八位。众所周知,0b1111 1111是0xFF,写寄存器的图中指令字节是0b 0000 0100,也就是0x04,这是8421-BCD码的知识点,不会的同学自己百度哦。
废话不多说,直接上程序。
头部宏定义
nop根据单片机的运行周期自行调整,没有很严格的要求
#define nop 0.07
#define CS D4
#define SPI_MOSI_PIN D11
#define SPI_SCK_PIN D13
#define SPI_MISO_PIN D12
模拟SPI写寄存器的部分程序
程序分成三个部分,分别为写入指令,写入地址,写入数据,三个流程结束,就成功完成了写寄存器的操作
void LD_writeReg(unsigned char address, unsigned char dataout)
{
unsigned char i = 0;
unsigned char Command = 0x04;//写寄存器的指令
cSLow();//片选使能
delayMicroseconds(nop * 3);//延时三个机器周期,如果通讯不正常,调整这里的延时时间可以解决问题
//写入 指令
for ( i = 0; i < 8; i++)//循环八次 循环结束就完整得写入了 Command
{
if ((Command & 0x80) == 0x80)//按位与 Command & 0b 1000 0000 判断最高位是否为1
digitalWrite(SPI_MOSI_PIN, HIGH);//置为高
else
digitalWrite(SPI_MOSI_PIN, LOW);//置为低
delayMicroseconds(nop * 3);//延时三个机器周期
digitalWrite(SPI_SCK_PIN, LOW);//时钟信号拉低
Command = (Command << 1);//将Command 左移一位
delayMicroseconds(nop * 3);//延时三个机器周期
digitalWrite(SPI_SCK_PIN, HIGH);//时钟信号拉高
}
//写入 地址 (和写入指令的操作一样)
for (i = 0; i < 8; i++)
{
if ((address & 0x80) == 0x80)
digitalWrite(SPI_MOSI_PIN, HIGH);
else
digitalWrite(SPI_MOSI_PIN, LOW);
delayMicroseconds(nop * 3);
digitalWrite(SPI_SCK_PIN, LOW);
address = (address << 1);
delayMicroseconds(nop * 3);
digitalWrite(SPI_SCK_PIN, HIGH);
}
delayMicroseconds(nop * 3);
//写入 数据(和写入指令的操作一样)
for (i = 0; i < 8; i++)
{
if ((dataout & 0x80) == 0x80)
digitalWrite(SPI_MOSI_PIN, HIGH);
else
digitalWrite(SPI_MOSI_PIN, LOW);
delayMicroseconds(nop * 3);
digitalWrite(SPI_SCK_PIN, LOW);
dataout = (dataout << 1);
delayMicroseconds(nop * 3);
digitalWrite(SPI_SCK_PIN, HIGH);
}
delayMicroseconds(nop * 3);//延时三个系统周期
cSHigh();//片选去使能
}
模拟SPI读寄存器的部分程序
程序分成三个部分,分别为写入指令,写入地址,读取数据,三个流程结束,就成功完成了读寄存器的操作
unsigned char LD_readReg(unsigned char address)
{
unsigned char i = 0;
unsigned char datain = 0 ;//用于存储读取到字节完整数据
unsigned char Temp = 0;//用于存储读取到的字节单个位的数据
unsigned char Command = 0x05;//读寄存器的指令
cSLow();//片选使能
delayMicroseconds(nop * 3);//延时三个系统周期
//写入 指令(和之前一样的操作,就不打备注了)
for ( i = 0; i < 8; i++)
{
if ((Command & 0x80) == 0x80)
digitalWrite(SPI_MOSI_PIN, HIGH);
else
digitalWrite(SPI_MOSI_PIN, LOW);
delayMicroseconds(nop * 3);
digitalWrite(SPI_SCK_PIN, LOW);
Command = (Command << 1);
delayMicroseconds(nop * 3);
digitalWrite(SPI_SCK_PIN, HIGH);
}
//写入 地址(和之前一样的操作,就不打备注了)
for (i = 0; i < 8; i++)
{
if ((address & 0x80) == 0x80)
digitalWrite(SPI_MOSI_PIN, HIGH);
else
digitalWrite(SPI_MOSI_PIN, LOW);
delayMicroseconds(nop * 3);
digitalWrite(SPI_SCK_PIN, LOW);
address = (address << 1);
delayMicroseconds(nop * 3);
digitalWrite(SPI_SCK_PIN, HIGH);
}
delayMicroseconds(nop * 3);
//读取数据
for (i = 0; i < 8; i++)
{
datain = (datain << 1);//将datain 左移一位
Temp = digitalRead(SPI_MISO_PIN);//读取到一位数据
delayMicroseconds(nop * 3);//延时三个系统周期
digitalWrite(SPI_SCK_PIN, LOW);//时钟信号拉低
if (Temp == 1) {//如果Temp 为1
datain |= 0x01;//或等于 将datain 的最低位变为1
}
delayMicroseconds(nop * 3);//延时三个系统周期
digitalWrite(SPI_SCK_PIN, HIGH);//时钟信号拉高
}
delayMicroseconds(nop * 3);//延时三个系统周期
cSHigh();//片选去使能
return datain;//返回最后结果
}
这里给不懂 & 和 | 的同学稍微解释一下
& 在C语言里面是位与运算符,| 在C语言里面是位或运算符,例如:
int a=0x03;//0b 0000 0011
int b=0x02;//0b 0000 0010
int c,d;
c=a&b;
d=a|b;
//c的值为0x02 0&1=0 1&1=1 0&0=0
//d的值为0x03 0|1=1 1|1=1 0|0=0
&= 和 |= 的意思就是与等于和或等于,例如:
a&=b 的意思就是,先将a和b按位与,再将结果赋值给a
a|=b 的意思就是,先将a和b按位或,再将结果赋值给a
好了跑题了,回到正题,上述通过模拟SPI读写寄存器的操作就完成了,后面的驱动部分,按照第一部分硬件SPI驱动LD3320的方法移植过来就好了。
补充一点,驱动程序里面的外部中断响应操作不规范,正常情况下,读取到外部中断后,应该先关闭该外部中断,然后去执行一系列中断触发的程序,执行结束后重新打开该外部中断。建议修改。
文章结尾附上源码和PCB的下载链接
三、IIC修改命令词
原理很简单,就是通过自己拟定的代码命令,来操控语音识别模块的初始化等等进程,唯一需要注意的是,Arduino的IIC通讯缓存区只有32个字节,所以这边建议将命令词进行切割分块发送,最后发送一个自拟定的结束命令,通知模块我发送完了,你可以装载命令词了,具体方法查看例程3.1。例程2.3里面有切割字符串并载入到3320里面的例程。
祝大家玩的愉快,LD3320的内容就告一段落了。