基于 STM32F103C8 的双轴太阳能自动追光系统 Proteus 仿真

前言

本文设计了一套基于 STM32F103C8 的双轴太阳能自动追光系统。系统使用东、南、西、北四个光敏电阻检测光照方向,通过两个步进电机分别控制水平轴和俯仰轴,使太阳能板朝向光照更强的方向。

系统具有自动追光和手动控制两种工作模式。自动模式根据四路光敏电阻的 ADC 数据计算东西方向、南北方向的光照差值;手动模式通过五个独立按键调整双轴角度。USART3 连接 Proteus 虚拟终端,支持模式切换、角度设置、状态查询和帮助命令。OLED 显示当前工作模式、四路光强、总光强和双轴角度。

开发环境如下:

项目 说明
主控芯片 STM32F103C8
开发工具 Keil MDK 5
编译器 ARM Compiler 5
固件库 STM32F10x 标准外设库
仿真软件 Proteus
显示模块 0.96 英寸 OLED
执行机构 两个四相步进电机
电机驱动 两片 ULN2003A
通信接口 USART3,9600 bit/s

完整工程资料免费获取

点赞 + 收藏本文,并在评论区留言“太阳能追光系统”,即可免费获取完整工程网盘链接。

工程资料包含:Keil 源码、Proteus 仿真文件、可直接加载的 HEX 文件和项目演示视频。
闲鱼店铺:大嘴花电子
专注单片机仿真与实物定制,支持 STM32、51 单片机、Arduino、Proteus 仿真等。
有需要仿真文件、代码程序、实物制作的朋友都可以咨询。
价格优惠,沟通好需求后定制方案,谢谢支持!


一、系统运行总图

在这里插入图片描述

图 1 太阳能双轴追光系统 Proteus 运行总图

整个系统由 STM32 最小系统、四路光敏检测、双轴步进电机、ULN2003A 驱动、五个控制按键、OLED 和虚拟终端组成。

运行截图中,OLED 已进入 AUTO 自动追光模式,显示总光强 T:4072,四路 ADC 数据分别为 E:1258S:973W:1008N:1074,水平轴与俯仰轴角度显示为 M1:+064.00M2:-064.00。虚拟终端同步输出 AUTOMANUHELPM1M2 命令说明,说明显示、串口和控制程序已经共同运行。

四个光敏电阻按照方位分布:

  • E:东侧光敏电阻;
  • S:南侧光敏电阻;
  • W:西侧光敏电阻;
  • N:北侧光敏电阻。

水平轴根据东、西光照差值转动,俯仰轴根据南、北光照差值转动。两个方向的光强差进入阈值范围后,电机停止,追光机构保持当前位置。


二、系统功能

本系统实现以下功能:

  1. 采集东、南、西、北四路光敏电阻电压;
  2. 每路 ADC 连续采样 8 次并计算平均值;
  3. 计算东西方向光强差和南北方向光强差;
  4. 自动控制水平轴向东或向西转动;
  5. 自动控制俯仰轴向南或向北转动;
  6. 总光强低于夜间阈值时停止自动追光;
  7. 按键切换自动模式和手动模式;
  8. 手动调节水平轴和俯仰轴角度;
  9. OLED 显示模式、光强和双轴角度;
  10. 虚拟终端发送命令控制模式和目标角度;
  11. 查询当前四路光强、差值和电机位置。

系统控制流程如下:

系统初始化
    ↓
读取四路光敏 ADC
    ↓
计算东西、南北光强差
    ↓
处理串口命令与模式按键
    ↓
自动模式:执行双轴追光
手动模式:处理四个角度按键
    ↓
每 200 ms 刷新 OLED
    ↓
返回四路光敏采集

三、STM32 最小系统与控制接口

在这里插入图片描述

图 2 STM32 最小系统、按键、虚拟终端和 OLED

主控采用 STM32F103C8。复位电路由 10 kΩ 上拉电阻、按键和电容组成,外部晶振连接 PD0、PD1。

