PWM原理+SG90舵机控制原理+STM32CubeMx配置输出可调PWM波

在这里插入图片描述

大家好我是 杰哥编程——猿学社

最近在做项目时需要stm32f103丝滑控制sg90舵机,软件采用HAL库+CubeMX方式配置32的定时器输出可变宽度PWM波,
特来分享一下。

欢迎关注我的Gitee仓库:https://gitee.com/wrj12138/embedSummary

PWM原理+SG90舵机控制原理+STM32CubeMx配置输出可调PWM波

本次项目使用1个stm32f103c8t6单片机、2个sg90舵机、一个二维舵机云台

二维舵机云台3D打印模型

3D打印模型网站链接:https://gitee.com/wrj12138/embedSummary/tree/master/SolarTrackingSystem/pic

  • 下图为我组装好的成品和散件

在这里插入图片描述
在这里插入图片描述

PWM脉冲宽度调制

原理:

PWM是一种应用广泛的利用微处理器的数字输出来对模拟电路进行控制的一种技术(即对脉冲宽度的控制)
PWM同时也是驱动蜂鸣器,驱动舵机,通信等重要的一环,
而对于初学者而言,点完灯的下一个程序就是驱动蜂鸣器,本小节简单介绍下PWM

PWM的全称是脉冲宽度调制(Pulse-width modulation),是通过将有效的电信号分散成离散形式从而来降低电信号所传递的平均功率的一种方式;

所以根据面积等效法则,可以通过对改变脉冲的时间宽度,来等效的获得所需要合成的相应幅值频率的波形;具体如下图所示;

在这里插入图片描述

由上图可知,脉冲宽度调制使用一个脉冲宽度会被调制的方波,并且波型的平均值会有所变化。

数学公式啥的就不教大家了 反正你们也看不懂。
反正就是高电平所占时间长度比上一个周期的长度等于占空比
一个周期的时间长度值的倒数就是频率的值 就这样!!!

在硬件的角度去理解pwm波知识就是 :

通过锯齿波/三角波(载波)所需要合成的波形(调制波)进行比较,然后确定PWM所需要输出的极性

看图:酱!!!!

在这里插入图片描述

sg90舵机控制原理

在这里插入图片描述

参数

长话短说 !!!

  • 接线方法:

红线 接 5v电源
棕色褐色黑色线 接 GND地
橙色黄色线 接 信号线

  • 相关参数:

尺寸很小
力矩不大
速度一般般
工作电压 4V~6V

工作原理

舵机的控制信号为周期是20ms 的脉宽调制(PWM)信号,其中脉冲宽度从0.5ms-2.5ms,相对应舵盘的位置为0—180度,呈线性变化。也就是说,给它提供一定的脉宽,它的输出轴就会保持在一个相对应的角度上,无论外界转矩怎样改变,直到给它提供一个另外宽度的脉冲信号,它才会改变输出角度到新的对应的位置上。舵机内部有一个基准电路,产生周期20ms,宽度1.5ms的基准信号,有一个比较器,将外加信号与基准信号相比较,判断出方向和大小,从而产生电机的转动信号。

控制电路板接受来自信号线相应的PWM控制信号,进而控制电机转动,电机带动一系列齿轮组,减速后传动至输出舵盘。舵机的输出轴和位置反馈电位计是相连的,舵盘转动的同时,带动位置反馈电位计,电位计将输出一个电压信号到控制电路板,进行反馈,然后控制电路板根据所在位置决定电机的转动方向和速度,从而达到目标停止。

舵机的控制需要MCU产生一个周期为20ms的脉冲信号,以0.5ms到2.5ms的高电平来控制舵机转动的角度。

如图:

在这里插入图片描述

在这里插入图片描述

  • 简单来说就是 用pwm信号控制 周期为20ms 频率50Hz 可转动角度范围0~180° 控制占空比范围2.5%~12.5%

程序设计编写

1 用CubeMX建立工程

