描述
<h1><strong>版本更新说明</strong></h1>
<ul>
<li><strong>V0.1</strong> 立项,调光功能完成</li>
<li><strong>V0.2</strong> 引入档位关联的温度控制</li>
<li><strong>V0.3</strong> 改用PID控制</li>
<li><strong>V0.4</strong> 平滑PID温控</li>
<li><strong>V1.0</strong> 首个完成版,开关机功能引入</li>
<li><strong>V1.1</strong> 修改上电时序,修复若干()bug,现版本
<strong>注意:图片不是最新的,PCB版本号独立,修复开关机问题炸了我五块板子,彩蛋是感谢ME6203这个美丽到爆炸的LDO。若干/bug里面也能有违禁词?</strong>
<hr>
<h1><strong>一、项目功能介绍</strong></h1>
<h3>1、核心功能概述</h3>
<p>可拓展小电包-手电模块-控制核心板
这是一个手电项目的核心板,实现了受按键控制的双路PWM输出,用以控制LED恒流模块,以及通过NTC实现的温度检测及与之对应的风扇PWM控制,为LED模块提供温度管理。
其余可能的应用:加热台、风扇控制等。</p></li>
</ul>
<p><img src="https://image.lceda.cn/oshwhub/pullImage/81c98f080c2b443ea5c696014665c424.png" alt="7B932AB2F481FF918C3747F1E1D8B6FA.png"></p>
<h3>2、LED调光控制</h3>
<ul>
<li>双通道独立控制:
<ul>
<li>通道A/B独立控制,支持单通道或双通道同时操作</li>
<li>三种工作模式:仅A通道、仅B通道、双通道同步</li>
</ul></li>
<li>丝滑调光功能:
<ul>
<li>短按:±10%占空比调节(20kHz PWM)</li>
<li>长按:无极调光(每60ms ±1%)</li>
</ul></li>
<li>状态记忆功能:
<ul>
<li>自动保存各通道亮度设置(不含掉电保存)</li>
<li>模式切换时无缝恢复历史状态</li>
</ul></li>
</ul>
<h3>3. 温度控制系统</h3>
<ul>
<li>温度监测:
<ul>
<li>10KΩ NTC温度传感器(B=3950)</li>
<li>基于内部ADC电压检测</li>
</ul></li>
<li>风扇控制:
<ul>
<li>基于增量式PID</li>
<li>带调整率限制平滑输出</li>
</ul></li>
</ul>
<h3>4. 用户交互系统</h3>
<ul>
<li>三按键简洁操作:
<ul>
<li>KEY1:增加亮度(短按+10%占空比、长按每60ms+1%)</li>
<li>KEY2:减少亮度(短按-10%占空比、长按每60ms-1%)</li>
<li>KEY3:短按模式切换/长按开关机</li>
</ul></li>
</ul>
<h1><strong>二、项目属性/开源协议</strong></h1>
<p>项目首次公开,本人原创,转载商用请联系
仅开源部分源代码和一个可供烧录的固件
使用CC BY-NC-SA 4.0协议
商业应用请联系作者,无许可的商业应用会被追责</p>
<h1><strong>三、硬件部分</strong></h1>
<p>此部分乏善可陈
使用si2300和2301两颗mos及两颗二极管构建了开关机电路
使用ME6203这颗LDO将不高于40V的电压降压至3.3V
10K的NTC(B=3950)与另一颗10K电阻构成分压电路检测温度
按键板与核心板分离便于设计结构
提供一体的PCB以节约打样时间及费用
警告:因为SI2300与2301耐压仅支持到20V,故请勿将此方案用于输入电压高于20V的场景,可更换MOS及LDO支持更高的电压
最新的硬件版本修复了此问题,输入电源先经过LDO防止炸穿单片机,输出部分通过专门的板载低侧mos驱动输出较高的电压,或使用0R电阻跳过驱动芯片,将驱动集成在电源管理模块端(后者是最后采用的方案)</p>
<h1><strong>四、软件部分</strong></h1>
<h3>1. 系统架构</h3>
<pre><code class="language-c">main.c
├── 初始化配置
│ ├── 时钟系统配置(64MHz)
│ ├── GPIO初始化(按键/PWM/电源)
│ ├── 定时器配置(TIM1/TIM3/TIM14)
│ └── ADC初始化(温度采样)
├── 主循环
│ └── 电源保护检测
└── 中断系统
├── TIM14中断(10ms按键扫描)
└── PID计算和输出</code></pre>
<h3>2. 核心算法实现</h3>
<h4>PID算法</h4>
<pre><code class="language-c">// 初始化PID控制器
void PID_Init(PID_Controller* pid, float setpoint, float Kp, float Ki, float Kd, float min, float max, float rate_max) {
pid->setpoint = setpoint;
pid->Kp = Kp;
pid->Ki = Ki;
pid->Kd = Kd;
pid->output_min = min;
pid->output_max = max;
pid->output_rate_max = rate_max;
pid->prev_error = 0.0;
pid->prev_error2 = 0.0;
pid->prev_output = 0.0;
}
// 增量式PID计算函数
float PID_Calculate(PID_Controller* pid, float measured_value) {
// 1. 计算误差 (保持您原来的定义)
float error = measured_value - pid->setpoint;
// 2. 计算增量 (根据增量式PID公式: Δu = Kp*(e[k]-e[k-1]) + Ki*e[k] + Kd*(e[k]-2e[k-1]+e[k-2]))
float delta_output = 0.0;
delta_output += pid->Kp * (error - pid->prev_error); // 比例项
delta_output += pid->Ki * error; // 积分项
delta_output += pid->Kd * (error - 2 * pid->prev_error + pid->prev_error2); // 微分项
// 3. 更新历史误差
pid->prev_error2 = pid->prev_error;
pid->prev_error = error;
// 4. 计算本次的目标输出值
float target_output = pid->prev_output + delta_output;
// 5. 对目标输出进行限幅
if (target_output > pid->output_max) {
target_output = pid->output_max;
} else if (target_output < pid->output_min) {
target_output = pid->output_min;
}
// 6. 应用输出变化率限制
float final_output = target_output; // 先假设最终输出等于目标输出
// 计算允许的最大和最小输出(基于上一次输出和变化率限制)
float max_output_allowed = pid->prev_output + pid->output_rate_max;
float min_output_allowed = pid->prev_output - pid->output_rate_max;
// 如果目标输出超出了变化率允许的范围,则将其限制在边界上
if (target_output > max_output_allowed) {
final_output = max_output_allowed;
} else if (target_output < min_output_allowed) {
final_output = min_output_allowed;
}
// 确保最终输出仍在绝对限幅范围内(二次保护)
if (final_output > pid->output_max) final_output = pid->output_max;
if (final_output < pid->output_min) final_output = pid->output_min;
// 7. 保存本次输出,用于下一次计算
pid->prev_output = final_output;
return final_output;
}</code></pre>
<h4>LED通道控制</h4>
<pre><code class="language-c">// KEY3短按处理(通道切换)
void Handle_Key3_Short(void) {
// 切换到下一个通道
current_channel = (current_channel == CH_BOTH) ? CH_A : (current_channel + 1);
// 根据通道模式更新PWM输出
switch(current_channel) {
case CH_A:
saved_dutyB = dutyB; //保存B的占空比
saved_dutyA = dutyA; //保存A的占空比
dutyB = 0; //关闭通道B
dutyA = saved_dutyA; //恢复通道A
break;
case CH_B:
saved_dutyA = dutyA;
dutyA = 0;
dutyB = saved_dutyB;
break;
case CH_BOTH:
// 恢复A通道的保存值
dutyA = saved_dutyA;
break;
}
// 更新PWM输出
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, dutyA * 32);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, dutyB * 32);
}</code></pre>
<h4>按键扫描函数</h4>
<pre><code class="language-c">// 按键扫描函数(在TIM14中断中调用)
void Key_Scan(void) {
for(int i = 0; i < 3; i++) {
uint8_t current = HAL_GPIO_ReadPin(keys[i].port, keys[i].pin);
switch(keys[i].state) {
case 0: // IDLE状态
if(current == GPIO_PIN_RESET) { // 按键按下
keys[i].state = 1; // 进入DEBOUNCE状态
keys[i].press_timer = 0;
}
break;
case 1: // DEBOUNCE状态
if(++keys[i].press_timer > 2) { // 20ms消抖
keys[i].state = (current == GPIO_PIN_RESET) ? 2 : 0; // PRESSED或IDLE
}
break;
case 2: // PRESSED状态
if(current == GPIO_PIN_SET) { // 按键释放
if(i == 2) Handle_Key3_Short(); // KEY3短按
else Handle_ShortPress(i); // KEY1/KEY2短按
keys[i].state = 0; // 回到IDLE
}
else {
// 长按判断:KEY1/KEY2为1秒(100*10ms),KEY3为3秒(300*10ms)
uint16_t threshold = (i == 2) ? 300 : 100;
if(++keys[i].press_timer > threshold) {
Handle_LongPress(i);
keys[i].state = 3; // 进入LONG_PRESS状态
}
}
break;
case 3: // LONG_PRESS状态
if(current == GPIO_PIN_SET) { // 按键释放
keys[i].state = 0; // 回到IDLE
}
else if(i != 2) { // 仅KEY1/KEY2有无极调光
// 每60ms调整一次 (10ms中断 * 6 = 60ms)
if(keys[i].press_timer % 6 == 0) {
Handle_ContinuousAdjust(i);
}
keys[i].press_timer++;
}
break;
}
}
}
</code></pre>
<h4>温度计算</h4>
<pre><code class="language-c">float Read_Temperature(void) {
//获取ADC值
uint32_t adc_value = HAL_ADC_GetValue(&hadc1);
//计算电阻值
float R_ntc = ((4095.0f - adc_value) / adc_value) * 10000.0f;
// Steinhart-Hart方程计算温度 (B=3950)
float T0 = 25.0f + 273.15f; // 25°C in Kelvin
float B = 3950.0f;
float R0 = 10000.0f; // NTC在25°C时的电阻值
float steinhart = logf(R_ntc / R0) / B + 1.0f / T0;
float temp_kelvin = 1.0f / steinhart;
return temp_kelvin - 273.15f; // 返回摄氏度
}</code></pre>
<h3>3. 关键设计特点</h3>
<ul>
<li>往tim14中断里塞塞塞,有按键扫描、PID计算和开机计时</li>
<li>较为清晰的函数封装,代价是注释写爽了
<h1><strong>五、复刻注意</strong></h1>
<p>注意:复刻项目时务必注意mos耐压,防止过压烧穿单片机
目前版本并无问题,以下再次重复注意点</p>
<p>输出部分通过专门的板载低侧mos驱动输出较高的电压,或使用0R电阻跳过驱动芯片,将驱动集成在电源管理模块端(后者是最后采用的方案)</p>
</li>
</ul>
<p><strong>详细的装配教程会在后续专门的手电工程开源,敬请期待哦~</strong></p>
<h3>1. 烧录:本项目使用SW烧录,需购买ST-link烧录器,依次链接以下引脚</h3>
<ul>
<li>GND-GND</li>
<li>5V-PWR</li>
<li>SWDIO-DIO</li>
<li>SWCLK-CLK</li>
</ul>
<h3>2.焊接</h3>
<p>注意STM32G030F6P6引脚比较密集,不要连锡,上电前排查短路情况</p>
<h1>六、大赛LOGO及演示视频</h1>
<p>请见附件及图片</p>
<p><img src="https://image.lceda.cn/oshwhub/pullImage/cbbfec07247e440d9c4aee7be04ee4a3.png" alt="1EA8DD147D4B6FFE0FC4D7E17E4FC9F7.png"></p>
评论(0)