开源——基于STM32的智能语音联网时钟(程序)
作品介绍
前段时间有段空闲时间,同时也是为了顺便通过短学期实践,制作了一款基于STM32的智能语音时钟。先放一张靓图。

王婆卖瓜,自卖自夸,咱当然也要来不害臊地自夸一波:首先视觉效果还行(仅限于正面),其次成本较低(语音模块5块,ESP01S6块,嘉立创的优惠打板(不是广告))。
但是,由于很多模块是新接触的,加上时间紧张(实际上是水平略菜)导致很多小错误产生,并且发生了一个巨大错误,供电线直接用了10mil,并且为了图省事部分用了自动走线(这个确实不该出错,俺反省),导致供电很抽风,如果要稳定使用,大家必须要再走一遍线。(之后可能会更正一版)
话不多说,上DIY思路,首先是模块介绍:

可以看到大致的模块,在设计之初就将其定义为一个可拓展的平台,还有一些接口为未使用,可以自主添加喜欢的功能。(未使用接口包括一个DHT11,两个普通按键,一个WKUP按键,一个UART引出排针,一个BAT供电接口)
工程源码:
工程如下,小部分程序取自正点原子,小部分程序思路取自网络大佬,归原创作者所有,该程序仅用于学习交流,请不要用于其他用途。

网盘链接:pan.bai去掉du.com/s/1pwww去掉DrX7E0muVQBE0MLA-Q 神秘代码(1fgx)
数码管显示实现
数码管显示通过TM1637实现,具体原理可以看在下之前博客,使用的贴片是SOP-20封装的贴片(天微电子),数码管是4个0.8寸的共阳数码管集中在一起实现的4位效果。
一个0.8寸数码管长这个样子:

代码中delay.h使用的为正点原子的delay函数,大家可以自主从正点原子下载。
代码如下:
#include "TM1637.h" #include "delay.h" void TM_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(TM_DIO_CLK|TM_SCL_CLK,ENABLE); GPIO_InitStructure.GPIO_Pin = TM_DIO_PIN | TM_SCL_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(TM_DIO_PORT,&GPIO_InitStructure); // TM_SCL=1; // TM_SDA=1; } //与寄存器操作取一个 /* void SDA_IN(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(TM_DIO_CLK,ENABLE); GPIO_InitStructure.GPIO_Pin = TM_DIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(TM_DIO_PORT,&GPIO_InitStructure); } void SDA_OUT(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(TM_DIO_CLK,ENABLE); GPIO_InitStructure.GPIO_Pin = TM_DIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(TM_DIO_PORT,&GPIO_InitStructure); } */ void TM_Start(void) { TM_SDA=1; delay_us(2); TM_SCL=1; delay_us(2); TM_SDA=0; delay_us(2); TM_SCL=0; delay_us(2); } void TM_Stop(void) { TM_SCL=0; delay_us(2); TM_SDA=0; delay_us(2); TM_SCL=1; delay_us(2); TM_SDA=1; delay_us(2); } void TM_Wait_Ask(void) { SDA_IN(); unsigned char i; TM_SCL=0; delay_us(5); while(READ_SDA==1&&(i<250))i++; TM_SCL=1; delay_us(2); TM_SCL=0; SDA_OUT(); } void TM_WriteByte(uint8_t txd) { uint8_t i; for(i=0;i<8;i++) { TM_SCL=0; delay_us(2); if(txd & 0x01){ TM_SDA=1; } else { TM_SDA=0; } delay_us(3); txd>>=1; TM_SCL=1; delay_us(3); } //TM_Wait_Ask(); } void TM_Display(uint8_t *discode) { uint8_t i; TM_Start(); TM_WriteByte(0x40); //40 地址自加模式 44 固定地址模式 TM_Wait_Ask(); TM_Stop(); TM_Start(); TM_WriteByte(0xc0); //首地址 TM_Wait_Ask(); for(i=0;i<4;i++) { TM_WriteByte(*(discode+i)); //依次发送数组数据 TM_Wait_Ask(); } TM_Stop(); TM_Start(); TM_WriteByte(0x89); //亮度 TM_Wait_Ask(); TM_Stop(); } void TM_AdDisplay(uint8_t addr,uint8_t data) { TM_Start(); TM_WriteByte(0x44); TM_Wait_Ask(); TM_Stop(); TM_Start(); TM_WriteByte(addr); TM_Wait_Ask(); TM_WriteByte(data); TM_Wait_Ask(); TM_Stop(); TM_Start(); TM_WriteByte(0x8d); //亮度 TM_Wait_Ask(); TM_Stop(); }
#ifndef __TM1637_H #define __TM1637_H #include "stm32f10x.h" //与库函数操作取一个 #define SDA_IN() {GPIOA->CRL&=0X0FFFFFFF;GPIOA->CRL|=(u32)8<<28;} #define SDA_OUT() {GPIOA->CRL&=0X0FFFFFFF;GPIOA->CRL|=(u32)3<<28;} #define TM_SCL_PORT GPIOA #define TM_SCL_CLK RCC_APB2Periph_GPIOA #define TM_SCL_PIN GPIO_Pin_5 #define TM_DIO_PORT GPIOA #define TM_DIO_CLK RCC_APB2Periph_GPIOA #define TM_DIO_PIN GPIO_Pin_7 #define TM_SCL PAout(5) #define TM_SDA PAout(7) #define READ_SDA PAin(7) void TM_Init(void); void TM_Start(void); void TM_Stop(void); void TM_Wait_Ask(void); void TM_WriteByte(uint8_t txd); void TM_Display(uint8_t *discode); void TM_AdDisplay(uint8_t addr,uint8_t data); //void SDA_IN(void); //void SDA_OUT(void); #endif
程序思路为使用模拟IIC向TM1637发送数据,地址是自加的,所以只需要发送一个数组数据即可,数组在main.c中全局定义(main.c放在最后)
MP3语音
语音模块使用的是一个硬件解码的UART通信MP3模块(广州锐欣),结构如下:(实际买到的是一个更加炫酷的黑色板)

