基于单片机设计的太阳能跟踪器

news2025/3/2 2:24:38

一、前言

随着对可再生能源的需求不断增长,太阳能作为一种清洁、可持续的能源形式,受到越来越多的关注和应用。太阳能光板通常固定在一个固定的角度上,这限制了它们对太阳光的接收效率。为了充分利用太阳能资源,提高太阳能光板的收集效率,需要设计一个能够自动跟踪太阳光的系统。

本项目采用基于单片机的设计方案,主控芯片选择STC89C52。在太阳能光板的四个角上,安装了四个光敏电阻,它们用于检测四个方向太阳光的最强位置。每个光敏电阻通过PCF8591模块与主控芯片相连,利用模数转换器(ADC)采集各个通道的数据值。

通过对四个光敏传感器采集到的数据进行处理和比较,主控芯片能够确定太阳光的最强位置所在。然后,通过控制两个28BYJ-48-5V步进电机的运动,太阳能光板可以实现左右和上下方向的旋转。通过调整太阳能光板的倾斜角度,使其与太阳光保持垂直,以获得最大的太阳能收集效率。

该太阳能跟踪器的设计旨在实现自动化的太阳光追踪,以提高太阳能光板的能源收集效率。通过使用光敏电阻、ADC转换和步进电机控制等技术手段,系统能够准确地确定太阳光的位置,并自动调整太阳能光板的朝向。这将大大提高太阳能系统的能源输出,并为可再生能源的利用做出贡献。

image-20230823173509365

image-20230823173630278

二、系统设计思路

2.1 硬件选型

【1】主控芯片:STC89C52 STC89C52是一款高性价比的单片机,具有丰富的外设和强大的计算能力。采用基于MCS-51内核的8位单片机架构,拥有存储容量大(8KB Flash和256B RAM)和丰富的IO口(32个),适合控制太阳能跟踪器系统的各种功能。

【2】光敏电阻:选择具有高灵敏度和较小尺寸的光敏电阻,并根据光照条件进行选择。通过与PCF8591模块连接,可以将光敏电阻的电阻值变化转换为相应的模拟电压信号。

【3】ADC模块:PCF8591 PCF8591是一款常用的4通道12位ADC模块,适用于将模拟信号转换为数字信号。通过连接4个光敏电阻到PCF8591的4个输入通道上,可以实现数据的采集和转换。

【4】步进电机:28BYJ-48-5V 28BYJ-48-5V步进电机是一个小型、低功耗的步进电机,适用于低速应用。使用两个步进电机可以控制太阳能光板在水平和垂直方向上的旋转,为太阳能跟踪器提供多个方向的调整。

2.2 设计思路

【1】硬件连接:根据项目需求,将STC89C52主控芯片与PCF8591模块、ULN2003驱动模块、28BYJ-48-5V步进电机、光敏电阻等进行正确的引脚连接。

【2】初始化设置:在主函数开始部分,进行必要的初始化设置,例如设置I/O口方向、定义引脚连接、初始化I2C总线等。

【3】光敏电阻采集:通过PCF8591模块采集4个光敏电阻的数据。使用I2C通信协议,向PCF8591模块发送控制字节,选择光敏电阻通道,并通过ADC转换获取光敏电阻的数值。将采集到的数据存储在名为lightSensor的数组中,每个元素对应一个光敏电阻通道。

【4】确定最强光位置:根据采集到的光敏电阻数据,通过比较找到最强光的位置。遍历lightSensor数组,记录最大值的索引,表示最强光所在的方向。

【5】步进电机控制:根据最强光的位置控制步进电机的旋转,使太阳能光板朝向最大光的方向。根据最大光位置的索引,使用条件语句判断旋转方向,然后调用StepperMotor_Rotate函数控制步进电机旋转。根据需求,可以设置旋转步数和旋转方向,以实现精确的转动控制。

【6】延时等待:在步进电机旋转完成后,可以添加适当的延时,以等待太阳能光板调整到新的位置。可以根据实际情况调整延时时间,确保光板稳定后进行下一次采集和控制。

【7】循环执行:将上述步骤放置在一个无限循环中,以实现持续的太阳能跟踪。程序将不断采集光敏电阻数据、确定最强光位置,并通过步进电机控制太阳能光板旋转,以获得最大的太阳能收集效率。