3.1 引脚分配

功能 STM32 引脚
东侧光敏 ADC PA0 / ADC1_IN0
南侧光敏 ADC PA1 / ADC1_IN1
西侧光敏 ADC PA2 / ADC1_IN2
北侧光敏 ADC PA3 / ADC1_IN3
模式切换按键 PA5
水平轴角度增加 PA6
水平轴角度减小 PA7
俯仰轴角度增加 PA11
俯仰轴角度减小 PA12
OLED SCL PB8
OLED SDA PB9
USART3 TX PB10
USART3 RX PB11
水平轴步进电机 PB12、PB13、PB14、PB15
俯仰轴步进电机 PB0、PB1、PB2、PB3

3.2 按键功能

五个按键均配置为上拉输入,按下时引脚变为低电平。

按键 引脚 功能
KEY1 PA5 自动/手动模式切换
KEY2 PA6 手动模式下水平角度 +2.5°
KEY3 PA7 手动模式下水平角度 -2.5°
KEY4 PA11 手动模式下俯仰角度 +2.5°
KEY5 PA12 手动模式下俯仰角度 -2.5°

按键检测包含 20 ms 消抖,并在按键释放后返回一次有效按键事件:

uint8_t Key_Mode_Pressed(void)
{
    if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5) == Bit_RESET)
    {
        Delay_ms(20);
        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5) == Bit_RESET)
        {
            while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_5) == Bit_RESET);
            Delay_ms(20);
            return 1;
        }
    }
    return 0;
}

四、四路光敏电阻检测

在这里插入图片描述

图 3 东、南、西、北四路光敏电阻

四个光敏电阻分别连接 ADC1 的通道 0~3:

#define CH_EAST   ADC_Channel_0
#define CH_SOUTH  ADC_Channel_1
#define CH_WEST   ADC_Channel_2
#define CH_NORTH  ADC_Channel_3

ADC 使用单次软件触发方式,数据右对齐,采样时间为 239.5 个周期:

uint16_t MyADC_GetValue(uint8_t channel)
{
    ADC_RegularChannelConfig(
        ADC1,
        channel,
        1,
        ADC_SampleTime_239Cycles5
    );

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);

    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);

    return ADC_GetConversionValue(ADC1);
}

每一路 ADC 连续读取 8 次并计算平均值:

#define FILTER_COUNT 8

uint16_t Read_ADC_Avg(uint8_t ch)
{
    uint32_t sum = 0;
    uint8_t i;

    for (i = 0; i < FILTER_COUNT; i++)
    {
        sum += MyADC_GetValue(ch);
    }

    return (uint16_t)(sum / FILTER_COUNT);
}

四路数据保存在 LightData_t 结构体中:

typedef struct
{
    uint16_t east;
    uint16_t south;
    uint16_t west;
    uint16_t north;
    int16_t  lr_err;
    int16_t  ud_err;
    uint16_t total;
} LightData_t;

方向差值计算公式为:

东西差值 LR = East - West
南北差值 UD = South - North
总光强 Total = East + South + West + North

对应程序如下:

void Read_Light(LightData_t *d)
{
    d->east  = Read_ADC_Avg(CH_EAST);
    d->south = Read_ADC_Avg(CH_SOUTH);
    d->west  = Read_ADC_Avg(CH_WEST);
    d->north = Read_ADC_Avg(CH_NORTH);

    d->lr_err = (int16_t)d->east - (int16_t)d->west;
    d->ud_err = (int16_t)d->south - (int16_t)d->north;
    d->total  = d->east + d->south + d->west + d->north;
}

五、双轴自动追光算法

自动追光使用三个参数:

#define TRACK_THRESHOLD   80
#define STEP_INTERVAL_MS  60
#define NIGHT_THRESHOLD   400
参数 数值 作用
光强差阈值 80 差值绝对值超过 80 时驱动对应轴
自动控制周期 60 ms 自动模式每 60 ms 执行一次判断
夜间总光强阈值 400 四路总光强低于 400 时停止追光

