☰
STM32F103太阳能逐日系统:Cortex-M3双轴追踪实战
2026/10/27 23:30:58 网站建设 项目流程

简介:这是一份基于STM32F103(Cortex-M3内核)的太阳能逐日系统嵌入式软件工程,面向学习STM32外设开发、光伏追日控制与MPPT算法的开发者。工程源码覆盖ADC电压电流采样、定时器中断、USART串口通信、电池充放电管理、系统保护与远程数据上报等关键模块,整体遵循硬件抽象层(HAL)设计思路,便于移植与二次开发,适合作为课程设计或实际项目参考。压缩包为rar格式,共181个文件,3.91MB,以43个.h头文件、33个.c源文件、8个.s启动汇编文件、.uvprojx/uvoptx工程配置以及.hex可烧录固件为主,另有.o/.crf/.lst等编译中间文件,可直接在Keil MDK中打开查看代码或结合硬件调试。目前已有51人学习下载。通过该工程可快速理解STM32在太阳能逐日系统中的完整控制流程,掌握MPPT最大功率点跟踪的实现思路、ADC与定时器等外设的初始化方法,并借助现成工程模板快速开展自己的追日控制项目。

1. STM32F103太阳能逐日系统:一块Cortex-M3如何追着太阳跑

追太阳这件事看起来有很多高级解法,GPS坐标、天文算法、光流传感器都能做,但真正在野外跑得久的系统,九成用的是最简单的四象限光感比较。这套STM32F103太阳能逐日系统,正是用Cortex-M3内核把这条路走通的完整软件资源:CubeMX配置生成MDK工程,外接四个光敏电阻做双轴跟踪,用TIM3中断驱动两路28BYJ-48步进电机,全程串口可观测。它没有花哨的云端曲线,胜在逻辑直白、硬件好买、坏了自己能查。适合光伏类毕业设计、小型离网充电装置,或者单纯想用F103练熟ADC采集、定时器中断、串口调试这三板斧的嵌入式工程师。

2. 硬件选型与最小系统:5V转3.3V之外的三个决定

2.1 为什么是F103C8T6:这个活儿不需要更贵的核

先把一个容易想反的问题说透:太阳能逐日看起来是控制问题,本质上是采样问题。光伏板转动的速度是分钟级的,光强变化是秒级的,主控真正要做的只是“每隔几百毫秒采一次光、算个差值、决定往哪转一小步”。这种节奏下,STM32F103C8T6的72MHz主频、12位ADC、四个定时器、三个USART已经完全溢出。选它不是因为性能极限,而是因为资料厚度和最小系统成本——一块最小系统板加上四个光敏电阻、两个步进电机,整套BOM能压到几十块钱,这在光伏棚、小水泵、户外充电这种场景里比什么都重要。

有人会问为什么不用51,51没有硬件ADC,逐日系统至少需要四路模拟输入,外挂ADC芯片虽然便宜但平白增加一路I2C/SPI的调试成本;也有人问为什么不用ESP32,它当然能做,但功耗高、启动慢、还要处理WiFi协议栈,在纯离网场景里属于资源浪费。F103C8T6的20KB RAM、64KB Flash对这套程序来说还有余量,后面加个SD卡日志记录、加个Modbus上报都能塞得下。新入门的人看这颗芯片会觉得老,但对于电机控制这种“慢控制”场景,Cortex-M3的老恰恰意味着踩坑记录足够多,网上随便搜一个问题都有三五个解决方案。

2.2 最小系统与5V转3.3V电路:AMS1117-3.3的电容和BOOT0

串口下载和调试都依赖3.3V供电,所以最小系统的第一件事就是把5V转成3.3V。最常见做法是用AMS1117-3.3,输入接5V,输出接3.3V,输入输出各并一组电容。输入侧用10μF钽电容加0.1μF陶瓷电容,输出侧同样,这两个0.1μF的小陶瓷电容负责滤高频噪声,不能省。输出稳压后给MCU供电,AMS1117最大输出1A,F103全负载也就几十毫安,余量足够。

BOOT0的处理是个容易翻车的点。下载程序时BOOT0接地就行,不要悬空。常见做法是BOOT0通过10k电阻下拉到GND,BOOT1直接接地,这样上电必进Flash启动。NRST引脚用100nF电容对地,配合外部按键复位。晶振用8MHz无源晶振加两个20pF负载电容,给PLL提供基准。

注意:F103内部有8MHz RC振荡器,但倍频到72MHz必须用外部晶振,否则USART的波特率会偏,串口打印会出来乱码或完全不输出。