三、项目代码

3.1 PCF8591采集代码

以下是利用PCF8591的光敏电阻采集并通过串口打印的实现代码。

#include <reg52.h>
#include <intrins.h>

// 定义PCF8591模块地址
#define PCF8591_ADDR 0x90

// 定义光敏电阻通道
#define LDR_CHANNEL_1 0x00
#define LDR_CHANNEL_2 0x01
#define LDR_CHANNEL_3 0x02
#define LDR_CHANNEL_4 0x03

// 定义波特率
#define BAUDRATE 9600

// 函数声明
void delay(unsigned int time);
void uartInit();
void uartSendByte(unsigned char dat);
void uartSendString(unsigned char *str);
void pcf8591Init();
unsigned char pcf8591ReadChannel(unsigned char channel);

void main() {
    unsigned char ldr1, ldr2, ldr3, ldr4;
    unsigned char str[20];
  
    uartInit();  // 初始化串口
    pcf8591Init();  // 初始化PCF8591模块
    
    while(1) {
        // 读取光敏电阻数据
        ldr1 = pcf8591ReadChannel(LDR_CHANNEL_1);
        ldr2 = pcf8591ReadChannel(LDR_CHANNEL_2);
        ldr3 = pcf8591ReadChannel(LDR_CHANNEL_3);
        ldr4 = pcf8591ReadChannel(LDR_CHANNEL_4);
      
        // 打印光敏电阻数据到串口
        sprintf(str, "LDR1: %d, LDR2: %d, LDR3: %d, LDR4: %d\r\n", ldr1, ldr2, ldr3, ldr4);
        uartSendString(str);
      
        delay(1000);  // 延时一段时间后再进行下一次采集和打印
    }
}

// 延时函数
void delay(unsigned int time) {
    unsigned int i, j;
  
    for(i = 0; i < time; i++) {
        for(j = 0; j < 125; j++);
    }
}

// 初始化串口
void uartInit() {
    TMOD = 0x20;  // 设置定时器1为模式2
    SCON = 0x50;  // 设置串口工作方式1,允许接收
    TH1 = 256 - _cror(_cror(FOSC/12, 4), 4) / BAUDRATE;  // 设置波特率
    TR1 = 1;  // 启动定时器1
}

// 串口发送单个字节
void uartSendByte(unsigned char dat) {
    SBUF = dat;
    while (!TI);  // 等待发送完成
    TI = 0;       // 清除发送完成标志位
}

// 串口发送字符串
void uartSendString(unsigned char *str) {
    while (*str) {
        uartSendByte(*str);
        str++;
    }
}

// 初始化PCF8591模块
void pcf8591Init() {
    // 发送启动转换命令
    I2C_Start();                     
    I2C_Send_Byte(PCF8591_ADDR);     // 发送设备地址
    I2C_Wait_Ack();
    I2C_Send_Byte(0x40);             // 发送转换命令,选择通道0
    I2C_Wait_Ack();
    I2C_Stop();
}

// 读取PCF8591模块的指定通道的数据值
unsigned char pcf8591ReadChannel(unsigned char channel) {
    unsigned char value;
  
    I2C_Start();
    I2C_Send_Byte(PCF8591_ADDR);      // 发送设备地址
    I2C_Wait_Ack();
    I2C_Send_Byte(channel);           // 发送通道号
    I2C_Wait_Ack();
    I2C_Start();                      // 重新启动
    I2C_Send_Byte(PCF8591_ADDR + 1);  // 发送读取命令
    I2C_Wait_Ack();
    value = I2C_Read_Byte();           // 读取数据
    I2C_Send_NAck();
    I2C_Stop();
  
    return value;
}

3.2 主项目框架代码

#include <reg52.h>

// 定义PCF8591模块的引脚连接
#define PCF8591_ADDRESS 0x90  // PCF8591模块的I2C地址
#define PCF8591_CONTROL 0x00  // PCF8591模块的控制寄存器地址

// 定义步进电机的引脚连接
sbit IN1 = P1^0;  // 步进电机引脚1
sbit IN2 = P1^1;  // 步进电机引脚2
sbit IN3 = P1^2;  // 步进电机引脚3
sbit IN4 = P1^3;  // 步进电机引脚4