这里采用的是常用的STM32F103C8T6芯片,选择外部高频时钟,SWD调试.

在这里插入图片描述
在这里插入图片描述

设置主时钟为72MHz

在这里插入图片描述

2 stm32如何配置pwm频率

  • 前文讲到pwm的原理和sg90的工作条件 周期20ms 频率50hz
  • 我们讲stm32的系统主频sys_freq配置为72Mhz,72x10^6
  • 定时器配置pwm输出有3个最重要的参数
  1. 预分频(psc):CPU运行频率先经过它分频再进入计时器,如CPU运行在 x Mhz 下,预分频为 y,那进入计时器的频率也就为 x/(y+1) Mhz(因为从0计数,所以是y+1)。
  2. 自动重装值(arr):一次周期的计数长度
  3. 脉冲长度:输出脉冲的技术长度
相信大家都有学过stm32的时钟树分频!!!
  • 所以通过设置psc和arr后 可以得到stm32的pwm输出频率:

(sys_freq/psc)/arr = Frequency
其中:Frequency=50Hz sys_freq=72x10^6Hz
为了方便计算 将psc设为1000 即可得出arr=1440
由于我们的舵机控制输出的pwm占空比是随时需要变化的 所以没有对脉冲值进行设置 默认0

懂了的扣懂了 不懂的扣眼珠子!!! 
下面进入实操

配置一个定时器的两个通道

在这里插入图片描述

3 生成代码 这个不用我教了吧

在这里插入图片描述
在这里插入图片描述

生成代码GENERATE CODE

4 业务逻辑代码编写原理

这里我借鉴了arduino丝滑控制舵机的逻辑

  for (pos = 0; pos <= 180; pos += 1) { // goes from 0 degrees to 180 degrees
    // in steps of 1 degree
    myservo.write(pos);              // tell servo to go to position in variable 'pos'
    delay(15);                       // waits 15ms for the servo to reach the position
  }

即15ms控制一次舵机,一次仅移动1°
还记得上面给定时器配置了重装载值arr为1440和sg90控制0°~180°角度范围对应的占空比范围为2.5%~12.5%

所以我们的脉冲值范围为1440*`2.5%~12.5%`=`36~180`
所以我们只需要给定时器对应的两个pwm通道写入`36~180`
即可输出0~180°

根据arduinio控制的方式一次仅移动1°
我当时脑袋迷糊了 一次移动了2个数值,对应角度大约在3°左右
不影响 不过也还算丝滑哈哈哈哈(王婆卖瓜!!!)

然后看了别的博客 发现他们都用下面这个函数直接修改对应通道的脉冲值

