基于STM32F1的自动追光云台(代码开源)

news/2024/10/19 23:33:32/

前一段时间做了一个自动追光云台(大家感兴趣的也可以自己DIY一个呀),用来自动捕捉阳光供太阳板发电提高太阳板的发电效率,我用了一款STM32f103c8t6为主控来控制云台舵机的旋转。感光元器件使用的是光敏传感器(淘宝随便买一款啊很便宜)来感知光强,所以用ADC进行模拟量采集光强信息。下面就来介绍介绍它吧!

  1. 舵机旋转的控制

舵机的主要组成部分为伺服电机,伺服就是服从信号的要求从而做出相应动作。在信号发出之前,电机停止不动;信号发出之后,电机立即做旋转运动。因此我们就可以给舵机输入不同的信号,来控制其旋转到不同的角度。舵机接收的是PWM信号,当信号进入内部电路产生一个偏置电压,触发电机通过减速齿轮带动电位器移动,使电压差为零时,电机停转,从而达到伺服的效果。简单来说就是给舵机一个特定的PWM信号,舵机就可以旋转到指定的位置。

舵机上有三根线,分别是GND、VCC和SIG,也就是地线、电源线和信号线,其中的PWM波就是从信号线输入给舵机的。一般来说,舵机接收的PWM信号频率为50HZ,即周期为20ms。当高电平的脉宽在0.5ms-2.5ms之间时舵机就可以对应旋转到不同的角度。

舵机是通过高电平的占空比来调节输出轴的角度,舵机的驱动信号是50Hz的方波信号,每个周期为20ms,其中高电平的占比在0.5ms-2.5ms之间。

在这Keil里编写PWM程序,通过使用TIM_SetComparex(TIMx, Compare)函数来改变PWM的输出占空比。ok下面就是PWM初始化程序

void PWM2_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);  //开启定时器2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //设置PA1引脚为复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);TIM_InternalClockConfig(TIM2);  //定时器2内部时钟配置TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;   //ARR自动重装值TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;   //PSC时钟预分频,这里我们使用的是f103的72MHz所以除以72进行分频得到1MHz的的时钟频率TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;             //输出为PWM模式2的向下计数模式TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;      //设置问低电平有效TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出通道使能TIM_OCInitStructure.TIM_Pulse = 0;                            //CCR这里设置为0,下面使用函数对其在运行时改变他的他的它大小TIM_OC2Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE);   //开启定时器2 
}void PWM_SetCompare2(uint16_t Compare)
{TIM_SetCompare2(TIM2, Compare);   //设置PWM占空比还能输函数,通过改变Compare的值来PWM占空比,其范围为0~ARR
}/*定时器3的初始化和定时器2是一样的了*/
void PWM3_Init(void)
{RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);TIM_InternalClockConfig(TIM3);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1;        //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;        //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 0;        //CCRTIM_OC4Init(TIM3, &TIM_OCInitStructure);//    TIM_OC4PreloadConfig(TIM3,TIM_OCPreload_Enable);
//    TIM_ARRPreloadConfig(TIM3,ENABLE);TIM_Cmd(TIM3, ENABLE);
}void PWM_SetCompare3(uint16_t Compare)
{TIM_SetCompare4(TIM3, Compare);
}

2.光敏传感器的模拟量采集

光敏传感器在上电后受到不同强度的光照射后会通过传感器上信号线反馈出一个连续的模拟信号,但是单片机在处理信息的时候都是数字量的,所以我们需要把模拟量转化为数字量。stm32f103上有自带的ADC采样的功能,分为8位和12位的不同方式,在这里我使用了12位ADC的模式。在这个追光云台上有4个光敏传感器模块,要对4个模拟量都进行采集,所以使用ADC的扫描模式,并且涉及到多通道数据采集为了不给CPU产生负担,准备使用DMA数据转运的方式进行数据的储存。

1.ADC简单介绍

