LD3320語音識別(基於ESP32)


  • (PS:本文章只寫識別)
  • 一、關於LD3320的簡介

LD3320 芯片是一款“語音識別”專用芯片。該芯片集成了語音識別處理器和一些外部電路,包括 AD、DA 轉換器、麥克風接口、聲音輸出接口等。本芯片不需要外接任何的輔助芯片如 Flash、RAM 等,直接集成在現有的產品中即可以實現語音識別/聲控/人機對話功能。並且,識別的關鍵詞語列表是可以任意動態編輯的(關鍵詞為中文拼音,比較銀杏化)。

 

  • 二、功能介紹

特征:

  • 工作供電為 3.3V
  • 不需要訓練,只需要指定關鍵詞,識別正確率高不需要外接任何輔助的 Flash 芯片,RAM 芯片和 AD 芯片,就可以完成語
  • 音識別功能。真正提供了單芯片語音識別解決方案每次識別最多可以設置 50 項候選識別句,每個識別句可以是單字,詞組或短句,長度為不超過 10 個漢字或者 79 個字節的拼音串。識別句內容可以編輯修改。

  • 芯片內部已經准備了 16 位 A/D 轉換器、16 位 D/A 轉換器和功放電路,
    麥克風、立體聲耳機和單聲道喇叭可以很方便地和芯片管腳連接。立體
    聲耳機接口的輸出功率為 20mW,而喇叭接口的輸出功率為 550mW,能產
    生清晰響亮的聲音

  • 支持並行和串行接口,串行方式可以簡化與其他模塊的連接
  • 可設置為休眠狀態,而且可以方便地激活
  • 支持 MP3 播放功能,無需外圍輔助器件,主控 MCU 將 MP3 數據依次送入
    LD3320 芯片內部就可以從芯片的相應 PIN 輸出聲音。產品設計可以選
    擇 從 立 體 聲 的 耳 機 或 者 單 聲 道 喇 叭 來 獲 得 聲 音 輸 出 。 支 持
    MPEG1(ISO/IEC11172-3), MPEG2(ISO/IEC13818-3) 和 MPEG 2.5 layer
    3 等格式

  • 芯片必須連接外部時鍾,可接受的頻率范圍是 4—48MHz;而芯片內部還
    有 PLL 頻率合成器,可產生特定的頻率供內部模塊使用

  • 通信是通過串行接口SPI協議與外部主控板連接
  • 三、IO接線

下面這個是中斷觸發,連IRQ引腳(我這里寫的是ESP32的GPIO4)

 

 

 

 

  • 四、ESP32的LD3320驅動碼源

 

#include "ld3320.h"
#include <SPI.h>

uint8_t g_Mic;
uint8_t MIC_VOL = 0x55;           //ADC增益初始值
uint8_t speech_endpoint = 0x10;   //語音端點檢測初始值
uint8_t speech_start_time = 0x08; //語音端點檢測開始時間初始值
uint8_t speech_end_time = 0x10;   //語音端點檢測結束時間初始值
uint8_t voice_max_length = 0xC3;  //最長語音段時間,默認20秒
uint8_t noise_time = 0x02;        //忽略上電噪聲時間
//uint8_t ASR_time最長時間
int readflag = 0;
int readnum = 0;

byte transfer(byte _data);

VoiceRecognition::VoiceRecognition() {}