完整追光函数如下:

void Track_Sun(const LightData_t *d)
{
    if (d->total < NIGHT_THRESHOLD)
    {
        return;
    }

    if (d->lr_err > TRACK_THRESHOLD)
    {
        Step_Once(&m1, 1, +1);
    }
    else if (d->lr_err < -TRACK_THRESHOLD)
    {
        Step_Once(&m1, 1, -1);
    }

    if (d->ud_err > TRACK_THRESHOLD)
    {
        Step_Once(&m3, 3, +1);
    }
    else if (d->ud_err < -TRACK_THRESHOLD)
    {
        Step_Once(&m3, 3, -1);
    }
}

水平轴控制规则:

东西差值 水平轴动作
LR > 80 水平轴正向步进
LR < -80 水平轴反向步进
-80 ≤ LR ≤ 80 水平轴保持

俯仰轴控制规则:

南北差值 俯仰轴动作
UD > 80 俯仰轴正向步进
UD < -80 俯仰轴反向步进
-80 ≤ UD ≤ 80 俯仰轴保持

每次自动调整只执行一个步进。系统持续采样和修正,直到东西差值与南北差值均进入阈值范围。


六、双步进电机与 ULN2003A 驱动

在这里插入图片描述

图 4 水平轴、俯仰轴与双 ULN2003A 驱动

在这里插入图片描述

图 5 两片 ULN2003A 驱动接口

系统使用两个四相步进电机:

  • 电机 M1 控制水平轴;
  • 电机 M3 控制俯仰轴。

6.1 水平轴输出

水平轴使用 PB12~PB15:

static void Motor1_Output(uint8_t s)
{
    switch (s & 0x03)
    {
        case 0: /* 1100 */ break;
        case 1: /* 0110 */ break;
        case 2: /* 0011 */ break;
        case 3: /* 1001 */ break;
    }
}

6.2 俯仰轴输出

俯仰轴使用 PB0~PB3,采用相同的四拍双相励磁顺序:

1100 → 0110 → 0011 → 1001

正转时相序索引加 1,反转时相序索引减 1:

void Step_Once(StepperState *m, uint8_t motor_id, int dir)
{
    if (dir > 0)
    {
        m->seq = (m->seq + 1) & 0x03;
        m->cur_steps++;
    }
    else
    {
        m->seq = (m->seq + 3) & 0x03;
        m->cur_steps--;
    }

    Motor_Output(motor_id, m->seq);
    Delay_ms(STEP_DELAY_MS);
}

电机参数如下:

#define STEPS_PER_REV  720L
#define STEP_DELAY_MS  5

每圈设为 720 步,因此:

每步角度 = 360° ÷ 720 = 0.5°

手动按键每次执行 5 步:

#define MANUAL_STEP_COUNT 5

因此每次按键改变角度:

5 × 0.5° = 2.5°

串口目标角度范围限制为 -180°~180°。


七、自动模式与手动模式

系统上电后默认进入自动模式:

uint8_t g_mode_auto = 1;

7.1 自动模式

自动模式每隔 60 ms 调用一次 Track_Sun()

if (g_mode_auto)
{
    if (millis() - last_track >= STEP_INTERVAL_MS)
    {
        last_track = millis();
        Track_Sun(&light);
    }
}

7.2 手动模式

KEY1 切换模式。进入手动模式后,KEY2~KEY5 控制双轴:

if (Key_HAdd_Pressed())
{
    Step_N(&m1, 1, +1, MANUAL_STEP_COUNT);
}

if (Key_HSub_Pressed())
{
    Step_N(&m1, 1, -1, MANUAL_STEP_COUNT);
}

if (Key_VAdd_Pressed())
{
    Step_N(&m3, 3, +1, MANUAL_STEP_COUNT);
}

