基于STM32电子钟语音报时

发布时间:2023-06-13 16:30

基于STM32电子钟语音报时

1.硬件平台

  • CPU:STM32F103ZE
  • 屏幕:3.5寸TFTLCD屏
  • 语音播报: VS1053
  • SD卡、外扩Sram

2.功能实现

  本示例主要设计两个界面:时间显示界面和日历界面
  1.通过按键切换界面;
  2.时间界面包含:时间表盘绘制、数字时间和日期显示、闹钟设置;支持按键校时和闹钟时间设置;
  3.日历界面支持查看日期;
  4.上电自动检测字库是否正常、报时语音包是否正常;支持SD卡字库和语音包更新;

3.示例效果

基于STM32电子钟语音播报

4.软件设计

4.1 VS1053初始化及播放音频

/**************硬件接口*****************
**VS_MISO -- PA6 主机输入
**VS_MOSI -- PA7 主机输出
**VS_SCK  -- PA5 时钟
**VS_XCS  -- PF7 命令片选(低电平有效)
**VS_XDCS -- PF6 数据片选(低电平有效)
**VS_DREQ -- PC13 数据请求线(高电平表示可以接收数据)
**VS_RST  -- PE6 复位脚(低电平复位)
**
*****************************************/

void VS1053_Init(void)
{
	/*1. 开时钟*/
	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.配置时钟寄存器*/
	VS1053_WriteRegDat(VS1053_CLOCKF,0x9800);
}
/*SPI收发一个字节*/
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;
}
/********************往寄存器中写入数据******************
**
**形参:u8 addr --地址
**			u16 data  -- 写入的数据
**********************************************************/
void VS1053_WriteRegDat(u8 addr,u16 data)
{
	while(VS_DREQ==0){}//等待数据线空闲
	VS_XDCS=1;//数据片选拉高
	VS_XCS=0;//命令片选拉低
	VS1053_SPI_ReadWriteData(0x02);//写指令
	VS1053_SPI_ReadWriteData(addr);	//寄存器地址
	VS1053_SPI_ReadWriteData(data>>8);
	VS1053_SPI_ReadWriteData(data>>0);//写入数据
	VS_XCS=1;
}
/*******************从寄存器中读取数据*******************/
u16 VS1053_ReadRegDat(u8 addr)
{
	u16 data=0;
	while(VS_DREQ==0){}//等待数据线空闲
	VS_XDCS=1;//数据片选拉高
	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;
}
/****************音量调节*****************
**
**形参:u8 vol_l -- 左声道 0~254
**      u8 vol_r -- 右声道 0~254
**每个增量表示0.5db的衰减,值越大,音量越小
**注意:如果设置 VOL 的值为 0xFFFF,将使芯片进入掉电模式。
**右声道是高 8 位 左声道是低 8 位
*******************************************/
void VS1053_SetVoice(u8 vol_l,u8 vol_r)
{
	u16 temp=vol_r<<8|vol_l;
	VS1053_WriteRegDat(VS1053_VOL,temp);
}
/***************VS1053硬件复位**************/
void VS1053_RST(void)
{
	//硬件复位
	VS_RST=0;
	Delay_Ms(20);
	VS_XDCS=1;//取消数据传输
	VS_XCS=1;//取消命令传输
	VS_RST=1;//完成复位
	//软件复位
	while(VS_DREQ==0){}//等待数据线空闲
	VS1053_WriteRegDat(VS1053_MODE,0x0804);//设置为新模式,进行软件复位
	Delay_Ms(2);	
	while(VS_DREQ==0){}//等待数据线空闲,复位完成
}
/****获取解码时间******/
u16 VS1053_Get_Time(void)
{
	u16 time=0;
	time=VS1053_ReadRegDat(VS1053_DECODE_TIME);
	return time;
}
/****清除解码时间******/
void VS1053_Clear_Time(void)
{
	VS1053_WriteRegDat(VS1053_DECODE_TIME,0);
}
/**************播放音乐****************************/
u8 VS1053_PlayOneMusic(const char *data,u32 data_size)
{
	u32 i=0;
   	/*3.设置音量*/
	VS1053_SetVoice(50,50);
  for(i=0;i;i++)>

4.2 表盘和日历

\"基于STM32电子钟语音报时_第1张图片\"

/*绘制表盘*/
void LCD_DrawClockDial(void)
{
  u8 buff[20];
	/*画表盘*/
	LCD_Circle(160,160,150,BLACK);//画圆
	LCD_Circle(160,160,151,BLACK);//画圆
	LCD_Circle(160,160,152,BLACK);//画圆
	/*画刻度*/
	u16 i;
	for(i=0;i<360;i+=6)
	{
		LCD_DrawAngleLine(160,160,i,151,15,BLUE);//画任意角度直线
	}
	for(i=0;i<360;i+=30)
	{
		LCD_DrawAngleLine(160,160,i,151,25,RED);//画任意角度直线
	}
	/*显示时间数据*/
	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);//清除上一秒
  /*处理时针*/
  LCD_DrawAngleLine(160,160,270+time_h*30.0+(time_min-1)*0.5,60,60,WHITE);//清除上次时针
  LCD_DrawAngleLine(160,160,270+time_h*30.0+time_min*0.5,60,60,BLACK);//画当前分位置
  /*处理分指针*/
  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);//画当前分位置
  //画秒针
	LCD_DrawAngleLine(160,160,270+time_sec*6.0,110,110,RED);//画当前秒位置
   /*数字时间显示*/
   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 *)\"时间到!\",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);//清除上一秒
   }
}