int VoiceRecognition::read() //識別結果讀取
{
    if (readflag == 1)
    {
        readflag = 0;
        return readnum;
    }
    return -1;
}
void update() //中斷服務函數
{
    uint8_t Asr_Count = 0;
    Serial.print("into interrupt...");
    Serial.print("\r\n");
    if ((readReg(0x2b) & 0x10) && readReg(0xb2) == 0x21 && readReg(0xbf) == 0x35) //如果有語音識別中斷、DSP閑、ASR正常結束
    {
        writeReg(0x29, 0);                  ///////////關中斷
        writeReg(0x02, 0);                  /////////////關FIFO中斷
        Asr_Count = readReg(0xba);          //讀中斷輔助信息
        if (Asr_Count > 0 && Asr_Count < 4) //////如果有識別結果
        {
            readnum = readReg(0xc5);
            readflag = 1;
        }
        writeReg(0x2b, 0);
        writeReg(0x1C, 0);
    }
    readReg(0x06);
    delay(10);
    readReg(0x06);
    writeReg(0x89, 0x03);
    delay(5);
    writeReg(0xcf, 0x43);
    delay(5);
    writeReg(0xcb, 0x02);
    writeReg(0x11, PLL_11);
    writeReg(0x1e, 0x00);
    writeReg(0x19, PLL_ASR_19);
    writeReg(0x1b, PLL_ASR_1B);
    writeReg(0x1d, PLL_ASR_1D);
    delay(10);
    writeReg(0xcd, 0x04);
    writeReg(0x17, 0x4c);
    delay(5);
    writeReg(0xcf, 0x4f);
    writeReg(0xbd, 0x00);
    writeReg(0x17, 0x48);
    delay(10);
    writeReg(0x3c, 0x80);
    writeReg(0x3e, 0x07);
    writeReg(0x38, 0xff);
    writeReg(0x3a, 0x07);
    writeReg(0x40, 0);
    writeReg(0x42, 8);
    writeReg(0x44, 0);
    writeReg(0x46, 8);
    delay(1);
    writeReg(0x1c, 0x09); ////////麥克風設置保留
    writeReg(0xbd, 0x20); /////////保留設置
    writeReg(0x08, 0x01); ///////////→清除FIFO_DATA
    delay(1);
    writeReg(0x08, 0x00); ////////////清除指定FIFO后再寫入一次00H
    delay(1);
    writeReg(0xb2, 0xff); ////////給0xB2寫FF
    writeReg(0x37, 0x06); ////////開始識別
    delay(5);
    writeReg(0x1c, g_Mic); ////////選擇麥克風
    writeReg(0x29, 0x10);  ////////開同步中斷
    writeReg(0xbd, 0x00);  /////////啟動為語音識別
}
void cSHigh()
{ //CS拉高
    digitalWrite(CS, HIGH);
}
void cSLow()
{ //CS腳拉低
    digitalWrite(CS, LOW);
}

void writeReg(unsigned char address, unsigned char value) ////////寫寄存器,參數(寄存器地址,數據)
{
    cSLow(); ////拉低CS
    delay(10);
    transfer(0x04); ////////////寫指令
    transfer(address);
    transfer(value);
    cSHigh(); ////拉高CS
}

unsigned char readReg(unsigned char address) ///讀寄存器,參數(寄存器地址)
{
    unsigned char result;
    cSLow(); ////拉低CS
    delay(10);
    transfer(0x05); ///////////讀指令
    transfer(address);
    result = transfer(0x00);
    cSHigh(); ///拉高CS
    return (result);
}

byte transfer(byte _data) /////////////////SPI sent_beyt
{
    int32_t i = 0;
    uint8_t data = 0;
    cSLow(); ////拉低CS
    for (i = 7; i >= 0; i--)
    {
        //MSB傳輸
        if (_data & (1 << i))
        {
            digitalWrite(SPI_MOSI_PIN, HIGH); //bit位為1 就置高電平
        }
        else
        {
            digitalWrite(SPI_MOSI_PIN, LOW); //bit位為0 就置低電平
        }
        if (digitalRead(SPI_MISO_PIN))
        { //發送完 馬上接收
            data |= (1 << i);
        }
        /*一定要看手冊和時序圖 下降沿有效*/
        /*SPI模式3 空閑CS=1,第一個跳邊沿觸發*/
        digitalWrite(SPI_SCK_PIN, LOW);
        delay(1);
        digitalWrite(SPI_SCK_PIN, HIGH);
        delay(1);
    }
    return data; //是否接收返回看需求
}