最后是电源和地。ULN2003驱动板从5V取电,MCU从3.3V取电,两个电源的GND必须连在一起,否则信号没有参考电平,电机不会转。后面2.4会专门讲地线怎么走,这里的底线是“共地”。

2.3 光敏电阻与电机选型:GL5528分压、28BYJ-48和ULN2003

光感部分用GL5528光敏电阻,亮阻约5到10kΩ,暗阻接近1MΩ。分压电路这样接:光敏电阻一端接3.3V,另一端接分压点,分压点再接一个10kΩ固定电阻到GND,分压点直接进ADC引脚。这样光照越强,ADC采到的电压越高,逻辑直观。四路光敏电阻分布在光伏板平面的四个象限,中间用一块垂直隔板隔开,制造出遮挡阴影,这个隔板的高度直接决定灵敏度,后面第5章细说。

执行机构用28BYJ-48步进电机加ULN2003驱动板。很多第一次接触的人会忽略减速比这个参数,28BYJ-48的步进角是5.625度,内部减速比1/64,也就是说电机轴转一圈需要4096个半步脉冲。如果用全步驱动是2048步一圈,用半步驱动是4096步一圈。逐日系统我建议用半步,转动更平稳,面板不容易在阵风里抖出共振。ULN2003是灌电流驱动,输入有1、2、3、4四相,接F103的四根GPIO即可。注意ULN2003的供电是5V,输入信号3.3V高电平完全够触发,不需要电平转换电路。

模块型号关键参数用途
主控STM32F103C8T672MHz / 64KB Flash / 20KB RAM逻辑与采样
光感GL5528 × 4亮阻5~10kΩ,暗阻1MΩ四象限光强采集
分压电阻10kΩ × 41%精度即可ADC分压
电机28BYJ-48 × 25.625° / 1:64减速 / 4096步每圈东西、南北双轴
驱动ULN2003 × 2灌电流500mA每通道电机驱动
稳压AMS1117-3.3输入5V,输出3.3V 1A主控供电

这个选型表的好处是每个器件都能在电商平台单独买到,替换性极强。GL5528坏了换一个就几毛钱,28BYJ-48电机五块钱以内,ULN2003驱动板带接口也就三块钱,整套硬件做下来比买一个成品追日支架便宜得多。

2.4 供电与共地:模拟地和电机地为什么要分开

逐日系统的翻车现场往往不在逻辑,而在电源。电机一启动,感性负载会产生电流尖峰,如果这个尖峰窜到ADC参考地,四路光敏采样值立刻乱跳,控制逻辑就会像喝醉了一样乱转。我一般会这样做:主控3.3V和电机5V在电源入口处共地,但从电源入口之后的走线尽量分开,ADC分压电路的GND单独铺一小块地,不要在电机驱动线旁边走模拟信号线。

另外,ULN2003驱动板的公共端VCC接5V后,电机绕组的电流会经由驱动板回到5V电源负极。如果5V电源的质量很差,可以用一个1000μF的电解电容并在驱动板电源输入上吸收尖峰。这个电容在很多逐日系统折腾帖里被称为“后悔药”——电机抖动、ADC乱跳、复位重启,很多时候就是少了一个储能电容。

3. Keil MDK工程搭建:pack包、St-Link调试与串口打印一条龙

3.1 pack包下载:F1系列DFP的版本匹配

MDK5之后工程不再自带设备支持库,必须先装pack包才能识别STM32F103。打开Keil后进Pack Installer,左侧搜“STM32F1xx_DFP”,选择STMicroelectronics发布的F1系列Device Family Pack装好。版本号不用追新,关键是和当前MDK版本匹配,MDK 5.2x以上装最新F1 DFP都能正常使用。如果CubeMX和Keil装在同一台机器上,CubeMX生成工程时也会自动检查并触发pack包的下载,省一步手动操作。

装完pack后新建工程就能在设备列表里找到“STMicroelectronics → STM32F1 Series → STM32F103C8”。这里顺便说一句,如果手头是C8T6、CBT6这类芯片,在CubeMX和Keil里都选“STM32F103C8Tx”,Flash和RAM大小选型时保持一致即可。

3.2 CubeMX生成MDK工程:目录结构和源文件放置

在CubeMX里选好芯片型号,先把时钟树配到72MHz:HSE接8MHz外部晶振,PLL倍频9倍。然后按下面的引脚分配表去配置GPIO:

