基于STM32(F103ZE)的LD3320語音控制系統(tǒng)

基于STM32(F103ZE)的LD3320語音控制系統(tǒng)

暑假快完了也什么成果,就打算做點東西來讓這個暑假充實點帽撑。發(fā)現(xiàn)手頭有一個LD3320語音模塊叛薯,又剛學C#上位機,就想結(jié)合一個做一個小項目瞧掺。
第一次寫,多多包含凡傅。

我的目的是:可以語音控制一個LED燈 如果有條件就控制明亮度然后在LCD上顯示出來辟狈,并且在上位機上也可以顯示當前狀態(tài)和控制LED狀態(tài)。


材料

  • STM32開發(fā)板(我用的是正點原子的戰(zhàn)艦V3 STM32F103ZET6大家也可以用自己的開發(fā)板做)
  • LD3320語音模塊
  • 杜邦線
  • 嗯 還需要電腦軟件
  • keil4 或 keil5 因為下載的例程文件是keil4寫的 而手頭LCD屏的頭文件都是keil5戰(zhàn)艦的 所以兩個都用到了
    -VS2017
    -串口調(diào)試助手 我用的是XCOME

然后就開始做了夏跷。

制作過程

  • 硬件部分

想去找一些關于LD3320的資料哼转。
我先去淘寶找了這個模塊,發(fā)現(xiàn)有瀏覽了幾家商家之后發(fā)現(xiàn)了資料關于LD3320的槽华,很完整壹蔓。附上料地址http://www.waveshare.net/wiki/LD3320_Board
發(fā)現(xiàn)他有keil4已經(jīng)寫好的程序,我就下載下來猫态,然后又找到引腳圖


**雖然已經(jīng)有圖了佣蓉,但是還要提醒下VCC和GND千萬不要接反了E恪! **

這樣硬件部分就連接好了勇凭,接下來就是程序和上位機了-

  • 程序部分

拿到例程后發(fā)現(xiàn)他的LED燈的端口不同疚膊,改成我們板子的LED

#define LED1_PIN            GPIO_Pin_5
#define LED1_GPIO_PORT      GPIOB
#define LED1_GPIO_CLK       RCC_APB2Periph_GPIOB

#define LED2_PIN            GPIO_Pin_5
#define LED2_GPIO_PORT      GPIOE
#define LED2_GPIO_CLK       RCC_APB2Periph_GPIOE