void VoiceRecognition::init(uint8_t mic) ////////模塊啟用,參數為麥克風選擇(MIC/MONO)與絲印對照,在SETUP中調用
{
    if (mic == MIC)
    {
        g_Mic = MIC;
    }
    else if (mic == MONO)
    {
        g_Mic = MONO;
    }
    pinMode(RSTB, OUTPUT);
    pinMode(CS, OUTPUT);
    cSHigh();

    pinMode(4, INPUT_PULLUP);
    pinMode(SPI_MISO_PIN, INPUT);
    pinMode(SPI_MOSI_PIN, OUTPUT);
    pinMode(SPI_SCK_PIN, OUTPUT);

    //  #ifndef SOFTWARE_SPI
    //   // SS must be in output mode even it is not chip select
    //   pinMode(LD_CHIP_SELECT_PIN, OUTPUT);
    //   digitalWrite(LD_CHIP_SELECT_PIN, HIGH); // disable any SPI device using hardware SS 拉高ss
    //   // Enable SPI, Master, clock rate f_osc/128
    //   //SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);/////初始化SPI寄存器
    //   // clear double speed
    //   //SPSR &= ~(1 << SPI2X);//2倍速
    // #endif  // SOFTWARE_SPI

    // SPCR = (SPCR & ~SPI_MODE_MASK) | 0x08;//設置SCK常態電平與取樣時間,0x08為SCK常態為高電平,下降沿有效
    reset(); //LD3320復位操作

    // #if defined(__AVR_ATmega32U4__)
    //     attachInterrupt(1,update,LOW);//開中斷
    // #else
    //     attachInterrupt(0,update,LOW);//開中斷
    // #endif
    attachInterrupt(4, update, FALLING); //開中斷

    ASR_init(); ///語音識別初始化函數
}

void VoiceRecognition::reset() //LD3320復位操作
{
    digitalWrite(RSTB, HIGH);
    delay(1);
    digitalWrite(RSTB, LOW);
    delay(1);
    digitalWrite(RSTB, HIGH);
    delay(1);
    cSLow();
    delay(1);
    cSHigh();
    delay(1);
    writeReg(0xb9, 0x00);
}

void VoiceRecognition::ASR_init() ////////////初始化語音識別模式、
{
    //添加狀態標記
    readReg(0x06);
    //      writeReg(0x17, 0x35);
    delay(10);
    readReg(0x06);
    writeReg(0x89, 0x03);
    delay(5);
    writeReg(0xcf, 0x43);
    delay(5);
    writeReg(0xcb, 0x02);
    writeReg(0x11, PLL_11);
    writeReg(0x1e, 0x00);
    writeReg(0x19, PLL_ASR_19);
    writeReg(0x1b, PLL_ASR_1B);
    writeReg(0x1d, PLL_ASR_1D);
    delay(10);
    writeReg(0xcd, 0x04);
    writeReg(0x17, 0x4c);
    delay(5);
    //      writeReg(0xb9, 0x00);
    writeReg(0xcf, 0x4f);
    writeReg(0xbd, 0x00);
    writeReg(0x17, 0x48);
    delay(10);
    writeReg(0x3c, 0x80);
    writeReg(0x3e, 0x07);
    writeReg(0x38, 0xff);
    writeReg(0x3a, 0x07);
    writeReg(0x40, 0);
    writeReg(0x42, 8);
    writeReg(0x44, 0);
    writeReg(0x46, 8);
    delay(1);
}
void VoiceRecognition::addCommand(char *pass, int num)
{
    int i;
    writeReg(0xc1, num);  //字符編號
    writeReg(0xc3, 0);    //添加時輸入00
    writeReg(0x08, 0x04); //不清除
    delay(1);
    writeReg(0x08, 0x00); //
    delay(1);
    for (i = 0; i <= 80; i++)
    {
        if (pass[i] == 0)
            break;
        writeReg(0x5, pass[i]); ///寫入FIFO_EXT
    }
    writeReg(0xb9, i);    //寫入當前添加字符串長度
    writeReg(0xb2, 0xff); //////////B2全寫ff
    writeReg(0x37, 0x04); //添加語句
}
unsigned char VoiceRecognition::start() //////開始識別
{
    writeReg(0x35, MIC_VOL); ////adc增益;會影響識別范圍即噪聲
    writeReg(0xb3, speech_endpoint); //語音端點檢測控制
    writeReg(0xb4, speech_start_time); //語音端點起始時間
    writeReg(0xb5, speech_end_time); //語音結束時間
    writeReg(0xb6, voice_max_length); //語音結束時間
    writeReg(0xb7, noise_time); //噪聲時間
    writeReg(0x1c, 0x09); ////////麥克風設置保留
    writeReg(0xbd, 0x20); /////////保留設置
    writeReg(0x08, 0x01); ///////////→清除FIFO_DATA
    delay(1);
    writeReg(0x08, 0x00); ////////////清除指定FIFO后再寫入一次00H
    delay(1);
    if (check_b2() == 0) ////////讀取0xB2寄存器函數如果DSP沒在閑狀態則RETURN 0
    {
        return 0;
    }
    writeReg(0xb2, 0xff); ////////給0xB2寫FF
    writeReg(0x37, 0x06); ////////開始識別
    delay(5);
    writeReg(0x1c, g_Mic); ////////選擇麥克風
    writeReg(0x29, 0x10);  ////////開同步中斷
    writeReg(0xbd, 0x00);  /////////啟動為語音識別
    return 1; ////返回1
}