ADC电路就是指模数转换电路。也就是将模拟信号变为数字信号。一般用在数据采集方面。ADC,Analog-to-Digital Converter的缩写,指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件。真实世界的模拟信号,例如温度、压力、声音或者图像等,需要转换成更容易储存、处理和发射的数字形式。STM32 的 ADC 最大的转换速率为 1Mhz,也就是转换时间为 1us(在 ADCCLK=14M,采样周期为 1.5 个 ADC 时钟下得到),不要让 ADC 的时钟超过 14M,否则将导致结果准确度下降。STM32 将 ADC 的转换分为 2 个通道组:规则通道组注入通道组。规则通道相当于你正常运行的程序,而注入通道呢,就相当于中断。在你程序正常执行的时候,中断是可以打断你的执行的。同这个类似,注入通道的转换可以打断规则通道的转换, 在注入通道被转换完成之后,规则通道才得以继续转换。12位ADC是一种逐次逼近型模拟数字转换器。它有多达18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐方式存储在16位数据寄存器中。

2.DMA简单介绍

DMA,全称为:Direct Memory Access,即直接存储器访问。DMA 传输方式无需 CPU 直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路,能使 CPU 的效率大为提高。

(这个图是参考江科大自动化协的)

好了直接上代码吧!

/*AD*/
uint16_t AD_Value[4]; //AD数据存放的位置
void AD_Init(void)   
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);  //开启ADC1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);    //开启DMA1的时钟RCC_ADCCLKConfig(RCC_PCLK2_Div6);  //ADC挂载的时钟6分频GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);   //规则组通道的配置ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 3, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 4, ADC_SampleTime_55Cycles5);ADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;  //数据右对齐ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;ADC_InitStructure.ADC_ScanConvMode = ENABLE;ADC_InitStructure.ADC_NbrOfChannel = 4;ADC_Init(ADC1, &ADC_InitStructure);DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;  //数据搬运的地址DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;       //数据将要搬到的地址DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;DMA_InitStructure.DMA_BufferSize = 4;DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_Cmd(DMA1_Channel1, ENABLE);ADC_DMACmd(ADC1, ENABLE);ADC_Cmd(ADC1, ENABLE);ADC_ResetCalibration(ADC1);while (ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while (ADC_GetCalibrationStatus(ADC1) == SET);ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

3.云台自动追光的实现方法

在图中可以看到,四个光敏传感器安装在四个角上,因为它们感受到的光强都是不同的,这里我处理的方法是avt=左上和右上之和除2、avb=左下和右下之和除2、avl=左上和左下之和除2、avr=右上和右下之和除2,这样就得到了avt,avb,abl,avr这4个值。在对这4个值进行对比判断时有一个特别重要的量不得不说,那就是tol它就是决定着我们这个云台对光强的灵敏度了,它就相当于一个阈值把要判断的结果卡在这个范围内,只有在这个范围内,才能进行接下来的上面得到的4个值的比较。

  1. 上下差值dvert=avt-avb 上-下 ,左右差值dhoriz=avl-avr 左-右;

  1. 如果上下差值dvert在tol(这就体现了上面说到的灵敏度这个概念了,tol越小就说明上下差值很容易满足在tol范围外)之外就可以比较avt和avb的大小了,每次判断增加或减速上下旋转舵机的占空比大小了最终使得上下差值进入tol范围内,舵机不在旋转达到我们的目标;

  1. 那么判断左右就和上下一样的方法啦。

这里就是主函数程序了

Servo1_SetAngle(50);             //控制上下旋转,PA1
Servo2_SetAngle(100);       //控制左右旋转,PB1
uint8_t tol=2;               //照度的相应范围,越小越灵敏while (1){zs = (float)AD_Value[0]/ 4095 * 100;   //PA4zx = (float)AD_Value[1]/ 4095 * 100;   //PA5ys = (float)AD_Value[2]/ 4095 * 100;   //PA6yx = (float)AD_Value[3]/ 4095 * 100;   //PA7avt=(zs+ys)/2;   //上avb=(zx+yx)/2;   //下avl=(zs+zx)/2;   //左avr=(ys+yx)/2;   //右dvert=avt-avb;   //上-下dhoriz=avl-avr;  //左-右if (-1*tol > dvert || dvert > tol) {if (avt > avb){servov++;if (servov > servovLimitHigh) { servov = servovLimitHigh;}}else if (avt < avb){servov--;if (servov < servovLimitHigh){servov = servovLimitLow;}}Servo1_SetAngle(50+servov);     //PA1}//检查差异是否在公差范围内,否则改变水平角度  if (-1*tol > dhoriz || dhoriz > tol) {if (avl > avr)         //左>右{servoh++; if (servoh > servohLimitHigh){servoh = servohLimitHigh;}}else if (avl < avr)    //右<左{servoh--;if (servoh < servohLimitLow){servoh = servohLimitLow;}}Servo2_SetAngle(100+servoh);     //PB1}Delay_ms(1);}
}