外设引脚模式
ADC1_IN0PA0东侧光敏
ADC1_IN1PA1西侧光敏
ADC1_IN2PA2北侧光敏
ADC1_IN3PA3南侧光敏
USART1 TXPA9推挽输出
USART1 RXPA10输入
电机A IN1~IN4PB0 PB1 PB10 PB11推挽输出
电机B IN1~IN4PB12 PB13 PB14 PB15推挽输出
限位开关PA8 PA9上拉输入

配置完成后选MDK-ARM V5工具链生成代码。生成的工程目录里,Core/Src放main.c和stm32f1xx_it.c,Core/Inc放头文件,Drivers下是HAL库和CMSIS。逐日系统的逻辑代码我不喜欢塞进main.c,我的习惯是在工程里新建一个App分组,放sun_tracker.c和sun_tracker.h,在main.c里只做初始化调用和主循环调度,这样CubeMX重新生成代码时不会覆盖自己的业务文件。

3.3 St-Link调试器配置:SWD速度与Connect under Reset

工程建好后,Options for Target → Debug,选择ST-Link Debugger,然后进Settings。第一件事是确认连接方式选SWD,不是JTAG,SWD只用两根线,四根线含电源和地。下载速度建议先从1MHz开始,如果提示“RDDI-DAP Error”或连接失败,降速到100kHz再试,大多数接触不良问题在这个速度下都能通。还有两个必勾选项:一个是Connect under Reset,一个是Flash Download页里的Reset and Run。前者让调试器先复位芯片再连接,防止固件里把SWD引脚复用成普通IO导致锁定;后者让程序烧录完成后自动运行,省掉每次手动复位。

提示:如果固件已经把SWD引脚复用为GPIO并烧进芯片,St-Link会连不上。解决办法是把BOOT0拉高、重新上电进入ISP模式,同时按住复位再连接,擦除Flash后恢复。

3.4 串口打印重定向:fputc加MicroLib才能printf

串口是逐日系统最重要的观测窗口,所有ADC值、电机状态、死区判断都要从这里打出来。CubeMX里把USART1配成异步模式,波特率115200、8位数据、无校验、1位停止位。生成代码后,在main.c里补上fputc重定向:

#include <stdio.h> int fputc(int ch, FILE *f) { while (!(USART1->SR & USART_SR_TXE)) { /* 等待发送寄存器为空 */ } USART1->DR = (uint8_t)ch; return ch; }

这段代码的作用是让printf底层调用重定向到USART1。USART_SR_TXE是发送数据寄存器空标志,必须等它置位才能写DR,否则数据会丢。写DR之后硬件自动开始发送,不需要软件干预。需要注意重定向只能处理字符型数据,float类型在MDK默认C库下是不支持的,打印浮点数要么用整数代替,要么启用MicroLib。MicroLib的勾选位置在Options for Target → Target页面,勾上Use MicroLib。第一次做串口打印的人经常卡在这里——printf编译通过,但串口什么都收不到,原因十有八九是没勾MicroLib或者没接USB转TTL的GND线。

4. CubeMX定时器3:用500Hz更新中断给步进电机打节拍

4.1 为什么用定时器中断:GPIO延时的抖动不可控

驱动步进电机最原始的做法是循环里Delay一下,翻转一次IO。程序简单,但有两个致命问题:一是延时函数不精确,系统里一旦插入ADC采样或串口打印,延时时间会被拉长,电机转速忽快忽慢;二是CPU在延时期间被占死,ADC采样和逐日判断都得排队,整个系统的实时性就崩了。所以我要用TIM3做节拍源,每2ms产生一次更新中断,中断里推进电机相位状态机,主循环只负责判断方向和计算步数。电机转动与主控运算完全解耦,这是嵌入式里“先让硬件自己跑起来,再想逻辑”的典型做法。

4.2 PSC和ARR推导:72MHz怎么刷出500Hz

先把时钟关系理清。STM32F103的APB1分频为2时,定时器时钟是2倍频,所以TIM3的输入时钟是72MHz。要让TIM3产生500Hz的更新中断,也就是周期2ms,用公式算:f = 72MHz / ((PSC+1) * (ARR+1))。

我常用的组合是PSC=719、ARR=199。代入公式为720乘以200等于144000,72MHz除以144000正好是500Hz,误差为零。PSC=719会把72MHz分频到100kHz整数,ARR=199再分频到500Hz,两级分频都是整数,长期运行不会积累误差。CubeMX里TIM3的配置如下:

