STM32 LD3320语音识别模块SPI驱动 (STM32CUBE MX HAL库)

LD3320语音识别模块驱动:

一、硬件连接
主控芯片:STM32F103VCT6
PA0-----------LD3320_IRQ
PC3-----------LD3320_WR
PA1-----------LD3320_RST

PA4-------------LD3320_CS
PA5-------------LD3320_SCK
PA6-------------LD3320_MISO
PA7-------------LD3320_MOSI

二、使用STM32CUBEMX创建工程
时钟配置:
在这里插入图片描述

引脚配置:
SPI配置:
在这里插入图片描述

其他引脚配置:
片选引脚配置:
在这里插入图片描述

IRQ中断引脚,配置为上拉下降沿触发:
在这里插入图片描述

使能中断,优先级随便:
在这里插入图片描述

WR引脚:
在这里插入图片描述

RST引脚:
在这里插入图片描述

使用串口1进行查看,串口1配置如下:
在这里插入图片描述二、驱动代码编写
这里我们使用商家给的代码进行移植,商家的代码时模拟SPI,我们转换为HAL的SPI;只需要修改两个函数以及添加一个函数即可:

/*写寄存器*/
void LD_WriteReg(uint8_t address,uint8_t dataout)
{
	uint8_t i = 0;
	uint8_t command=0x04;
	SCS = 0;
	delay_us(1);

	//write command
	for (i=0;i < 8; i++)
	{
		if (command & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		
		delay_us(1);
		SDCK = 0;
		command = (command << 1);  
		delay_us(1);
		SDCK = 1;  
	}
	//write address
	for (i=0;i < 8; i++)
	{
		if (address & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		delay_us(1);
		SDCK = 0;
		address = (address << 1); 
		delay_us(1);
		SDCK = 1;  
	}
	//write data
	for (i=0;i < 8; i++)
	{
		if (dataout & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		delay_us(1);
		SDCK = 0;
		dataout = (dataout << 1); 
		delay_us(1);
		SDCK = 1;  
	}
	delay_us(1);
	SCS = 1;
}
uint8_t LD_ReadReg(uint8_t address)
{
	#ifndef USE_HAL_LIB
	uint8_t i = 0; 
	uint8_t datain =0 ;
	uint8_t temp = 0; 
	uint8_t command=0x05;
	SCS = 0;
	delay_us(1);

	//write command
	for (i=0;i < 8; i++)
	{
		if (command & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		delay_us(1);
		SDCK = 0;
		command = (command << 1);  
		delay_us(1);
		SDCK = 1;  
	}

	//write address
	for (i=0;i < 8; i++)
	{
		if (address & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		delay_us(1);
		SDCK = 0;
		address = (address << 1); 
		delay_us(1);
		SDCK = 1;  
	}
	delay_us(1);

	//Read
	for (i=0;i < 8; i++)
	{
		datain = (datain << 1);
		temp = SDO;
		delay_us(1);
		SDCK = 0;  
		if (temp == 1)  
			datain |= 0x01; 
		delay_us(1);
		SDCK = 1;  
	}

	delay_us(1);
	SCS = 1;
	return datain;
	#else
	uint8_t rx_data;
	uint8_t tx_data[3];

  CS_LOW;
	delay_us(1);
	tx_data[0] = 0x05;
	tx_data[1] = address;
	tx_data[2] = 0x00;
	rx_data = SPI_RreadWrite_Data(tx_data,3);
	delay_us(1);
	CS_HIGH;
	return rx_data;
	#endif
}

这两个函数为软件模拟SPI时序读写代码,我们改成以下即可:

/*硬件SPI读写*/
uint8_t SPI_RreadWrite_Data(uint8_t *tx_data, uint8_t len)
{
	uint8_t rx_data[len];	
	HAL_SPI_TransmitReceive(&LD3320_SPI,tx_data,rx_data,len,0XFFFF);
	return rx_data[len-1];
}
/*写寄存器*/
void LD_WriteReg(uint8_t address,uint8_t dataout)
{
	uint8_t tx_data[3];
	CS_LOW;
	delay_us(1);
	tx_data[0] = 0x04;
	tx_data[1] = address;
	tx_data[2] = dataout;
	SPI_RreadWrite_Data(tx_data,3);
	delay_us(1);
	CS_HIGH;
}
/*读寄存器*/
uint8_t LD_ReadReg(uint8_t address)
{
	uint8_t rx_data;
	uint8_t tx_data[3];
  	CS_LOW;
	delay_us(1);
	tx_data[0] = 0x05;
	tx_data[1] = address;
	tx_data[2] = 0x00;
	rx_data = SPI_RreadWrite_Data(tx_data,3);
	delay_us(1);
	CS_HIGH;
	return rx_data;
}
/*中断回调函数*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == LD3320_IRQ_Pin)
	{
		if(HAL_GPIO_ReadPin(LD3320_IRQ_GPIO_Port,LD3320_IRQ_Pin) == GPIO_PIN_RESET)
		{
			ProcessInt(); 
			printf("进入中断12\r\n");	
		}
	}
}

改成这样就可以使用了,由与我们配置使用STM32CUBEMX进行配置,所以不需要再对引脚进行初始化。
驱动文件:
LD3320.c

#include "LD3320.h"
#include <stdio.h>
#include "delay.h"
#include "spi.h"
/************************************************************************************
//	nAsrStatus 用来在main主程序中表示程序运行的状态,不是LD3320芯片内部的状态寄存器
//	LD_ASR_NONE:			表示没有在作ASR识别
//	LD_ASR_RUNING:		表示LD3320正在作ASR识别中
//	LD_ASR_FOUNDOK:		表示一次识别流程结束后,有一个识别结果
//	LD_ASR_FOUNDZERO:	表示一次识别流程结束后,没有识别结果
//	LD_ASR_ERROR:			表示一次识别流程中LD3320芯片内部出现不正确的状态
********************************************************************************
*/

#define USE_HAL_LIB//使用HAL库
uint8_t nAsrStatus=0;
uint8_t nAsrRes=0;
uint8_t flag=0;
///用户修改
extern uint8_t nAsrStatus;
uint8_t  nLD_Mode=LD_MODE_IDLE;
uint8_t  ucRegVal;

#define LD3320_SPI	hspi1

/*硬件SPI读写*/
uint8_t SPI_RreadWrite_Data(uint8_t *tx_data, uint8_t len)
{
	uint8_t rx_data[len];
	HAL_SPI_TransmitReceive(&LD3320_SPI,tx_data,rx_data,len,0XFFFF);
	return rx_data[len-1];
}

void LD3320_main(void)
{
	nAsrStatus = LD_ASR_NONE;		//	初始状态:没有在作ASR
	//CS_LOW;
	printf("运行程序\r\n");
	while(1)
	{
		switch(nAsrStatus)
		{
			case LD_ASR_RUNING:
			case LD_ASR_ERROR:	
					 break;
			case LD_ASR_NONE:
			{
				nAsrStatus=LD_ASR_RUNING;
				if (RunASR()==0)	/*	启动一次ASR识别流程:ASR初始化,ASR添加关键词语,启动ASR运算*/
				{
					nAsrStatus = LD_ASR_ERROR;
				}
				break;
			}

			case LD_ASR_FOUNDOK: /*	一次ASR识别流程结束,去取ASR识别结果*/
			{
				nAsrRes = LD_GetResult();		/*获取结果*/												
				User_Modification(nAsrRes);
				nAsrStatus = LD_ASR_NONE;
				break;
			}
			case LD_ASR_FOUNDZERO:
			default:
			{
				nAsrStatus = LD_ASR_NONE;
				break;
			}
		} 
	}
	
}

uint8_t LD3320_Init(void)
{
//	GPIO_InitTypeDef  GPIO_InitStructure;
//	__HAL_RCC_GPIOA_CLK_ENABLE();
//	__HAL_RCC_GPIOB_CLK_ENABLE();
//	
 	RCC_APB2PeriphClockCmd(LD3320_SDCK_GPIO_CLK|LD3320_SDO_GPIO_CLK|LD3320_SDI_GPIO_CLK|\
	LD3320_SCS_GPIO_CLK|LD3320_RSTB_GPIO_CLK|LD3320_IRQ_GPIO_CLK,ENABLE);	 //使能PA端口时钟  | RCC_APB2Periph_AFIO
	//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//要先开时钟,再重映射;关闭jtag,保留swd。
//	
//	
// 	GPIO_InitStructure.Pin = GPIO_PIN_5;				//端口配置
// 	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 		  //推挽输出
// 	GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
//	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
//	
//	GPIO_InitStructure.Pin = GPIO_PIN_7;				//端口配置
// 	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 		  //推挽输出
// 	GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
//	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
//	
//	GPIO_InitStructure.Pin = GPIO_PIN_4;				//端口配置
// 	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 		  //推挽输出
// 	GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
//	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
//	
//	GPIO_InitStructure.Pin = GPIO_PIN_15;				//端口配置
// 	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 		  //推挽输出
// 	GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
//	HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
//	
//	GPIO_InitStructure.Pin = GPIO_PIN_6;				//端口配置
// 	GPIO_InitStructure.Mode = GPIO_MODE_INPUT; 		  //推挽输出
// 	GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
//	HAL_GPIO_Init(GPIOA,&GPIO_InitStructure);
//		
//	GPIO_InitStructure.Pin = GPIO_PIN_13;				//端口配置
// 	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; 		  //推挽输出
// 	GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
//	HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
//	
//	HAL_GPIO_WritePin(LD3320_WR_GPIO_Port,LD3320_WR_Pin,GPIO_PIN_RESET);//拉低WR引脚

//	/* GPIO Ports Clock Enable */
//	__HAL_RCC_GPIOB_CLK_ENABLE();
//	  /*Configure GPIO pin : PB12 */
//  GPIO_InitStructure.Pin = GPIO_PIN_12;
//  GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
//  HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
//	
//	  /* EXTI interrupt init*/
//  HAL_NVIC_SetPriority(EXTI15_10_IRQn,2,0);
//  HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
	HAL_Delay(500);
	HAL_GPIO_WritePin(LD3320_RST_GPIO_Port,LD3320_RST_Pin,GPIO_PIN_SET);//拉低WR引脚
	HAL_GPIO_WritePin(LD3320_CS_GPIO_Port,LD3320_CS_Pin,GPIO_PIN_SET);//拉低WR引脚
	HAL_GPIO_WritePin(LD3320_WR_GPIO_Port,LD3320_WR_Pin,GPIO_PIN_RESET);//拉低WR引脚
	return LD3320_Check();
}

/***********************************************************
* 名    称:用户执行函数 
* 功    能:识别成功后,执行动作可在此进行修改 
* 入口参数: 无 
* 出口参数:无
* 说    明: 					 
**********************************************************/
void User_Modification(uint8_t dat)
{
	if(dat)
	{
		switch(nAsrRes)		   /*对结果执行相关操作,客户修改*/
		{
			case CODE_DMCS:			/*命令“代码测试”*/
					printf(" 代码测试 识别成功\r\n"); /*text.....*/
												break;
			case CODE_CSWB:			/*命令“测试完毕”*/
					printf(" 测试完毕 识别成功\r\n"); /*text.....*/
												break;
			
			case CODE_1KL1:	 /*命令“北京”*/
					printf(" 北京 识别成功\r\n"); /*text.....*/
												break;
			case CODE_1KL2:		/*命令“上海”*/
		
					printf(" 上海 识别成功\r\n"); /*text.....*/
												break;
			case CODE_1KL3:	 /*命令“开灯”*/
					printf(" 开灯 识别成功\r\n"); /*text.....*/
												break;
			case CODE_1KL4:		/*命令“关灯”*/				
					printf(" 关灯 识别成功\r\n"); /*text.....*/
												break;
			
			case CODE_2KL1:	 /*命令“....”*/
					printf(" 广州 识别成功\r\n"); /*text.....*/
												break;
			case CODE_2KL2:	 /*命令“....”*/
					printf(" 深圳 识别成功\r\n"); /*text.....*/
												break;
			case CODE_2KL3:	 /*命令“....”*/
					printf(" 向左转 识别成功\r\n"); /*text.....*/
												break;
			case CODE_2KL4:	 /*命令“....”*/
					printf(" 向右转 识别成功\r\n"); /*text.....*/
															break;
						
			case CODE_3KL1:	 /*命令“....”*/
					printf(" 打开空调 识别成功\r\n"); /*text.....*/
												break;
			case CODE_3KL2:	 /*命令“....”*/
					printf(" 关闭空调 识别成功\r\n"); /*text.....*/
												break;
			case CODE_5KL1:	 /*命令“....”*/
					printf(" 后退 识别成功"); /*text.....*/
												break;
//		case CODE_3KL4:	 /*命令“....”*/
//				printf("\"代码测试\"识别成功"); /*text.....*/
//											break;
//					
//					case CODE_4KL1:	 /*命令“....”*/
//							printf("O"); /*text.....*/
//														break;
//					case CODE_4KL2:	 /*命令“....”*/
//							printf("P"); /*text.....*/
//														break;
//					case CODE_4KL3:	 /*命令“....”*/
//							printf("Q"); /*text.....*/
//														break;
//					case CODE_4KL4:	 /*命令“....”*/
//							printf("R"); /*text.....*/
//														break;
			
			default:break;
		}
	}
	
}
/************************************************************************
功能描述: 	 复位LD模块
入口参数:	 none
返 回 值: 	 none
其他说明:	 none
**************************************************************************/
void LD_Reset(void)
{
	RST_HIGH;
	HAL_Delay(5);
	RST_LOW;
	HAL_Delay(5);
	RST_HIGH;
	
	HAL_Delay(5);
	CS_LOW;
	HAL_Delay(5);
	CS_HIGH;
	HAL_Delay(5);
}
/************************************************************************
功能描述: LD模块命令初始化
入口参数: none
返 回 值: none
其他说明: 该函数为出厂配置,一般不需要修改;
					 有兴趣的客户可对照开发手册根据需要自行修改。
**************************************************************************/
void LD_Init_Common(void)
{
	LD_ReadReg(0x06);  
	LD_WriteReg(0x17, 0x35);

	HAL_Delay(5);
	LD_ReadReg(0x06);  

	LD_WriteReg(0x89, 0x03); 
	HAL_Delay(5);
	LD_WriteReg(0xCF, 0x43);
	HAL_Delay(5);
	LD_WriteReg(0xCB, 0x02);
	
	/*PLL setting*/
	LD_WriteReg(0x11, LD_PLL_11);
	if (nLD_Mode == LD_MODE_MP3)
	{
		LD_WriteReg(0x1E,0x00);
		LD_WriteReg(0x19, LD_PLL_MP3_19); 
		LD_WriteReg(0x1B, LD_PLL_MP3_1B);		
		LD_WriteReg(0x1D, LD_PLL_MP3_1D);
	}
	else
	{
		LD_WriteReg(0x1E,0x00);
		LD_WriteReg(0x19, LD_PLL_ASR_19); 
		LD_WriteReg(0x1B, LD_PLL_ASR_1B);
	  LD_WriteReg(0x1D, LD_PLL_ASR_1D);
	}	
	HAL_Delay(5);
	
	LD_WriteReg(0xCD, 0x04);
	LD_WriteReg(0x17, 0x4c);
	HAL_Delay(5);
	LD_WriteReg(0xB9, 0x00);
	LD_WriteReg(0xCF, 0x4F);
	LD_WriteReg(0x6F, 0xFF);
}

/************************************************************************
功能描述: 	 LD模块 ASR功能初始化
入口参数:	 none
返 回 值: 	 none
其他说明:	 该函数为出厂配置,一般不需要修改;
					 有兴趣的客户可对照开发手册根据需要自行修改。
**************************************************************************/
void LD_Init_ASR(void)
{
	nLD_Mode=LD_MODE_ASR_RUN;
	LD_Init_Common();

	LD_WriteReg(0xBD, 0x00);
	LD_WriteReg(0x17, 0x48);
	HAL_Delay(5);

	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);
	HAL_Delay(5);
}

/************************************************************************
功能描述: 	中断处理函数
入口参数:	 none
返 回 值: 	 none
其他说明:	当LD模块接收到音频信号时,将进入该函数,
						判断识别是否有结果,如果没有从新配置寄
            存器准备下一次的识别。
**************************************************************************/
void ProcessInt(void)
{
	uint8_t nAsrResCount=0;

	ucRegVal = LD_ReadReg(0x2B);
	LD_WriteReg(0x29,0) ;
	LD_WriteReg(0x02,0) ;
	if((ucRegVal & 0x10)&&LD_ReadReg(0xb2)==0x21&&LD_ReadReg(0xbf)==0x35)			/*识别成功*/
	{	
		nAsrResCount = LD_ReadReg(0xba);
		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);
	LD_WriteReg(0x02,0);
	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*/
}

/************************************************************************
功能描述:  检测LD模块是否空闲
入口参数:	none
返 回 值: 	flag:1-> 空闲
其他说明:	none
**************************************************************************/
uint8_t LD_Check_ASRBusyFlag_b2(void)
{
	uint8_t j,i;
	uint8_t flag = 0;
	for (j=0; j<5; j++)
	{
		i=LD_ReadReg(0xb2);
		if ( i== 0x21)
		{
			flag = 1;						
			break;
		}
		HAL_Delay(20);		
	}
	return flag;
}
/************************************************************************
功能描述: 	启动ASR
入口参数:	none
返 回 值: 	none
其他说明:	none
**************************************************************************/
void LD_AsrStart(void)
{
	LD_Init_ASR();
}
/************************************************************************
功能描述: 	运行ASR
入口参数:	none
返 回 值: 	1:启动成功
其他说明:	none
**************************************************************************/
uint8_t LD_AsrRun(void)
{
	LD_WriteReg(0x35, MIC_VOL);
	LD_WriteReg(0x1C, 0x09);
	LD_WriteReg(0xBD, 0x20);
	LD_WriteReg(0x08, 0x01);
	HAL_Delay(5);
	LD_WriteReg(0x08, 0x00);
	HAL_Delay(5);

	if(LD_Check_ASRBusyFlag_b2() == 0)
	{
		return 0;
	}

	LD_WriteReg(0xB2, 0xff);
	LD_WriteReg(0x37, 0x06);
	HAL_Delay(5);
    LD_WriteReg(0x37, 0x06);
	HAL_Delay(5);
	LD_WriteReg(0x1C, 0x0b);
	LD_WriteReg(0x29, 0x10);	
	LD_WriteReg(0xBD, 0x00);
	return 1;
}
/************************************************************************
功能描述: 向LD模块添加关键词
入口参数: none
返 回 值: flag:1->添加成功
其他说明: 用户修改.
					 1、根据如下格式添加拼音关键词,同时注意修改sRecog 和pCode 数组的长度
					 和对应变了k的循环置。拼音串和识别码是一一对应的。
					 2、开发者可以学习"语音识别芯片LD3320高阶秘籍.pdf"中
           关于垃圾词语吸收错误的用法,来提供识别效果。
**************************************************************************/
uint8_t LD_AsrAddFixed(void)
{
	uint8_t k, flag;
	uint8_t nAsrAddLength;
	
	#define DATE_A 14   //数组二维数值
	#define DATE_B 20		//数组一维数值
	
	
	uint8_t sRecog[DATE_A][DATE_B] = {
																		"xiao jie",\
																		"dai ma ce shi",\
																		"ce shi wan bi",\
		
																		"bei jing",\
																		"shang hai",\
																		"kai deng",\
																		"guan deng",\
		
																		"guang zhou",\
																		"shen zhen",\
																		"xiang zuo zhuan",\
																		"xiang you zhuan",\
		
																		"da kai kong tiao",\
																		"guan bi kong tiao",\
																		"hou tui",\
																 };	/*添加关键词,用户修改*/
	uint8_t pCode[DATE_A] = {
													CODE_CMD,\
													CODE_DMCS,\
													CODE_CSWB,\
												
													CODE_1KL1,\
		                      CODE_1KL2,\
		                      CODE_1KL3,\
		                      CODE_1KL4,\
		
													CODE_2KL1,\
		                      CODE_2KL2,\
		                      CODE_2KL3,\
		                      CODE_2KL4,\
		
													CODE_3KL1,\
		                      CODE_3KL2,\
													CODE_5KL1,
												};	/*添加识别码,用户修改*/	
	flag = 1;
	for (k=0; k<DATE_A; k++)
	{
			
		if(LD_Check_ASRBusyFlag_b2() == 0)
		{
			flag = 0;
			break;
		}
		
		LD_WriteReg(0xc1, pCode[k] );
		LD_WriteReg(0xc3, 0 );
		LD_WriteReg(0x08, 0x04);
		HAL_Delay(1);
		LD_WriteReg(0x08, 0x00);
		HAL_Delay(1);

		for (nAsrAddLength=0; nAsrAddLength<DATE_B; nAsrAddLength++)
		{
			if (sRecog[k][nAsrAddLength] == 0)
				break;
			LD_WriteReg(0x5, sRecog[k][nAsrAddLength]);
		}
		LD_WriteReg(0xb9, nAsrAddLength);
		LD_WriteReg(0xb2, 0xff);
		LD_WriteReg(0x37, 0x04);
	}
    return flag;
}

/************************************************************************
功能描述: 	运行ASR识别流程
入口参数:	none
返 回 值:  asrflag:1->启动成功, 0—>启动失败
其他说明:	识别顺序如下:
						1、RunASR()函数实现了一次完整的ASR语音识别流程
						2、LD_AsrStart() 函数实现了ASR初始化
						3、LD_AsrAddFixed() 函数实现了添加关键词语到LD3320芯片中
						4、LD_AsrRun()	函数启动了一次ASR语音识别流程					
						任何一次ASR识别流程,都需要按照这个顺序,从初始化开始
**************************************************************************/
uint8_t RunASR(void)
{
	uint8_t i=0;
	uint8_t asrflag=0;
	for (i=0; i<5; i++)			//	防止由于硬件原因导致LD3320芯片工作不正常,所以一共尝试5次启动ASR识别流程
	{
		LD_AsrStart();
		HAL_Delay(5);
		if (LD_AsrAddFixed()==0)
		{
			LD_Reset();			//	LD3320芯片内部出现不正常,立即重启LD3320芯片
			HAL_Delay(5);			//	并从初始化开始重新ASR识别流程
			continue;
		}
		delay_us(5);
		if (LD_AsrRun() == 0)
		{
			LD_Reset();			//	LD3320芯片内部出现不正常,立即重启LD3320芯片
			HAL_Delay(5);			//	并从初始化开始重新ASR识别流程
			continue;
		}	
		asrflag=1;
		break;					//	ASR流程启动成功,退出当前for循环。开始等待LD3320送出的中断信号
	}
	return asrflag;
}

/************************************************************************
功能描述: 	获取识别结果
入口参数:	none
返 回 值: 	LD_ReadReg(0xc5 );  读取内部寄存器返回识别码。
其他说明:	none
**************************************************************************/
uint8_t LD_GetResult(void)
{	
	return LD_ReadReg(0xc5 );
}

/*写寄存器*/
void LD_WriteReg(uint8_t address,uint8_t dataout)
{
	#ifndef USE_HAL_LIB
	uint8_t i = 0;
	uint8_t command=0x04;
	SCS = 0;
	delay_us(1);

	//write command
	for (i=0;i < 8; i++)
	{
		if (command & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		
		delay_us(1);
		SDCK = 0;
		command = (command << 1);  
		delay_us(1);
		SDCK = 1;  
	}
	//write address
	for (i=0;i < 8; i++)
	{
		if (address & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		delay_us(1);
		SDCK = 0;
		address = (address << 1); 
		delay_us(1);
		SDCK = 1;  
	}
	//write data
	for (i=0;i < 8; i++)
	{
		if (dataout & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		delay_us(1);
		SDCK = 0;
		dataout = (dataout << 1); 
		delay_us(1);
		SDCK = 1;  
	}
	delay_us(1);
	SCS = 1;
	#else
	uint8_t tx_data[3];
	CS_LOW;
	delay_us(1);
	tx_data[0] = 0x04;
	tx_data[1] = address;
	tx_data[2] = dataout;
	SPI_RreadWrite_Data(tx_data,3);
	delay_us(1);
	CS_HIGH;
	#endif
}
/*读寄存器*/
uint8_t LD_ReadReg(uint8_t address)
{
	#ifndef USE_HAL_LIB
	uint8_t i = 0; 
	uint8_t datain =0 ;
	uint8_t temp = 0; 
	uint8_t command=0x05;
	SCS = 0;
	delay_us(1);

	//write command
	for (i=0;i < 8; i++)
	{
		if (command & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		delay_us(1);
		SDCK = 0;
		command = (command << 1);  
		delay_us(1);
		SDCK = 1;  
	}

	//write address
	for (i=0;i < 8; i++)
	{
		if (address & 0x80) 
			SDI = 1;
		else
			SDI = 0;
		delay_us(1);
		SDCK = 0;
		address = (address << 1); 
		delay_us(1);
		SDCK = 1;  
	}
	delay_us(1);

	//Read
	for (i=0;i < 8; i++)
	{
		datain = (datain << 1);
		temp = SDO;
		delay_us(1);
		SDCK = 0;  
		if (temp == 1)  
			datain |= 0x01; 
		delay_us(1);
		SDCK = 1;  
	}

	delay_us(1);
	SCS = 1;
	return datain;
	#else
	uint8_t rx_data;
	uint8_t tx_data[3];

  CS_LOW;
	delay_us(1);
	tx_data[0] = 0x05;
	tx_data[1] = address;
	tx_data[2] = 0x00;
	rx_data = SPI_RreadWrite_Data(tx_data,3);
	delay_us(1);
	CS_HIGH;
	return rx_data;
	#endif
}

/************************************************************************
功能描述: 模块检测(检查寄存器的读写是否正确)
入口参数:	none
返 回 值:成功返回 0 ,失败返回1
其他说明:	none
**************************************************************************/
uint8_t LD3320_Check(void)
{
	uint8_t flag = 1;
	LD_Reset();//语音复位
	LD_ReadReg(0x6);
	LD_WriteReg(0x35, 0x33);
	LD_WriteReg(0x1b, 0x55);
	LD_WriteReg(0xb3, 0xaa);

	if(LD_ReadReg(0x35) == 0x33 && LD_ReadReg(0x1b) == 0x55 && LD_ReadReg(0xb3) == 0xaa)
	{
		flag = 0;
	}
	return flag;
}

/*中断回调函数*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if(GPIO_Pin == LD3320_IRQ_Pin)
	{
		if(HAL_GPIO_ReadPin(LD3320_IRQ_GPIO_Port,LD3320_IRQ_Pin) == GPIO_PIN_RESET)
		{
			ProcessInt(); 
			printf("进入中断12\r\n");	
		}
	}
}


LD3320.h:

#ifndef LD3320_H__
#define LD3320_H__

#include "stm32f1xx_hal.h"
#include "main.h"

//	以下三个状态定义用来记录程序是在运行ASR识别还是在运行MP3播放
#define LD_MODE_IDLE			0x00
#define LD_MODE_ASR_RUN		0x08
#define LD_MODE_MP3		 		0x40


//	以下五个状态定义用来记录程序是在运行ASR识别过程中的哪个状态
#define LD_ASR_NONE				0x00	//	表示没有在作ASR识别
#define LD_ASR_RUNING			0x01	//	表示LD3320正在作ASR识别中
#define LD_ASR_FOUNDOK		0x10	//	表示一次识别流程结束后,有一个识别结果
#define LD_ASR_FOUNDZERO 	0x11	//	表示一次识别流程结束后,没有识别结果
#define LD_ASR_ERROR	 		0x31	//	表示一次识别流程中LD3320芯片内部出现不正确的状态


#define CLK_IN   				24	/* 用户注意修改输入的晶振时钟大小 */
#define LD_PLL_11				(uint8_t)((CLK_IN/2.0)-1)
#define LD_PLL_MP3_19		0x0f
#define LD_PLL_MP3_1B		0x18
#define LD_PLL_MP3_1D   (uint8_t)(((90.0*((LD_PLL_11)+1))/(CLK_IN))-1)

#define LD_PLL_ASR_19 	(uint8_t)(CLK_IN*32.0/(LD_PLL_11+1) - 0.51)
#define LD_PLL_ASR_1B 	0x48
#define LD_PLL_ASR_1D 	0x1f

#define MIC_VOL 0x43	 //咪头增益



//识别码客户修改处
#define CODE_CMD   0x00   //该命令码0x00用户不可进行修改。
#define CODE_DMCS  0x01		//代码测试
#define CODE_CSWB  0x02	  //测试完毕

#define CODE_1KL1 0x03
#define CODE_1KL2 0x04
#define CODE_1KL3 0x05
#define CODE_1KL4 0x06
                  
#define CODE_2KL1 0x18
#define CODE_2KL2 0x19
#define CODE_2KL3 0x1A
#define CODE_2KL4 0x1B
                  
#define CODE_3KL1 0x1C
#define CODE_3KL2 0x1D
#define CODE_3KL3 0x1E
#define CODE_3KL4 0x1F
                  
#define CODE_4KL1 0x20
#define CODE_4KL2 0x21
#define CODE_4KL3 0x22
#define CODE_4KL4 0x23

#define CODE_5KL1 0x24

#define RST_HIGH			HAL_GPIO_WritePin(LD3320_RST_GPIO_Port,LD3320_RST_Pin,GPIO_PIN_SET)
#define RST_LOW				HAL_GPIO_WritePin(LD3320_RST_GPIO_Port,LD3320_RST_Pin,GPIO_PIN_RESET)

#define SCK_HIGH			HAL_GPIO_WritePin(LD3320_SCK_GPIO_Port,LD3320_SCK_Pin,GPIO_PIN_SET)
#define SCK_LOW				HAL_GPIO_WritePin(LD3320_SCK_GPIO_Port,LD3320_SCK_Pin,GPIO_PIN_RESET)

#define MOSI_HIGH			HAL_GPIO_WritePin(LD3320_MOSI_GPIO_Port,LD3320_MOSI_Pin,GPIO_PIN_SET)
#define MOSI_LOW			HAL_GPIO_WritePin(LD3320_MOSI_GPIO_Port,LD3320_MOSI_Pin,GPIO_PIN_RESET)

#define IRQ_HIGH			HAL_GPIO_WritePin(LD3320_IRQ_GPIO_Port,LD3320_IRQ_Pin,GPIO_PIN_SET)
#define IRQ_LOW				HAL_GPIO_WritePin(LD3320_IRQ_GPIO_Port,LD3320_IRQ_Pin,GPIO_PIN_RESET)

#define CS_HIGH				HAL_GPIO_WritePin(LD3320_CS_GPIO_Port,LD3320_CS_Pin,GPIO_PIN_SET)
#define CS_LOW				HAL_GPIO_WritePin(LD3320_CS_GPIO_Port,LD3320_CS_Pin,GPIO_PIN_RESET)
void LD3320_main(void);

void User_Modification(uint8_t dat);
//函数声明
void LD_Reset(void);
void LD_Init_Common(void);
void LD_Init_ASR(void);
void ProcessInt(void);
void LD_AsrStart(void);
uint8_t LD_AsrRun(void);
uint8_t LD_AsrAddFixed(void);
uint8_t RunASR(void);
uint8_t LD_GetResult(void);

uint8_t LD3320_Init(void);
void LD_WriteReg(uint8_t address,uint8_t dataout);
uint8_t LD_ReadReg(uint8_t address);
uint8_t LD3320_Check(void);

#endif


测试举例:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "spi.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "ld3320.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/*****************************************
函数名:
参数:无
功能描述:printf输出重定向到串口1
返回值:
*****************************************/
int fputc(int ch,FILE *f){
 uint8_t temp[1]={ch};
 HAL_UART_Transmit(&huart1,temp,1,2);
 return ch;
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	printf("LD3320 START TEST\r\n");
	if(!LD3320_Init())
		printf("LD3320 init suc\r\n");
	else 
		printf("LD3320 init failed\r\n");
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  LD3320_main();	  
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */


以上就是驱动相关代码,注意:在使用时LD3320_WR引脚一定要默认拉低。

三、测试结果
在这里插入图片描述

最终测试成功,移植完成。

  • 10
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 22
    评论
好的,我会尽力为您解答。首先,让我们来了解一下凑硬币问题。 在凑硬币问题中,我们需要找到最少的硬币数来凑成给定的金额。例如,如果我们需要凑成 11 元,而有 1 元、2 元、5 元三种不同面值的硬币可供使用,那么最少需要使用 3 枚硬币,即 5 元、5 元和 1 元。 下面是一种使用动态规划解决凑硬币问题的方法: 1. 创建一个长度为 amount+1 的数组 dp,其中 dp[i] 表示凑成面值为 i 元所需的最少硬币数。 2. 初始化 dp[0] 为 0,其余元素初始化为一个较大的数(例如 amount+1),表示当前面值无法被凑成。 3. 遍历硬币数组 coins,对于每个硬币,遍历 dp 数组,更新 dp[j] 的值为 dp[j-coins[i]]+1,其中 j>=coins[i],表示当前面值可以被凑成。 4. 最终 dp[amount] 就是凑成 amount 元所需的最少硬币数,如果 dp[amount]>amount,则表示无法凑成给定金额。 下面是使用 JavaScript 实现该算法的代码: ```javascript function coinChange(coins, amount) { const dp = new Array(amount + 1).fill(amount + 1); dp[0] = 0; for (let i = 0; i < coins.length; i++) { for (let j = coins[i]; j <= amount; j++) { dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1); } } return dp[amount] > amount ? -1 : dp[amount]; } ``` 这个函数接受两个参数,一个是硬币数组 coins,另一个是需要凑成的金额 amount。最终返回凑成 amount 元所需的最少硬币数,如果无法凑成则返回 -1。 例如,若 coins = [1, 2, 5],amount = 11,则 coinChange(coins, amount) 的返回值为 3,表示凑成 11 元最少需要 3 枚硬币。 希望这个简单的解释和代码能够帮到您,如果您还有其他问题,请随时问我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值