// 定义步进电机旋转方向
#define CW 0  // 顺时针
#define CCW 1  // 逆时针

// 定义光敏电阻通道
#define CHANNEL_0 0  // 光敏电阻通道0
#define CHANNEL_1 1  // 光敏电阻通道1
#define CHANNEL_2 2  // 光敏电阻通道2
#define CHANNEL_3 3  // 光敏电阻通道3

// 延时函数
void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = ms; i > 0; i--)
        for (j = 110; j > 0; j--);
}

// I2C总线启动
void I2C_Start() {
    SDA = 1;
    SCL = 1;
    delay(1);
    SDA = 0;
    delay(1);
    SCL = 0;
    delay(1);
}

// I2C总线停止
void I2C_Stop() {
    SDA = 0;
    SCL = 1;
    delay(1);
    SDA = 1;
    delay(1);
}

// I2C发送一个字节的数据
void I2C_SendByte(unsigned char dat) {
    unsigned char i;
    for (i = 0; i < 8; i++) {
        SDA = (dat & 0x80) >> 7;
        dat <<= 1;
        delay(1);
        SCL = 1;
        delay(1);
        SCL = 0;
        delay(1);
    }
    SDA = 1;
    delay(1);
    SCL = 1;
    delay(1);
    while (SDA) continue;
    SCL = 0;
}

// 从PCF8591读取一个字节的数据
unsigned char PCF8591_ReadByte() {
    unsigned char i, dat = 0;
    SDA = 1;
    for (i = 0; i < 8; i++) {
        dat <<= 1;
        SCL = 0;
        delay(1);
        SCL = 1;
        delay(1);
        if (SDA) dat |= 0x01;
    }
    SCL = 0;
    return dat;
}

// 设置PCF8591的控制字节
void PCF8591_SetControl(unsigned char ctrl) {
    I2C_Start();
    I2C_SendByte(PCF8591_ADDRESS);
    I2C_SendByte(PCF8591_CONTROL);
    I2C_SendByte(ctrl);
    I2C_Stop();
}

// 读取光敏电阻的数据
unsigned int ReadLightSensor(unsigned char channel) {
    unsigned int value;
    PCF8591_SetControl(0x40 | channel);  // 选择光敏电阻通道
    delay(10);  // 延时等待转换完成
    I2C_Start();
    I2C_SendByte(PCF8591_ADDRESS | 0x01);  // 续上一段

    value = PCF8591_ReadByte();  // 读取高字节
    value = (value << 8) + PCF8591_ReadByte();  // 读取低字节
    I2C_Stop();
    return value;
}

// 控制步进电机旋转
void StepperMotor_Rotate(unsigned char direction, unsigned int steps) {
    unsigned int i;
    for (i = 0; i < steps; i++) {
        // 顺时针旋转
        if (direction == CW) {
            IN1 = 1; IN2 = 0; IN3 = 0; IN4 = 0;
            delay(10);
            IN1 = 0; IN2 = 1; IN3 = 0; IN4 = 0;
            delay(10);
            IN1 = 0; IN2 = 0; IN3 = 1; IN4 = 0;
            delay(10);
            IN1 = 0; IN2 = 0; IN3 = 0; IN4 = 1;
            delay(10);
        }
        // 逆时针旋转
        else if (direction == CCW) {
            IN1 = 0; IN2 = 0; IN3 = 0; IN4 = 1;
            delay(10);
            IN1 = 0; IN2 = 0; IN3 = 1; IN4 = 0;
            delay(10);
            IN1 = 0; IN2 = 1; IN3 = 0; IN4 = 0;
            delay(10);
            IN1 = 1; IN2 = 0; IN3 = 0; IN4 = 0;
            delay(10);
        }
    }
}

