赞
踩
本次小制作 前后花了我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编辑器新功能而已 具体的当然还是得看流程图!
*为带注释的项
FLAG0超过24h未置位 重启STM32*
为什么要有这一句呢 因为在程序实际运行中我发现 接收中断运行到一定次数之后 DMA搬运过来的数据永远都只有一个字节 我也不清楚问题出在哪里 这个可以通过复位解决 那显然不是8266的问题 问题肯定出在接收上 但是我找不到问题出在哪里
于是就写了这个 相当于一个看门狗程序吧
为了保证时间准确的无奈之举
里面大量用到了中断回调函数 但原理与中断无异 所以我流程图简化为中断函数
关于中断回调函数与ESP8266编程 我看的是B站上的视频 有兴趣学习的可以看看
同样的附上链接 B站 ESP8266 IoT教程
拆机发现 这个万年历使用的数码管为5101A 共阳极
下面是我用表量出来的接线图
段选:一个框内的表示这些数码管的 阴极 a b c d e f g 是连在一起的 颜色与右下角IC位置引脚对应
注:其中月份的十位、天数的十位 比较特殊 只有 1 或者 2 驱动的时候对这部分需要特殊处理
位选:这里相同颜色的框表示这些数码管阳极是连在一起的
有了以上的工作,再来后续的驱动程序设计,着手进行编程
程序员这行(我肯定是个假程序员 本科机电工程 在读每天为了头秃的机械加工头疼) 素来有不要重复造轮子这个说法 ,本着这一精神的指导,我迅速找到了普中科技开发板的配套源码中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)|ACC; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_IO; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz GPIO_Init(GPIOE, &GPIO_InitStructure); //根据设定参数初始化GPIOD } return(ACC); } /****************************************************************************** 函数名称:DS1302_WriteOneByteAtAddr 函数功能:在DS1302的指定位置写入一个数据 入口参数:ucAddr-地址 ; ucData-数据 返回值:无 备注:无 *******************************************************************************/ void DS1302_WriteOneByteAtAddr(u8 ucAddr, u8 ucData) { DS1302_RST = 0; DS1302_CLK = 0; DS1302_RST = 1; DS1302_WriteOneByte(ucAddr); DS1302_WriteOneByte(ucData); DS1302_CLK = 1; DS1302_RST = 0; DS1302_IO = 0; } /****************************************************************************** 函数名称:DS1302_ReadOneByteAtAddr 函数功能:在指定的位置读取一个数据 入口参数:ucAddr-地址 返回值:读取的数据 备注:无 *******************************************************************************/ u8 DS1302_ReadOneByteAtAddr(u8 ucAddr) { unsigned char ucBackValue; DS1302_RST = 0; DS1302_CLK = 0; DS1302_RST = 1; DS1302_WriteOneByte(ucAddr); ucBackValue = DS1302_ReadOneByte(); DS1302_CLK = 1; DS1302_RST = 0; return(ucBackValue); } u8 DS1302_Get_Time(void){ //从DS1302读取时间--转码--存入数组中 u8 year=0,mon,day,week,hour,min,sec; year=DS1302_ReadOneByteAtAddr(ADDR_YEAR|DS1302_READ); //读取 年 mon=DS1302_ReadOneByteAtAddr(ADDR_MONTH|DS1302_READ); //读取 月 day=DS1302_ReadOneByteAtAddr(ADDR_DAY|DS1302_READ); //读取 日 week=DS1302_ReadOneByteAtAddr(ADDR_WEEK|DS1302_READ); //读取 周 hour=DS1302_ReadOneByteAtAddr(ADDR_HOUR|DS1302_READ); //读取 时 min=DS1302_ReadOneByteAtAddr(ADDR_MIN|DS1302_READ); //读取 分 sec=DS1302_ReadOneByteAtAddr(ADDR_SEC|DS1302_READ); //读取 秒 /********************************************************************/ //BCD转十进制 // //嗯!这个 原创 + 1 // TimeDate[Data_Year]=year/16*10+year%16; // TimeDate[Data_Week]=week; // TimeDate[Data_Mon]=mon/16*10+mon%16; // TimeDate[Data_Day]=day/16*10+day%16; // TimeDate[Data_Hour]=hour/16*10+hour%16; // TimeDate[Data_Min]=min/16*10+min%16; // TimeDate[Data_Sec]=sec/16*10+sec%16; // // //阳历转阴历函数参数赋值 // osolar.year = TimeDate[Data_Year]+2000; // osolar.month = TimeDate[Data_Mon]; // osolar.day = TimeDate[Data_Day]; // if(week>=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
这里我选择用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
万事开头难
穿完第一行排线1个小时获取了
两排焊接完,已经是凌晨5点了…
狗命要紧 赶快睡觉
第二天一睡醒 立马上电 看看能不能点亮管子!
一堆乱码…
经过无数次的Debug
终于发现了
原来有一个引脚定义错了…
修改完了
上电!
一切顺利!
NICE 兄dei
可是到了夜里 忽然发现…
13月…emmmm
原来是天数的十位 f段 连接在了 月份 的b c 段上
(农历月份同样存在这个问题)
Debug…好了 这下终于完美了
苦于没有好的农历算法 只好把农历先空着
终于
某天
蹲在厕所拉
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。