htim3.Instance = TIM3; htim3.Init.Prescaler = 719; /* 72MHz / 720 = 100kHz */ htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 199; /* 100kHz / 200 = 500Hz */ htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim3);

500Hz对应每2ms一个节拍,每个节拍推进一步。28BYJ-48半步模式4096步转一圈,也就是大约8.2秒转一圈。逐日系统要的就是这种慢而稳的转速,太快不仅机械结构受不了,光感判断也来不及收敛。如果你换了别的步进电机,公式不变,只需要调整PSC和ARR。比如想让转速翻倍到1000Hz,就把ARR改成99,保持PSC=719不变。

4.3 中断回调推进相位状态机:两个轴共用一个节拍

TIM3更新中断里做两件事:推进东西轴相位、推进南北轴相位。两个电机的相位状态机共用同一个500Hz节拍,相位表是8个半步序列,每来一次中断,相位序号加一或减一,方向由控制逻辑设置的全局变量决定:

const uint8_t half_step_table[8] = {0x1, 0x3, 0x2, 0x6, 0x4, 0xC, 0x8, 0x9}; volatile uint8_t motor_en[2] = {0, 0}; volatile int8_t motor_dir[2] = {1, -1}; volatile uint8_t step_phase[2] = {0, 0}; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance != TIM3) { return; } for (int axis = 0; axis < 2; axis++) { if (!motor_en[axis]) { continue; } if (motor_dir[axis] > 0) { step_phase[axis] = (step_phase[axis] + 1) & 0x07; } else { step_phase[axis] = (step_phase[axis] + 7) & 0x07; } set_motor_coil(axis, step_phase[axis]); } }

相位表0x1, 0x3, 0x2, 0x6, 0x4, 0xC, 0x8, 0x9是半步驱动的8个通电状态,每个状态对应IN1到IN4的四根线电平组合。正转时相位序号加一,反转时加7再按位与7,相当于减一对8取模。motor_en是使能位,主循环里置0可以随时停住电机,置1后中断自动驱动。这里有个细节:中断里只做相位推进,不做方向判断,判断逻辑全放在主循环,避免在中断里做复杂运算拖长响应时间。

4.4 如果换成A4988驱动板:同一公式切到PWM模式

有的方案会用42步进电机加A4988驱动板,这种驱动板只需要STEP脉冲和DIR方向两个信号。这时TIM3就可以从更新中断改成PWM模式,直接把500Hz方波输出到STEP引脚,占空比设50%。CubeMX里TIM3通道1选择PWM Generation,PSC和ARR依然用719和199,CCR设为100(占空比为100除以200等于50%)。DIR信号用普通GPIO输出。这样换硬件方案时,定时器分频思路完全不用改,只是把中断驱动改成硬件PWM驱动,CPU占用率还能更低。两种方案各有取舍:ULN2003便宜、扭矩小,适合轻量光伏板;A4988加42电机扭矩大,适合大板子,但驱动板和电机成本翻几倍。

5. 逐日控制策略:四象限光感比较、死区设置和增量步进

5.1 四象限光感比较:一块隔板和四个光敏电阻

逐日系统的核心传感器布置是这样的:在光伏板平面的中心竖一块隔板,隔板四个象限各放一个光敏电阻。当太阳在正上方时,四路光强基本一致。当太阳偏东时,东侧光敏电阻被照到,西侧被隔板挡住,ADC值立刻拉开差距。这个差距就是跟踪的依据。

东西轴的偏差量等于东侧减去西侧,南北轴的偏差量等于北侧减去南侧。不需要知道太阳的绝对角度,只需要知道差值往哪个方向偏。比GPS天文算法少算两个坐标系转换,比单光敏加定时扫描抗干扰能力强很多。隔板高度是灵敏度关键:隔板高了,阴影遮挡明显,差值大但有效跟踪角度窄;隔板矮了,差值小,容易误判。我一般让隔板高度等于光敏电阻间距的一半,然后根据实测日志再微调。

5.2 ADC采集与滑动平均:采8次求均值再判断

光敏电阻的信号噪声很大,尤其是中午有云层飘过时,瞬间光强波动能超过有效信号。如果每采一次就判断一次,电机会频繁抖动,机械结构很快报废。所以ADC采集之后必须滤波。我用滑动平均的简化版本:每轮采样连续采集8次,去掉最高最低,剩下求平均,然后才做比较。