// 主函数
void main() {
    unsigned int lightSensor[4];
    unsigned char maxIndex;
    
    while (1) {
        // 采集光敏电阻数据
        lightSensor[0] = ReadLightSensor(CHANNEL_0);
        lightSensor[1] = ReadLightSensor(CHANNEL_1);
        lightSensor[2] = ReadLightSensor(CHANNEL_2);
        lightSensor[3] = ReadLightSensor(CHANNEL_3);
        
        // 确定最强光位置
        maxIndex = 0;
        if (lightSensor[1] > lightSensor[maxIndex]) maxIndex = 1;
        if (lightSensor[2] > lightSensor[maxIndex]) maxIndex = 2;
        if (lightSensor[3] > lightSensor[maxIndex]) maxIndex = 3;
        
        // 控制步进电机旋转
        if (maxIndex == 0) {
            StepperMotor_Rotate(CW, 100);  // 右转
        } else if (maxIndex == 1) {
            StepperMotor_Rotate(CCW, 100);  // 左转
        } else if (maxIndex == 2) {
            StepperMotor_Rotate(CW, 100);  // 右转
            StepperMotor_Rotate(CW, 100);  // 右转
        } else if (maxIndex == 3) {
            StepperMotor_Rotate(CCW, 100);  // 左转
            StepperMotor_Rotate(CCW, 100);  // 左转
        }
        
        delay(1000);  // 延时一段时间
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1157888.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Oracle(10)Managing Undo Data

目录 一、基础知识 1、AUM :Init Parameters AUM:初始化参数 2、AUM:Other Parameters AUM:其他参数 3、AUM:Sizing an UNDO TS AUM:调整UNDOTS的大小 4、AUM :Undo Quota AUM:撤消配额 5、Get Undo Segment Info 获取撤消段信息 二、基础操作 1、AUM:UNDO Tablespace …

2、循环依赖详解(二)

bean的实例化过程源码解析 建议用IDEA的debug模式来观察Spring的IOC过程 进入到此类的构造方法中 查看setConfigLocations&#xff0c;就是将配置文件加载到configLocations里去 向下执行&#xff0c;查看refresh() this.prepareRefresh(): 此方法是准备工作&#xff0c;大家…

Spring Boot 3 整合 xxl-job 实现分布式定时任务调度,结合 Docker 容器化部署(图文指南)

目录 前言初始化数据库Docker 部署 xxl-job下载镜像创建容器并运行访问调度中心 SpringBoot 整合 xxl-jobpom.xmlapplication.ymlXxlJobConfig.java执行器注册查看 定时任务测试添加测试任务配置定时任务测试结果 结语附录xxl-job 官方文档xxl-job 源码测试项目源码 前言 xxl-…

防雷接地测试方法完整方案

防雷接地是保障电力系统、电子设备和建筑物安全的重要措施&#xff0c;防雷接地测试是检验防雷接地装置是否合格的必要手段。本文介绍了防雷接地测试的原理、方法和注意事项&#xff0c;以及如何编写防雷接地测试报告。 地凯科技防雷接地测试的原理 防雷接地测试的基本原理是…

人工智能快速发展时代下的“AI诈骗防范”

当前&#xff0c;AI技术的广泛应用为社会公众提供了个性化智能化的信息服务&#xff0c;也给网络诈骗带来可乘之机&#xff0c;如不法分子通过面部替换语音合成等方式制作虚假图像、音频、视频仿冒他人身份实施诈骗、侵害消费者合法权益。你认为AI诈骗到底应该如何防范&#xf…

TimeGPT-1——第一个时间序列数据领域的大模型他来了

一直有一个问题:时间序列的基础模型能像自然语言处理那样存在吗?一个预先训练了大量时间序列数据的大型模型&#xff0c;是否有可能在未见过的数据上产生准确的预测?最近刚刚发表的一篇论文&#xff0c;Azul Garza和Max Mergenthaler-Canseco提出的TimeGPT-1&#xff0c;将ll…

国内某发动机制造工厂RFID智能制造应用解决方案

一、工厂布局和装备 国内某发动机制造工厂的装配车间布局合理&#xff0c;设备先进&#xff0c;在这个5万平方米的生产区域内&#xff0c;各个工位之间流程紧密&#xff0c;工厂采用了柔性设备&#xff0c;占比达到了67%&#xff0c;数控化率超过90%&#xff0c;自动化率达到了…

AD教程(四)排针类元件模型的创建

AD教程&#xff08;四&#xff09;排针类元件模型的创建 新建元件&#xff0c;输入排针型号作为元件命名 快捷键TC 快速创建元件 放置外框 放置管脚&#xff0c;排针管脚号在原理图上一般不显示&#xff0c;需要将管脚号隐藏&#xff0c;但一般不建议隐藏&#xff0c;如果将管…

如何在《阴阳师》游戏中使用Socks5搭建工具

题目&#xff1a;如何在《阴阳师》游戏中使用S5搭建工具S5一键搭建脚本进行游戏战队组建&#xff1f; 引言&#xff1a; 游戏加速和游戏战队组建已经成为《阴阳师》玩家们非常关心的话题。在这篇文章中&#xff0c;我们将向您展示如何在《阴阳师》游戏中使用S5搭建工具S5一键搭…

有奖快来抱走全新HUAWEI WATCH GT4

亲爱的openGauss用户&#xff0c; 为了给您提供更好的社区体验&#xff0c;现诚邀您参与openGauss社区满意度问卷调研。您的每一个宝贵建议都是我们进步的方向。 手机扫描二维码即可填写问卷&#xff0c;请根据您真实的体验情况填写问卷&#xff0c;问卷反馈越真实有效越有机…

Kafka基本原理、生产问题总结及性能优化实践 | 京东云技术团队

Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的&#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各种需求场景&a…

超越终端:Java语言在命令行环境中的无限潜力

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任 Java 开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、Java&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏&…

为什么要安装防静电门禁闸机

安装防静电门禁闸机可以带来以下几个方面的好处&#xff1a; 防止静电干扰&#xff1a;静电是一种非常危险的物理现象&#xff0c;它可以对电子元器件、电路板和其他敏感设备造成损害&#xff0c;甚至导致设备故障和生产中断。防静电门禁闸机可以有效地防止静电的产生和传导&am…

药监局瑞数6 分析 2023版

网站地址 aHR0cHM6Ly93d3cubm1wYS5nb3YuY24veWFvcGluL3lwamdkdC9pbmRleC5odG1s 清除cookie 选中脚本调试 第一次获取的结果ts 第二次获取的结果是一个294cc83.js&#xff0c;可以固定 第三次获取的结果 content和ts属性每次都要换,还有ts属性一定要和content对应,否则你怎么…

面试算法47:二叉树剪枝

题目 一棵二叉树的所有节点的值要么是0要么是1&#xff0c;请剪除该二叉树中所有节点的值全都是0的子树。例如&#xff0c;在剪除图8.2&#xff08;a&#xff09;中二叉树中所有节点值都为0的子树之后的结果如图8.2&#xff08;b&#xff09;所示。 分析 下面总结什么样的节…

【C++项目实战】基于多设计模式下的同步于异步的日志系统(完整详细)

&#x1f307;个人主页&#xff1a;平凡的小苏 &#x1f4da;学习格言&#xff1a;命运给你一个低的起点&#xff0c;是想看你精彩的翻盘&#xff0c;而不是让你自甘堕落&#xff0c;脚下的路虽然难走&#xff0c;但我还能走&#xff0c;比起向阳而生&#xff0c;我更想尝试逆风…

机器人仿真-gazebo学习笔记(4)xacro和传感器添加

1.xacro简介 URDF文件不具备代码复用的特性&#xff08;在上一篇文章也能发现&#xff0c;其实左右轮是极其相似的但还是要单独描述&#xff09;&#xff0c;一个复杂的机器人模型会拥有大量了的传感器和关节组件&#xff0c;这时候使用URDF文件就太难阅读了。精简化、可复用、…

阿里云急了,云服务器老用户优惠价格99元一年!

2023阿里云服务器双11优惠价格99元一年经济型e实例&#xff0c;并且续费不涨价&#xff0c;云服务器ECS-经济型e实例2核2G配置、3M带宽、40G ESSD entry系统盘优惠价99元一年&#xff0c;原价956.64元/年&#xff0c;可用于中小型网站建设、开发测试、小程序或app搭建&#xff…

Cross-Entropy Loss(多分类损失函数)

文章目录 1. 网络输出output&#xff1a;score2. Cross-Entropy Loss(多分类损失函数) 1. 网络输出output&#xff1a;score 2. Cross-Entropy Loss(多分类损失函数) 先用softmax function把score 变成 probabilities。再用交叉熵损失函数来进行Loss的计算

基于深度学习的动物识别 - 卷积神经网络 机器视觉 图像识别 计算机竞赛

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…