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

2024-02-19 12:30

本文主要是介绍基于STM32F1的自动追光云台(代码开源),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前一段时间做了一个自动追光云台(大家感兴趣的也可以自己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

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

这篇关于基于STM32F1的自动追光云台(代码开源)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/724655

相关文章

利用Python调试串口的示例代码

《利用Python调试串口的示例代码》在嵌入式开发、物联网设备调试过程中,串口通信是最基础的调试手段本文将带你用Python+ttkbootstrap打造一款高颜值、多功能的串口调试助手,需要的可以了... 目录概述:为什么需要专业的串口调试工具项目架构设计1.1 技术栈选型1.2 关键类说明1.3 线程模

Python Transformers库(NLP处理库)案例代码讲解

《PythonTransformers库(NLP处理库)案例代码讲解》本文介绍transformers库的全面讲解,包含基础知识、高级用法、案例代码及学习路径,内容经过组织,适合不同阶段的学习者,对... 目录一、基础知识1. Transformers 库简介2. 安装与环境配置3. 快速上手示例二、核心模

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

pytorch自动求梯度autograd的实现

《pytorch自动求梯度autograd的实现》autograd是一个自动微分引擎,它可以自动计算张量的梯度,本文主要介绍了pytorch自动求梯度autograd的实现,具有一定的参考价值,感兴趣... autograd是pytorch构建神经网络的核心。在 PyTorch 中,结合以下代码例子,当你

Python如何自动生成环境依赖包requirements

《Python如何自动生成环境依赖包requirements》:本文主要介绍Python如何自动生成环境依赖包requirements问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录生成当前 python 环境 安装的所有依赖包1、命令2、常见问题只生成当前 项目 的所有依赖包1、

使用Python实现全能手机虚拟键盘的示例代码

《使用Python实现全能手机虚拟键盘的示例代码》在数字化办公时代,你是否遇到过这样的场景:会议室投影电脑突然键盘失灵、躺在沙发上想远程控制书房电脑、或者需要给长辈远程协助操作?今天我要分享的Pyth... 目录一、项目概述:不止于键盘的远程控制方案1.1 创新价值1.2 技术栈全景二、需求实现步骤一、需求

Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码

《Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码》:本文主要介绍Java中日期时间转换的多种方法,包括将Date转换为LocalD... 目录一、Date转LocalDateTime二、Date转LocalDate三、LocalDateTim