基于STM32的双轴太阳能追日系统设计:硬件选型到代码实现全解析

基于STM32的双轴太阳能追日系统设计:硬件选型到代码实现全解析 简介面向嵌入式与物联网学习者的太阳能电池板追日光跟踪系统设计资料包以STM32单片机为核心控制器系统涵盖光敏传感器检测、日晷算法位置推算、步进电机PWM驱动以及角度闭环调整等完整实现路径适合毕业设计、课程设计或希望深入理解微控制器控制系统的自学者。资源共365个文件压缩包约84MB主体为C语言源码.c/.h与Keil工程文件.uvproj/.uvopt同时包含原理图.schdoc、Word/PDF设计文档、MP4操作演示视频以及JPEG图片素材可支撑从原理图阅读、代码编译下载到实物调试的完整流程。已有6053人学习资料热度较高且文件目录层级清晰便于按模块查找。借助源码、文档与视频可掌握STM32的ADC采样、PWM输出、步进电机控制策略以及防风稳定性、温度影响和电源管理等工程化细节包内还附有硬件选型指南、调试技巧和冗余传感器设计思路能有效帮助学习者规避常见问题缩短项目落地周期。 做太阳能追日系统这几年见过不少方案从纯模拟电路比较器搭的简易追踪器到带云台、GPS、光线传感器的复杂闭环五花八门。但如果你现在正准备做毕业设计、电子设计竞赛或者单纯想给自己家小功率光伏板加个“向日葵”功能STM32 方案依然是最平衡的选择价格便宜、资料多、调试工具成熟而且性能远超任务需求。这篇就完整拆解一套基于 STM32 的太阳能电池板双轴追光系统从硬件选型、电路设计到核心代码和常见坑一次讲清楚。1. 追光系统整体设计思路1.1 为什么要做追日跟踪固定安装的太阳能板一天之中只有正午前后那段时间阳光接近垂直入射其余时间阳光斜照发电效率打折很厉害。根据我的实测数据在晴朗天气下单轴追光系统比固定式平均能提升 15%~25% 的发电量双轴追光在低纬度地区甚至可以到 30% 以上。追日系统的本质就是让光伏板始终尽量正对太阳保证光线垂直入射。这个项目用到的核心器件很简单ST 公司的 STM32 单片机作为主控、四个光敏电阻组成光照检测阵列、两个舵机组成双轴执行机构。系统运行时主控通过 ADC 不断采集四路光强信号判断当前太阳偏离方向然后输出 PWM 给舵机把光伏板转到光照最强的位置循环往复实现全天候自动追踪。这类项目的目标人群很广电子信息、自动化专业的毕业生准备蓝桥杯或电赛的在校生还有想在自家阳台搞小功率光伏充电的爱好者。它的知识密度适中涉及 GPIO、ADC、定时器 PWM、传感器采样、闭环控制逻辑几乎覆盖了 STM32 入门到进阶的核心外设做完一个项目等于把常用外设全练了一遍。1.2 单轴还是双轴追光系统分为单轴和双轴两种结构。单轴系统只有一个旋转自由度通常是水平方向从东到西跟踪太阳双轴系统多了一个俯仰自由度既能水平旋转又能上下调节倾角。我的建议是如果做毕业设计或者想体现系统完整性直接上双轴。双轴的机械结构并不比单轴复杂太多——两个舵机、一个支架成本多不了几十块钱但控制逻辑和调试空间大很多论文和演示效果也更好。单轴的优势在于结构简单、功耗低适合入门练手或者太阳能板面积特别大的场景。电源方面也要考虑进去追光系统本身的功耗由舵机主导SG90 这种微型舵机在转动时峰值电流 500mA 左右静态保持 10mA 上下STM32 开发板本身功耗可以忽略。整套系统用 5V / 2A 的 USB 供电就能跑得很稳如果现场不方便接电源用 18650 电池组升压到 5V 也可以。1.3 主控为什么选 STM32F103C8T6市面上做追光的主控很多51 单片机能做Arduino 也能做甚至纯硬件用运放比较器也能实现但我依然推荐 STM32F103C8T6。原因有三点第一片内 ADC 是 12 位的分辨率比 51 外挂的 8 位 ADC 高不少追光角度精度有保障第二片内定时器资源丰富可以同时输出多路 PWM控制水平舵机和俯仰舵机互补干扰第三3.3V 供电、主频 72MHz运行这类轻量级控制算法绰绰有余而且现在市价十几块钱性价比极高。特别是如果你将来还想扩展功能比如加上 OLED 显示屏、蓝牙远程监控、K210 视觉模块寻光STM32 的 USART、I2C、SPI 接口都能撑得住。选择这个平台后续升级空间很大。2. 硬件选型与电路细节2.1 光照检测方案光敏电阻阵列 vs 光敏传感器模块追光系统的“眼睛”是光照传感器这部分方案选型直接决定跟踪精度。最常用的是光敏电阻阵列。四个光敏电阻分别朝东、西、南、北或者简化成左、右、上、下安装中间用不透光的隔板隔开。当太阳偏东时东侧的光敏电阻受光更强阻值变小对应的分压点电压变化STM32 通过 ADC 读取电压值就能判断光线来自哪个方向。相比之下光敏电阻阵列成本极低单个电阻几分钱电路就是简单的电阻分压很适合课程设计和毕设。缺点是光敏电阻响应速度较慢但对追日这种每秒变化极其缓慢的场景完全够用。另一种方案是使用集成的环境光传感器模块比如 BH1750 或 OPT101。这些模块通过 I2C 输出数字光照强度无需 ADC精度更高抗干扰也更好。缺点是价格高一些而且程序需要处理 I2C 时序。如果作品想显得更“现代化”可以用这种方案但我个人建议还是光敏电阻阵列起步把原理吃透之后再升级。注意光敏电阻分压电路的方向很容易搞反。光敏电阻受光增强时阻值下降如果光敏电阻接在 VCC 和 ADC 采样点之间、固定电阻接在采样点和 GND 之间那么光照越强 ADC 读数越低注意区分。2.2 执行机构舵机、减速电机与供电执行机构的选择同样关键。微型追光系统我推荐 SG90 舵机价格便宜、控制简单50Hz PWM 信号就能精确控制角度。水平旋转舵机承受的扭矩较大最好选 MG995 / MG996R 这类金属齿轮舵机俯仰舵机负载轻SG90 就够了。如果你做的是大型光伏板那舵机就不合适了必须换成带减速箱的直流电机加限位开关甚至步进电机加驱动。舵机无法精确反馈位置的高级需求但是对课程设计和一般演示是足够的。供电是新手最容易忽略的坑。舵机启动瞬间电流很大如果用 STM32 开发板的 3.3V 或 5V 直接给舵机供电LCD 花屏、ADC 跳变、程序反复复位都是家常便饭。正确的做法是舵机单独供电电源地和开发板共地。2.3 引脚分配与连接注意事项我这次用的引脚分配如下供参考功能引脚说明水平舵机 PWMPA0TIM2_CH150Hz 脉冲信号俯仰舵机 PWMPA1TIM2_CH250Hz 脉冲信号光敏电阻 1东PA2ADC1_IN2分压点上拉至 3.3V光敏电阻 2西PA3ADC1_IN3分压点上拉至 3.3V光敏电阻 3上PA4ADC1_IN4分压点上拉至 3.3V光敏电阻 4下PA5ADC1_IN5分压点上拉至 3.3V系统指示灯PB0调试定位用需要注意几个细节光敏电阻分压网络供电最好用 3.3V因为 STM32 的 ADC 参考电压就是 3.3V如果分压网络用 5V超过 3.3V 的区间读出来全是满量程白白浪费分辨率。如果确实用了 5V 供电需要外加分压电阻限幅或用运放做电平转换。舵机信号线接 STM32 引脚时如果是 5V 供电的舵机最好在信号线上串一个 1kΩ 限流电阻避免信号线电流过大把引脚打坏。开发板的地和舵机电源地必须连在一起否则 PWM 信号没有公共参考地舵机会乱抖。3. 软件核心逻辑与代码实现3.1 追光算法的核心思路追光算法不需要太复杂的数学模型核心就是一句话比较四个方向的光强朝光强的方向转。原理跟向日葵一样哪个方向光强就往哪转。具体实现时算法分三步用 ADC 采集四个光敏电阻分压点的电压值得到东、西、上、下四路光照数据。比较东和西如果东路光强明显大于西路水平舵机向东转一个角度反之向西转。比较上和下如果上路光强明显大于下路俯仰舵机向上转反之向下转。这个简单的逻辑需要加两个关键优化一个是“死区”两个方向的光强差别不大时舵机不动作防止在平衡位置反复抖动另一个是“步进角”每次只转一小步而不是直接转到极限角度防止超调。死区的设置是算法的关键经验。我实测过如果死区设得太小ADC 差值小于 10云台在太阳移动缓慢的正午时段会持续高频微调舵机嗡嗡响电量消耗也大如果死区太大大于 100光线偏离 10 度以上才能触发动作跟踪滞后明显。比较合理的值是 15~30我在代码里用的 20。3.2 ADC 采集与均值滤波STM32 的 ADC 是 12 位的理论上能分辨 4096 级光强变化但实际使用中光敏电阻的输出噪声和供电波动都会导致读数跳动所以采集必须做滤波。我习惯用最简单的“多次采样剔除极值求平均”连续采 8 次去掉最大值和最小值剩下的求平均作为当前方向的最终光强值。这个滤波方法在光环境这种慢变信号上效果非常好计算量也小。下面是核心的 ADC 初始化与滤波采样代码void ADC_Init(void) { GPIO_InitTypeDef gpio; ADC_InitTypeDef adc; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); // 72MHz / 6 12MHz gpio.GPIO_Pin GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; gpio.GPIO_Mode GPIO_Mode_AIN; GPIO_Init(GPIOA, gpio); adc.ADC_Mode ADC_Mode_Independent; adc.ADC_ScanConvMode DISABLE; adc.ADC_ContinuousConvMode DISABLE; adc.ADC_ExternalTrigConv ADC_ExternalTrigConv_None; adc.ADC_DataAlign ADC_DataAlign_Right; adc.ADC_NbrOfChannel 1; ADC_Init(ADC1, adc); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while (ADC_GetCalibrationStatus(ADC1)); } uint16_t ADC_ReadFiltered(uint8_t channel) { uint16_t buf[8], temp; uint8_t i, j; for (i 0; i 8; i) { ADC_RegularChannelConfig(ADC1, channel, 1, ADC_SampleTime_55Cycles5); ADC_SoftwareStartConvCmd(ADC1, ENABLE); while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC)); buf[i] ADC_GetConversionValue(ADC1); } // 冒泡排序去掉最大最小值 for (i 0; i 7; i) { for (j 0; j 7 - i; j) { if (buf[j] buf[j 1]) { temp buf[j]; buf[j] buf[j 1]; buf[j 1] temp; } } } return (buf[2] buf[3] buf[4] buf[5]) / 4; }3.3 舵机 PWM 控制示例舵机的控制信号是周期 20ms、脉宽 0.5ms~2.5ms 的 PWM对应 0 度到 180 度。STM32 产生这样的信号非常简单——配置定时器输出比较模式即可。以 TIM2 产生 50Hz PWM 为例预分频器设 71自动重载值设 19999这样定时器时钟就是72MHz / (711) / (199991) 50Hz。改变比较寄存器的值就能改变舵机角度。void Servo_Init(void) { GPIO_InitTypeDef gpio; TIM_TimeBaseInitTypeDef tim; TIM_OCInitTypeDef oc; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); gpio.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1; gpio.GPIO_Mode GPIO_Mode_AF_PP; gpio.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, gpio); tim.TIM_Prescaler 71; tim.TIM_CounterMode TIM_CounterMode_Up; tim.TIM_Period 19999; tim.TIM_ClockDivision TIM_CKD_DIV1; TIM_TimeBaseInit(TIM2, tim); oc.TIM_OCMode TIM_OCMode_PWM1; oc.TIM_OutputState TIM_OutputState_Enable; oc.TIM_Pulse 1500; // 1.5ms 对应中位舵机 0 度 oc.TIM_OCPolarity TIM_OCPolarity_High; TIM_OC1Init(TIM2, oc); // PA0 TIM_OC2Init(TIM2, oc); // PA1 TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable); TIM_Cmd(TIM2, ENABLE); } void Servo_SetAngle(TIM_TypeDef* tim, uint8_t channel, uint16_t angle) { uint16_t pulse 500 angle * 2000 / 180; // 0度-500, 180度-2500 switch (channel) { case 1: TIM_SetCompare1(tim, pulse); break; case 2: TIM_SetCompare2(tim, pulse); break; } }主循环里的追光逻辑就很简单了while (1) { uint16_t east ADC_ReadFiltered(ADC_Channel_2); uint16_t west ADC_ReadFiltered(ADC_Channel_3); uint16_t up ADC_ReadFiltered(ADC_Channel_4); uint16_t down ADC_ReadFiltered(ADC_Channel_5); // 光敏电阻受光越强分压点电压越低ADC读数越小 if (east DEAD_ZONE west h_angle 180) { h_angle STEP; } else if (west DEAD_ZONE east h_angle 0) { h_angle - STEP; } if (up DEAD_ZONE down v_angle 180) { v_angle STEP; } else if (down DEAD_ZONE up v_angle 0) { v_angle - STEP; } Servo_SetAngle(TIM2, 1, h_angle); Servo_SetAngle(TIM2, 2, v_angle); Delay_ms(50); }这段代码的关键是每一步只转 2~5 度STEP 值循环周期 50ms所以舵机看起来是平滑转动的既不会因为大步长来回振荡也不会转得太猛把机械结构带崩。4. 调试与常见问题排查实录4.1 烧录报错 “no stm32 target found”STM32 开发中最高频的报错就是error: no stm32 target found! if your product embeds debug authentication, please check if the device is locked。我第一次用 ST-Link 烧程序时也被这个卡了半天。出现这个报错常见原因有几个一是连接线问题。SWDIO、SWCLK、GND 三根线接错或者接触不良尤其杜邦线插在面包板上容易虚接。排查方法很简单检查接线后用万用表量通断确认 ST-Link 端和板子端的 SWDIO、SWCLK 一一对应。二是板子供电异常目标板没上电调试器检测不到芯片。三是芯片被读保护锁住了这种情况多见于二手芯片需要用 ST-Link Utility 的 Connect Under Reset 模式连一次然后执行 Full Chip Erase。如果接线和供电都没问题但依然报错还有一种办法先把 BOOT0 拉高让芯片进入系统存储器模式这时候调试器就能连上清掉 Flash 后再把 BOOT0 拉回低电平就好了。我后来用这个方法救活过好几块“假死”的芯片。4.2 舵机抖动与 ADC 值跳变舵机抖动是追光系统最常见的毛病。抖动分为两种一种是舵机在平衡位置来回快速小幅度摆动这是死区设置太小导致的加大 DEAD_ZONE 就好另一种是整个舵机周期性抽搐这种大概率是供电问题——舵机转动瞬间拉低电压开发板上的 STM32 也受到影响ADC 读数跳变进一步加剧舵机乱转。排查供电问题时可以拿示波器看舵机电源引脚转动瞬间电压跌落超过 0.5V 就说明电源余量不足。没有示波器的话用万用表也能大致观察。最简单的改善方案就是舵机电源和单片机电完全分开共地即可——这是我强烈建议的做法从一开始就这么设计后面能省很多事。ADC 值跳变还有一个隐蔽的原因光敏电阻的引脚线太长在空气里形成了微型天线环境电磁干扰串进模拟输入端。解决办法是杜邦线尽量短或者用双绞线再不行就在 ADC 引脚对地并联一个 0.1μF 的滤波电容。4.3 常见问题速查表问题现象可能原因解决办法烧录报 no target foundSWD线接错/接触不良检查SWDIO/SWCLK接线Connect Under Reset舵机不转程序卡死舵机供电不足拉死电压独立5V电源共地不共用光照方向判断反向分压电路接法相反交换VCC端和GND端或把比较逻辑取反舵机来回抖动死区太小增大DEAD_ZONE我用的20~30电池供电时ADC明显漂移电源纹波太大加100μF电解电容0.1μF瓷片电容转到了极限位置还一直转缺少角度限位软件限制角度0~180度并增加限位开关调试阶段我强烈建议加一个串口打印通过 USART1 把四路 ADC 原始值实时发到上位机。我一般用的是串口助手波特率 115200打印格式像这样E:1234 W:1500 U:1800 D:2200。这样对着窗口调整舵机位置观察哪个方向光强变化能直接看到不用瞎猜。做这个项目最深的体会是追光系统看起来简单但真把精度和稳定性同时做上去比你想象的需要多花一倍时间。别指望一次成功先让舵机能转再让光敏电阻读数稳定最后再把追光逻辑闭环跑通一步一步来。最后再分享一个小技巧如果你手上正好有 K210 这类带摄像头的 AI 模块可以在 STM32 追光基础上扩展视觉寻光功能用摄像头识别太阳或强光源的位置然后通过串口把目标角度发给 STM32主控负责执行云台动作。这个组合在电赛里拿奖的概率很高也是你从“能做出来”进阶到“做得有亮点”的捷径。本文还有配套的精品资源点击获取