本系统设计了一个基于STM32单片机的自动视力检测系统,通过单片机控制LCD高清液晶显示屏显示“E”字形的开口方向和大小,来替代传统的视力测试面板;通过语音播放模块进行测试过程的提示语播放、语音识别模块进行识别测试者对于当前屏幕显示“E”形状的开口方向的回答,完成对测试者的自动视力检测过程;通过无线按钮为听力受损人员提供手动按下方向按键回答屏幕显示“E”行方向和大小的功能。本系统整体设计功能和参数如下。
设计功能:
(1)采用LCD高清液晶显示“E”字形,涵盖视力检测板所有“E”形状和大小。(2)自动化语音提示,确保检测者知道当前需要做什么。
(3)智能化语音识别,系统精准采集测试者的回答结果。
(4)无线按键,方便用户通过选择按下的方向按键回答显示屏“E”开口方向,扩大适用人群和使用环境。
(5)采用QT设计安卓程序,通过蓝牙和主控板连接,能够记录和显示测试者本人的近期视力检测结果,可以通过蓝牙应用程序或者在液晶屏上获取到对应的折线图,使用户方便直观的看到视力检测结果的变化。
(6)检测系统在调取检测结果时,可以根据历史检测结果做出温馨提醒,比如“近期视力检测下降,请注意适当休息哦!”。
设计参数:
(1)显示屏幕细腻,分辨率最低480*320,屏幕尺寸采用3.5寸。
(2)无线通讯距离不低于5米。
(3)语音识别:非特定人识别,2米内识别准确率95%以上,识别速度2秒以内。
(4)识别语种:中文。
(5)语音播放功率不小于3W。
(6)检测结果可存储不低于100人,可记录每人最低20条最近测试结果。
注意:本次使用的蓝牙模块是HC-05。
本系统程序过多,只放置可以参考的代码,在做此方面功能的可用于参考思路。
/******************************************************************************** 文件名称:基于STM32单片机的自动视力检测仪* 实验目的:1.* 2.* 程序说明:完整程序Q:277 227 2579;@: itworkstation@ hotmail* 日期版本:本项目分享关键细节,熟悉使用单片机的可做参考代码。完整讲解+源代码工程可联系获取,可定制。*******************************************************************************//* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "lcdUart.h"
#include "key.h"
#include "bsp_timer3.h"
#include "usart3.h"
#include "wireless.h"
#include "e2prom.h"
#include "usart1.h"
#include "bluetooth.h"
#include <math.h>
#include <stdlib.h>u8 EPROM[10][18]; //10个测试者,每个20空间。1-10, 21-40,...
extern u8 E2ReadNowUserTab[18];/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
void StoreEpRom(void) //将结果存储
{u8 i=0;for(i=0;i<18;i++){if(EPROM[LCD_InfoStru.TestUser-1][i] == 0){break; //找到数组最近的为0的位置}}if(i==18) //已经存储了18次测试记录,删掉最久存储的一次{for(i=0;i<17;i++){EPROM[LCD_InfoStru.TestUser-1][i] = EPROM[LCD_InfoStru.TestUser-1][i+1];}EPROM[LCD_InfoStru.TestUser-1][17] = LCD_InfoStru.Hang_Now+1; //改变一下存储值,因为显示时1.2应该是15,原值为14}else{EPROM[LCD_InfoStru.TestUser-1][i] = LCD_InfoStru.Hang_Now+1; //改变一下存储值,因为显示时1.2应该是15,原值为14}for(i=0;i<10;i++){E2WriteStr(1+i*20,EPROM[i],18);}
}
/*** @说明 主函数* @参数 None * @返回值 None*/
int main(void)
{u8 EpPos=0,EpPosY=0;LED_Init();Proc_Init();USART2_Init(); Delay_ms(1000);//上电等待 1 秒是串口屏模块正常工作的前提,如果没有足够的等待时间模块有可能无法正常的接收指令而导致系统出错。 printf("CLR(%d);DIR(3);BL(%d);rn",LCD_BackColor,LCD_Light); //CLR(16);DIR(1);BL(10); //白色,横屏,背光10(0-255:最亮:最暗)LCD_CheckBusy(); Key_Init();TIM4_Init(); LCD_Menu = LCD_DEFAULT; LCD_Display();USART3_Init();USART1_Init();E2Init();for(EpPos=0;EpPos<10;EpPos++) // 清空数据,初始化数组{for(EpPosY=0;EpPosY<18;EpPosY++){EPROM[EpPos][EpPosY] = 0;}}if(e2Struct.is_newE2){for(EpPos=0;EpPos<10;EpPos++)//0-10 0-17{E2WriteStr(1+EpPos*20,EPROM[EpPos],18);}}else{for(EpPos=0;EpPos<10;EpPos++){E2ReadStr(1+EpPos*20,EPROM[EpPos],18); //获得历史检测结果}}TIM3_Init();while(1){ if(TIM4_FlagStatus.Flag_500MS == TRUE){TIM4_FlagStatus.Flag_500MS = FALSE;LED_Control(REVERSE); }parseBLUETOOTHBuffer();if(LCD_Menu == LCD_TEST) {parseWIRELESSBuffer();if(WIRELESS_Data.isUsefull){if(WIRELESS_Data.isResultRight) //收到从机数据,且结果为真{ LCD_InfoStru.is_ResultRight = 1;LCD_Display();Delay_ms(3000);LCD_InfoStru.is_ResultRight = 0;LCD_InfoStru.Hang_Time ++;if(LCD_InfoStru.Hang_Time>=5) //结束{LCD_InfoStru.Hang_RightTime = 0;LCD_InfoStru.Hang_ErrorTime = 0;LCD_InfoStru.Hang_Time = 0;StoreEpRom();LCD_Menu = LCD_TESTEND;if(LCD_InfoStru.Hang_Now<13)WIRELESS_sendMessage("$O1@");elseWIRELESS_sendMessage("$O2@");}LCD_InfoStru.Hang_RightTime ++;if(LCD_InfoStru.Hang_RightTime > 2){if(LCD_InfoStru.Hang_Now < Hang_Init) { //初始化行上面,正确3次,结束StoreEpRom();LCD_Menu = LCD_TESTEND;WIRELESS_sendMessage("$O1@");}else{//3次正确,下一行LCD_InfoStru.Hang_Now ++;if(LCD_InfoStru.Hang_Now >Hang_END){LCD_InfoStru.Hang_Now = Hang_END;//1.2视力,结束StoreEpRom();LCD_Menu = LCD_TESTEND;WIRELESS_sendMessage("$O2@");}} LCD_InfoStru.Hang_RightTime = 0;LCD_InfoStru.Hang_ErrorTime = 0;LCD_InfoStru.Hang_Time = 0;}}else{LCD_InfoStru.is_ResultRight = 2;LCD_Display();Delay_ms(3000);LCD_InfoStru.is_ResultRight = 0;//从机识别错误LCD_InfoStru.Hang_Time ++;if(LCD_InfoStru.Hang_Time>=5) //结束{LCD_InfoStru.Hang_RightTime = 0;LCD_InfoStru.Hang_ErrorTime = 0;LCD_InfoStru.Hang_Time = 0;StoreEpRom();LCD_Menu = LCD_TESTEND;if(LCD_InfoStru.Hang_Now<13)WIRELESS_sendMessage("$O1@");elseWIRELESS_sendMessage("$O2@");}LCD_InfoStru.Hang_ErrorTime ++;if(LCD_InfoStru.Hang_ErrorTime > 2){if(LCD_InfoStru.Hang_Now <= Hang_Init) {//3次错误,初始行或者小于初始行,退到上一行LCD_InfoStru.Hang_Now --;if(LCD_InfoStru.Hang_Now < 1){WIRELESS_sendMessage("$O1@");//最上行,结束StoreEpRom();LCD_Menu = LCD_TESTEND;} }else{if(LCD_InfoStru.Hang_Now<13)WIRELESS_sendMessage("$O1@");elseWIRELESS_sendMessage("$O2@");//往下检测时,3次错误,结束StoreEpRom();LCD_Menu = LCD_TESTEND;}LCD_InfoStru.Hang_RightTime = 0;LCD_InfoStru.Hang_ErrorTime = 0;LCD_InfoStru.Hang_Time = 0;}}LCD_InfoStru.rand_E = rand()%4; WIRELESS_Clear_Data(); //使用一次,清空数据sprintf(WIRELESS_Data.SendDire,"$D%1d@",LCD_InfoStru.rand_E+1);WIRELESS_sendMessage(WIRELESS_Data.SendDire);TIM3_DateClear(); // 重新计时LCD_Display(); //刷新}if(TIM3_FlagStatus.Flag_1000MS == TRUE){TIM3_FlagStatus.Flag_1000MS = FALSE;TIM3_FlagStatus.Time_jishi --;if(TIM3_FlagStatus.Time_jishi<0){ TIM3_FlagStatus.Time_jishi = 0; LCD_InfoStru.is_ResultRight = 2;LCD_Display();Delay_ms(3000);LCD_InfoStru.is_ResultRight = 0;LCD_InfoStru.Hang_Time ++;if(LCD_InfoStru.Hang_Time>=5) //结束{LCD_InfoStru.Hang_RightTime = 0;LCD_InfoStru.Hang_ErrorTime = 0;LCD_InfoStru.Hang_Time = 0;StoreEpRom();LCD_Menu = LCD_TESTEND;if(LCD_InfoStru.Hang_Now<13)WIRELESS_sendMessage("$O1@");elseWIRELESS_sendMessage("$O2@");}LCD_InfoStru.Hang_ErrorTime ++;if(LCD_InfoStru.Hang_ErrorTime > 2){if(LCD_InfoStru.Hang_Now <= Hang_Init) {//3次错误,初始行或者小于初始行,退到上一行LCD_InfoStru.Hang_Now --;if(LCD_InfoStru.Hang_Now < 1){WIRELESS_sendMessage("$O1@");//最上行,结束StoreEpRom();LCD_Menu = LCD_TESTEND;} }else{//往下检测时,3次错误,结束StoreEpRom();LCD_Menu = LCD_TESTEND;if(LCD_InfoStru.Hang_Now<13)WIRELESS_sendMessage("$O1@");elseWIRELESS_sendMessage("$O2@");}LCD_InfoStru.Hang_RightTime = 0;LCD_InfoStru.Hang_ErrorTime = 0;LCD_InfoStru.Hang_Time = 0;} LCD_InfoStru.rand_E = rand()%4; WIRELESS_Clear_Data(); //使用一次,清空数据sprintf(WIRELESS_Data.SendDire,"$D%1d@",LCD_InfoStru.rand_E+1);WIRELESS_sendMessage(WIRELESS_Data.SendDire);TIM3_DateClear(); //重新计时,计算}LCD_Display(); //刷新} }Proc_Key(); }
}
/*******************************************************************************
* 文件名称:最小系统板STM32C8T6
* 实验目的:1.
* 2.
* 程序说明:
* 日期版本:
*******************************************************************************//* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart1.h"
#include "JQ_8400.h"
#include "key.h"
#include "usart2.h"
#include "wireless.h"
#include "usart3.h"
#include "Lu_ASR01.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*//*** @说明 主函数* @参数 None * @返回值 None*/
int main(void)
{u8 keyValue=0xff;LED_Init();LED_Control(ON);Delay_ms(1000);LED_Control(OFF);Proc_Init();USART1_Init();JQ8400_Init();Key_Init();USART2_Init();USART3_Init();TIM4_Init(); while(1){ if(TIM4_FlagStatus.Flag_200MS == SET){TIM4_FlagStatus.Flag_200MS = RESET;LED_Control(REVERSE); Proc_200Ms(); }parseWIRELESSBuffer();if(WIRELESS_Data.isUsefull) // 只有接收到方向后才启动语音识别{parseASR01Buffer(); if(ASR01_Data.isUsefull){//无线接收到方向,语音识别到方向if(ASR01_Data.Direction == WIRELESS_Data.Direction){ WIRELESS_SendStr("$T@");}else{ WIRELESS_SendStr("$F@");}ASR01_Clear_Data();WIRELESS_Clear_Data();}keyValue = Key_Scan();if(keyValue != 0xff){if(WIRELESS_Data.Direction == keyValue){JQ8400_6x00SendCmd(SELECTE_PLAY,2); WIRELESS_SendStr("$T@");}else{JQ8400_6x00SendCmd(SELECTE_PLAY,3); WIRELESS_SendStr("$F@");}WIRELESS_Clear_Data();}}else{ASR01_Clear_Data();} }
}
#ifndef __Lu_ASR01_H
#define __Lu_ASR01_H#include "stm32f10x.h"
#include "Def_config.h"#define ASR01_Buffer_Length 50
#define ASR01_Length 5typedef struct
{char ASR01_Rec_Buffer[ASR01_Buffer_Length];ENUM_JUDGE isGetData; //是否获取到数据ENUM_JUDGE isParseData; //是否解析完成ENUM_JUDGE isUsefull; //信息是否有效uint16_t Direction;
} _ASR01Data;extern _ASR01Data ASR01_Data;void ASR01_RecHandle(u8 Res);
void ASR01_Clear_Data(void);
void parseASR01Buffer(void);#endif
#include "Lu_ASR01.h"
#include <string.h>
#include <stdio.h>#include "JQ_8400.h" _ASR01Data ASR01_Data;
char ASR01_RX_BUF[ASR01_Buffer_Length]; //接收缓冲,最大ASR01_Buffer_Length个字节.末字节为换行符
u8 LuAsr_point2 = 0;
void ASR01_RecHandle(u8 Res)
{if(Res == '$'){LuAsr_point2 = 0; }ASR01_RX_BUF[LuAsr_point2++] = Res;if(Res == '@' || LuAsr_point2 >3) {memset(ASR01_Data.ASR01_Rec_Buffer, 0, ASR01_Buffer_Length); //清空memcpy(ASR01_Data.ASR01_Rec_Buffer, ASR01_RX_BUF, LuAsr_point2); //保存数据ASR01_Data.isGetData = TRUE; LuAsr_point2 = 0;memset(ASR01_RX_BUF, 0, ASR01_Buffer_Length); //清空} if(LuAsr_point2 >= ASR01_Buffer_Length){LuAsr_point2 = ASR01_Buffer_Length;}
}
u8 ASR01_Find(char *a) // 串口命令识别函数
{ if(strstr(ASR01_Data.ASR01_Rec_Buffer,a)!=NULL)return 1;elsereturn 0;
}
void ASR01_Clear_Data(void)
{ASR01_Data.isGetData = FALSE;ASR01_Data.isParseData = FALSE;ASR01_Data.isUsefull = FALSE;memset(ASR01_Data.ASR01_Rec_Buffer, 0, ASR01_Buffer_Length); //清空memset(ASR01_RX_BUF, 0, ASR01_Buffer_Length);ASR01_Data.Direction = 0;
}
void parseASR01Buffer(void)
{if (ASR01_Data.isGetData) //获得语音模块的数据 --- $1@ : $4@{ASR01_Data.isGetData = FALSE;if(ASR01_Find("$D1@")){ASR01_Data.isParseData = TRUE;ASR01_Data.isUsefull = TRUE;ASR01_Data.Direction = 1;
// JQ8400_6x00SendCmd(SELECTE_PLAY,1); //选择指定曲目播放}else if(ASR01_Find("$D2@")){ASR01_Data.isParseData = TRUE;ASR01_Data.isUsefull = TRUE;ASR01_Data.Direction = 2;}else if(ASR01_Find("$D3@")){ASR01_Data.isParseData = TRUE;ASR01_Data.isUsefull = TRUE;ASR01_Data.Direction = 4; //右}else if(ASR01_Find("$D4@")){ ASR01_Data.isParseData = TRUE;ASR01_Data.isUsefull = TRUE;ASR01_Data.Direction = 3;}else{ASR01_Clear_Data(); //清空接收到的数据---数据帧无效}}
}
#ifndef __BLUETOOTH_h
#define __BLUETOOTH_h#include "stm32f10x.h"
#include <string.h>
#include <stdio.h>
#include "Def_config.h"#include "usart1.h"
#define BLUETOOTH_SendStr USART1_SendString#define BLUETOOTH_Buffer_Length 100
#define BLUETOOTH_Length 50
#define BLUETOOTH_IntLength 4
typedef struct
{char BLUETOOTH_Rec_Buffer[BLUETOOTH_Buffer_Length];ENUM_JUDGE isGetData; //是否获取到数据ENUM_JUDGE isParseData; //是否解析完成ENUM_JUDGE isUsefull; //信息是否有效char sendMessage[BLUETOOTH_Buffer_Length];char userIDchar[BLUETOOTH_IntLength];u16 userIDInt;
} _BLUETOOTHData;
extern _BLUETOOTHData BLUETOOTH_Data;void BLUETOOTH_sendMessage(void);
void BLUETOOTH_RecHandle(u8 Res);
void BLUETOOTH_Clear_Data(void);
void parseBLUETOOTHBuffer(void);#endif
#include "bluetooth.h"
_BLUETOOTHData BLUETOOTH_Data;
char BLUETOOTH_RX_BUF[BLUETOOTH_Buffer_Length]; //接收缓冲,最大BLUETOOTH_Buffer_Length个字节.末字节为换行符
u8 BLUETOOTH_point2 = 0;
void BLUETOOTH_RecHandle(u8 Res)
{if(Res == '$'){BLUETOOTH_point2 = 0; }BLUETOOTH_RX_BUF[BLUETOOTH_point2++] = Res;if(Res == '@') {memset(BLUETOOTH_Data.BLUETOOTH_Rec_Buffer, 0, BLUETOOTH_Buffer_Length); //清空memcpy(BLUETOOTH_Data.BLUETOOTH_Rec_Buffer, BLUETOOTH_RX_BUF, BLUETOOTH_point2); //保存数据BLUETOOTH_Data.isGetData = TRUE; BLUETOOTH_point2 = 0;memset(BLUETOOTH_RX_BUF, 0, BLUETOOTH_Buffer_Length); //清空} if(BLUETOOTH_point2 >= BLUETOOTH_Buffer_Length){BLUETOOTH_point2 = BLUETOOTH_Buffer_Length;}
}
void BLUETOOTH_Clear_Data(void)
{BLUETOOTH_Data.isGetData = FALSE;BLUETOOTH_Data.isParseData = FALSE;BLUETOOTH_Data.isUsefull = FALSE;memset(BLUETOOTH_Data.BLUETOOTH_Rec_Buffer, 0, BLUETOOTH_Buffer_Length); //清空memset(BLUETOOTH_Data.sendMessage, 0, BLUETOOTH_Buffer_Length); //清空memset(BLUETOOTH_Data.userIDchar, 0, BLUETOOTH_IntLength); BLUETOOTH_Data.userIDInt = 0;
}
u8 BLUETOOTH_Find(char *a) // 串口命令识别函数
{ if(strstr(BLUETOOTH_Data.BLUETOOTH_Rec_Buffer,a)!=NULL)return 1;elsereturn 0;
}
u16 transform(u8 dat)
{u16 date_shili = 0;switch(dat){case 1:date_shili=5;break;case 2:date_shili=6;break;case 3:date_shili=8;break;case 4:date_shili=10;break;case 5:date_shili=12;break;case 6:date_shili=15;break;case 7:date_shili=20;break;case 8:date_shili=25;break;case 9:date_shili=30;break;case 10:date_shili=40;break;case 11:date_shili=50;break;case 12:date_shili=60;break;case 13:date_shili=80;break;case 14:date_shili=100;break;case 15:date_shili=120;break;default:break;}return date_shili;
}
extern u8 EPROM[10][18];
void BLUETOOTH_sendMessage(void)
{u8 i=0;u8 length=0;u16 E2ReadNowUserTab[18];for(i=0;i<18;i++){E2ReadNowUserTab[i] = 0;}for(i=0;i<18;i++){E2ReadNowUserTab[i] = transform(EPROM[BLUETOOTH_Data.userIDInt-1][i]);if(E2ReadNowUserTab[i] == 0){break;} }if(i==0)length = 0;elselength = i;memset(BLUETOOTH_Data.sendMessage, 0, BLUETOOTH_Buffer_Length); //清空sprintf(BLUETOOTH_Data.sendMessage,"$%d,%d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,%03d,@",BLUETOOTH_Data.userIDInt,length,E2ReadNowUserTab[0],E2ReadNowUserTab[1],E2ReadNowUserTab[2],E2ReadNowUserTab[3],E2ReadNowUserTab[4],E2ReadNowUserTab[5],E2ReadNowUserTab[6],E2ReadNowUserTab[7],E2ReadNowUserTab[8],E2ReadNowUserTab[9],E2ReadNowUserTab[10],E2ReadNowUserTab[11],E2ReadNowUserTab[12],E2ReadNowUserTab[13],E2ReadNowUserTab[14],E2ReadNowUserTab[15],E2ReadNowUserTab[16],E2ReadNowUserTab[17]);BLUETOOTH_SendStr(BLUETOOTH_Data.sendMessage);
}
uint16_t CharToInt(char *dat)
{uint16_t date=0;while((*dat) != '