#define ADC_SAMPLE_COUNT 8 uint16_t adc_raw[4]; uint32_t sum[4] = {0, 0, 0, 0}; for (int sample = 0; sample < ADC_SAMPLE_COUNT; sample++) { HAL_ADC_Start(&hadc1); for (int ch = 0; ch < 4; ch++) { HAL_ADC_PollForConversion(&hadc1, 10); adc_raw[ch] = (uint16_t)HAL_ADC_GetValue(&hadc1); } HAL_ADC_Stop(&hadc1); for (int ch = 0; ch < 4; ch++) { sum[ch] += adc_raw[ch]; } } uint32_t east = sum[0] / ADC_SAMPLE_COUNT; uint32_t west = sum[1] / ADC_SAMPLE_COUNT; uint32_t north = sum[2] / ADC_SAMPLE_COUNT; uint32_t south = sum[3] / ADC_SAMPLE_COUNT;

CubeMX里要把ADC1的四个通道配成扫描模式,转换顺序按PA0到PA3排列。HAL_ADC_Start启动一组扫描,HAL_ADC_PollForConversion依次等每个通道转换完成并读取,最后HAL_ADC_Stop关闭。四个通道采集一轮大约几十微秒,对逐日这种秒级控制来说完全够。采样次数取8是一个折中,采少了滤不干净,采多了响应变慢,晴天系统用8到16次都正常。

5.3 方向判断与死区:先转一小步再看差值

滤波得到四路光强后,真正决定转不转的是死区和增量步进。先算出东西差值和南北差值:

int32_t ew_diff = (int32_t)east - (int32_t)west; int32_t ns_diff = (int32_t)north - (int32_t)south; #define DEAD_ZONE 60 #define STEP_ANGLE 32

判断逻辑很简单:差值大于死区就往亮的一侧转一小步,转完重新采样再判断。每次只转32个半步,对应约2.8度,而不是直接按差值比例转一大步。这样做的好处是系统成为闭环逼近,不会因为机械回程差和惯性冲过头。死区值设多少要看实测,我这边四路ADC在光照均衡时抖动大约正负40,所以死区取60,刚好压制抖动又不至于太迟钝。如果发现电机频繁启停,说明死区小了;如果太阳明显偏移了还在发呆,说明死区大了。

if (ew_diff > DEAD_ZONE) { motor_dir[EAST_AXIS] = 1; motor_en[EAST_AXIS] = 1; step_target += STEP_ANGLE; } else if (ew_diff < -DEAD_ZONE) { motor_dir[EAST_AXIS] = -1; motor_en[EAST_AXIS] = 1; step_target += STEP_ANGLE; }

step_target累计要走的步数,主循环在TIM3中断每推进一拍就减一,减到0时把motor_en置0停机。这样电机不会在一个判断周期里无止境地转下去。南北轴同理,只是实际运行时南北方向变化非常慢,可以在代码里把南北轴的STEP_ANGLE设得更小,或者每隔几个判断周期才动一次。

5.4 限位与夜间策略:机械挡板和低功耗等待

双轴系统必须有限位保护,否则支架会转到机械极限位置卡死,轻则电机堵转发热,重则齿轮打滑报废。我在东西轴正负极限各装一个机械限位开关,GPIO上拉输入,开关闭合接地。中断推进相位前检查限位状态:如果正转方向被挡住,强制清掉正值方向的运行标志;反转同理。软件上还设一个步数软上限,比如MAX_STEPS_EAST = 1200,两个机制同时生效。

夜间策略方面,当四路ADC都低于某个低光阈值(比如总和小于400)时,系统判定为夜晚,进入低功耗停机状态:电机断电、ADC停止采样,只保留一个定时唤醒。第二天光强恢复后重新初始化并回到跟踪循环。F103没有复杂的低功耗管理模式,最简单的做法是主循环里什么都不做加HAL_Delay,考虑到逐日本来就不耗电,这已经够用。

6. 排查与验证:五个高频坑和一段串口日志的定位法

6.1 St-Link连不上、printf不出字、电机抖不转:五个高频坑

第一个坑:St-Link识别不到芯片。现象是Keil报“No Target Connected”或下载时提示找不到Cortex-M SW Device。原因十有八九是SWDIO和SWCLK两根信号线接触不良,或者板上电时序不对。解决:先确认目标板供电正常,再用最短杜邦线连接SWDIO、SWCLK、GND三根线,下载速度降到100kHz。如果还不行,BOOT0拉高进ISP模式,连接后擦除Flash再恢复BOOT0。

