SPI+LD3320语音芯片学习

SPI介绍       

        SPI是串行外设接口(Serial Peripheral Interface)的缩写,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,如今越来越多的芯片集成了这种通信协议。

案例1:

STM32F103C8+LD3320语音方案

STM32的SPI介绍:

对应的引脚:

SPI1_NSS  ===========> PA4
SPI1_SCK  ===========> PA5
SPI1_MISO ===========> PA6
SPI1_MOSI ===========> PA7

SPI2_NSS  ===========> PB12
SPI2_SCK  ===========> PB13
SPI2_MISO ===========> PB14
SPI2_MOSI ===========> PB15

利用SPI2和LD3320芯片4线链接。

代码实现

STM32对SPI2的初始化代码如下:

//SPI2初始化
static void LD3320_SPI_cfg(void)
{
   SPI_InitTypeDef  SPI_InitStructure;
   GPIO_InitTypeDef GPIO_InitStructure;
  //spi端口配置
  RCC_APB1PeriphClockCmd(LD3320SPI_CLK,ENABLE);
	
  RCC_APB2PeriphClockCmd(LD3320WR_GPIO_CLK | LD3320SPIMISO_GPIO_CLK | LD3320SPIMOSI_GPIO_CLK | LD3320SPISCK_GPIO_CLK,ENABLE);
  
  GPIO_InitStructure.GPIO_Pin = LD3320SPIMISO_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_Init(LD3320SPIMISO_GPIO_PORT,&GPIO_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = LD3320SPIMOSI_PIN;
  GPIO_Init(LD3320SPIMOSI_GPIO_PORT,&GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = LD3320SPISCK_PIN;
  GPIO_Init(LD3320SPISCK_GPIO_PORT,&GPIO_InitStructure);
	
  GPIO_InitStructure.GPIO_Pin = LD3320WR_PIN;				
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_Init(LD3320WR_GPIO_PORT, &GPIO_InitStructure);	
	
  LD_CS_H();
	
  SPI_Cmd(LD3320SPI, DISABLE);

  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;   	//全双工
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;				//主模式
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;			//8位
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;	//时钟极性 空闲状态时,SCK保持低电平
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	//时钟相位 数据采集从第一个时钟边沿开始     
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;     			//软件产生NSS
  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_64;   //波特率控制 SYSCLK/128
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;    		//数据保卫在前
  SPI_InitStructure.SPI_CRCPolynomial = 7; 	//CRC多项式寄存器初始值为7
	
  SPI_Init(LD3320SPI, &SPI_InitStructure);

  SPI_Cmd(LD3320SPI, ENABLE);
  SPI2_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
	
}


//寄存器读写操作
static uint8 spi_send_byte(uint8 byte)
{
	while (SPI_I2S_GetFlagStatus(LD3320SPI, SPI_I2S_FLAG_TXE) == RESET);
	SPI_I2S_SendData(LD3320SPI,byte);
	while (SPI_I2S_GetFlagStatus(LD3320SPI,SPI_I2S_FLAG_RXNE) == RESET);
	return SPI_I2S_ReceiveData(LD3320SPI);
}

其中SPI2的管教定义如下:

#define	LD3320SPI				SPI2
#define LD3320SPI_CLK				RCC_APB1Periph_SPI2	

#define LD3320CS_PIN				GPIO_Pin_12		
#define LD3320CS_GPIO_PORT		        GPIOB
#define LD3320CS_GPIO_CLK			RCC_APB2Periph_GPIOB
#define LD_CS_H()				GPIO_SetBits(GPIOB, GPIO_Pin_12)
#define LD_CS_L()	         		GPIO_ResetBits(GPIOB, GPIO_Pin_12)

#define LD3320SPISCK_PIN			GPIO_Pin_13
#define LD3320SPISCK_GPIO_PORT		        GPIOB
#define LD3320SPISCK_GPIO_CLK			RCC_APB2Periph_GPIOB

#define LD3320SPIMISO_PIN			GPIO_Pin_14
#define LD3320SPIMISO_GPIO_PORT		        GPIOB
#define LD3320SPIMISO_GPIO_CLK		        RCC_APB2Periph_GPIOB

#define LD3320SPIMOSI_PIN			GPIO_Pin_15
#define LD3320SPIMOSI_GPIO_PORT		        GPIOB
#define LD3320SPIMOSI_GPIO_CLK		        RCC_APB2Periph_GPIOB

查看LD3320数据手册

串行SPI方式写时序

       写的时候要先给 SDI 发送一个 “写”指令(04H),然后给 SDI 发送 8 位寄存器地址,再给 SDI 发送 8 位数据。在这期间, SCS*必须保持在有效(低电平)。

static void LD_WriteReg(uint8 addr,uint8 data1)
{
	LD_CS_L();
	LD_SPIS_L();
	spi_send_byte(0x04);
	spi_send_byte(addr);
	spi_send_byte(data1);
	LD_CS_H();
	LD_SPIS_H();
}

 

SPI方式读时序

        写的时候要先给 SDI 发送一个 “读”指令(05H),然后给 SDI 发送 8 位寄存器地址,再从 SDO 接受 8 位数据。在这期间, SCS*必须保持在有效(低电平)。

static uint8 LD_ReadReg(uint8 reg_add)
{
	uint8 i;
	LD_CS_L();
	LD_SPIS_L();
	spi_send_byte(0x05);
	spi_send_byte(reg_add);
	i=spi_send_byte(0x00);
	LD_CS_H();
	LD_SPIS_H();
	return(i);
}

 

SPI寄存器的详细说明在LD3320开发手册第6页

LD3320开发手册

语音识别

语音识别的操作顺序是:

语音识别用初始化(包括通用初始化)→写入识别列表→开始识别,
并准备好中断响应函数,打开中断允许位。
这里需要说明一下,如果不用中断方式,也可以通过查询方式工作。在“开
始识别”后,读取寄存器 B2H 的值,如果为 21H 就表示有识别结果产生。
在此之后读取候选项等操作与中断方式相同。

  • 1、通用初始化
static void LD_Init_Common(void)
{
	LD_ReadReg(0x06);         //FIFO状态
	LD_WriteReg(0x17, 0x35);  //进行软复位
	LD3320_delay(5);
	LD_ReadReg(0x06);         //FIFO状态

	LD_WriteReg(0x89, 0x03);  //模拟电路初始化控制写03H,MP3播放时候写FFH
	LD3320_delay(5);
	LD_WriteReg(0xCF, 0x43);  //内部省电摸模式初始化写43H  
	LD3320_delay(5);
	LD_WriteReg(0xCB, 0x02);  //ASR:读取ASR结果(候补4)
	
	/*PLL setting*/
	LD_WriteReg(0x11, LD_PLL_11); //时钟频率设置       
	if (nLD_Mode == LD_MODE_MP3)  //MP3模式
	{
		LD_WriteReg(0x1E, 0x00);  //ADC专用控制,应初始化为00H
		LD_WriteReg(0x19, LD_PLL_MP3_19);   //时钟设置频率2
		LD_WriteReg(0x1B, LD_PLL_MP3_1B);   //时钟设置频率3
		LD_WriteReg(0x1D, LD_PLL_MP3_1D);   //时钟设置频率4
	}
	else
	{
		LD_WriteReg(0x1E,0x00);  //ADC专用控制,应初始化为00H
		LD_WriteReg(0x19, LD_PLL_ASR_19); //时钟设置频率2
		LD_WriteReg(0x1B, LD_PLL_ASR_1B); //时钟设置频率3		
	        LD_WriteReg(0x1D, LD_PLL_ASR_1D); //时钟设置频率4
	}
	LD3320_delay(5);
	
	LD_WriteReg(0xCD, 0x04);  //允许DSP休眠
	LD_WriteReg(0x17, 0x4c);  //写4CH可以是DSP休眠,比较省电
	LD3320_delay(1);
	LD_WriteReg(0xB9, 0x00);  //ASR:当前添加识别的字符串长度(拼音字符串)初始化时写入00H,每添加一条识别语句后要设定一次
	LD_WriteReg(0xCF, 0x4F);  //ASR初始化时写入4FH
	LD_WriteReg(0x6F, 0xFF);  //对芯片进行初始化设置为0xFF
}
  • 2、语音识别用初始化
//语音识别初始化
static void LD_Init_ASR(void)
{
	nLD_Mode=LD_MODE_ASR_RUN;
	LD_Init_Common();

	LD_WriteReg(0xBD, 0x00); //初始化控制寄存器,写入00H,启动ASR,写入02H,为MP3
	LD_WriteReg(0x17, 0x48); //48H激活DSP	
	LD3320_delay(5);
	LD_WriteReg(0x3C, 0x80); //FIFO_EXT下限低8位   
	LD_WriteReg(0x3E, 0x07); //FIFO_EXT下限高8位 
	LD_WriteReg(0x38, 0xff); //FIFO_EXT上限低8位    
	LD_WriteReg(0x3A, 0x07); //FIFO_EXT上限高8位 
	LD_WriteReg(0x40, 0);    //FIFO_EXT MCU水线低8位       
	LD_WriteReg(0x42, 8);    //FIFO_EXT MCU水线高8位       
	LD_WriteReg(0x44, 0);    //FIFO_EXT DSP水线低8位
	LD_WriteReg(0x46, 8);    //FIFO_EXT DSP水线高8位
	LD3320_delay( 1 );
}
  • 3、写入识别列表

      列表的规则是,每个识别条目对应一个特定的编号(1 个字节),不同的识别条目的编号可以相同,而且不用连续。本芯片最多支持 50个识别条目,每个识别条目是标准普通话的汉语拼音(小写),每 2个字(汉语拼音)之间用一个空格间隔。下面是一个简单的例子:

等待芯片空闲

//校验LD3320时候在空闲状态
uint8 LD_Check_ASRBusyFlag_b2(void)
{
	uint8 j;
	uint8 flag = 0;
	for (j=0; j<10; j++)
	{
		if (LD_ReadReg(0xb2) == 0x21) //读取寄存器0xB2的值,如果是0x21则是空闲状态
		{
			flag = 1;
			break;
		}
		LD3320_delay(10);		
	}
	return flag;
}

设定编号

LD_WriteReg(0xc1, pCode[k] ); //ASR:识别字Index(00H-FFH)
LD_WriteReg(0xc3, 0);         //ASR:识别字添加时输入00
LD_WriteReg(0x08, 0x04);     //清除FIFO内容 写入04H---->清除FIFO_EXT
LD3320_delay(1);           
LD_WriteReg(0x08, 0x00);     //清除FIFO_DATA
LD3320_delay(1);

将字符串的数据按顺序,一次写入寄存器0x05

for (nAsrAddLength=0; nAsrAddLength<DATE_B; nAsrAddLength++)
{
	if (sRecog[k][nAsrAddLength] == 0)
		break;
	LD_WriteReg(0x5, sRecog[k][nAsrAddLength]);
}

讲【字符长度】写入寄存器B9

向寄存器B2写入0xFF

向寄存器37写入0x04

LD_WriteReg(0xb9, nAsrAddLength);
LD_WriteReg(0xb2, 0xff);
LD_WriteReg(0x37, 0x04);
  • 4、开始识别

设置几个相关的寄存器,就可以控制LD3320芯片开始语音识别。

static uint8 LD_AsrRun(void)
{
	LD_WriteReg(0x35, MIC_VOL); //设置MIC的音量
	LD_WriteReg(0x1C, 0x09);    //09H Reserve保留命令字,
 
	LD_WriteReg(0xBD, 0x20); // 20H,Reserve保留命令字
	LD_WriteReg(0x08, 0x01); //清除FIFO_DATA 
	LD3320_delay( 5 );
	LD_WriteReg(0x08, 0x00); //
	LD3320_delay( 5);

	if(LD_Check_ASRBusyFlag_b2() == 0) //判断是否空闲状态
	{
		return 0;
	}

	LD_WriteReg(0xB2, 0xff); //0x21表示闲。	
	LD_WriteReg(0x37, 0x06); //通知DSP开始识别语音
	LD_WriteReg(0x37, 0x06); //通知DSP开始识别语音
	LD3320_delay(5);
	LD_WriteReg(0x1C, 0x0b); //写麦克风输入ADC通道可用
	LD_WriteReg(0x29, 0x10); //FIFO中断允许,1表示允许,0表示不允许
	LD_WriteReg(0xBD, 0x00); //启动ASR模块  
	return 1;
}
  • 5、响应中断

    如果麦克风采集到声音,不管是否识别出正常结果,都会产生一个中断信号。而中断程序要根据寄存器的值分析结果。

读取 BA 寄存器的值,可以知道有几个候选答案,而 C5 寄存器里的答案是得分最高、最可能正确的答案。

STM32中断处理

//中断初始化
static void LD3320_EXTI_Cfg(void)
{
  EXTI_InitTypeDef EXTI_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);	

  RCC_APB2PeriphClockCmd(LD3320IRQ_GPIO_CLK, ENABLE);
  GPIO_InitStructure.GPIO_Pin =LD3320IRQ_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(LD3320IRQ_GPIO_PORT, &GPIO_InitStructure);
	//外部中断线配置
  GPIO_EXTILineConfig(LD3320IRQEXIT_PORTSOURCE, LD3320IRQPINSOURCE);
  EXTI_InitStructure.EXTI_Line = LD3320IRQEXITLINE;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger =EXTI_Trigger_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
	//中断嵌套配置
  NVIC_InitStructure.NVIC_IRQChannel = LD3320IRQN;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
}

中断处理

void EXTI9_5_IRQHandler(void)
{
	if(EXTI_GetITStatus(LD3320IRQEXITLINE)!= RESET ) 
	{
		ProcessInt(); 
 		printf("½øÈëÖжÏ5\r\n");	
		EXTI_ClearFlag(LD3320IRQEXITLINE);
		EXTI_ClearITPendingBit(LD3320IRQEXITLINE);//Çå³ýLINEÉϵÄÖжϱê־λ  
	} 
}

//中断处理函数
static void ProcessInt(void)
{
        uint8 nAsrResCount=0;

        ucRegVal = LD_ReadReg(0x2B);

//语音识别产生的中断
//(有声音输入,不论识别成功或失败都有中断)
	LD_WriteReg(0x29,0) ; //关掉FIFO中断
	LD_WriteReg(0x02,0) ; //关掉FIFO中断

	if((ucRegVal & 0x10) && LD_ReadReg(0xb2)==0x21 && LD_ReadReg(0xbf)==0x35)	 //正确识别到一次ASR
	{	 
			nAsrResCount = LD_ReadReg(0xba); //N个识别候选

			if(nAsrResCount>0 && nAsrResCount<=4) 
			{
				nAsrStatus=LD_ASR_FOUNDOK; 				
			}
			else
			{
				nAsrStatus=LD_ASR_FOUNDZERO;
			}	
	}
	else
	{
		nAsrStatus=LD_ASR_FOUNDZERO;//执行没有识别
	}

	LD_WriteReg(0x2b,0); //
	LD_WriteReg(0x1C,0); //写0:ADC不可用
	LD_WriteReg(0x29,0); //关FIFO中断
	LD_WriteReg(0x02,0); //关FIFO中断
	LD_WriteReg(0x2B,0); //中断请求编号
	LD_WriteReg(0xBA,0); //中断辅助信息	
	LD_WriteReg(0xBC,0); //识别过程强制结束	
	LD_WriteReg(0x08,1); //清除FIFO_DATA
	LD_WriteReg(0x08,0); //清除FIFO_DATA 再次写0
}

 

 

一次识别ASR识别流程结束,读取ASR识别结果

LD_ReadReg(0xc5);

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/ZOROE123/article/details/95724616
SPI