它的通信也很简单,这次我使用的是TF卡储存数据,并将字库文件放入了MP3里。


字库命名如下:(这部分大家可以通过语音合成软件自主制作,均为MP3文件,且存放在TF卡下的MP3文件夹目录下,参照上图,直接使用0x12指令)

MP3.c程序如下:
#include "MP3.h" #include "usart3.h" #include "stm32f10x.h" #include "delay.h" static uint8_t Send_buf[10] = {0} ; void MP3_Init(void) { USART3_Init(); //初始化其通讯串口 GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOB, &GPIO_InitStructure); } void DoSum( uint8_t *Str, uint8_t len) { uint16_t xorsum = 0; uint8_t i; for(i=0; i<len; i++) { xorsum = xorsum + Str[i]; } xorsum = 0 -xorsum; *(Str+i) = (uint8_t)(xorsum >>8); *(Str+i+1) = (uint8_t)(xorsum & 0x00ff); } void SendCmd(uint8_t len) { uint8_t i = 0 ; USART3_SendByte(0x7E); //起始 for(i=0; i<len; i++)//数据 { USART3_SendByte(Send_buf[i]) ; } USART3_SendByte(0xEF) ;//结束 } void Uart_SendCMD(uint8_t CMD ,uint8_t feedback , uint16_t dat) { Send_buf[0] = 0xff; //保留字节 Send_buf[1] = 0x06; //长度 Send_buf[2] = CMD; //控制指令 Send_buf[3] = feedback;//是否需要反馈 Send_buf[4] = (uint8_t)(dat >> 8);//datah Send_buf[5] = (uint8_t)(dat); //datal DoSum(&Send_buf[0],6); //校验 SendCmd(8); //发送此帧数据 } void Readtime(uint8_t mon,uint8_t day,uint8_t hour, uint8_t min, uint8_t sec) { Uart_SendCMD(0x12,0,0x0b); //现在时间是 delay_ms(100); //使MP3模块有时间拉低BUSY信号 while(BUSY!=1); //读月份 if(mon/10!=0) { Uart_SendCMD(0x12,0,0x0a); delay_ms(100); while(BUSY!=1); } Uart_SendCMD(0x12,0,mon%10); delay_ms(100); while(BUSY!=1); Uart_SendCMD(0x12,0,0x12); delay_ms(100); while(BUSY!=1); //读日 if(day/10!=0) { Uart_SendCMD(0x12,0,day/10); delay_ms(100); while(BUSY!=1); Uart_SendCMD(0x12,0,0x0a); delay_ms(100); while(BUSY!=1); } Uart_SendCMD(0x12,0,day%10); delay_ms(100); while(BUSY!=1); Uart_SendCMD(0x12,0,0x13); delay_ms(100); while(BUSY!=1); //读时 if(hour/10!=0) { Uart_SendCMD(0x12,0,0x0a); delay_ms(100); while(BUSY!=1); } Uart_SendCMD(0x12,0,hour%10); delay_ms(100); while(BUSY!=1); Uart_SendCMD(0x12,0,0x0c); delay_ms(100); while(BUSY!=1); //读分 if(min/10!=0) { Uart_SendCMD(0x12,0,min/10); delay_ms(100); while(BUSY!=1); Uart_SendCMD(0x12,0,0x0a); delay_ms(100); while(BUSY!=1); } Uart_SendCMD(0x12,0,min%10); delay_ms(100); while(BUSY!=1); Uart_SendCMD(0x12,0,0x0d); delay_ms(100); while(BUSY!=1); //读秒 if(sec/10!=0) { Uart_SendCMD(0x12,0,sec/10); delay_ms(100); while(BUSY!=1); Uart_SendCMD(0x12,0,0x0a); delay_ms(100); while(BUSY!=1); } Uart_SendCMD(0x12,0,sec%10); delay_ms(100); while(BUSY!=1); Uart_SendCMD(0x12,0,0x0e); }
mp3.c的逻辑比较简单,我们传入RTC时钟的值,然后通过判断数字依次读出数字值,最终效果是“现在时间是 xx时 xx分 xx秒"。
MP3.h
#ifndef __MP3_H #define __MP3_H #include "stm32f10x.h" #define BUSY GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1) void MP3_Init(void); void DoSum( uint8_t *Str, uint8_t len); void SendCmd(uint8_t len); void Uart_SendCMD(uint8_t CMD ,uint8_t feedback , uint16_t dat); void Readtime(uint8_t mon,uint8_t day,uint8_t hour, uint8_t min, uint8_t sec); #endif
usart3.c
#include "stm32f10x.h" #include "stm32f10x_usart.h" #include "usart3.h" #include "stdio.h" uint8_t res[20]; void USART3_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; //NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //GPIO时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE); //串口外设时钟 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //推挽复用输出 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_11; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOB,&GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 9600 ; //波特率,这里改为9600以和MP3默认波特率匹配 USART_InitStructure.USART_WordLength=USART_WordLength_8b; //数据帧字长 8位 USART_InitStructure.USART_StopBits=USART_StopBits_1; //配置停止位 1个 USART_InitStructure.USART_Parity=USART_Parity_No; //校验位,无校验位 USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不使用用硬件流控制 USART_InitStructure.USART_Mode=USART_Mode_Tx|USART_Mode_Rx; //收发一体 USART_Init(USART3,&USART_InitStructure); //完成初始化 USART_Cmd(USART3,ENABLE); //使能串口 // USART_ITConfig(USART3,USART_IT_RXNE,ENABLE); //使能串口中断 // // //优先级配置 // NVIC_InitStructure.NVIC_IRQChannel=USART3_IRQn; // NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; // NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; // NVIC_Init(&NVIC_InitStructure); } void USART3_SendByte(uint8_t ch) { //先读取TC值状态,使之读取后清零,避免第一个字节丢失 while(USART_GetFlagStatus(USART3,USART_FLAG_TC)==RESET); USART_SendData(USART3, ch);//向串口3发送数据 } void USART3_SendString(char *str) { unsigned int k=0; //先执行,后判断 (目前与先判断后执行效果一样) do { USART3_SendByte(*(str+k)); k++; }while(*(str+k)!='\0'); // null字符 while(USART_GetFlagStatus(USART3,USART_FLAG_TC)!=SET); }
usart3.h
#ifndef __USART3_H #define __USART3_H #include "stm32f10x.h" #include "stdio.h" extern uint8_t res[]; void USART3_Init(void); void USART3_SendByte(uint8_t ch); void USART3_SendString(char *str); #endif
ESP01S
我们通过ESP01S来获取网络时间,ESP01S的连线比较简单,这里我们使用USART2接口
、
ESP01.c代码如下:
#include "stm32f10x.h" #include "delay.h" #include "ESP01.h" #include "usart2.h" #include "usart3.h" #include "rtc.h" #include "key.h" #include "MP3.h" unsigned char time_data[19]; extern u16 USART_RX_STA; u16 year; u8 month,day,hour,minute,second; //不使用EN RST,直接上电,简化流程 /* void ESP01_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = RST_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(RST_CTRL,&GPIO_InitStructure); RST_OFF; GPIO_InitStructure.GPIO_Pin = EN_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(EN_CTRL,&GPIO_InitStructure); EN_OFF; } */ void ESP01_Getweb(void) { EN_ON; delay_ms(50); printf("AT+CWMODE=1"); delay_ms(50); RST_ON; delay_ms(100); RST_OFF; delay_ms(50); USART_RX_STA2=0x0000; printf("AT+CWJAP=\"wifi账号\",\"wifi密码\""); delay_ms(1000); //注意delay时间限制 delay_ms(1000); delay_ms(1000); delay_ms(1000); delay_ms(1000); } void ESP01_Gettime(void) { printf("+++\r\n"); delay_ms(500); // printf("AT+RST\r\n"); // delay_ms(1000); // delay_ms(1000); // delay_ms(1000); // delay_ms(1000); // delay_ms(1000); printf("AT+CIPMUX=0\r\n"); delay_ms(500); printf("AT+CIPSTART=\"TCP\",\"api.k780.com\",80\r\n"); delay_ms(1600); printf("AT+CIPMODE=1\r\n"); delay_ms(500); printf("AT+CIPSEND\r\n"); delay_ms(500); USART_RX_STA2=0x0000; //使数据从数组头开始记录 printf("GET http://api.k780.com:88/?app=life.time&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json&HTTP/1.1\r\n"); delay_ms(1600); }
//此部分未使用
/* void ESP01_Getweather(void) { u8 weaflag=0; printf("+++\r\n"); delay_ms(500); // printf("AT+RST\r\n"); // delay_ms(1000); // delay_ms(1000); // delay_ms(1000); // delay_ms(1000); // delay_ms(1000); printf("AT+CIPMUX=0\r\n"); delay_ms(500); printf("AT+CIPSTART=\"TCP\",\"api.thinkpage.cn\",80\r\n"); delay_ms(1600); printf("AT+CIPMODE=1\r\n"); delay_ms(500); printf("AT+CIPSEND\r\n"); delay_ms(500); Uart_SendCMD(0x12,0,0x15); //提醒选择城市 while(KEY3!=0) { if(KEY4==0) { weaflag+=1; if(weaflag==2){weaflag=0;} delay_ms(20); while(KEY4==0); Uart_SendCMD(0x12,0,0x16+weaflag); //读所选城市,目前支持两个 对应22 23 delay_ms(100); while(BUSY!=1); } } delay_ms(20); while(KEY3==0); delay_ms(20); Uart_SendCMD(0x12,0,0x14); delay_ms(100); while(BUSY!=1); USART_RX_STA2=0x0000; //使数据从数组头开始记录 switch(weaflag) { case 0: printf("GET https://api.thinkpage.cn/v3/weather/now.json?key=wcmquevztdy1jpca&location=Shanghai&language=en&unit=c/r/n"); break; case 1: printf("GET https://api.thinkpage.cn/v3/weather/now.json?key=wcmquevztdy1jpca&location=Wuhan&language=en&unit=c/r/n"); break; } delay_ms(1000); delay_ms(1000); }
*/ void ESP01_Settime(void) { uint8_t ESPmon,ESPday,ESPhou,ESPmin,ESPsec; ESPmon=((USART_RX_BUF2[69]-'0')*10+(USART_RX_BUF2[70]-'0')); ESPday=((USART_RX_BUF2[72]-'0')*10+(USART_RX_BUF2[73]-'0')); ESPhou=((USART_RX_BUF2[75]-'0')*10+(USART_RX_BUF2[76]-'0')); ESPmin=((USART_RX_BUF2[78]-'0')*10+(USART_RX_BUF2[79]-'0')); ESPsec=((USART_RX_BUF2[81]-'0')*10+(USART_RX_BUF2[82]-'0')); RTC_Set(2020,ESPmon,ESPday,ESPhou,ESPmin,ESPsec); }
这个程序主要功能为连接wifi并获取时间,把它存入一个数组内,然后提取时间值写入RTC
ESP01.h
#ifndef __ESP01_H #define __ESP01_H #define RST_CTRL GPIOB #define EN_CTRL GPIOB #define RST_PIN GPIO_Pin_4 #define EN_PIN GPIO_Pin_3 #define RST_ON GPIO_ResetBits(GPIOB,RST_PIN) #define RST_OFF GPIO_SetBits(GPIOB,RST_PIN) #define EN_ON GPIO_SetBits(GPIOB,EN_PIN) #define EN_OFF GPIO_ResetBits(GPIOB,EN_PIN) void ESP01_Init(void); //void ESP01_Open(void); void ESP01_Gettime(void); void ESP01_Settime(void); void ESP01_Getweather(void); #endif
usart2.c(部分使用了大神网友的接收判断)
#include "stm32f10x.h" #include "stm32f10x_usart.h" #include "usart2.h" #include "stdio.h" //unsigned char res[100]; u8 USART_RX_BUF2[512]; u16 USART_RX_STA2; void USART2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //GPIO时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE); //串口外设时钟 GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //推挽复用输出 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_3; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_Init(GPIOA,&GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200 ; //波特率,这里改为115200与ESP8266通信 USART_InitStructure.USART_WordLength=USART_WordLength_8b; //数据帧字长 8位 USART_InitStructure.USART_StopBits=USART_StopBits_1; //配置停止位 1个 USART_InitStructure.USART_Parity=USART_Parity_No; //校验位,无校验位 USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不使用用硬件流控制 USART_InitStructure.USART_Mode=USART_Mode_Tx|USART_Mode_Rx; //收发一体 USART_Init(USART2,&USART_InitStructure); //完成初始化 USART_Cmd(USART2,ENABLE); //使能串口 USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); //使能串口中断 //优先级配置 NVIC_InitStructure.NVIC_IRQChannel=USART2_IRQn; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_Init(&NVIC_InitStructure); } void USART2_SendByte(uint8_t ch) { //先读取TC值状态,使之读取后清零,避免第一个字节丢失 while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET); USART_SendData(USART2, ch);//向串口2发送数据 } void USART2_SendString(char *str) { unsigned int k=0; //先执行,后判断 (目前与先判断后执行效果一样) do { USART2_SendByte(*(str+k)); k++; }while(*(str+k)!='\0'); // null字符 while(USART_GetFlagStatus(USART2,USART_FLAG_TC)!=SET); } int fputc(int ch, FILE *f) { while((USART2->SR&0X40)==0);//循环发送,直到发送完毕 USART2->DR = (u8) ch; return ch; } void USART2_IRQHandler(void) { u8 Res; if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾) { Res =USART_ReceiveData(USART2); //读取接收到的数据 if((USART_RX_STA2&0x8000)==0)//接收未完成 { if(USART_RX_STA2&0x4000)//接收到了0x7D} { if(Res!=0x7D)USART_RX_STA2=0;//接收错误,重新开始 else USART_RX_STA2|=0x8000; //接收完成了 } else //还没收到0X7D } { if(Res==0x7D)USART_RX_STA2|=0x4000; else { USART_RX_BUF2[USART_RX_STA2&0X3FFF]=Res ; USART_RX_STA2++; if(USART_RX_STA2>(1024-1))USART_RX_STA2=0;//接收数据错误,重新开始接收 } } } } }
usart2.h
#ifndef __USART2_H #define __USART2_H #include "stm32f10x.h" #include "stdio.h" void USART2_Init(void); void USART2_SendByte(uint8_t ch); void USART2_SendString(char *str); //#define USART_REC_LEN 300 #define EN_USART1_RX 1 extern u8 USART_RX_BUF2[512]; extern u16 USART_RX_STA2; //extern unsigned char res[]; #endif
其他:
其他程序(key led rtc delay sys和库函数)请下载分享链接或自主构建完成。
MAIN
整个系统流程较简单,main函数使用了简单的轮询:
在下面的程序中,Data和DataDp供TM1637的字符组。time是数码管四个位数据的寄存区。
大家还可以结合ESP01S的可联网特性,增加一些自己的功能,例如天气播报(之后可能会增加开源),按键控制局域网中的电器(例如冬天在被窝里幸福地远程关掉卧室灯),设计一个随机音乐小闹钟等。这些东西可以很轻松地通过现有按键,增加一个if程序段即可实现。
#include "stm32f10x.h" //start up中SystemInit记得去掉注释 #include "TM1637.h" #include "delay.h" #include "led.h" #include "rtc.h" #include "key.h" #include "usart.h" #include "usart3.h" #include "usart2.h" #include "MP3.h" #include "ESP01.h" unsigned char Data[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; unsigned char DataDp[]={0xbf,0x86,0xdb,0xcf,0xe6,0xed,0xfd,0x87,0xff,0xef}; uint8_t time[4]={0x3f,0x3f,0x3f,0x3f}; //uint8_t thrh[4]={0x66,0x6d,0x7d,0x07}; extern _calendar_obj calendar; extern unsigned char rec_dat[]; int main() { u8 hour1,hour2,min1,min2; //u8 sec1,sec2; uart_init(115200); //调试接口 delay_init(); RTC_Init(); TM_Init(); led_Init(); key_Init(); USART2_Init(); MP3_Init(); //串口在其内初始化 Uart_SendCMD(0x06,0,0x18); //音量 while(1) { hour1=calendar.hour/10; hour2=calendar.hour%10; min1=calendar.min/10; min2=calendar.min%10; //sec1=calendar.sec/10; //sec2=calendar.sec%10; time[0]=Data[hour1]; time[1]=DataDp[hour2]; time[2]=Data[min1]; time[3]=Data[min2]; TM_Display(time); //读取时间 if(KEY2==0) { LED0_ON; delay_ms(100); Readtime(calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec); //读出时间 while(KEY2==0); delay_ms(10); LED0_OFF; } //网络校时并读时间 if(KEY3==0) { LED0_ON; delay_ms(100); Uart_SendCMD(0x12,0,0x0f); //提醒正在校时 ESP01_Gettime(); delay_ms(300); for(uint16_t i=69;i<512;i++) { USART1_SendByte(USART_RX_BUF2[i]); //调试 } if(USART_RX_BUF2[64]==0x32) { ESP01_Settime(); Uart_SendCMD(0x12,0,0x10); //校时成功 delay_ms(100); while(BUSY!=1); delay_ms(500); Readtime(calendar.w_month,calendar.w_date,calendar.hour,calendar.min,calendar.sec); //读时间 } else { Uart_SendCMD(0x12,0,0x11); //提醒校时失败,检查网络环境 delay_ms(100); while(BUSY!=1); } while(KEY3==0); delay_ms(10); LED0_OFF; }
硬件开源
由于最近比较忙碌,加上硬件还有些缺陷,之后可能会修正一些硬件错误并打板验证后开源,并一起开源建模。(现在就先鸽了,咕咕咕)
当然大家也可以自主进行电路板设计绘制,目前的原理图如下:

注意:图中有一些设计缺陷:
PA15的LED是SWD调试复用接口,而SWD调试是经常使用的,建议去掉。
ESP01S只需要连接VCC,GND,RXD,TXD即可,其他几路可以去掉。
由于焊接工具限制,STM32使用了核心板,在一定程度上破坏了美观度,有能力的可以直接使用C8T6贴片。
TM1637建议使用5V供电,3.3V供电会在一定程度上扰乱C8T6的供电稳定性,所以SW2可以去掉。
软件部分的开源就到这里,一些程序与图片取自网络,归创作者所有,我个人的程序可随意取用,只需标明博客即可,此篇文章仅用于学习交流。
阿韬 STM32个人制作

浙公网安备 33010602011771号