单片机闹钟的设计(自制低成本闹钟)
事情是这样的,想做一个成本很低的时钟练手,再带一个闹钟功能。
准备自己设计电路、外壳,再自己写代码、组装成品。
要实现这样一个时钟,硬件方面我需要用到:
- 查理复用法驱动数码管
- 外接ds1302时钟芯片
- 无源“蜂鸣器”:用来播音乐
- 热敏电阻:用来测量温度
这个时钟还需要兼容:HK32F030M、STM8S003、n76e003at20
最后的成品应该是这样的。
电路原理说明
这是用查理复用法3个io驱动6个LED的原理图。
假设此时P1高,P3低,P2高阻,此时LED6亮,虽然LED1和LED3串联有正向电压,但由于LED6钳位,使得电压不足以同时导通。
若P1高,P2、P3都为低,此时LED1和LED6都亮,都亮度不及上面的状态,因为两个LED的电流都流过P3电阻,P3电阻分压加大。
所以每个限流电阻上并联一个二极管,利用单向导通性,使得电阻在特定电流方向下,两端分压一致,使得同时亮的LED亮度一致。
PCB走线如下
核心算法说明
不是说我代码不开源,而是代码放出来了,即使有100个人下载。然而有80个人看不懂。
那我不如只列出特别的算法,图文并茂地讲解,让更多的人能看懂。
1.ADC超采样
由技术文档可知HK32F030M的ADC有效精度是8bit,如果温度要显示到小数点后1位起码要12bit的ADC,
这怎么办呢?
这时就可以用超采样技术。
第一步,ADC设置成连续转换,EOC转换完成中断
void ADC_init()
{
GPIO_InitTypeDef GPIO_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_ADC_PIN ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GOIO_ADC_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(GOIO_ADC_PORT,GPIO_PinSource6,GPIO_AF_7);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC ,ENABLE);
ADC_DeInit(ADC1);
ADC_StructInit(&ADC_InitStructure);
ADC_ClockModeConfig(ADC1,ADC_ClockMode_SynClkDiv4);//系统时钟4分频,8M
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//开启连续转换
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Backward;//向后扫描
ADC_Init(ADC1,&ADC_InitStructure);
/* ADC1 regular channels configuration */
ADC_ChannelConfig(ADC1, ADC_Channel_1 , ADC_SampleTime_239_5Cycles);
/* Enable EOC interrupt */
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);//转换结束中断使能
/* Configure and enable ADC1 interrupt */
NVIC_InitStructure.NVIC_IRQChannel = ADC1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
ADC_GetCalibrationFactor(ADC1);//ADC校准
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY));
ADC_StartOfConversion(ADC1);
}
第二步,在中断里累加ADC读回来的值(反正后面都要求平均数,不如先加起来)。
这里由于按键与热敏电阻共用同一路ADC,为了按键扫描正常,刷新率取比1000Hz稍大的数。
extern uint16_t ntc_data_13b[2];
void ADC1_COMP_IRQHandler(void)
{
static uint8_t i=0;
if(ADC_GetITStatus(ADC1, ADC_IT_EOC) != RESET)
{
ntc_data_13b[0] =ADC_GetConversionValue(ADC1)>>4;//读取后自动清除EOC标志位
i ;
if(i>=32)//32分频,1041.6Hz
{
i=0;
ntc_data_13b[1]=ntc_data_13b[0];//存入二缓
ntc_data_13b[0]=0;//一缓清零
}
}
}
2.数码管上下移动动画
动画特效一大堆,这里举一个简单一点的,用下移做例子
上图展示的是下移的过程,提取重复的部分,只有一个步骤(上移同理)
#define digital_a 0x01
#define digital_b 0x02
#define digital_c 0x04
#define digital_d 0x08
#define digital_e 0x10
#define digital_f 0x20
#define digital_g 0x40
uint8_t digital_putdown(uint8_t dat)
{
uint8_t temp=0;
temp|=(dat&digital_g)?digital_d:0;//G赋值到D
temp|=(dat&digital_a)?digital_g:0;//A赋值到G
temp|=(dat&digital_b)?digital_c:0;//B赋值到C
temp|=(dat&digital_f)?digital_e:0;//F赋值到E
return temp;//其余没赋值的数据丢弃
}
3.走时误差校准
首先这个DS1302很多人都反馈不准,其实是有诀窍的:
这个网址可以参考下:http://www.51hei.com/bbs/dpj-177015-1.html
下面的代码调时间的。
//输入最小数值-86400
void time_adjust(int32_t sec)
{
uint8_t time_temp[7];
uint8_t i;
for(i=0;i<7;i )
time_temp[i]=bcd2hex(*((uint8_t *)&time_data i));
if(sec<0)//直接借一天做减法
{
sec =86400;
time_temp[5]--;//星期是从1到7,不担心溢出
}
time_temp[0] =sec`;
if(time_temp[0]>=60)
{
time_temp[1] =time_temp[0]/60;
time_temp[0]%=60;
}
time_temp[1] =sec600/60;
if(time_temp[1]>=60)
{
time_temp[2] =time_temp[1]/60;
time_temp[1]%=60;
}
time_temp[2] =sec/3600;
if(time_temp[2]>=24)
{
time_temp[5] =time_temp[2]/24;
time_temp[2]%=24;
time_temp[5]%=7;
time_temp[5]=time_temp[5]?time_temp[5]:7;//将零偏移到7
}
for(i=0;i<7;i )
*((uint8_t *)&time_data i)=hex2bcd(time_temp[i]);
}
由于我这里没有年月日,只有星期,所以轻松一点。不用考虑更多的进制。
和上面网址里的一样,挑一个夜深人静的时候校准。当然校准时有概率会跳过闹钟。但谁会设置午夜凶铃呢?
加一点补充,怕有人不理解自动校准。
比如你的手表一天快了1分钟。当我只观察这个手表时,这个手表走了1天多1分钟时,调慢1分钟,就能和标准时间对上。
当然在校准前一刻快了的59秒。
但每天的累计误差被压缩了。
现在经过我这套算法。
设置好后,一个星期最大相差1秒以内。
一年下来,误差也就一分钟。
4.时间校准方法
每天在同一个固定的时间段来调整时间,先根据第一天的观察来决定要每天偏移多少秒。
输入到AC_DAY的设置里。然后再重新设置时间。
同理,一个星期后再调节AC_WEEK。这样就完成了。
由于校准数据是存在DS1302的内存里的。如果电池没电数据就清空的,建议在PCB上写上校准参数。
外壳制作过程通过数字的槽内壁喷上一层黑漆,再倒入不同颜色的树脂的方法:
黑漆防止“泛光”,白色树脂将led的光线“匀”开,透明树脂将光投射到上层。
遮盖
喷漆 倒胶 擦拭油漆
喷漆前一天熬夜上头忘记拍喷漆和倒胶了,就只留个倒胶图吧。加上滴胶凝固大约1天关于滴胶之前尝试过按1/3比例配置但比例没控制好,重新试了下发现树脂混合时比较稀的时候效果最好。
成品
滴胶挺硬的,两侧泛光的现象几乎没有,但led的光晕开效果不明显,而且正面效果确实不咋地。
数字面板几乎是全黑的,等想做2.0看先灌白色树脂再喷漆效果如何吧。
文章中所有资料的来源如下
最丐条形时钟 - 嘉立创EDA开源硬件平台
如果你认为这篇文章有用的话
欢迎点赞、关注、转发~
我会持续更新优质开源项目
,免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com