#define __HAL_TIM_SET_COMPARE(__HANDLE__, __CHANNEL__, __COMPARE__) \
  (((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCR1 = (__COMPARE__)) :\
   ((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCR2 = (__COMPARE__)) :\
   ((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCR3 = (__COMPARE__)) :\
   ((__HANDLE__)->Instance->CCR4 = (__COMPARE__)))

使用起来比较丝滑,修改占空比的间隙,舵机比较不呆。所以直接用它得了。
不过看它的命名方式有下划线,不像是st官方对用户开放的api,将就一下吧!!!

5 上代码

只上传了控制的接口源码 主函数里直接调用就行
你只要能保证15ms调用一次下面的函数
就能让舵机转动看起来比较丝滑

//2.5%到12.5%
//0.025*1440 = 36      reload arr min   0°
//0.125*1440 = 180     reload arr max   180°
#define ROTA_ANGLE_VAL 1 //舵机角度控制间隙  我自己代码用的2 特地为了你们改为1了 哈哈
#define SG90_TIM_MAX	180 //sg90 180° 对应的定时器pwm输出最大脉冲值
#define SG90_TIM_MIN	36  //sg90 0° 对应的定时器pwm输出最小脉冲值

static int sg90_1_angle = 0;
static int sg90_2_angle = 0;

/*
* 描述:
    函数名字忘记是抄谁的了 将就看吧 逻辑也比较直接 大家可以在评论区优化一下喔
* 函数原理:
        如 控制通道1 且控制方向为正向 则PWM输出脉冲值增加 
        增加值为ROTA_ANGLE_VAL 
        且水平旋转范围最大为180°(其实要360°都要能转的)
        俯仰角范围最大为90° (90°对应脉冲值你们自己算啊 我写的108)
* 参数:
    1.选择要控制的通道
    2:选择要控制的方向
* 返回:
    无
*/
void sg90_RotateAngle(uint8_t ch, uint8_t dir)
{

	if (ch==1)
	{
		if (dir==ANGLE_ADD)
		{
			sg90_1_angle+=ROTA_ANGLE_VAL;
			(sg90_1_angle>SG90_TIM_MAX)?(sg90_1_angle=SG90_TIM_MAX):(0);
		}else
		{
			sg90_1_angle-=ROTA_ANGLE_VAL;
			(sg90_1_angle<SG90_TIM_MIN)?(sg90_1_angle=SG90_TIM_MIN):(0);
		}
		__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,sg90_1_angle);
	}else if (ch==2)
	{
		if (dir==ANGLE_ADD)
		{
			sg90_2_angle+=ROTA_ANGLE_VAL;
			(sg90_2_angle>108)?(sg90_2_angle=108):(0);
		}else
		{
			sg90_2_angle-=ROTA_ANGLE_VAL;
			(sg90_2_angle<SG90_TIM_MIN)?(sg90_2_angle=SG90_TIM_MIN):(0);
		}
		__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_2,sg90_2_angle);
	}
	return;
	
}

6 上主函数

main.c伪代码

int main(void)
{
  MX_TIM1_Init();
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
  while (1)
  {
    HAL_Delay(2);
    sg90_RotateAngle(1,ANGLE_ADD);
    sg90_RotateAngle(2,ANGLE_ADD);
  }
}

本次分享到此为止,如有问题欢迎各位指出!!!

产品实物图

stm32追光储能系统实物图

在这里插入图片描述

在这里插入图片描述

基于STM32 HAL库编写PWM控制SG90舵机的代码通常会包括初始化GPIO、TIM(Timer)、设置PWM占空比以及更新舵机脉冲等步骤。这里是一个简化的示例,假设我们已经配置了GPIOB作为输出,连接到SG90舵机的信号线: ```c #include "stm32f1xx_hal.h" // 舵机配置 #define DEGREE_PER_PULSE 18 // 每个PWM周期对应的角度变化 #define MIN_DEGREES -90 #define MAX_DEGREES 90 #define PWM_FREQUENCY 50 // PWM频率 // GPIO初始化 static GPIO_TypeDef* GPIOB = GPIOB_base; // 假设GPIOB用于PWM输出 __IO uint16_t GPIO_Pin = GPIO_PIN_1; // 舵机信号引脚 // TIM初始化 static TIM_HandleTypeDef htim2; void setup(void) { // 初始化GPIO GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 初始化TIM htim2.Instance = TIM2; htim2.Init.Prescaler = (SystemCoreClock / PWM_FREQUENCY) - 1; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 1000; // 1000个周期等于1s HAL_TIM_PWM_Init(&htim2); HAL_TIM_PWM_MspInit(&htim2); } void setServoAngle(int angle) { int dutyCycle = map(angle, MIN_DEGREES, MAX_DEGREES, 0, 1000); // 映射角度到0-1000的 Duty Cycle HAL_TIM_PWM_SetCompare(&htim2, TIM_CHANNEL_1, dutyCycle); } // 更新舵机的角度 void updateServoPosition(float position) { int angle = map(position, 0.f, 1.f, MIN_DEGREES, MAX_DEGREES); setServoAngle(angle); } int main(void) { // 开启系统时钟并进入主循环 HAL_Init(); while (1) { updateServoPosition(servoPosition); // 假设servoPosition是你需要动态改变的角度 } return 0; } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值