int check_b2() ////////用作檢測芯片工作是否正常,或者DSP是否忙,不需用戶操作,正常/閑返回1
{
    for (int j = 0; j < 10; j++)
    {
        if (readReg(0xb2) == 0x21)
        {
            return 1;
        }
        delay(10);
    }
    return 0;
}

void VoiceRecognition::micVol(uint8_t vol) //調整ADC增益,參數(0x00~0xFF,建議10-60);
{
    MIC_VOL = vol;
    writeReg(0x35, MIC_VOL); ////adc增益;會影響識別范圍即噪聲
}
void VoiceRecognition::speechEndpoint(uint8_t speech_endpoint_) //調整語音端點檢測,參數(0x00~0xFF,建議10-40);
{
    speech_endpoint = speech_endpoint_;
    writeReg(0xb3, speech_endpoint); //語音端點檢測控制
}

void VoiceRecognition::speechStartTime(uint8_t speech_start_time_) //調整語音端點起始時間,參數(0x00~0x30,單位10MS);
{
    speech_start_time = speech_start_time_;
    writeReg(0xb4, speech_start_time); //語音端點起始時間
}
void VoiceRecognition::speechEndTime(uint8_t speech_end_time_) //調整語音端點結束時間(吐字間隔時間),參數(0x00~0xC3,單位10MS);
{
    speech_end_time = speech_end_time_;
    writeReg(0xb5, speech_end_time); //語音結束時間
}
void VoiceRecognition::voiceMaxLength(uint8_t voice_max_length_) //最長語音段時間,參數(0x00~0xC3,單位100MS);
{
    voice_max_length = voice_max_length_;
    writeReg(0xb6, voice_max_length); //語音
}
void VoiceRecognition::noiseTime(uint8_t noise_time_) //上電噪聲略過,參數(0x00~0xff,單位20MS);
{
    noise_time = noise_time_;
    writeReg(0xb7, noise_time); //噪聲時間
}
LD3320.cpp

 

 

 

#ifndef LD3320_H
#define LD3320_H

#include <Arduino.h>
#include <Wire.h>
#include "SPI.h"

#define uint8 unsigned char
//#define SPI_MODE_MASK 0x0C  // CPOL = bit 3, CPHA = bit 2 on SPCR
#define MIC 0x0b
#define MONO 0x23
#define uint8 unsigned char
#define CLK_IN            24///頻率
#define PLL_11            (uint8)((CLK_IN/2.0)-1)
#define PLL_ASR_19         (uint8)(CLK_IN*32.0/(PLL_11+1) - 0.51)
#define PLL_ASR_1B         0x48
#define PLL_ASR_1D         0x1f

/** SPI Master Out Slave In pin */
#define SPI_MOSI_PIN   15
/** SPI Master In Slave Out pin */
#define SPI_MISO_PIN   14
/** SPI Clock pin */
#define SPI_SCK_PIN   27

#define  RSTB  13 //RSTB引腳定義
#define  CS  32 //RSTB引腳定義



class VoiceRecognition
{

public:
    VoiceRecognition();

    void reset();
    void init(uint8_t mic=MIC);
    void ASR_init();
    unsigned char start();
    void addCommand(char *pass,int num);
    int read();
    
