开源——基于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个人制作

posted @ 2020-09-14 17:47  阿韬  阅读(2409)  评论(4)    收藏  举报