STM32使用PWM控制舵机

2024-04-29 18:04
文章标签 使用 stm32 控制 舵机 pwm

本文主要是介绍STM32使用PWM控制舵机,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

系列文章目录

STM32单片机系列专栏

C语言术语和结构总结专栏


文章目录

1. 舵机简介

2. 硬件连接

3. 代码实现

3.1 PWM.c

3.2 PWM.h

3.3 Servo.c

3.4 Servo.h

3.5 main.c

3.6 完整工程文件


PWM和OC输出详解:

STM32定时器的OC比较和PWM​​​​​​​

1. 舵机简介

舵机是一种位置伺服驱动器器,并且是一种根据输入PWM信号占空比来控制输出角度的装置。通过PWM向伺服器发送一个控制信号时,输出轴就可以转到特定的位置。只在控制信号持续不变,伺服机构就会保持相对的角度位置不变。如果控制信号发生变化,输出轴的位置也会相应发生变化。

图片中的舵机型号为SG90,具有三根输入线:电源线,地线和信号线,PWM就是输入到信号线来控制舵机。控制信号进入内部驱动电路以后,获得直流偏置电压,从而产生周期为20ms,宽度为1.5ms的基准信号,将获得的直流偏置电压与电位器的电压比较,获得电压差输出。电压差的正负输出到电机驱动芯片决定电机的正反转,并且内部有一个电位器,用来检测当前舵机的角度。

输入PWM信号要求为:周期为20ms,高电平宽度为0.5ms - 2.5ms,也就是说周期是20ms,如果高电平时0.5ms,那么舵机输出轴转角为 -90°。

2. 硬件连接

本文是以STM32F103C8T6作为主控芯片,通过PA0端口输出PWM,实现控制180°舵机。

实际应用时,电源线连接5V,GND连接单片机的GND,PWM只是一个通信线,不需要大功率,所以PWM信号线连接单片机的一个引脚。

STM32舵机
5V+5V
GNDGND
PA0PWM

3. 代码实现

这里实现的功能为:按键控制舵机角度,每次按下舵机的角度增加30°,并且通过oled显示。要使用的库函数文件依然为:stm32f10x_tim.h,拖到最下面,在这里可以找到定时器TIM需要使用到的函数。

3.1 PWM.c

