LD3320語音模塊
暑假快完了也什麼成果,就打算做點東西來讓這個暑假充實點。發現手頭有一個LD3320語音模塊,又剛學C#上位機,就想結合一個做一個小項目。
第一次寫,多多包含。
我的目的是:可以語音控制一個LED燈 如果有條件就控制明亮度然後在LCD上顯示出來,並且在上位機上也可以顯示當前狀態和控制LED狀態。
材料
- STM32開發板(我用的是正點原子的戰艦V3 STM32F103ZET6大家也可以用自己的開發板做)
- LD3320語音模塊
- 杜邦線
- 嗯 還需要電腦軟件
- keil4 或 keil5 因爲下載的例程文件是keil4寫的 而手頭LCD屏的頭文件都是keil5戰艦的 所以兩個都用到了
-VS2017
-串口調試助手 我用的是XCOME
然後就開始做了。
製作過程
- 硬件部分
想去找一些關於LD3320的資料。
我先去淘寶找了這個模塊,發現有瀏覽了幾家商家之後發現了資料關於LD3320的,很完整。附上料地址http://www.waveshare.net/wiki/LD3320_Board
發現他有keil4已經寫好的程序,我就下載下來,然後又找到引腳圖
雖然已經有圖了,但是還要提醒下VCC和GND千萬不要接反了!!
這樣硬件部分就連接好了,接下來就是程序和上位機了-
- 程序部分
拿到例程後發現他的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
改之後,看他已經寫的指令有哪些,有“流水燈”、“閃爍”、“按鍵觸發”、“全滅”、“狀態”這幾個,試試管用不管 看串口部分配置是 115200 的波特率,打開串口調試工具。
設置好波特率之後打開串口 按復位鍵 對着模塊用清晰的普通話講“流水燈” (哈哈 開始說的幾次都沒識別出來 還以爲有問題 慢點清晰後成功率明顯提高了。
附圖:
第一部分 嘗試就成功了 接下來就是 修改代碼 加上LCD 在LCD上也可以顯示當時命令和狀態。
還有就是把發送給上位機的串口數據做好 以便上位機能更好的識別命令和執行命令。
找了半天,戰艦V3的LCD就只有正點原子哥寫好的,是用keil5寫的,還是在sys的支持下(因爲sys頭文件我在移植到keil4上時就有很多error),所以只能在5上寫了。首先先創個工程把源代碼原封不動的移植到5上,執行後結果正常,然後添加LCD的程序,因爲sys頭文件包含了delay、sys、usart所以串口部分也要替換掉,在主函數裏添加頭文件 初始化等函數;
//添加頭文集
#include "delay.h"
#include "sys.h"
//主函數下添加
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init();
uart_init(115200);
然後試試,也正常。
接下來就是添加LCD程序
//添加頭文件
#include "lcd.h" LCD_Init();
//主函數裏添加
LCD_Clear(WHITE);
POINT_COLOR=RED;
LCD_ShowString(72,100,200,16,16,"Hello STM32!");
除了在代碼里加這些 還要在工程項目裏把lcd的.c文件添加進去 在路含 lcd文件夾
顯示正常 然後我發現這樣就只能寫英文 不能寫漢字 於是就添加了漢字顯示
//在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)); //得到字體一個字符對應點陣集所佔的字節數
for(t=0;t<csize;t++)
{
if(size==12)temp=chinese_12[num][t]; //調用1206字體
else if(size==16)temp=chinese_16[num][t]; //調用1608字體
else if(size==24)temp=chinese_24[num][t]; //調用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軟件,配置好後,添加我們要用到的字體。
下邊就是我們要的字組,複製到剛纔說的數組裏,在程序中添加 就OK了,還有好多漢字 我就不一一列舉了,方法是一樣的。 注意要把數字變大足以裝下字組。
//主函數下添加
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文件下 語識別後要執行的部分 添加要顯示的漢字 這樣就做好了。注意:要在顯示之前添加
LCD_ShowString(72,100,200,16,16," ");
這樣顯示其下次的會把上次的覆蓋掉,顯示的就是新的內容。 試試
完美!
接下來就是給上位機寫好數據 我們用串口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) //接收中斷(接收的數據必須是0x0d 0x0a結尾)
{
Res =USART_ReceiveData(USART1); //讀取接收到的數據
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; //接收數據錯誤重新開始接收
}
}
}
}
#if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS爲真,則需要支持OS.
OSIntExit();
#endif
}
#endif
- 軟件部分
我要做一個能控制LED燈 並且可以顯示當前LED狀態的上位機
經過不斷變化 最後設計的形象是這樣的
在打開時自動掃描端口。
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 { }
}
打開軟件後自動監測COM口,然後波特率是115200點擊連接,連接成功,連接按鈕就會變成斷開,連接失敗就會彈出錯誤窗口。注意:如果在啓動軟件的時候沒有把板子插上並打開,那麼在打開軟件的時候就不會有COM口,這是就需要通電板子並重新打開軟件。
在連接之後就要監測LED當前狀態,在keil5程序中,在監測到正確命令後,就會發送一個字符串printf("流水燈,指令識別成功")
的提示,我們需要做的就是監測是否有”流水燈“字樣的字符。我想在一個窗口顯示接收到的數據,一個窗口顯示目前的LED狀態。遇到的問題就是serialPort1.ReadExisting();
把接收代碼裏的字符串直接去識別的話是識別不成功的,如果去識別serialPort1.ReadLine();
的話,可以成功識別,但是會造成顯示窗口的卡死,爲了解決這個問題,我又加了一個textbox控件,但是這個控件並不顯示,只是拿到數據去做對比。這樣就解決了這個問題。代碼如下:
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 = "按鍵觸發";
else if (textBox2.Text.Contains("全"))
textBox3.Text = "全滅";
else if (textBox2.Text.Contains("狀"))
textBox3.Text = "狀態";
這樣就可以顯示了。
附圖:
:
最後就是控制LED 。因爲程序一直在中斷中運行,就需要不斷給板子發命令,他纔可以一直保持這種狀態。這裏我用一個定時器控件,當按鍵觸發後,每隔很短的時間就給板子發送命令。代碼如下:
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://pan.baidu.com/s/1kXAMFqVfTKJ0X5KmulqszQ 密碼:1q8j
還有很多不足的地方,希望大佬可以指正。也希望和愛好者交流學習。
第一次寫,使用的很不熟練,寫的不好希望不要介意。以後還會寫。望多多交流學習。
如有侵犯版權問題,立即刪除。歡迎轉載。