基于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

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景

Python中顺序结构和循环结构示例代码

《Python中顺序结构和循环结构示例代码》:本文主要介绍Python中的条件语句和循环语句,条件语句用于根据条件执行不同的代码块,循环语句用于重复执行一段代码,文章还详细说明了range函数的使... 目录一、条件语句(1)条件语句的定义(2)条件语句的语法(a)单分支 if(b)双分支 if-else(

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

Go Mongox轻松实现MongoDB的时间字段自动填充

《GoMongox轻松实现MongoDB的时间字段自动填充》这篇文章主要为大家详细介绍了Go语言如何使用mongox库,在插入和更新数据时自动填充时间字段,从而提升开发效率并减少重复代码,需要的可以... 目录前言时间字段填充规则Mongox 的安装使用 Mongox 进行插入操作使用 Mongox 进行更

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型