本次小制作 前后花了我10天时间 其中连续爆肝(10点起床凌晨4点左右睡觉)6天半
涉及:
STM32:GPIO、UART3、DMA1、TIM3 操作
ESP8266 :SDK编程(入门级)、WiFi配置、STNP服务器设置、UART、定时器、GPIO
万年历PCB板走线(我手动一个个量的 想明白原理图花了一晚上……
数码管5101A(共阳极)
Ds1302
DHT11
这部分内容 当然得写的高大上一点啦!其实这个东西它也不难,有兴趣想动手的可以试一下,很好操作的,除了有点儿废头发……
但是 毕竟有句俗话说的好!
“遇到困难¥%……&#¥% !!!!”
对!没错!自带画面和声音的文字,就是这么diao
我会把整个工程文件都打包附在后面,每部分主要代码也都会在后面说明
想折腾的朋友可以参考一下
家里的万年历是 11年产的康巴丝的万年历,在它服役的这些年中,IC已经坏过一次了,上次在某宝买了同款IC给它强行续了2年的命。这几天突然发现它又罢工了,时间跳回到了11年7月,重复出现3次,以为是IC又坏了,既然这样那就开始动手给它大修一下吧。【后经室友提醒,发现是纽扣电池没电了…并不是IC的问题,不过那都不重要了,好不容易才决定要升级它的】
其实 在学校的时候,就有想过把这个钟给升级成WiFi联网的,只是回家之后一直动力不足,有时间就打游戏了,这波正好赶上了,正好干TND一波。
先来看一下前后对比 硬件方面 升级之前
经过研究发现这货是8-12V交流电供电
功能对比 | 之前的主控 | 我写的32主控 | 说明 |
---|---|---|---|
时间 | √ | √ | 每30分钟对时一次 |
阳历 | √ | √ | |
农历 | √ | √ | 用阳历转农历算法 |
温度 | √ | √ | DHT11 |
湿度 | x | √ | 强行安排* |
闹钟 | √ | x | 个人感觉一个很鸡肋的功能 |
流水鸟叫 | √ | x | &……*@&# |
按键设置 | √ | x | 物联网时代了 砍掉 |
秒闪烁 | √ | x | *LED闪烁会让其他数码管变暗 |
强行安排*本来是没有湿度功能的,那自然没有湿度显示的位置 这里我让温度湿度循环在原来的温度位置上显示 每10秒换一下 因为我家在南方 湿度常年90%以上 所以湿度和温度很好区分
那个流水和鸟鸣*声音是万年历的一个功能 见过的人肯定知道 不懂的我也说不明白 就这样吧 反正很鸡贼
这里阳历转农历算法是用的 博客园@离子 前辈的代码 接触过的这部分的人都知道 阳历算法很容易 但是农历比较复杂 不像阳历那样有规律可循 得依赖天文学家的 推算 因此农历这部分是最后完成的 阳历转农历算法在网上找了好几天 终于找到这个 亲测可用
引用过来后 结构体定义仍用hzj定义 以示敬意
附上链接 农历和阳历互转(c语言)-离子
LED闪烁* 不懂电路 全是暴力推挽输出。 让 LED闪烁 示意秒 结合这个电路,算法上我实现了 但是在实际发现GPIO驱动能力不够 会导致数码管亮度变化特别大 于是砍掉了
调用序列图-这里只放个大概 详细的还得结合程序说去年暑假的时候 自学8266SDK编程 照着XianYu上的卖的8266气象站 照葫芦画瓢 自己做了一个
因此我积累了一点点ESP8266编程基础 这里的8266运行我写的固件 上电自动连接家里的WiFi 连接成功之后 连接SNTP服务器获取时间 之后定时通过UART发送时间
而不是用等待AT指令做32从机
程序流程图上面的图是个大概 顺手玩玩CSDN编辑器新功能而已 具体的当然还是得看流程图!
STM32流程图 *为带注释的项
FLAG0超过24h未置位 重启STM32*
为什么要有这一句呢 因为在程序实际运行中我发现 接收中断运行到一定次数之后 DMA搬运过来的数据永远都只有一个字节 我也不清楚问题出在哪里 这个可以通过复位解决 那显然不是8266的问题 问题肯定出在接收上 但是我找不到问题出在哪里
于是就写了这个 相当于一个看门狗程序吧
为了保证时间准确的无奈之举
ESP8266流程图里面大量用到了中断回调函数 但原理与中断无异 所以我流程图简化为中断函数
关于中断回调函数与ESP8266编程 我看的是B站上的视频 有兴趣学习的可以看看
同样的附上链接 B站 ESP8266 IoT教程
拆机发现 这个万年历使用的数码管为5101A 共阳极
下面是我用表量出来的接线图
段选:一个框内的表示这些数码管的 阴极 a b c d e f g 是连在一起的 颜色与右下角IC位置引脚对应
注:其中月份的十位、天数的十位 比较特殊 只有 1 或者 2 驱动的时候对这部分需要特殊处理
位选:这里相同颜色的框表示这些数码管阳极是连在一起的
有了以上的工作,再来后续的驱动程序设计,着手进行编程
[ 程序 ] 各硬件驱动 8266通信 STM32与DS1302程序员这行(我肯定是个假程序员 本科机电工程 在读每天为了头秃的机械加工头疼) 素来有不要重复造轮子这个说法 ,本着这一精神的指导,我迅速找到了普中科技开发板的配套源码中DS1302的驱动部分 移植了一手 三下五除二的调了一上午。。。。
//引脚定义(这个我能保证绝对原创 hhhhh
#define DS1302_RST PEout(4)
#define DS1302_IO PEout(6)
#define DS1302_IO_R PEin(6)
#define DS1302_CLK PEout(5)
#define GPIO_Pin_RST GPIO_Pin_4
#define GPIO_Pin_IO GPIO_Pin_6
#define GPIO_Pin_CLK GPIO_Pin_5
u8 ACC=0;
u8 TimeDate[7]={0};
extern hjz osolar;
void DS1302_Init(void){
GPIO_InitTypeDef GPIO_InitStructure; //GPIO结构体
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE); //使能PE端口时钟
GPIO_InitStructure.GPIO_Pin = (GPIO_Pin_RST|GPIO_Pin_IO|GPIO_Pin_CLK);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOE, &GPIO_InitStructure); //根据设定参数初始化GPIOD
GPIO_SetBits(GPIOE,GPIO_Pin_RST|GPIO_Pin_IO|GPIO_Pin_CLK); //PE.12 PE.14 输出高
}
/******************************************************************************
函数名称:DS1302_WriteOneByte
函数功能:向DS1302写入一个字符
入口参数:ucData-数据
返回值:无
备注:无
*******************************************************************************/
void DS1302_WriteOneByte(unsigned char ucData)
{
unsigned char i;
ACC = ucData;
DS1302_RST = 1;
for(i=8; i>0; i--)
{
DS1302_IO = ACC&0x01;
DS1302_CLK = 0;
DS1302_CLK = 1; //先写入最低位,上升沿写入
ACC = ACC >> 1;
}
}
/******************************************************************************
函数名称:DS1302_ReadOneByte
函数功能:从DS1302读取一个数据
入口参数:无
返回值:读取的数据
备注:无
*******************************************************************************/
u8 DS1302_ReadOneByte(void)
{
unsigned char i,temp=0;
GPIO_InitTypeDef GPIO_InitStructure;
DS1302_RST = 1;
for(i=8; i>0; i--)
{
ACC = ACC >>1; //相当于汇编中的 RRC
DS1302_IO = 1;
DS1302_CLK = 1;
DS1302_CLK = 0; //下降沿读取,先读最低位
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_IO;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOE, &GPIO_InitStructure); //根据设定参数初始化GPIOE
temp=0;
temp=GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_IO);
// temp=DS1302_IO_R;
ACC = (temp<=7){ //
printf("\n\n\t获取时间失败,请检查DS1302连接\r\n\r\n"); //
return 0; //
} //
/********************************************************************/
return 1;
}
/******************************************************************************
函数名称:DS1302_SetInit
函数功能:设置初始化
入口参数:pClk-初始化数组的指针
返回值:无
备注:无
*******************************************************************************/
void DS1302_SetInit(u8 *pClk)
{
u8 i;
u8 ucAddr = 0x80;
DS1302_WriteOneByteAtAddr(0x8E,0x00); /* 控制命令,WP=0,写操作*/
for(i =7; i>0; i--)
{
DS1302_WriteOneByteAtAddr(ucAddr,*pClk); /* 秒 分 时 日 月 星期 年 */
pClk++;
ucAddr +=2;
}
DS1302_WriteOneByteAtAddr(0x8E,0x80); /* 控制命令,WP=1,写保护*/
printf("\r\n\t已从网络更新时间到本地\r\n");
}
人家的代码 每个函数的说明部分 整的多好 赏心悦目的
我想了想 还是算了吧 不如我的省事儿
这部分的DHT11 代码同样是我 白嫖来的
出处为 技新课堂 ESP8266 IOT教程->时钟温湿计 Demo
本来此处准备放代码的
但是我感觉一大段一大段的代码
放这儿大家可能也懒得看
还是不放了吧 有心人自己去工程文件看吧
路径在
./HARDWARE/dht11.c
ESP8266编程
这里我选择用8脚的ESP01 主要考虑到只需要联网串口功能
用ESP12有点儿浪费
第一次用ESP01编程
发现 烧录 开机 复位都比较麻烦
于是焊了一个电路板 给他烧录用
实践发现下载烧录的过程中 不能长时间通电 不然芯片会特别烫
温度上升会导致烧录失败
ESP8266获取网络时间
这里因为我之前玩过 所以上手还算轻松
这个部分搞懂他的回调函数是关键
这部分教程来自 B站
附上视频链接:P45 物联网教程_43_SNTP
前面的1302 DHT11 包括8266的网络设置 都是比较轻松的 毕竟都是之前接触过的
但是双机通信这块 是之前没接触过的
想了想 还是用串口通信吧 毕竟这个是最简单了
说一下我的方案
STM32打开串口3的接收中断
STM32打开DMA
8266每30分钟串口发送一次时间戳数据
触发中断
DMA将串口外设搬运到内存
之后对数据有效性进行验证
将有效数据经行处理
最后将数据写入1302 完成网络时间更新
这部分主要是对GPIO的操作 定义引脚
小写a b c d e f g 为段
大写A B C D E F 为位
以往接触到的数码管都是共阴极的 要点亮对应的段只需要拉高电平就行了
这次的数码管是共阳极的
我还是按照以往共阴极的办法定义的 只是最后在GPIO输出的时候 按位取反了
这个是我之前在学校玩数码管的时候
针对共阴极的管子写的
可以供大家参考
#define Num_bit_a 0x01
#define Num_bit_b 0x02
#define Num_bit_c 0x04
#define Num_bit_d 0x08
#define Num_bit_e 0x10
#define Num_bit_f 0x20
#define Num_bit_g 0x40
#define Num_bit_dp 0x80
#define Num_0 Num_bit_a|Num_bit_b|Num_bit_c|Num_bit_d|Num_bit_e|Num_bit_f
#define Num_1 Num_bit_b|Num_bit_c
#define Num_2 Num_bit_a|Num_bit_b|Num_bit_g|Num_bit_e|Num_bit_d
#define Num_3 Num_bit_a|Num_bit_b|Num_bit_c|Num_bit_d|Num_bit_g
#define Num_4 Num_bit_b|Num_bit_c|Num_bit_g|Num_bit_f
#define Num_5 Num_bit_a|Num_bit_c|Num_bit_g|Num_bit_f|Num_bit_d
#define Num_6 Num_bit_a|Num_bit_d|Num_bit_c|Num_bit_e|Num_bit_f|Num_bit_g
#define Num_7 Num_bit_a|Num_bit_b|Num_bit_c
#define Num_8 Num_bit_a|Num_bit_b|Num_bit_c|Num_bit_d|Num_bit_e|Num_bit_f|Num_bit_g
#define Num_9 Num_bit_a|Num_bit_b|Num_bit_d|Num_bit_c|Num_bit_f|Num_bit_g
#define Num_A Num_bit_a|Num_bit_b|Num_bit_c|Num_bit_e|Num_bit_f|Num_bit_g
#define Num_b Num_bit_c|Num_bit_d|Num_bit_e|Num_bit_f|Num_bit_g
#define Num_C Num_bit_a|Num_bit_d|Num_bit_e|Num_bit_f
#define Num_d Num_bit_b|Num_bit_c|Num_bit_d|Num_bit_e|Num_bit_g
#define Num_E Num_bit_a|Num_bit_d|Num_bit_e|Num_bit_f|Num_bit_g
#define Num_F Num_bit_a|Num_bit_e|Num_bit_f|Num_bit_g
#define Num_G Num_bit_a|Num_bit_c|Num_bit_d|Num_bit_e|Num_bit_f
#define Num_H Num_bit_b|Num_bit_c|Num_bit_e|Num_bit_f|Num_bit_g
[Again 硬件]电路板引线到GPIO
万事开头难
穿完第一行排线1个小时获取了
两排焊接完,已经是凌晨5点了…
狗命要紧 赶快睡觉
第二天一睡醒 立马上电 看看能不能点亮管子!
一堆乱码…
经过无数次的Debug
终于发现了
原来有一个引脚定义错了…
修改完了
上电!
一切顺利!
NICE 兄dei
可是到了夜里 忽然发现…
13月…emmmm
原来是天数的十位 f段 连接在了 月份 的b c 段上
(农历月份同样存在这个问题)
Debug…好了 这下终于完美了
苦于没有好的农历算法 只好把农历先空着
终于
某天
蹲在厕所拉