我們繼續在上文口令模式的Demo示例程序基礎上,實現本文的目標。
上文連結如下:
語音識別LD3320模塊控制LED和舵機
改造STC工程將識別結果以JSON字符串的形式輸出只留五句PrintCom串口輸出,對應五個命令:
JSON字符串含義{"VoiceCommandCode":0}喚醒詞{"VoiceCommandCode":1}開燈{"VoiceCommandCode":2}關燈{"VoiceCommandCode":3}開門{"VoiceCommandCode":4}關門cJSON相關知識參考閱讀:
Keil環境下STM32工程加入cJSON
用cJSON解析心知天氣返回的數據包
程序編譯沒有錯誤之後,將編譯結果下載至LD3320模塊中驗證程序是否正確。
LD3320串口輸出測試測試四個口令,查看串口輸出的字符串是否符合預期。
由上可以看出,四個指令跟咱們預先設定的值是一致的,將LD3320模塊與STM32的串口相連,然後STM32對接收到的串口數據進行解析,進而做不同的動作即可,至此LD3320端的開發完畢。
STC單片機更新程序的方法參考網文:
STC單片機開發環境建立及更新LD3320模塊程序
修改STM32工程硬體連接LD3320模塊與STM32F103的串口4相連,具體原理圖如下圖所示:
初始化串口4因為LD3320模塊使用的波特率為9600,所以串口4也要初始化為波特率9600,串口初始化調用的代碼如下:
uart4_init(9600);
USART4_RX_STA=0;
memset(USART4_RX_BUF, 0, sizeof(USART4_RX_BUF));串口初始化代碼中添加了TIM4的初始化代碼,設置的定時器時間為10ms,串口4的初始化詳細代碼如下:
//初始化串口4
//bound:波特率
void uart4_init(u32 bound){
//GPIO埠設置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_UART4, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能USART4,GPIOC時鐘
USART_DeInit(UART4); //復位串口4
//USART4_TX PC.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PC10
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //復用推輓輸出
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化PC10
//USART4_RX PC.11
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化PC11
//USART 初始化設置
USART_InitStructure.USART_BaudRate = bound;//一般設置為9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬體數據流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式
USART_Init(UART4, &USART_InitStructure); //初始化串口
#if EN_USART4_RX //如果使能了接收
//Usart4 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = UART4_IRQn; //中斷號
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//搶佔優先級3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子優先級3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根據指定的參數初始化VIC寄存器
USART_ITConfig(UART4, USART_IT_RXNE, ENABLE);//開啟中斷
#endif
USART_Cmd(UART4, ENABLE); //使能串口
TIM4_Int_Init(99,7199); //10ms中斷
USART4_RX_STA=0; //清零
TIM_Cmd(TIM4,DISABLE); //關閉定時器4
}
接收來自於LD3320的串口數據void UART4_IRQHandler(void)
{
u8 res;
if(USART_GetITStatus(UART4, USART_IT_RXNE) != RESET)//接收到數據
{
res =USART_ReceiveData(UART4);
if((USART4_RX_STA&(1<<15))==0)//接收完的一批數據,還沒有被處理,則不再接收其他數據
{
if(USART4_RX_STA<USART4_REC_LEN) //還可以接收數據
{
TIM_SetCounter(TIM4,0); //計數器清空 //計數器清空
if(USART4_RX_STA==0) //使能定時器4的中斷
{
TIM_Cmd(TIM4,ENABLE); //使能定時器4
}
USART4_RX_BUF[USART4_RX_STA++]=res; //記錄接收到的值
}
else
{
USART4_RX_STA|=1<<15; //強制標記接收完成
}
}
}
}從接收到第一個字符開始啟動定時器,當收到一個新字符的時候,計數器清空,直到定時器中斷產生時,即計時到10ms時間之後,如果不再收到新的串口數據,證明一個完整的數據包接收完畢,在TIM4的中斷函數中,對串口4是否接收完成數據的變量USART4_RX_STA進行標記,代表數據接收完畢。
//定時器4中斷服務程序
void TIM4_IRQHandler(void)
{
if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)//是更新中斷
{
USART4_RX_STA|=1<<15; //標記接收完成
TIM_ClearITPendingBit(TIM4, TIM_IT_Update ); //清除TIM4更新中斷標誌
TIM_Cmd(TIM4, DISABLE); //關閉TIM4
}
}
使用cJSON解析收到的串口數據並動作//LD3320
if(USART4_RX_STA&0x8000)
{
uart4Len=USART4_RX_STA&0x3f; //得到此次接收到的數據長度
receive_json = cJSON_Parse(USART4_RX_BUF); //創建JSON解析對象,返回JSON格式是否正確
if (!receive_json)
{
printf("JSON格式錯誤:%s \r\n", cJSON_GetErrorPtr()); //輸出json格式錯誤信息
}
else
{
item_obj = receive_json->child;
while(item_obj)
{
char * string = item_obj->string;
if(!strcmp(string,"VoiceCommandCode"))
{
if(item_obj->valueint==0)
{
printf("收到一級口令 小哈 ... \r\n");
}
else if(item_obj->valueint==1)
{
printf("「開燈」命令識別成功 \r\n");
RelayControl(1);
}
else if(item_obj->valueint==2)
{
printf("「關燈」命令識別成功\r\n");
RelayControl(0);
}
else if(item_obj->valueint==3)
{
printf("「開門」命令識別成功\r\n");
}
else if(item_obj->valueint==4)
{
printf("「關門」命令識別成功\r\n");
}
}
item_obj = item_obj->next;
}
}
cJSON_Delete(receive_json);
USART4_RX_STA=0;
memset(USART4_RX_BUF, 0, sizeof(USART4_RX_BUF)); //清空數組
}
總結本文實現的方法和使用LD3320直接控制設備的方法相比,利用串口使STM32和LD3320模塊進行數據通信的優勢是:傳統設備,只需要預留一個串口,就可以為傳統設備添加語音控制功能。主控制板無需大的改動即可升級完畢,而且這種方式更靈活,也可以將項目轉移到自己熟悉的領域(假如不熟悉STC單片機,熟悉STM32的話),降低了開發難度。
結果展示