基于STM32CubeMX、STM32F103c8t6或Arduino的太阳能追光系统

目录

创作原因: 

太阳能追光系统

所需硬件

​编辑

STM32CubeMX+STM32F103c8t6

​编辑

STM32F103c8t6main.c

Arduino Uno代码 

成品

相关资料:

更新 :
​​​​​​​​​​​​​​


创作原因: 

本文章将分别从STM32CubeMX、STM32F103c8t6(Hal库)和Arduino两个方向进行记录,顺便也记录一下制作过程。本设备经过一系列改进原是全国赛比赛作品中的一部分,作为供电系统使用,本条内容仅为升级版前代,但经过测试可以作为太阳能追光系统使用,也可以根据自己的实际情况进行升级改造。该文章带有程序、硬件及板图,看明白,可以直做出成品!!!

废话不多说直接进入主题。

太阳能追光系统

所需硬件

1.光敏传感器*4

在这里插入图片描述

 因为四个传感器需要接的线太多,可以自己打个小板子,可以省去不少线,下面是我画的PCB板将4个光敏传感器跟x轴舵机接在同一块板子上

 

2.太阳能充电模块*1

我用的是CN3795,其他太阳能充电模块也都可以,只要与自己选择的太阳能板功率相匹配即可。

3.单片机

本文以stm32f103c8t6最小系统板和Arduino Uno R3+Uno R3拓展板(有拓展板好插线)为例,其他型号也同理。

4.电池

储存太阳能板发的电,根据实际需要选择即可

5.舵机*2

根据太阳能板大小选择合适的舵机,本文结尾带有太阳能追光系统支架的AutoCAD板图,较为小巧所以使用SG90(0-180°)的即可,想跟换大型太阳能板的根据需要自行放大即可,根据重量更换大号舵机如MG996(0-180°)

6.杜邦线若干

STM32CubeMX+STM32F103c8t6

我是用的是Hal库,用STM32CubeMX进行端口的配置,具体如下图所示:使用了四个如图所示的光敏传感器,分别接入ADC1的0-3号口,TIM1的CH1和CH2使用PWM模式分别控制X轴转动跟Y轴转动,并开了USART3串口作为调试口。

 ADC1使用了DMA采集模式,加快了数据的采集速度

STM32F103c8t6main.c

以下为Keil uVision5中的主程序,看到本条文章的人肯定都是带着想要程序的目的来的,我把主程序挂在下面供大家参考:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "dma.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#define tol   60     //照度的相应范围,越小越敏感,反之迟缓,如果需要修改灵敏度,改这里的数值即可  (取值10~100 根据环境光强度不同敏感度也不同,室内光源变化幅度大,阳光下变化小)
int servoh = 1500;             //默认角度(为设备的初始角度,1500对应的为90°)
int servohLimitHigh = 2500;   //水平转动最大角度,对应为180°(使用的为0-180°舵机)
int servohLimitLow = 500;      //水平转动最小角度,对应为0°(使用的为0-180°舵机)
int servov = 1500;             //默认角度(为设备的初始角度,1500对应的为90°)
int servovLimitHigh = 2500;   //垂直转动最大角度,对应为180°(使用的为0-180°舵机)
int servovLimitLow = 1500;     //垂直转动最小角度,对应为90°(使用的为0-180°舵机)

#include "stdio.h"
int fputc(int ch, FILE *f)    //     串口重定向(测试使用)
 {	 
	 HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xffff);
   return ch;
 }

uint16_t ADCvalue[4]={0};

void ADC_DMA_caiji()
{
  HAL_ADC_Start_DMA(&hadc1,(uint32_t *)ADCvalue,4);//启动DMA
}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)//DMA采集完成中断服务函数
{
//	printf("左上%d",ADCvalue[0]);
//	printf("右上%d",ADCvalue[1]);
//	printf("左下%d",ADCvalue[2]);
//	printf("右下%d\r\n",ADCvalue[3]);	
	HAL_ADC_Stop_DMA(&hadc1);//关闭DMA的ADC采集
	int lt = ADCvalue[0]; //左上
  int rt = ADCvalue[1]; //右上
  int ld = ADCvalue[2]; //左下
  int rd = ADCvalue[3]; //右下
	int avt = (lt + rt) / 2; 
  int avd = (ld + rd) / 2; 
  int avl = (lt + ld) / 2; 
  int avr = (rt + rd) / 2;
  int dvert = avt - avd;    //上下行 
  int dhoriz = avl - avr;	
	
	//检查差异是否在公差范围内,否则改变垂直角度
	if (-1*tol > dvert || dvert > tol) 
   {
      if (avt < avd)
      {
        servov = servov+11;
         if (servov > servovLimitHigh) 
         { 
           servov = servovLimitHigh;
         }
      }
      else if (avt > avd)
      {
        servov= servov-11;
        if (servov < servovLimitLow)
        {
          servov = servovLimitLow;
        }
      }
			__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,servov);
    }

  //检查差异是否在公差范围内,否则改变水平角度  
  if (-1*tol > dhoriz || dhoriz > tol) 
  {
    if (avl < avr)
    {
      servoh = servoh-11;
      if (servoh < servohLimitLow)
      {
        servoh = servohLimitLow;
      }
    }
    else if (avl > avr)
    {
       servoh = servoh+11;
       if (servoh > servohLimitHigh)
       {
         servoh = servohLimitHigh;
       }
    }
		__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,servoh);
  }
}

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_USART3_UART_Init();
  MX_TIM1_Init();
  /* USER CODE BEGIN 2 */