原工程代码:

链接:https://pan.baidu.com/s/1ZhUwJNlQqWLXtNjR9RtSdw

提取码:m5w6

创作不易在这里如有不对的地方还请大家可以指出!!


http://www.ppmy.cn/news/625995.html

相关文章

OpenGL入门教程

概述 OpenGL OpenGL是渲染2D、3D矢量图形硬件的一种软件接口。本质上说&#xff0c;它是一个3D图形和模型库&#xff0c;具有高度的可移植性&#xff0c;并且具有非常快的渲染速度。OpenGL并不是一种语言&#xff0c;而是更像一个C运行时函数库。它提供了一些预包装的功能&am…

echart 绘制一个基础的中国地图

echart&#xff0c;绘制一个基础的中国地图 代码示例 <template><div id"china_map_box"><div id"china_map"></div></div> </template><script> import * as echarts from echarts import chinaJson from ./…

从零开始用树莓派做一个社交距离检测仪(树莓派4B+摄像头推流教程)持续更新

最近&#xff0c;内蒙古地区的疫情越来越严重了&#xff0c;我们本地也开始了全体核酸检测&#xff0c;在排队的过程中&#xff0c;往往会有排队距离太近的情况发生。我有很多做志愿者的朋友&#xff0c;大家在聊天的时候也会时常提到这个问题。因此&#xff0c;我决定使用树莓…

基于树莓派的追光系统(python)

目录 前言 一、材料 二、硬件--控制逻辑 1.主设备的准备 1.启用树莓派的i2c设备 2.安装python-smbus 2.从设备的准备 1.BH1750 2.L298N驱动芯片 3.云台的准备 1.增加电机固定模块 2.增加bh1750固定模块 三、软件--程序逻辑 1.总程序逻辑 2.光强检测程序逻辑 3.…

利用ESP32控制LED亮灭

1.项目背景 随着全球政治经济水平的不断提高&#xff0c;许多的问题也产生了。其中单身独居人士家中安全问题也十分突出。 本系统利用esp32实现了远程控制家中灯亮灭&#xff0c;可以减少入室盗窃的几率。而且配合监控系统也可以有效查看家中情况。 2.设计目标 基于esp32的远…

物联网卡在物联网生态中扮演着什么样的角色?

随着物联网行业的快速发展&#xff0c;联网设备更是呈指数级增长&#xff0c;物联网卡应运而生。为满足市场需求&#xff0c;三大运营商在建立各自物联网公共服务网络的基础上&#xff0c;打造物联网开放平台&#xff0c;推广物联网解决方案。。我们先来回顾下物联网的概念&…

【学习笔记】《基于φ-OTDR的分布式扰动传感系统定位算法研究-北交-通信与信息系统-吴》重点笔记

目录 一、绪论 1.1 引言 1.2 分布式光纤传感技术 1.2.1 基于瑞利散射的分布式光纤传感技术 1.2.2 基于拉曼散射的分布式光纤传感技术 1.2.3 基于布里渊散射的分布式光纤传感技术 1.3 φ-OTDR分布式传感系统及其定位算法研究现状 1.4 论文主要研究内容 1.4.1 课题背景 …

【学习笔记】数字图像基础02

数字图像基础 ---整理自老师课件 1. 视觉感知基础 彩色光波长范围&#xff1a; 400 ~ 700 nm&#xff1b; 描述 彩色光源质量的3个基本量&#xff1a; • 辐射率&#xff1a;从光源流出能量的总量&#xff1b;&#xff08;单位&#xff1a;瓦&#xff09;• 光强&#xff1a;给…