第二个坑:printf什么都打不出来。现象是串口工具打开115200,程序跑起来了但界面空白。原因一般是三个:没勾Use MicroLib导致底层库冲突、USB转TTL的TXD和RXD接反、GND没连。解决:三个依次排查,先看接线,再检查Options for Target里MicroLib是否勾上。有个更隐蔽的坑是CubeMX里USART1的GPIO被配置成了复用模式而不是推挽输出,HAL库生成的代码里GPIO模式是GPIO_MODE_AF_PP,这个是对的,不要手动改成普通推挽。

第三个坑:电机嗡嗡响但不转圈。现象是电机有电流声、发热,但轴不转。原因是经过减速齿轮箱的负载太重,或者半步驱动频率太高导致转子跟不上脉冲。解决:把TIM3更新频率降到500Hz再试,确认驱动板5V电源带1000μF电解电容,最后检查相位表顺序。相位表顺序错了的表现是电机来回抖,这时将half_step_table逆序即可对调方向,不需要改逻辑代码。

第四个坑:ADC四路值全是4095。现象是串口日志里四个通道满量程,怎么调都降不下来。原因是ADC输入引脚悬空,或是分压电阻到GND的一端虚焊,分压点被直接拉到3.3V。解决:用万用表量分压点对地电压,正常光照下应该在1到2.5V之间。如果量出来是3.3V,补焊固定电阻的接地焊盘。这种问题用软件调是调不出来的,必须动烙铁。

第五个坑:电机一转,ADC值乱跳。现象是最气人的——电机不转时光强数据很稳,电机一启动,四路ADC值全部开始波动,系统像喝醉了乱转。原因是电机感性负载的电流尖峰通过公共地窜入ADC采样回路。解决:给ULN2003电源输入端并一个大电容吸收尖峰;ADC采样任务放到电机停稳之后执行。我在主循环里的做法是“先判断、再停稳、后采样”:需要转动时先转,转到目标步数后等50ms,再做下一轮ADC采集。

6.2 串口日志才是听诊器:用一段日志判断跟踪是否健康

把系统跑起来之后,串口打印是唯一能直观看到“它在想什么”的通道。我在main.c里加了一个每2秒打印一次的调试任务:

printf("E:%u W:%u N:%u S:%u | EW:%d NS:%d | RUN:%d %d\r\n", (unsigned)east, (unsigned)west, (unsigned)north, (unsigned)south, (int)ew_diff, (int)ns_diff, step_phase[0], step_phase[1]);

观察日志时我有一套快速判断法。上午太阳在东边,EW差值应该是稳定的正值,并且随着时间推移逐步减小,表明面板正在追着太阳转。到了中午,四路ADC值接近,EW差值在死区范围内小幅波动,电机不应频繁启停。下午差值翻负,电机反转。如果日志里EW差值反复跨越死区边界,电机一会儿正转一会儿反转,说明死区太小或单次步进太大,按5.3的参数方向调。如果差值稳定但电机长时间不动,说明STEP_ANGLE太小,系统需要很多个周期才能消除偏差。

日志表现诊断结果调整方向
差值正负交替、电机频繁启停死区过小增大DEAD_ZONE
差值长时间越界、电机不动步进被限位打断查限位开关接线
四路ADC接近4095分压电路对地断开检查固定电阻焊点
差值稳定但在死区内系统正常不需要调整
差值在临界点反复横跳云层遮光干扰增加采样次数到16

这套日志不仅用来调试,装机后也可以接个USB转TTL挂在电脑上看两天,比任何仿真器都好用。

6.3 24小时老化与看门狗:装到屋顶前的最后一关

程序逻辑跑通后,我会强制要求自己做一个24小时老化,而不是立刻装上屋顶。老化期间串口日志以2秒一次持续记录,晚上检查全天的数据走势。重点看三件事:早晨能否从低光阈值正常启动,中午能否稳定收敛在死区内,下午能否平滑换向。如果有任何一段日志出现电机疯狂抖动或长时间无响应,都说明参数还没调到位,必须回去改参数重新跑24小时。

老化通过后,最后加上独立看门狗IWDG。CubeMX里配置IWDG超时约1.6秒,主循环里每轮循环末尾调用一次HAL_IWDG_Refresh(&hiwdg)。这样即使程序因为异常跑飞,看门狗也能在1.6秒内强制重启,系统不至于在无人值守时彻底罢工。从那以后,我每次做逐日系统都会先跑满24小时串口日志,再谈装机。这段日志不仅是调参依据,也是判断系统有没有问题的黑匣子,能省掉大量现场排查的时间,希望这些参数和坑位记录也能帮你少走几趟弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询