HAL_ADCEx_Calibration_Start(&hadc1);//校准ADC
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		ADC_DMA_caiji();
		HAL_Delay(50);//设备追光的间歇时间,数值增大,间歇时间延长,但不可省去!!!
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

Arduino Uno代码 

接下来为arduino 的代码,该代码经过测试可以直接使用,且相对于STM32来说更为简单,

#include <Servo.h> 

#define SERVOPINH  5 //水平舵机
#define SERVOPINV  6 //垂直舵机

#define dtime   30   //延时参数,数值越小相应速度越快,反之相应慢   单位毫秒 一般取值(10~100) 
#define tol   40     //照度的相应范围,越小越敏感,反之迟缓  (取值10~100 根据环境光强度不同敏感度也不同,室内光源变化幅度大,阳光下变化小)
/*以上2参数太小,会对光线的细微变化极其敏感,进而产生抖动,
  为消除抖动,可以使用滤波处理,或者调节参数,减慢反应时间或敏感度来应对。 */

          
// 水平舵机的设置
Servo horizontal;            //水平舵机
int servoh = 90;             //默认角度
int servohLimitHigh = 175;   //水平转动最大角度
int servohLimitLow = 5;      //水平转动最小角度

// 垂直舵机的设置
Servo vertical;              //垂直舵机
int servov = 90;             //默认角度
int servovLimitHigh = 180;   //垂直转动最大角度
int servovLimitLow = 90;     //垂直转动最小角度

// 4个光敏电阻模块的接线口   
const int ldrlt = A0;   //左上
const int ldrrt = A1;   //右上
const int ldrld = A2;   //左下
const int ldrrd = A3;   //右下

void setup()
{
  horizontal.attach(SERVOPINH); 
  vertical.attach(SERVOPINV);
  delay(100);
}

void loop() 
{
  //分别读取4个光敏电阻模块的照度值
  int lt = analogRead(ldrlt); //左上
  int rt = analogRead(ldrrt); //右上
  int ld = analogRead(ldrld); //左下
  int rd = analogRead(ldrrd); //右下
  
  //将邻近光敏电阻模块的读数平均    
  int avt = (lt + rt) / 2; 
  int avd = (ld + rd) / 2; 
  int avl = (lt + ld) / 2; 
  int avr = (rt + rd) / 2; 
  
  //再计算上下行和左右排平均值的差值
  int dvert = avt - avd;    //上下行 
  int dhoriz = avl - avr;

   //检查差异是否在公差范围内,否则改变垂直角度    
   if (-1*tol > dvert || dvert > tol) 
   {
      if (avt < avd)
      {
        servov = ++servov;
         if (servov > servovLimitHigh) 
         { 
           servov = servovLimitHigh;
         }
      }
      else if (avt > avd)
      {
        servov= --servov;
        if (servov < servovLimitLow)
        {
          servov = servovLimitLow;
        }
      }
      vertical.write(servov); //舵机旋转角度和光线相反的话 用(180- servov)或 (servov) 调换方向即可
    }

  //检查差异是否在公差范围内,否则改变水平角度  
  if (-1*tol > dhoriz || dhoriz > tol) 
  {
    if (avl < avr)
    {
      servoh = --servoh;
      if (servoh < servohLimitLow)
      {
        servoh = servohLimitLow;
      }
    }
    else if (avl > avr)
    {
       servoh = ++servoh;
       if (servoh > servohLimitHigh)
       {
         servoh = servohLimitHigh;
       }
    }
    horizontal.write(servoh); //舵机旋转角度和光线相反的话 用(180- servoh) 或 (servoh) 调换方向即可
  }
   delay(dtime);
}

成品

以下为制作过程中的效果图

 

 

相关资料:

看到这之前其实可以根据自己需要做出来了,想偷懒的我也把相关文件放在下面了,可自取~

太阳能追光支架AutoCAD板图:

https://download.csdn.net/download/qq_33288274/86624896?spm=1001.2014.3001.5503

STM32和Arduino的太阳能追光系统(所有程序文件):

https://download.csdn.net/download/qq_33288274/86625023?spm=1001.2014.3001.5503

更新 :

​​​​​​​​​​​​​​

基于STM32F4P6的太阳能追光系统板,可独立完成追光动作,并可通过串口与主控板通讯。

  • 39
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值