1.硬件平臺(tái)
2.功能實(shí)現(xiàn)
本示例主要設(shè)計(jì)兩個(gè)界面:時(shí)間顯示界面和日歷界面
??1.通過(guò)按鍵切換界面;
??2.時(shí)間界面包含:時(shí)間表盤(pán)繪制、數(shù)字時(shí)間和日期顯示、鬧鐘設(shè)置;支持按鍵校時(shí)和鬧鐘時(shí)間設(shè)置;
??3.日歷界面支持查看日期;
??4.上電自動(dòng)檢測(cè)字庫(kù)是否正常、報(bào)時(shí)語(yǔ)音包是否正常;支持SD卡字庫(kù)和語(yǔ)音包更新;
3.軟件設(shè)計(jì)
3.1 VS1053初始化及播放音頻
/**************硬件接口*****************
**VS_MISO -- PA6 主機(jī)輸入
**VS_MOSI -- PA7 主機(jī)輸出
**VS_SCK -- PA5 時(shí)鐘
**VS_XCS -- PF7 命令片選(低電平有效)
**VS_XDCS -- PF6 數(shù)據(jù)片選(低電平有效)
**VS_DREQ -- PC13 數(shù)據(jù)請(qǐng)求線(高電平表示可以接收數(shù)據(jù))
**VS_RST -- PE6 復(fù)位腳(低電平復(fù)位)
**
*****************************************/
void VS1053_Init(void)
{
/*1. 開(kāi)時(shí)鐘*/
RCC->APB2ENR|=1<<2;//PA
RCC->APB2ENR|=1<<4;//PC
RCC->APB2ENR|=1<<6;//PE
RCC->APB2ENR|=1<<7;//PF
GPIOA->CRL&=0x000FFFFF;
GPIOA->CRL|=0x38300000;
GPIOF->CRL&=0x00FFFFFF;
GPIOF->CRL|=0x33000000;
GPIOC->CRH&=0xFF0FFFFF;
GPIOC->CRH|=0x00800000;
GPIOE->CRL&=0xF0FFFFFF;
GPIOE->CRL|=0x03000000;
VS_XCS=1;
VS_XDCS=1;
VS1053_RST();
VS1053_SetVoice(255,255);
/*2.配置時(shí)鐘寄存器*/
VS1053_WriteRegDat(VS1053_CLOCKF,0x9800);
}
/*SPI收發(fā)一個(gè)字節(jié)*/
u8 VS1053_SPI_ReadWriteData(u8 data_tx)
{
u8 data_rx=0;
u8 i=0;
for(i=0;i<8;i++)
{
VS_SCK=0;
if(data_tx&0x80)VS_MOSI=1;
else VS_MOSI=0;
VS_SCK=1;
data_tx<<=1;
data_rx<<=1;
if(VS_MISO)data_rx|=0x01;
}
return data_rx;
}
/********************往寄存器中寫(xiě)入數(shù)據(jù)******************
**
**形參:u8 addr --地址
** u16 data -- 寫(xiě)入的數(shù)據(jù)
**********************************************************/
void VS1053_WriteRegDat(u8 addr,u16 data)
{
while(VS_DREQ==0){}//等待數(shù)據(jù)線空閑
VS_XDCS=1;//數(shù)據(jù)片選拉高
VS_XCS=0;//命令片選拉低
VS1053_SPI_ReadWriteData(0x02);//寫(xiě)指令
VS1053_SPI_ReadWriteData(addr); //寄存器地址
VS1053_SPI_ReadWriteData(data>>8);
VS1053_SPI_ReadWriteData(data>>0);//寫(xiě)入數(shù)據(jù)
VS_XCS=1;
}
/*******************從寄存器中讀取數(shù)據(jù)*******************/
u16 VS1053_ReadRegDat(u8 addr)
{
u16 data=0;
while(VS_DREQ==0){}//等待數(shù)據(jù)線空閑
VS_XDCS=1;//數(shù)據(jù)片選拉高
VS_XCS=0;//命令片選拉低
VS1053_SPI_ReadWriteData(0x03);//讀指令
VS1053_SPI_ReadWriteData(addr); //寄存器地址
data=VS1053_SPI_ReadWriteData(0xff)<<8;
data|=VS1053_SPI_ReadWriteData(0xff);
VS_XCS=1;
return data;
}
/****************音量調(diào)節(jié)*****************
**
**形參:u8 vol_l -- 左聲道 0~254
** u8 vol_r -- 右聲道 0~254
**每個(gè)增量表示0.5db的衰減,值越大,音量越小
**注意:如果設(shè)置 VOL 的值為 0xFFFF,將使芯片進(jìn)入掉電模式。
**右聲道是高 8 位 左聲道是低 8 位
*******************************************/
void VS1053_SetVoice(u8 vol_l,u8 vol_r)
{
u16 temp=vol_r<<8|vol_l;
VS1053_WriteRegDat(VS1053_VOL,temp);
}
/***************VS1053硬件復(fù)位**************/
void VS1053_RST(void)
{
//硬件復(fù)位
VS_RST=0;
Delay_Ms(20);
VS_XDCS=1;//取消數(shù)據(jù)傳輸
VS_XCS=1;//取消命令傳輸
VS_RST=1;//完成復(fù)位
//軟件復(fù)位
while(VS_DREQ==0){}//等待數(shù)據(jù)線空閑
VS1053_WriteRegDat(VS1053_MODE,0x0804);//設(shè)置為新模式,進(jìn)行軟件復(fù)位
Delay_Ms(2);
while(VS_DREQ==0){}//等待數(shù)據(jù)線空閑,復(fù)位完成
}
/****獲取解碼時(shí)間******/
u16 VS1053_Get_Time(void)
{
u16 time=0;
time=VS1053_ReadRegDat(VS1053_DECODE_TIME);
return time;
}
/****清除解碼時(shí)間******/
void VS1053_Clear_Time(void)
{
VS1053_WriteRegDat(VS1053_DECODE_TIME,0);
}
/**************播放音樂(lè)****************************/
u8 VS1053_PlayOneMusic(const char *data,u32 data_size)
{
u32 i=0;
/*3.設(shè)置音量*/
VS1053_SetVoice(50,50);
for(i=0;i;i++)>
3.2 表盤(pán)與指針繪制
電子表盤(pán)和日歷
/*繪制表盤(pán)*/
void LCD_DrawClockDial(void)
{
u8 buff[20];
/*畫(huà)表盤(pán)*/
LCD_Circle(160,160,150,BLACK);//畫(huà)圓
LCD_Circle(160,160,151,BLACK);//畫(huà)圓
LCD_Circle(160,160,152,BLACK);//畫(huà)圓
/*畫(huà)刻度*/
u16 i;
for(i=0;i<360;i+=6)
{
LCD_DrawAngleLine(160,160,i,151,15,BLUE);//畫(huà)任意角度直線
}
for(i=0;i<360;i+=30)
{
LCD_DrawAngleLine(160,160,i,151,25,RED);//畫(huà)任意角度直線
}
/*顯示時(shí)間數(shù)據(jù)*/
LCD_Display_Str(160-8,10+30,16,(u8 *)"12",BLACK);
LCD_Display_Str(160-4,160+150-45,16,(u8 *)"6",BLACK);
LCD_Display_Str(160+150-35,160-8,16,(u8 *)"3",BLACK);
LCD_Display_Str(10+35,160-8,16,(u8 *)"9",BLACK);
/*鬧鐘*/
snprintf((char *)buff,sizeof(buff),"鬧鐘1:%s",alarm_clock1);
LCD_Display_Str(Timeinfo.alarm1_x,Timeinfo.alarm1_y,24,(u8 *)buff,BLACK);
snprintf((char *)buff,sizeof(buff),"鬧鐘2:%s",alarm_clock2);
LCD_Display_Str(Timeinfo.alarm2_x,Timeinfo.alarm2_y,24,(u8 *)buff,BLACK);
}
/*繪制指針*/
static const char *celen_week[]={"一","二","三","四","五","六","日"};
void LCD_DrawTime(void)
{
char buff[100];
int time_sec=RTC_Time.sec;
int time_min=RTC_Time.min;
int time_h=RTC_Time.hour;
static u8 stat=0;
/*清除秒針*/
LCD_DrawAngleLine(160,160,270+(time_sec-1)*6.0,110,110,WHITE);//清除上一秒
/*處理時(shí)針*/
LCD_DrawAngleLine(160,160,270+time_h*30.0+(time_min-1)*0.5,60,60,WHITE);//清除上次時(shí)針
LCD_DrawAngleLine(160,160,270+time_h*30.0+time_min*0.5,60,60,BLACK);//畫(huà)當(dāng)前分位置
/*處理分指針*/
LCD_DrawAngleLine(160,160,270+(time_min-1)*6.0,90,90,WHITE);//清除上一分
LCD_DrawAngleLine(160,160,270+time_min*6.0,90,90,BLACK);//畫(huà)當(dāng)前分位置
//畫(huà)秒針
LCD_DrawAngleLine(160,160,270+time_sec*6.0,110,110,RED);//畫(huà)當(dāng)前秒位置
/*數(shù)字時(shí)間顯示*/
stat=!stat;
if(stat)
{
snprintf(buff,sizeof(buff),"%02d:%02d",time_h,time_min);
}
else
{
snprintf(buff,sizeof(buff),"%02d %02d",time_h,time_min);
}
LCD_Display_Str(Timeinfo.time_x,330,24,(u8 *)buff,BLACK);
snprintf(buff,sizeof(buff),"%04d/%02d/%02d 周 %s",RTC_Time.year,RTC_Time.month,RTC_Time.day,celen_week[RTC_Time.week-1]);
LCD_Display_Str(Timeinfo.date_x,Timeinfo.date_y,24,(u8 *)buff,BLACK);
LCD_Refresh();
if(time_sec==0 && time_min==0)
{
Voice_ReadData(voice_addr[time_h]);
LCD_DrawAngleLine(160,160,270+(time_sec)*6.0,110,110,WHITE);//清除上一秒
}
snprintf(buff,sizeof(buff),"%02d:%02d",time_h,time_min);
if((!strcmp(buff,(char *)alarm_clock1) || !strcmp(buff,(char *)alarm_clock2)) && time_sec==0)
{
LCD_Display_Str(LCD_WIDTH-24*3-30,Timeinfo.alarm1_y+12,24,(u8 *)"時(shí)間到!",RED);
LCD_Refresh();
Voice_ReadData(voice_addr[24]);
LCD_Display_Str(LCD_WIDTH-24*3-30,Timeinfo.alarm1_y+12,24,(u8 *)" ",RED);
LCD_Refresh();
LCD_DrawAngleLine(160,160,270+(time_sec)*6.0,110,110,WHITE);//清除上一秒
}
}
3.3 整點(diǎn)報(bào)時(shí)語(yǔ)音獲取
文字轉(zhuǎn)語(yǔ)音網(wǎng)址:https://uutool.cn/text2voice/
文字轉(zhuǎn)語(yǔ)音
4.4 字庫(kù)加載、語(yǔ)音包加載
字庫(kù)和語(yǔ)音包檢測(cè)
res=f_mount(&fs,"",1);
if(res)
{
printf("磁盤(pán)掛載失敗ret=%d\n",res);
snprintf(buff,sizeof(buff),"err%d",res);
LCD_Display_Str(20+strlen("SD卡狀態(tài)")*12+20,20,16,(u8 *)buff,RED);
LCD_Display_Str(20,50,16,(u8 *)"請(qǐng)檢查SD卡是否插入!",RED);
LCD_Refresh();
Delay_Ms(1000);
goto AA;
}
else LCD_Display_Str(20+strlen("SD卡狀態(tài)")*12+20,20,16,(u8 *)"OK",RED);
/*字庫(kù)檢測(cè)*/
LCD_Display_Str(LCD_WIDTH/2-strlen("字庫(kù)檢測(cè)")/2*16,40,16,(u8 *)"字庫(kù)檢測(cè)",RED);
LCD_Refresh();
GBK_16:
W25Q64_ReadData(GBK_16_ADDR-10,(u8*)buff,9);//GBK16_OK
if(strstr(buff,"GBK16_OK"))
{
LCD_Display_Str(20,60,16,(u8 *)"GBK16 OK",RED);
LCD_Refresh();
}
else
{
LCD_Display_Str(20,60,16,(u8 *)"GBK16 NO",RED);
LCD_Display_Str(LCD_WIDTH/2-strlen("更新GBK16字庫(kù)")/2*16,80,16,(u8 *)"更新GBK16字庫(kù)",RED);
LCD_Refresh();
if(SDcard_DownFont("0:/font/GBK_16.DZK",GBK_16_ADDR,16))//字庫(kù)更新
{
LCD_Display_Str(LCD_WIDTH/2-strlen(" ")/2*16,80,16,(u8 *)" ",WHITE);
LCD_Display_Str(10,80,16,(u8 *)"請(qǐng)將GBK_16.DZK放到/font/目錄下,重啟!",BLACK);
LCD_Refresh();
}
else
{
LCD_Display_Str(LCD_WIDTH/2-strlen(" ")/2*16,80,16,(u8 *)" ",WHITE);
LCD_Display_Str(20,100,16,(u8 *)" ",WHITE);
LCD_Refresh();
goto GBK_16;
}
}
/*GBK24_OK*/
GBK_24:
W25Q64_ReadData(GBK_24_ADDR-10,(u8*)buff,9);
if(strstr(buff,"GBK24_OK"))
{
LCD_Display_Str(20,100,16,(u8 *)"GBK24 OK",RED);
LCD_Refresh();
}
else
{
LCD_Display_Str(20,100,16,(u8 *)"GBK24 NO",RED);
LCD_Display_Str(LCD_WIDTH/2-strlen("更新GBK24字庫(kù)")/2*16,120,16,(u8 *)"更新GBK24字庫(kù)",RED);
LCD_Refresh();
if(SDcard_DownFont("0:/font/GBK_24.DZK",GBK_24_ADDR,24))//字庫(kù)更新
{
LCD_Display_Str(LCD_WIDTH/2-strlen(" ")/2*16,120,16,(u8 *)" ",WHITE);
LCD_Display_Str(10,120,16,(u8 *)"請(qǐng)將GBK_24.DZK放到/font/目錄下,重啟!",BLACK);
LCD_Refresh();
}
else
{
LCD_Display_Str(LCD_WIDTH/2-strlen(" ")/2*16,120,16,(u8 *)" ",WHITE);
LCD_Display_Str(20,140,16,(u8 *)" ",WHITE);
LCD_Refresh();
goto GBK_24;
}
}
/*語(yǔ)音包檢測(cè)*/
LCD_Display_Str(LCD_WIDTH/2-strlen("語(yǔ)音包檢測(cè)")/2*16,140,16,(u8 *)"語(yǔ)音包檢測(cè)",RED);
LCD_Refresh();
VOICE_STAT:
Voice_ReadAddr(voice_addr);//獲取每個(gè)音頻文件存放位置
for(i=0;i<25;i++)
{
if(voice_addr[i]==0)break;
}
if(i!=25)
{
LCD_Display_Str(20,140+20,16,(u8 *)"語(yǔ)音包 ERR",RED);
LCD_Display_Str(LCD_WIDTH/2-strlen("更新語(yǔ)音包")/2*16,140+40,16,(u8 *)"更新語(yǔ)音包",RED);
LCD_Refresh();
if(Voice_Download_W25Q64("voice"))//將語(yǔ)音包數(shù)據(jù)寫(xiě)入到W25Q64中
{
LCD_Display_Str(LCD_WIDTH/2-strlen(" ")/2*16,160+60,16,(u8 *)" ",WHITE);
LCD_Display_Str(10,140+60,16,(u8 *)"請(qǐng)將語(yǔ)音MP3放到/voice/目錄下,重啟!",BLACK);
LCD_Refresh();
goto VOICE_STAT;
}
else
{
LCD_Display_Str(10,160+60,16,(u8 *)"語(yǔ)音包更新完成",RED);
LCD_Refresh();
}
}
else
{
LCD_Display_Str(20,160+60,16,(u8 *)"語(yǔ)音包 OK",RED);
LCD_Refresh();
}
f_unmount("");//取消SD卡掛載
LCD_Display_Str(10,160+100,16,(u8 *)"系統(tǒng)文件自檢完成,請(qǐng)移除SD卡!",RED);
LCD_Display_Str(55,160+130,24,(u8 *)"歡迎使用電子時(shí)鐘!",RED);
LCD_Display_Str(75,160+165,24,(u8 *)"正在啟動(dòng)中",RED);
4.5 主函數(shù)
VS1053_Init();//VS1053初始化
LCD_DrawClockDial();
RTC_Init();//RTC初始化
rtc_stat=1;
u8 touch_stat=0;
u16 x1,x2,y;
while(1)
{
if(usart1_flag)
{
usart1_rx_buff[usart1_cnt]='\0';
printf("rx:%s\r\n",usart1_rx_buff);
//rx:*20210609115254
// printf("usart1_rx_cnt=%d\n",usart1_cnt);
if(usart1_rx_buff[0]=='*' && usart1_cnt == 15)
{
RTC_Time.year=(usart1_rx_buff[1]-'0')*1000+(usart1_rx_buff[2]-'0')*100+(usart1_rx_buff[3]-'0')*10+(usart1_rx_buff[4]-'0')*1;
RTC_Time.month=(usart1_rx_buff[5]-'0')*10+(usart1_rx_buff[6]-'0')*1;
RTC_Time.day=(usart1_rx_buff[7]-'0')*10+(usart1_rx_buff[8]-'0')*1;
RTC_Time.hour=(usart1_rx_buff[9]-'0')*10+(usart1_rx_buff[10]-'0')*1;
RTC_Time.min=(usart1_rx_buff[11]-'0')*10+(usart1_rx_buff[12]-'0')*1;
RTC_Time.sec=(usart1_rx_buff[13]-'0')*10+(usart1_rx_buff[14]-'0')*1;
printf("%d/%d/%d -- %d:%d:%d\r\n",RTC_Time.year,
RTC_Time.month,
RTC_Time.day,
RTC_Time.hour,
RTC_Time.min,
RTC_Time.sec);
//RTC時(shí)間校準(zhǔn)
Time_Conversion_Sec(RTC_Time.year, RTC_Time.month,RTC_Time.day,RTC_Time.hour,RTC_Time.min,RTC_Time.sec);
LCD_Clear(WHITE);
LCD_DrawClockDial();
}
usart1_flag=0;
usart1_cnt=0;
}
if((key_val&1<<0) && rtc_stat!=1)//時(shí)鐘界面
{
key_val&=~(1<<0);
rtc_stat=1;
LCD_Clear(WHITE);
LCD_DrawClockDial();
}
else if(key_val&1<<1)//日歷界面
{
key_val&=~(1<<1);
rtc_stat=2;
LcdDrawcalen(RTC_Time.year,RTC_Time.month,RTC_Time.day);
u16 year=RTC_Time.year;
u8 month=RTC_Time.month;
u8 day=RTC_Time.day;
while(1)
{
if(key_val)break;
touch_stat=XPT2046_ReadXY();
if(touch_stat)
{
x1=touch_info.x;
while(T_PEN==0)//等待松開(kāi)
{
XPT2046_ReadXY();
x2=touch_info.x;
}
if(x1-x2>20)
{
if(month>=12)
{
month=1;
year++;
}
else month++;
LcdDrawcalen(year,month,day);
}
else if(x2-x1>20)
{
if(month<=1)
{
month=12;
year--;
}
else month--;
LcdDrawcalen(year,month,day);
}
}
}
}
touch_stat=XPT2046_ReadXY();
if(touch_stat && rtc_stat==1)////進(jìn)入時(shí)間設(shè)置
{
y=touch_info.y;
if(y<=Timeinfo.time_y+24 && y>=Timeinfo.time_y)
{
u16 hour=RTC_Time.hour;
u8 min=RTC_Time.min;
u16 year=RTC_Time.year;
u16 month=RTC_Time.month;
u16 day=RTC_Time.day;
Time_Calibration(year,month,day,hour,min);
//printf("設(shè)置時(shí)間\r\n");
}
else if(y<=Timeinfo.alarm1_y+24 && y>=Timeinfo.alarm1_y)//設(shè)置鬧鐘時(shí)間1
{
u8 hour,min;
sscanf((char *)alarm_clock1,"%02d:%02d",(int *)&hour,(int *)&min);
//printf("hour=%d,min=%d\n",hour,min);
Time_SetAlarm(hour,min,1);
}
else if(y<=Timeinfo.alarm2_y+24 && y>=Timeinfo.alarm2_y)//設(shè)置鬧鐘時(shí)間2
{
u8 hour,min;
sscanf((char *)alarm_clock2,"%02d:%02d",(int *)&hour,(int *)&min);
//printf("hour=%d,min=%d\n",hour,min);
Time_SetAlarm(hour,min,2);
}
}
}
}
鬧鐘調(diào)整和整點(diǎn)報(bào)時(shí)
審核編輯:湯梓紅
-
STM32
+關(guān)注
關(guān)注
2290文章
11017瀏覽量
362412 -
電子鐘
+關(guān)注
關(guān)注
7文章
189瀏覽量
37355 -
STM32F10
+關(guān)注
關(guān)注
0文章
46瀏覽量
15819 -
STM32F103ZET6
+關(guān)注
關(guān)注
9文章
67瀏覽量
21604
發(fā)布評(píng)論請(qǐng)先 登錄
STM32應(yīng)用案例 基于STM32F103ZE開(kāi)發(fā)的數(shù)碼相冊(cè)

STM32F103ZE最小系統(tǒng)版
怎樣去設(shè)計(jì)一種基于STM32F103ZE的LD3320語(yǔ)音控制系統(tǒng)
STM32F103ze簡(jiǎn)介
嵌入式 STM32F103ZE主要特性

基于STM32F103ZE的USART2 端口時(shí)鐘

新版STM32F103ZE核心板原理圖詳細(xì)資料免費(fèi)下載

STM32F103ZE核心板電路原理圖和PCB圖免費(fèi)下載

新版STM32F103ZE核心板原理圖免費(fèi)下載

評(píng)論