改之后,看他已經(jīng)寫的指令有哪些虾标,有“流水燈”寓盗、“閃爍”、“按鍵觸發(fā)”璧函、“全滅”傀蚌、“狀態(tài)”這幾個,試試管用不管 看串口部分配置是 115200 的波特率蘸吓,打開串口調(diào)試工具善炫。
設置好波特率之后打開串口 按復位鍵 對著模塊用清晰的普通話講“流水燈” (哈哈 開始說的幾次都沒識別出來 還以為有問題 慢點清晰后成功率明顯提高了。
附圖:


第一部分 嘗試就成功了 接下來就是 修改代碼 加上LCD 在LCD上也可以顯示當時命令和狀態(tài)库继。
還有就是把發(fā)送給上位機的串口數(shù)據(jù)做好 以便上位機能更好的識別命令和執(zhí)行命令销部。
找了半天,戰(zhàn)艦V3的LCD就只有正點原子哥寫好的制跟,是用keil5寫的,還是在sys的支持下(因為sys頭文件我在移植到keil4上時就有很多error)酱虎,所以只能在5上寫了雨膨。首先先創(chuàng)個工程把源代碼原封不動的移植到5上,執(zhí)行后結(jié)果正常读串,然后添加LCD的程序聊记,因為sys頭文件包含了delay、sys恢暖、usart所以串口部分也要替換掉排监,在主函數(shù)里添加頭文件 初始化等函數(shù);

//添加頭文集 
#include "delay.h"
#include "sys.h"
 
//主函數(shù)下添加 
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
uart_init(115200);

然后試試杰捂,也正常舆床。
接下來就是添加LCD程序

//添加頭文件   
#include "lcd.h" LCD_Init();
//主函數(shù)里添加 
LCD_Clear(WHITE);
POINT_COLOR=RED;
LCD_ShowString(72,100,200,16,16,"Hello STM32!");     

除了在代碼里加這些 還要在工程項目里把lcd的.c文件添加進去 在路含 lcd文件夾
顯示正常 然后我發(fā)現(xiàn)這樣就只能寫英文 不能寫漢字 于是就添加了漢字顯示

//在lcd.c下添加 
void LCD_ShowChinese(u16 x,u16 y,u8 num,u8 size,u8 mode)
{                                       
    u8 csize;
    u8 temp,t1,t;   
    u16 y0=y;
    csize=(2*(size/8+((size%8)?1:0))*(size/2));     //得到字體一個字符對應點陣集所占的字節(jié)數(shù)
    for(t=0;t<csize;t++)
    {   
        if(size==12)temp=chinese_12[num][t];        //調(diào)用1206字體  
        else if(size==16)temp=chinese_16[num][t];   //調(diào)用1608字體
        else if(size==24)temp=chinese_24[num][t];   //調(diào)用2412字體
        else return;                                //沒有字體庫
        for(t1=0;t1<8;t1++)
        {               
            if(temp&0x80)LCD_Fast_DrawPoint(x,y,POINT_COLOR);
            else if(mode==0)LCD_Fast_DrawPoint(x,y,BACK_COLOR);
            temp<<=1;
            y++;                                
            if((y-y0)==size)
            {
                y=y0;
                x++;
                break;
            }   
        }    
    }                                         
} 
//在lcd.h下添加
void LCD_ShowChinese(u16 x,u16 y,u8 num,u8 size,u8 mode);//在指定位置顯示一個漢字

因為用的時16*16大小的字體 所以在字庫里添加字組時要添加到const unsigned char chinese_16[40][32]里。
接下來就是字體取模 我用這個PCtoLCD2002軟件嫁佳,配置好后,添加我們要用到的字體挨队。



下邊就是我們要的字組,復制到剛才說的數(shù)組里蒿往,在程序中添加 就OK了盛垦,還有好多漢字 我就不一一列舉了,方法是一樣的瓤漏。 注意要把數(shù)字變大足以裝下字組腾夯。

//主函數(shù)下添加 
LCD_ShowChinese(29,40,5,16,1);      //開
LCD_ShowChinese(46,40,6,16,1);      //始
LCD_ShowChinese(63,40,7,16,1);      //運
LCD_ShowChinese(78,40,8,16,1);      //行
LCD_ShowChinese(95,40,9,16,1);      //L
LCD_ShowChinese(103,40,10,16,1);    //D
LCD_ShowChinese(111,40,11,16,1);    //3
LCD_ShowChinese(119,40,12,16,1);    //3
LCD_ShowChinese(127,40,13,16,1);    //2
LCD_ShowChinese(135,40,14,16,1);    //0
LCD_ShowChinese(143,40,15,16,1);    //測
LCD_ShowChinese(160,40,16,16,1);    //試
LCD_ShowChinese(177,40,17,16,1);    //程
LCD_ShowChinese(194,40,18,16,1);    //序

這樣就可以正常顯示漢字了
然后找到LD3320.c文件下 語識別后要執(zhí)行的部分 添加要顯示的漢字 這樣就做好了颊埃。注意:要在顯示之前添加
LCD_ShowString(72,100,200,16,16," ");這樣顯示其下次的會把上次的覆蓋掉,顯示的就是新的內(nèi)容蝶俱。 試試

添加或修改口令

在LD3320.c里

 //在LD_AsrAddFixed函數(shù)中sRecog[DATE_A][DATE_B]數(shù)組里加上你要說的口令的拼音
 //比如第二個閃爍
 uint8  sRecog[DATE_A][DATE_B] = {
                "liu shui deng",\
                "shan shuo",\
                "an jian chu fa",\
                "quan mie",\
                "zhuang tai"\

};
//和我的一樣即可
//在pCode[DATE_A]數(shù)組中添加指令班利,數(shù)組序號要對應比如上邊第二個是閃爍,這個數(shù)組中就要相應閃爍的函數(shù)
//比如第二個ss
    uint8  pCode[DATE_A] = {
                                                                CODE_LSD,   \
                                                                CODE_SS,    \
                                                                CODE_AJCF,\
                                                                CODE_QM,    \
                                                                CODE_JT     \
                                                            };  
        
//在Board_text函數(shù)中添加相應執(zhí)行的函數(shù)
//比如第二個Flicker_LED()
static void Board_text(uint8 Code_Val)
{                                                                                    
    switch(Code_Val)  //???á1??′DD?à1?2ù×÷
    {
        case CODE_LSD:  //?üá??°á÷??μ??±
            Glide_LED();
        break;
        case CODE_SS:     //?üá??°éá???±
            Flicker_LED();
        break;
        case CODE_AJCF: //?üá??°°′?ü′¥·¢?±
            Key_LED();
        break;
        case CODE_QM:       //?üá??°è??e?±
            Off_LED();
        break;
        case CODE_JT:       //?üá??°×′ì??±
            Jt_LED();
        break;
        default:break;
    }   
}                   
//在下邊添加相應函數(shù)所要執(zhí)行的動作
//Flicker_LED函數(shù)
static void Flicker_LED(void)
{
    LED1_ON();
    LED2_ON();
    Delayms(0XFFF);
    LED1_OFF();
    LED2_OFF();
    Delayms(0XFFF);
}
                                    

這樣就添加了我們想要的命令了

完美跷乐!

接下來就是給上位機寫好數(shù)據(jù) 我們用串口1肥败。除了添加中斷是否接收完成,還要添加們要用指令愕提。
代碼如下:

//在usart.c或者stm32f10x_it.c里添加
void USART1_IRQHandler(void)                    //串口1中斷服務程序
{
    u8 Res;
    int  i=0;
    #if SYSTEM_SUPPORT_OS       //如果SYSTEM_SUPPORT_OS為真馒稍,則需要支持OS
    OSIntEnter();    
    #endif
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中斷(接收的數(shù)據(jù)必須是0x0d 0x0a結(jié)尾)
    {
        Res =USART_ReceiveData(USART1); //讀取接收到的數(shù)據(jù)
        if((USART_RX_STA&0x8000)==0)//接受未完成
        {
            if(USART_RX_STA&0x4000)//接收到了0x0d
            {
                if(Res!=0x0a)USART_RX_STA=0;//接收錯誤,重新開始
                else USART_RX_STA|=0x8000;  //接收完成了
                if(USART_RX_STA&0x8000)
                {
                    if(strcmp(USART_RX_BUF,"全亮")==0)
                        {
                            GPIO_ResetBits(GPIOE,GPIO_Pin_5);
                            GPIO_ResetBits(GPIOB,GPIO_Pin_5);       
                        }
                        if(strcmp(USART_RX_BUF,"第一個")==0)
                        {
                            GPIO_ResetBits(GPIOE,GPIO_Pin_5);
                            GPIO_SetBits(GPIOB,GPIO_Pin_5);     
                        }
                        if(strcmp(USART_RX_BUF,"第二個")==0)
                        {
                            GPIO_SetBits(GPIOE,GPIO_Pin_5);
                            GPIO_ResetBits(GPIOB,GPIO_Pin_5);       
                        }
                        if(strcmp(USART_RX_BUF,"全滅")==0)
                        {
                            GPIO_SetBits(GPIOE,GPIO_Pin_5);
                            GPIO_SetBits(GPIOB,GPIO_Pin_5);     
                        }       
                        USART_RX_STA=0x00000;
                        for(i=0;i<USART_REC_LEN;i++)
                        {
                            USART_RX_BUF[i]=0;
                        }                   
                    }
                }
            else //還沒收到0X0D
            {   
                if(Res==0x0d)USART_RX_STA|=0x4000;
                else
                {
                    USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
                    USART_RX_STA++;
                    if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0; //接收數(shù)據(jù)錯誤重新開始接收
                }        
            }
        }            
    }
    #if SYSTEM_SUPPORT_OS     //如果SYSTEM_SUPPORT_OS為真浅侨,則需要支持OS.
    OSIntExit();                                             
    #endif
} 
#endif

  • 軟件部分

我要做一個能控制LED燈 并且可以顯示當前LED狀態(tài)的上位機
經(jīng)過不斷變化 最后設計的形象是這樣的


在打開時自動掃描端口纽谒。

for (int i = 0; i < 20; i++)
{
     try
     {
           string str = "COM" + i;
           serialPort1.PortName = str;
           serialPort1.Open();
           comboBox1.Items.Add(str);
           serialPort1.Close();
           comboBox1.Text = str;
     }
     catch { }
} 

打開軟件后自動監(jiān)測COM口,然后波特率是115200點擊連接如输,連接成功鼓黔,連接按鈕就會變成斷開,連接失敗就會彈出錯誤窗口不见。注意:如果在啟動軟件的時候沒有把板子插上并打開澳化,那么在打開軟件的時候就不會有COM口,這是就需要通電板子并重新打開軟件稳吮。
在連接之后就要監(jiān)測LED當前狀態(tài)缎谷,在keil5程序中,在監(jiān)測到正確命令后灶似,就會發(fā)送一個字符串printf("流水燈列林,指令識別成功")的提示,我們需要做的就是監(jiān)測是否有”流水燈“字樣的字符酪惭。我想在一個窗口顯示接收到的數(shù)據(jù)希痴,一個窗口顯示目前的LED狀態(tài)。遇到的問題就是serialPort1.ReadExisting();把接收代碼里的字符串直接去識別的話是識別不成功的春感,如果去識別serialPort1.ReadLine();的話砌创,可以成功識別,但是會造成顯示窗口的卡死鲫懒,為了解決這個問題纺铭,我又加了一個textbox控件,但是這個控件并不顯示刀疙,只是拿到數(shù)據(jù)去做對比舶赔。這樣就解決了這個問題。代碼如下:

            textBox2.Text=serialPort1.ReadExisting();
            textBox1.AppendText(textBox2.Text);
            if (textBox2.Text.Contains("流")) 
                textBox3.Text = "流水燈";
            else if (textBox2.Text.Contains("閃"))
                textBox3.Text = "閃爍";
            else if (textBox2.Text.Contains("按"))
                textBox3.Text = "按鍵觸發(fā)";
            else if (textBox2.Text.Contains("全"))
                textBox3.Text = "全滅";
            else if (textBox2.Text.Contains("狀"))
                textBox3.Text = "狀態(tài)";

這樣就可以顯示了谦秧。
附圖:



最后就是控制LED 竟纳。因為程序一直在中斷中運行撵溃,就需要不斷給板子發(fā)命令,他才可以一直保持這種狀態(tài)锥累。這里我用一個定時器控件缘挑,當按鍵觸發(fā)后,每隔很短的時間就給板子發(fā)送命令桶略。代碼如下:

private void button2_Click(object sender, EventArgs e)
{
    if (button2.Text == "打開")
    try
    {
         button2.Text = "關閉";
         timer1.Start();                        
    }
    catch { }
    else
    {
          button2.Text = "打開";
          timer1.Stop();
    }
}

private void timer1_Tick(object sender, EventArgs e)
{
    try
    {
         serialPort1.Write(comboBox3.Text + Environment + Environment.NewLine);
         textBox3.Text = comboBox3.Text;
     }
     catch
     { }
}   

成功了语淘!這樣這個項目就完成了!
最后附上完整的項目文件际歼。自行下載惶翻。
https://download.csdn.net/download/weixin_42320020/16594044

還有很多不足的地方,希望大佬可以指正鹅心。也希望和愛好者交流學習吕粗。
第一次寫,使用的很不熟練旭愧,寫的不好希望不要介意颅筋。以后還會寫。望多多交流學習输枯。

禁止轉(zhuǎn)載R楸谩!桃熄!
QQ:2039723308
VX:Shiboven

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末先口,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蜻拨,更是在濱河造成了極大的恐慌,老刑警劉巖桩引,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缎讼,死亡現(xiàn)場離奇詭異,居然都是意外死亡坑匠,警方通過查閱死者的電腦和手機血崭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來厘灼,“玉大人夹纫,你說我怎么就攤上這事∩璋迹” “怎么了舰讹?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長闪朱。 經(jīng)常有香客問我月匣,道長钻洒,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任锄开,我火速辦了婚禮素标,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘萍悴。我一直安慰自己头遭,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布癣诱。 她就那樣靜靜地躺著计维,像睡著了一般。 火紅的嫁衣襯著肌膚如雪狡刘。 梳的紋絲不亂的頭發(fā)上享潜,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音嗅蔬,去河邊找鬼剑按。 笑死,一個胖子當著我的面吹牛澜术,可吹牛的內(nèi)容都是我干的艺蝴。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼鸟废,長吁一口氣:“原來是場噩夢啊……” “哼猜敢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起盒延,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤缩擂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后添寺,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胯盯,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年计露,在試婚紗的時候發(fā)現(xiàn)自己被綠了博脑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡票罐,死狀恐怖叉趣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情该押,我是刑警寧澤疗杉,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站蚕礼,受9級特大地震影響乡数,放射性物質(zhì)發(fā)生泄漏椭蹄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一净赴、第九天 我趴在偏房一處隱蔽的房頂上張望绳矩。 院中可真熱鬧,春花似錦玖翅、人聲如沸翼馆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽应媚。三九已至,卻和暖如春猜极,著一層夾襖步出監(jiān)牢的瞬間中姜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工跟伏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留丢胚,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓受扳,卻偏偏與公主長得像携龟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子勘高,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容