描述
<h3 style="line-height:1.8;">1、项目功能介绍</h3>
<hr>
<p style="line-height:1.8;">本项目是一个基于<strong>PY32F002B</strong>单片机的超低功耗牙刷底座, 安装电池后按下轻触开关,底座开始85天倒计时,计时结束后,底座上的 LED 指示灯会以呼吸的方式提醒更换牙刷,有利于培养科学的口腔护理习惯(注意:请在每次倒计时结束后,同时更换牙刷与电池。底座的设计寿命为9~10个月,超出后可能会发生定时不准等情况)</p>
<h3 style="line-height:1.8;">2、开源协议</h3>
<hr>
<p style="line-height:1.8;">转载时请标明原作者,未经原作者允许的情况下,不得将产品量产/商用/售卖</p>
<h3 style="line-height:1.8;">3、软件部分</h3>
<hr>
<p style="line-height:1.8;">单片机 .c代码</p>
<p>#include "py32f0xx_hal.h"</p>
<p>/* -------------------- 定义 -------------------- */<br>#define TARGET_SECONDS (85UL * 24 * 3600) // 85 天</p>
<p>/* -------------------- 全局变量 -------------------- */<br>TIM_HandleTypeDef htim2;</p>
<p>volatile uint8_t system_state = 0; // 0-待机, 1-计时中, 2-LED亮<br>volatile uint32_t seconds_cnt = 0; // 已计秒数<br>volatile uint8_t led_blink_cnt = 0; // LED 闪烁计数器(0~5)</p>
<p>uint8_t key_last = 1; // 上次按键电平(高=释放)<br>uint8_t key_pressed = 0; // 按键是否处于按下状态<br>uint32_t press_duration = 0; // 按键持续按下的秒数</p>
<p>/* -------------------- 函数声明 -------------------- */<br>void SystemClock_Config(void);<br>void GPIO_Init(void);<br>void TIM2_Init(void);</p>
<p>/* -------------------- 主函数 -------------------- */<br>int main(void)<br>{<br> HAL_Init();<br> SystemClock_Config();<br> GPIO_Init();<br> TIM2_Init();</p>
<p> HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断</p>
<p> while (1)<br> {<br> uint8_t key_now = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);</p>
<p> // ------ 按键状态机 ------<br> // 检测下降沿(按下瞬间)<br> if (key_last == 1 && key_now == 0)<br> {<br> key_pressed = 1;<br> press_duration = 0; // 开始计时按下时长<br> }</p>
<p> // 检测上升沿(释放瞬间)<br> if (key_last == 0 && key_now == 1)<br> {<br> key_pressed = 0;<br> // 短按判断:按下时间 < 1 秒(press_duration 只会是 0 或 1)<br> if (press_duration < 1)<br> {<br> // 仅在待机状态启动计时<br> if (system_state == 0)<br> {<br> system_state = 1; // 开始计时<br> seconds_cnt = 0;<br> HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // 关灯<br> }<br> }<br> press_duration = 0; // 清零<br> }</p>
<p> // 长按检测(在亮灯状态下,按下持续 5 秒)<br> if (key_pressed && system_state == 2)<br> {<br> if (press_duration >= 5)<br> {<br> // 复位<br> system_state = 0; // 回到待机<br> seconds_cnt = 0;<br> led_blink_cnt = 0;<br> HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // 关灯<br> key_pressed = 0; // 防止重复触发<br> press_duration = 0;<br> }<br> }</p>
<p> key_last = key_now; // 保存本次状态<br> }<br>}</p>
<p>/* -------------------- 系统时钟(HSI 8MHz) -------------------- */<br>void SystemClock_Config(void)<br>{<br> RCC_OscInitTypeDef RCC_OscInitStruct = {0};<br> RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};</p>
<p> RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;<br> RCC_OscInitStruct.HSIState = RCC_HSI_ON;<br> RCC_OscInitStruct.HSICalibrationValue = 16;<br> RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;<br> HAL_RCC_OscConfig(&RCC_OscInitStruct);</p>
<p> RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;<br> RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;<br> RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;<br> RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;<br> HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);<br>}</p>
<p>/* -------------------- GPIO 初始化 -------------------- */<br>void GPIO_Init(void)<br>{<br> GPIO_InitTypeDef GPIO_InitStruct = {0};</p>
<p> __HAL_RCC_GPIOA_CLK_ENABLE();<br> __HAL_RCC_GPIOB_CLK_ENABLE();</p>
<p> // PA0 按键输入,上拉<br> GPIO_InitStruct.Pin = GPIO_PIN_0;<br> GPIO_InitStruct.Mode = GPIO_MODE_INPUT;<br> GPIO_InitStruct.Pull = GPIO_PULLUP;<br> HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);</p>
<p> // PB0 输出(推挽),驱动 SI2302<br> GPIO_InitStruct.Pin = GPIO_PIN_0;<br> GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;<br> GPIO_InitStruct.Pull = GPIO_NOPULL;<br> GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;<br> HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);</p>
<p> HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // 初始关灯<br>}</p>
<p>/* -------------------- TIM2 初始化(1 秒中断) -------------------- */<br>void TIM2_Init(void)<br>{<br> __HAL_RCC_TIM2_CLK_ENABLE();</p>
<p> htim2.Instance = TIM2;<br> htim2.Init.Prescaler = 8000 - 1; // 8MHz / 8000 = 1kHz<br> htim2.Init.CounterMode = TIM_COUNTERMODE_UP;<br> htim2.Init.Period = 1000 - 1; // 1kHz / 1000 = 1Hz<br> htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;<br> HAL_TIM_Base_Init(&htim2);</p>
<p> HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);<br> HAL_NVIC_EnableIRQ(TIM2_IRQn);<br>}</p>
<p>/* -------------------- TIM2 中断处理 -------------------- */<br>void TIM2_IRQHandler(void)<br>{<br> HAL_TIM_IRQHandler(&htim2);<br>}</p>
<p>/* -------------------- 定时器溢出回调(每秒执行) -------------------- */<br>void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)<br>{<br> if (htim->Instance == TIM2)<br> {<br> // 1. 更新按下持续时间(如果按键仍按着)<br> if (key_pressed)<br> {<br> press_duration++;<br> }</p>
<p> // 2. 倒计时逻辑<br> if (system_state == 1) // 计时中<br> {<br> seconds_cnt++;<br> if (seconds_cnt >= TARGET_SECONDS)<br> {<br> system_state = 2; // 进入 LED 亮灯状态<br> led_blink_cnt = 0; // 重置闪烁计数器<br> }<br> }</p>
<p> // 3. LED 闪烁控制(亮灯状态)<br> if (system_state == 2)<br> {<br> led_blink_cnt++; // 0~5 循环<br> if (led_blink_cnt >= 6) led_blink_cnt = 0;</p>
<p> // 亮 1 秒(当计数器为 5 时亮)<br> if (led_blink_cnt == 5)<br> HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);<br> else<br> HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);<br> }<br> }<br>}</p>
<p style="line-height:1.8;"> </p>
<h3 style="line-height:1.8;">4、大赛LOGO验证</h3>
<hr>
<p style="line-height:1.8;"> </p>
<h3 style="line-height:1.8;">5、成品展示</h3>
<hr>
<p style="line-height:1.8;">(成品展示预计在8月26日发布,敬请期待……)</p>
评论(0)