#include "stm32f10x.h" //PWM初始化
void PWM_Init(void)
{//开启时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟//GPIO初始化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(GPIOA, &GPIO_InitStructure);							//将PA1引脚初始化为复用推挽输出	//受外设控制的引脚,均需要配置为复用模式//配置时钟源TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟//时基单元初始化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的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;            //重复计数器,高级定时器才会用到TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元//输出比较初始化TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量TIM_OCStructInit(&TIM_OCInitStructure);                         //结构体初始化,若结构体没有完整赋值//则最好执行此函数,给结构体所有成员都赋一个默认值//避免结构体初值不确定的问题TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;               //输出比较模式,选择PWM模式1TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;       //输出极性,选择为高,若选择极性为低,则输出高低电平取反TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;   //输出使能TIM_OCInitStructure.TIM_Pulse = 0;								//初始的CCR值TIM_OC2Init(TIM2, &TIM_OCInitStructure);                        //将结构体变量交给TIM_OC2Init,配置TIM2的输出比较通道2//TIM使能TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}//PWM设置CCR
void PWM_SetCompare2(uint16_t Compare)
{TIM_SetCompare2(TIM2, Compare);		//设置CCR2的值
}

RCC_APB1PeriphClockCmd

  • TIM2 代表定时器2,它是STM32的一个基础硬件定时器。在STM32的某些系列中,TIM2连接到的是APB1总线。
  • ENABLE 是一个宏定义,用来开启某项功能,这里用来开启TIM2的时钟。如果传递 DISABLE 则会关闭外设的时钟。
  • 简单来说,RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 这行代码的作用是开启连接到APB1总线的定时器2(TIM2)的时钟。只有开启了时钟,程序中关于TIM2的其他功能(如计时、计数、PWM发生等)才能正常工作。

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  • GPIO的初始化中,选择AF_PP复用推挽输出,因为对于普通的开漏推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想用定时器来控制引脚,就需要使用复用开漏/推挽输出模式。

时基单元中的数值设置:

代入公式为:

  • 频率 = CK_PSC(72M) / (PSC + 1) / (ARR + 1) = 50
  • 占空比 = CCR / (ARR + 1) 
  • 分辨率为 = 1 / (ARR + 1)
  • 因为PSC和ARR的值不固定,所以通过验证得出最适合的值为:ARR = 20000, PSC = 71,这时,ARR的20000代表20ms,如果CCR = 500,就是0.5ms,对应0°(-90),CCR = 2500,就是2.5ms,对应180°(+90)

3.2 PWM.h

接着是PWM.h文件,这部分引用声明一下即可

#ifndef __PWM_H
#define __PWM_Hvoid PWM_Init(void);
void PWM_SetCompare2(uint16_t Compare);#endif

 

3.3 Servo.c

这部分是舵机的角度参数设置代码: 

#include "stm32f10x.h"                  // Device header
#include "PWM.h"//函    数:舵机初始化
void Servo_Init(void)
{PWM_Init();									//初始化舵机的底层PWM
}//舵机设置角度
void Servo_SetAngle(float Angle)
{PWM_SetCompare2(Angle / 180 * 2000 + 500);	//设置占空比//将角度线性变换,对应到舵机要求的占空比范围上
}

0°代表500,180°代表2500,角度相差180,CCR相差2000,所以角度除以180再乘以2000,加上偏移500。例如角度为180,180/180*2000 + 500 = 2500,对应180°

3.4 Servo.h

同样进行声明

#ifndef __SERVO_H
#define __SERVO_Hvoid Servo_Init(void);
void Servo_SetAngle(float Angle);#endif

3.5 main.c

使用刚刚编写的PWM和舵机代码函数来实现功能:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"uint8_t KeyNum;			//定义用于接收键码的变量
float Angle;			//定义角度变量int main(void)
{//模块初始化OLED_Init();		//OLED初始化Servo_Init();		//舵机初始化Key_Init();			//按键初始化//显示静态字符串OLED_ShowString(1, 1, "Angle:");	//1行1列显示字符串Angle:while (1){KeyNum = Key_GetNum();			//获取按键键码if (KeyNum == 1)				//按键1按下{Angle += 30;				//角度变量自增30if (Angle > 180)			//角度变量超过180后{Angle = 0;				//角度变量归零}}Servo_SetAngle(Angle);			//设置舵机的角度为角度变量OLED_ShowNum(1, 7, Angle, 3);	//OLED显示角度变量}
}

3.6 完整工程文件

STM32通过PWM驱动舵机

这篇关于STM32使用PWM控制舵机的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

一文详解如何使用Java获取PDF页面信息

《一文详解如何使用Java获取PDF页面信息》了解PDF页面属性是我们在处理文档、内容提取、打印设置或页面重组等任务时不可或缺的一环,下面我们就来看看如何使用Java语言获取这些信息吧... 目录引言一、安装和引入PDF处理库引入依赖二、获取 PDF 页数三、获取页面尺寸(宽高)四、获取页面旋转角度五、判断

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

使用Python删除Excel中的行列和单元格示例详解

《使用Python删除Excel中的行列和单元格示例详解》在处理Excel数据时,删除不需要的行、列或单元格是一项常见且必要的操作,本文将使用Python脚本实现对Excel表格的高效自动化处理,感兴... 目录开发环境准备使用 python 删除 Excphpel 表格中的行删除特定行删除空白行删除含指定

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

prometheus如何使用pushgateway监控网路丢包

《prometheus如何使用pushgateway监控网路丢包》:本文主要介绍prometheus如何使用pushgateway监控网路丢包问题,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录监控网路丢包脚本数据图表总结监控网路丢包脚本[root@gtcq-gt-monitor-prome

Python通用唯一标识符模块uuid使用案例详解

《Python通用唯一标识符模块uuid使用案例详解》Pythonuuid模块用于生成128位全局唯一标识符,支持UUID1-5版本,适用于分布式系统、数据库主键等场景,需注意隐私、碰撞概率及存储优... 目录简介核心功能1. UUID版本2. UUID属性3. 命名空间使用场景1. 生成唯一标识符2. 数

浅析Spring如何控制Bean的加载顺序

《浅析Spring如何控制Bean的加载顺序》在大多数情况下,我们不需要手动控制Bean的加载顺序,因为Spring的IoC容器足够智能,但在某些特殊场景下,这种隐式的依赖关系可能不存在,下面我们就来... 目录核心原则:依赖驱动加载手动控制 Bean 加载顺序的方法方法 1:使用@DependsOn(最直

SpringBoot中如何使用Assert进行断言校验

《SpringBoot中如何使用Assert进行断言校验》Java提供了内置的assert机制,而Spring框架也提供了更强大的Assert工具类来帮助开发者进行参数校验和状态检查,下... 目录前言一、Java 原生assert简介1.1 使用方式1.2 示例代码1.3 优缺点分析二、Spring Fr

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期