if (Key_VSub_Pressed())
{
    Step_N(&m3, 3, -1, MANUAL_STEP_COUNT);
}

按键控制与串口角度控制均使用同一组电机状态变量,因此 OLED 显示的角度与实际累计步数保持一致。


八、OLED 数据显示

OLED 使用 PB8、PB9 模拟 I2C:

OLED 信号 STM32 引脚
SCL PB8
SDA PB9

OLED 每 200 ms 刷新一次:

#define OLED_REFRESH_MS 200

显示内容如下:

显示区域 内容
第 1 行 工作模式、四路总光强
第 2 行 东侧、南侧 ADC
第 3 行 西侧、北侧 ADC
第 4 行 水平轴、俯仰轴角度

显示格式:

MODE:AUTO T:xxxx
E:xxxx    S:xxxx
W:xxxx    N:xxxx
M1:xxx.xx M2:xxx.xx

M1、M2 的角度由累计步数除以 2 得到:

float motor1_steps = (float)m1.cur_steps / 2.0f;
float motor3_steps = (float)m3.cur_steps / 2.0f;

九、虚拟终端与串口命令

在这里插入图片描述

图 6 按键、虚拟终端与 OLED

在这里插入图片描述

图 7 虚拟终端帮助信息与 OLED 启动提示

上电后,OLED 首先显示按键和串口操作提示;虚拟终端输出完整帮助菜单。该截图适合放在串口协议介绍之前,让读者先看到实际人机交互效果,再阅读命令格式。

系统使用 USART3:

串口信号 STM32 引脚
TX PB10
RX PB11

串口参数:

波特率:9600 bit/s
数据位:8 位
停止位:1 位
校验位:无
硬件流控:无

串口接收使用 RXNE 中断,以回车或换行作为一条命令结束标志。

9.1 命令表

命令 功能
AUTO 切换到自动追光模式
MANU 切换到手动模式
Q 查询当前系统状态
QUERY 查询当前系统状态
HELP 显示命令帮助
M145 水平轴移动到 45°
M1-30 水平轴移动到 -30°
M245 俯仰轴移动到 45°
M2-15 俯仰轴移动到 -15°

执行 M1 或 M2 角度命令后,系统自动切换到手动模式。

9.2 状态查询

发送 QQUERY 后,虚拟终端返回:

MODE=AUTO E=xxxx S=xxxx W=xxxx N=xxxx
TOT=xxxx LR=xxxx UD=xxxx M1=xxxx M2=xxxx

返回数据包含:

  • 当前工作模式;
  • 四路 ADC 数据;
  • 四路总光强;
  • 东西方向差值;
  • 南北方向差值;
  • 水平轴累计步数;
  • 俯仰轴累计步数。

十、主程序设计

系统初始化顺序如下:

SysTick_MyInit();
MyADC_Init();
Motor_GPIO_Init();
Key_GPIO_Init();
OLED_Init();
USART3_MyInit();
Show_Boot_Tips();

主循环完成五项任务:

  1. 读取四路光敏数据;
  2. 解析 USART3 命令;
  3. 处理模式和目标角度;
  4. 执行自动追光或手动控制;
  5. 定时刷新 OLED。

核心结构如下:

while (1)
{
    Read_Light(&light);

    if (g_cmd_ready)
    {
        USART3_ProcessCommand();
    }

    /* 处理串口模式、角度和查询标志 */

    if (Key_Mode_Pressed())
    {
        g_mode_auto = !g_mode_auto;
    }

    if (g_mode_auto)
    {
        /* 每 60 ms 自动追光 */
    }
    else
    {
        /* 处理四个手动角度按键 */
    }

    if (millis() - last_oled >= OLED_REFRESH_MS)
    {
        last_oled = millis();
        OLED_ShowData(&light);
    }
}

SysTick 每 1 ms 进入一次中断并累加 g_ms_ticks。自动追光和 OLED 刷新均使用时间差判断,不使用长时间主循环延时。