4.3 整点报时语音获取

  文字转语音网址:https://uutool.cn/text2voice/
\"基于STM32电子钟语音报时_第2张图片\"

4.4 字库加载、语音包加载

\"基于STM32电子钟语音报时_第3张图片\"

  res=f_mount(&fs,\"\",1);
  if(res)
  {
    printf(\"磁盘挂载失败ret=%d\\n\",res);
    snprintf(buff,sizeof(buff),\"err%d\",res);
    LCD_Display_Str(20+strlen(\"SD卡状态\")*12+20,20,16,(u8 *)buff,RED);
    LCD_Display_Str(20,50,16,(u8 *)\"请检查SD卡是否插入!\",RED);
    LCD_Refresh();
    Delay_Ms(1000);
    goto AA;
  }
  else LCD_Display_Str(20+strlen(\"SD卡状态\")*12+20,20,16,(u8 *)\"OK\",RED);
	/*字库检测*/
	LCD_Display_Str(LCD_WIDTH/2-strlen(\"字库检测\")/2*16,40,16,(u8 *)\"字库检测\",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字库\")/2*16,80,16,(u8 *)\"更新GBK16字库\",RED);
		LCD_Refresh();
		if(SDcard_DownFont(\"0:/font/GBK_16.DZK\",GBK_16_ADDR,16))//字库更新
		{
			LCD_Display_Str(LCD_WIDTH/2-strlen(\"                 \")/2*16,80,16,(u8 *)\"                 \",WHITE);
			LCD_Display_Str(10,80,16,(u8 *)\"请将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字库\")/2*16,120,16,(u8 *)\"更新GBK24字库\",RED);
		LCD_Refresh();
		if(SDcard_DownFont(\"0:/font/GBK_24.DZK\",GBK_24_ADDR,24))//字库更新
		{
			LCD_Display_Str(LCD_WIDTH/2-strlen(\"                 \")/2*16,120,16,(u8 *)\"                 \",WHITE);
			LCD_Display_Str(10,120,16,(u8 *)\"请将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;
		}
	}  
  /*语音包检测*/
	LCD_Display_Str(LCD_WIDTH/2-strlen(\"语音包检测\")/2*16,140,16,(u8 *)\"语音包检测\",RED);
	LCD_Refresh();  
VOICE_STAT:
  Voice_ReadAddr(voice_addr);//获取每个音频文件存放位置
  for(i=0;i<25;i++)
  {
    if(voice_addr[i]==0)break;
  }
  if(i!=25)
  {
    LCD_Display_Str(20,140+20,16,(u8 *)\"语音包    ERR\",RED);
    LCD_Display_Str(LCD_WIDTH/2-strlen(\"更新语音包\")/2*16,140+40,16,(u8 *)\"更新语音包\",RED);
		LCD_Refresh();
    if(Voice_Download_W25Q64(\"voice\"))//将语音包数据写入到W25Q64中
    {
      LCD_Display_Str(LCD_WIDTH/2-strlen(\"                 \")/2*16,160+60,16,(u8 *)\"                 \",WHITE);
			LCD_Display_Str(10,140+60,16,(u8 *)\"请将语音MP3放到/voice/目录下,重启!\",BLACK);
			LCD_Refresh();
      goto VOICE_STAT;
    }
    else 
    {
			LCD_Display_Str(10,160+60,16,(u8 *)\"语音包更新完成\",RED);
			LCD_Refresh();
    }
  }
  else 
  {
    LCD_Display_Str(20,160+60,16,(u8 *)\"语音包 OK\",RED);
		LCD_Refresh();
  }
  f_unmount(\"\");//取消SD卡挂载
  LCD_Display_Str(10,160+100,16,(u8 *)\"系统文件自检完成,请移除SD卡!\",RED);
  LCD_Display_Str(55,160+130,24,(u8 *)\"欢迎使用电子时钟!\",RED);
  LCD_Display_Str(75,160+165,24,(u8 *)\"正在启动中\",RED);

4.5 主函数

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时间校准
         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)//时钟界面
      {
        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)//等待松开
              {
                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)进入时间设置
      {
        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(\"设置时间\\r\\n\");
        }
        else if(y<=Timeinfo.alarm1_y+24 && y>=Timeinfo.alarm1_y)//设置闹钟时间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)//设置闹钟时间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);
        }
      }
	}
}


  示例链接:https://download.csdn.net/download/weixin_44453694/85456736

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号