    void micVol(uint8_t vol);
    void speechEndpoint(uint8_t speech_endpoint_);
    void speechStartTime(uint8_t speech_start_time_);
    void speechEndTime(uint8_t speech_end_time_);
    void voiceMaxLength(uint8_t voice_max_length_);
    void noiseTime(uint8_t noise_time_);
 private:

};

    void writeReg(unsigned char address,unsigned char value);
    unsigned char readReg(unsigned char address);
    byte transfer(byte _data);
    void cSHigh(void); 
    void cSLow(void);
    void update();
    int check_b2();
#endif
LD3320.h

 

 

 

 

#include <Arduino.h>
#include <LD3320.h>
VoiceRecognition Voice; //聲明一個語音識別對象
void Vioce_INIT();
void Recognize_Voice();

void setup()
{
    Serial.begin(115200);
    SPI.begin(SPI_SCK_PIN, SPI_MISO_PIN, SPI_MOSI_PIN, CS);
    Vioce_INIT(); /*LD3320語音識別初始化*/
}

void loop()
{

    Recognize_Voice(); /*測試LD3320*/
}

void Vioce_INIT()
{
    /**********LD3320Init*****************/
    Voice.init(MIC);                    //初始化VoiceRecognition模塊
    Voice.addCommand("kai deng", 0);    //添加指令,參數(指令內容,指令標簽(可重復))
    Voice.addCommand("wang ba dan", 1); //添加指令,參數(指令內容,指令標簽(可重復))
    Voice.addCommand("wei wei", 2);     //添加指令,參數(指令內容,指令標簽(可重復))
    Voice.addCommand("ni hao", 3);      //添加指令,參數(指令內容,指令標簽(可重復))
    Voice.start();                      //開始識別
    /*************************************/

    /*************LD3320調試code**************/
    // Voice.reset();
    // readReg(0x6);
    // writeReg(0x35, 0x33);
    // writeReg(0x1b, 0x55);
    // writeReg(0xb3, 0xaa);
    // Serial.print(readReg(0x06) );
    // Serial.print(readReg(0x06) );
    // Serial.print(readReg(0x35) );
    // Voice.addCommand("kai deng",0);
    // Serial.print(readReg(0xBF) );
    // Serial.print("\r\n");
    //delay(2000);
}

void Recognize_Voice()
{
    /******************************************/
    switch (Voice.read()) //判斷識別
    {
    case 0: //若是指令“kai deng”
        Serial.print("000\r\n");
        break;
    case 1: //若是指令“guan deng”
        Serial.print("111\r\n");
        break;
    default:
        //Serial.print("no\r\n");
        break;
    }
    /*****************************************/
}
測試程序

 

 

 

 

 

 

  • 五、調試寄存器步驟

(建議先做這個以排除通信問題,就是先寫后讀看看對錯與否)

1、先運行LD_reset();然后依次讀取並打印0x06  0x06  0x35  0xb3寄存器的值

調試代碼如下:

LD_reset();
LD_ReadReg(0x6);
LD_WriteReg(0x35, 0x33);
LD_WriteReg(0x1b, 0x55);
LD_WriteReg(0xb3, 0xaa);
Serial.println(LD_ReadReg(0x35));
Serial.println(LD_ReadReg(0x1b));
Serial.println(LD_ReadReg(0xb3));
View Code

 

 

如果依次打印的值是 (00 87 80 FF)或者(87 87 80 FF)那就正常,第一次讀0x06是激活了芯片

 

2、運行過程中檢查寄存器狀態

如果第一步的測試正常還是不能正常的話就需要檢查工作中的寄存器狀態了。

(1) 在完成 LD_AsrAddFixed() 函數后,檢測 0xBF 寄存器的數值,看是否是 0x31;

(2) 在 LD_AsrRun()函數中的LD_WriteReg(0x37, 0x06);delay( 5 );后讀取 0xBF 寄存器的數值並通過串口打印看看是否不再是 0x31,而是 0x32~0x3a 之間的某一個數值;

(3)如果這兩個步驟檢查不正確,可以先適當延長 初始化函數中 和 LD_AsrRun()函數中各個 delay 的時間,看是否可以調整好;

(4)如果以上調試過還是不行那就可以從初始化函數里面具體的每一步去讀取寄存器的值,看看那步出現問題。

 

(PS:0x17,0x87,0x89這三個寄存器是在向其寫入數據后,再去讀取回來的數值並不是寫入的數據)

 

參考資料來自:ICRoute

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM