八月正值盛夏,室外烈日蝉鸣,家中清凉舒爽,吃着空调吹着西瓜,觉着无比惬意,然而,一阵急促的电话铃声将我从空调房中喊出。原来是外卖抵达,我在小区门口抱着大包小包的炸鸡汉堡和饮料,满头大汗的冲回家中,又回到了那个清凉美好的世界里…………等等是不是哪里不对?!管他呢,我直接炫完外卖,呼呼大睡,醒了就继续游戏世界。
到了晚上,我意识到了问题,中午我顶着毒辣的太阳拿着外卖,赶着回屋,然而大门敞开却被我遗忘,就这样过了一个下午,好在现在很和平,并没有歹人进屋,想到这个心中一阵恶寒。
于是我便开始计划,是不是可以做出一个什么报警装置,要是忘记关门,便可以及时发出提醒!我称之为“忘记关门终结者”!!
方案设计
- 芯片选型考虑
为了尽量减少来回拆卸,我希望待机时间尽可能够长,那就需要电路上的总体功耗非常低
使用低功耗的STM32C031,从数据手册可得在待机状态下电流消耗非常小,16MHz主频时可以兼顾外设速度和整体功耗。
屏幕的选择我也选了JDI的低功耗memory lcd屏幕,没有屏幕背光,只控制光线经过不同液晶像素,使用环境光作为照明显示,既能保证显示效果又能降低功耗,不刷新电流4uA 刷新180uA,而且可以在程序控制,只在报警时初始化屏幕并刷新,剩下时间可以直接把屏幕彻底关闭,进一步增加了待机时间。
此外我希望在每次开门时自动触发开启电路并计时,所以我加入了一个磁敏传感器,在没有磁场时输出低电平,低电平又可以关闭DCDC电路,完成关机;在开门后因为没有磁场,传感器输出高电平,开启DCDC,完成电路供电,MCU上电之后自动开始计时。
由于每次关门后系统就完全掉电,所以我需要记录下定时的设计,每次上电后自动恢复先前的定时,在尽量不增加电路复杂度的前提下,增加了一个封装很小的eeprom,在存储定时参数之余,还可以记录一些其他的数据,比如未来计划加入的“乐谱功能”增加报警铃声的趣味性。(在当前设计下略微复杂,因为C031不支持USB功能,所以外部写入信息没有那么便利,未来可以换用其他支持USB通信的主控芯片)
- 电路设计
以上就是对功能的设计,接下来采购芯片并设计电路图,使用KiCad进行设计,发现软件内集成了很多常见芯片的符号和封装,甚至是STM32下面的众多系列的各种后缀都有记录在内,所以极大的方便了制作符号与封装库流程,对于特别新的型号也有类似芯片,可以拿出来复用或略作修改。
如图,电路分为主控STM32C031、用于显示的LCD接口、用于调节参数的多向按键、电源管理电路包括锂电池充电和系统供电转换芯片、存储数据的eeprom,报警的蜂鸣器以及检测是否关门的核心器件——磁敏开关。
使用DCDC芯片产生3.3V的芯片工作电压,在查找资料时发现了一颗眼前一亮的芯片,来自TI的TPS82130这并不是一颗芯片,而是连封装都变得和一般芯片不一样,它集成电感器,提升电路效率(可达95%)的同时减少了外围电路,这样设计电路时只用外围加入反馈电阻即可。
磁敏芯片,需要使用全极性的,这样不用区分靠近的磁铁是N还是S极,下图是输出曲线,可以发现恰好,这个芯片是在有磁场时输出低电平,没有磁场时输出高电平,这与开启电源的逻辑正好相同。
封装的选择我使用了直插型,可以焊接在电路上之后还留有一定的引脚长度,可以手动调整到最灵敏的方向。为了尽可能减少来回拆装充电,降低待机状态下的漏电流,所以我在输出管脚上加了一个非常大的电阻,保证顺利开机也能降低漏电。
锂电池的充电部分使用了常见的4056,加上限流电阻就可以使用。
从锂电池出来的供电,直接接到了DCDC芯片上,只要芯片的EN没有启动,漏电流就很低,但如果真的要很长时间都不用,还是有可能将电池放空的,这样并不好,于是在供电上串联了一个开关,默认开启就行,长期保存时,可以调整到关闭,断开电路就完全没有功耗了。
屏幕的符号和引脚定义就略微麻烦一些,购买的店铺并没有相关的资料,好在有别人的开源项目可以参考,直接查找JDI memoryLCD就能找到管脚定义,如图是屏幕的接口。
设计之初就希望能有办法实现离线调整参数,在板子上加入多个按键又略显臃肿,所以找到了这样的三向按键,拨动和按下,对应了三个输入。
使用无源蜂鸣器就需要外加交流信号,使用MOS作为开关,将芯片的PWM输出引脚接入
- PCB设计
由于需要检测磁场,所以将传感器放置在板子边缘,其他器件尽可能紧凑放置即可,最后使用GND层铺铜。
- 代码实现
我希望核心的消耗非常小,所以在没有任务的状态,可以将芯片设置为休眠,并且可以随时从中断、唤醒、调试中恢复。
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_SPI1_Init();
MX_ADC1_Init();
MX_TIM1_Init();
MX_TIM16_Init();
MX_TIM17_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);
HAL_Delay(5);//测试蜂鸣器
HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1);
DisplayStartupSequence();
HAL_I2C_Mem_Read(&hi2c1, I2C_ADDR_24C02, 0, I2C_MEMADD_SIZE_8BIT, &DelayTime, 1, 1000);//读取报警时长
HAL_TIM_Base_Start_IT(&htim1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
__WFI();//这里在初始化结束后 直接休眠
}
/* USER CODE END 3 */
}
LCD的刷新我使用了硬件SPI通信这样可以使用DMA辅助传输,在RAM中开辟一个能存放所有像素的缓存数组,程序中开启DMA循环发送模式,这样绘图就分成了两个独立部分:准备像素和自动刷新。
void DisplayUpdate()
{
if(wTransferState == TRANSFER_COMPLETE)
{
HAL_SPI_Transmit_DMA(&hspi1, &BufferDMA, sizeof(BufferDMA)/sizeof(uint16_t));
wTransferState = TRANSFER_WAIT;
}
}
最后是按键和报警部分,在定时中断里读取按键,并判断是否到达预定的报警时间
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(htim);
if (&htim1 == htim)
{
Get_Key_State(Key);
if(Get_Long(K_O))
{
KeyState = 1;
stop_flag = 1;
}
if(0==Key[K_O].State && KeyState)
{
KeyState = 0;
BSP_LCD_DrawCircle(36, 72, 30, 0&0x07);
stop_flag = 1;
}
if (stop_flag == 0)
{
if(DelayTime_ms < DelayTime*100)
{
DelayTime_ms++;
}
else
{
HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);
}
}
else if (stop_flag == 1)
{
HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1);
stop_flag = 2;
}
}
心得与展望
现在这个小板子已经牢牢粘在我的门框上了,每天开门的时候它都会被唤醒,守卫家里大门的安全,圆满实现了它的使命。
然而此次也有遗憾,本次设计中使用的STM32C031不支持USB功能,所以没法使用USB连接到电脑进行调整参数,但这也给我未来对这次DIY进行升级埋下了种子。希望在之后能深入学习USB通信,实现更多的功能。
最后,致谢本次活动的主办方,让我有机会实现对身边物体的改造。