十一、工程目录

实际参与编译的应用文件如下:

program
├─ Hardware
│  ├─ adc.c
│  ├─ adc.h
│  ├─ motor.c
│  ├─ motor.h
│  ├─ OLED.c
│  ├─ OLED.h
│  ├─ OLED_Data.c
│  ├─ OLED_Data.h
│  ├─ usart.c
│  └─ usart.h
├─ User
│  ├─ main.c
│  ├─ stm32f10x_conf.h
│  ├─ stm32f10x_it.c
│  └─ stm32f10x_it.h
├─ Library
├─ Start
├─ Objects
│  └─ Project.hex
└─ Project.uvprojx

tracker.ctracker.h 保存在 Hardware 目录中,但未加入当前 Keil 工程。当前运行的追光算法位于 User/main.c


十二、Keil 编译与 Proteus 运行

12.1 Keil 编译

  1. 打开 program/Project.uvprojx
  2. 选择 Target 1
  3. 执行 Build;
  4. 生成 program/Objects/Project.hex

工程编译结果:

0 Error(s), 0 Warning(s)

工程目录中保存的 Project.build_log.htm 记录了 μVision V5.40、ARM Compiler V5.05 update 1,并显示 0 Error(s), 0 Warning(s)。链接映射文件记录的资源占用为:

Total RO Size  = 21676 Bytes(21.17 kB)
Total RW Size  =  2776 Bytes( 2.71 kB)
Total ROM Size = 21760 Bytes(21.25 kB)

在这里插入图片描述

图 08:Keil Build Output 编译成功

12.2 Proteus 运行

  1. 打开项目中的 Proteus 仿真文件;
  2. 双击 STM32F103C8;
  3. Program File 选择 program/Objects/Project.hex
  4. 启动仿真;
  5. 调节四个 LDR 的光照强度;
  6. 观察水平轴和俯仰轴转动;
  7. 使用 KEY1 切换自动、手动模式;
  8. 使用 KEY2~KEY5 调节双轴角度;
  9. 打开虚拟终端发送控制命令;
  10. 观察 OLED 显示数据。
    在这里插入图片描述

图 09:STM32 Program File 配置截图


十三、功能测试

13.1 东西方向追光

增大东侧光敏电阻的光照,使 East - West > 80,水平轴正向步进。

增大西侧光敏电阻的光照,使 East - West < -80,水平轴反向步进。

东西差值进入 -80~80 后,水平轴停止。
在这里插入图片描述

图 10:东西方向追光对比图

13.2 南北方向追光

增大南侧光敏电阻的光照,使 South - North > 80,俯仰轴正向步进。

增大北侧光敏电阻的光照,使 South - North < -80,俯仰轴反向步进。

南北差值进入 -80~80 后,俯仰轴停止。
在这里插入图片描述

图 11:南北方向追光对比图

13.3 手动角度控制

切换到手动模式后:

  • KEY2:水平轴 +2.5°;
  • KEY3:水平轴 -2.5°;
  • KEY4:俯仰轴 +2.5°;
  • KEY5:俯仰轴 -2.5°。
    在这里插入图片描述

图 12:手动控制角度

13.4 串口角度控制

虚拟终端发送:

M145

水平轴移动到 45°。

在这里插入图片描述

图 13:串口角度命令执行


十四、总结

本设计完成了基于 STM32F103C8 的双轴太阳能自动追光系统。

系统使用四路 ADC 获取东、南、西、北光照数据,通过东西差值和南北差值控制两个步进电机,实现水平轴和俯仰轴自动追光。五个按键完成模式切换和手动角度调节,USART3 虚拟终端完成远程命令控制与状态查询,OLED 实时显示系统运行数据。

工程综合使用了 STM32 ADC、GPIO、SysTick、USART 中断、软件 I2C 和步进电机相序控制,形成了光照采集、方向判断、双轴执行、人机交互和数据显示的完整控制系统。


Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