STM32-PWM驱动舵机——HAL库

什么是舵机?

舵机,也叫伺服电机,在嵌入式开发中,舵机作为一种常见的运动控制组件,具有广泛的应用。

舵机型号介绍:

市面上常见的舵机型号有 SG90、MG90S、MG995、MG996R 等等,主要是扭矩大小、工作电压大小、齿轮材质塑料或金属的不同。

一般分为180度和360度:

  • 180度:可以控制旋转角度、有角度定位。上电后舵机自动复位到0度,通过一定参数的脉冲信号控制它的角度。
  • 360°舵机版本不可控制角度,只能控制顺时针旋转、逆时针旋转、停止和调节转速。

引脚接线参考如下:

SG90STM32
PWM 信号线(橙色线)任意GPIO
VCC(红线)3.3/5V
GND(棕色线)GND

SG90原理

        舵机的控制信号是通过脉冲宽度调制(PWM)来实现的。PWM 信号的周期通常为20ms,而脉冲宽度则在 0.5ms 至 2.5ms 之间变化。这个脉冲宽度与舵盘的位置呈线性关系,范围从0度到180度。

        当给舵机提供特定宽度的脉冲信号时,输出轴会保持在相应的角度上,不受外界转矩的影响,直到接收到不同宽度的脉冲信号才会改变输出角度,使舵盘移动到新的位置。舵机内部有一个基准电路,产生周期为 20ms、宽度为 1.5ms 的基准信号。同时,还有一个比较器,用于将外部输入信号与基准信号进行比较,以确定转动方向和幅度,并生成驱动电机转动的信号。

        为了控制舵机,需要使用单片机来生成周期为 20ms 的脉冲信号,并通过控制脉冲的高电平时间在 0.5ms 至 2.5ms 之间来控制舵机的角度。这样,我们可以通过调整 PWM 信号的脉冲宽度来精确控制舵机的位置和运动。

以 SG90,180度版为例,那么对应的控制关系是这样的:

脉冲高电平角度占空比
0.5ms2.5%
1.0ms45°5.0%
1.5ms90°7.5%
2.0ms135°10.0%
2.5ms180°12.5%

PWM驱动舵机:

 因为:PWM 信号的周期通常为20ms,高电平宽度为0.5ms~2.5ms

代码:

TIM_HandleTypeDef tim3_handle = {0};
// init函数
void tim3_init(void)
{
    TIM_OC_InitTypeDef pwm_config = {0};
    
    tim3_handle.Instance = TIM3;
    tim3_handle.Init.Prescaler = 7200 - 1;
    tim3_handle.Init.Period = 200 - 1;
    tim3_handle.Init.CounterMode = TIM_COUNTERMODE_UP;
    HAL_TIM_PWM_Init(&tim3_handle);
    
    pwm_config.OCMode = TIM_OCMODE_PWM1;
    pwm_config.Pulse = 100;
    pwm_config.OCPolarity = TIM_OCPOLARITY_HIGH;
    HAL_TIM_PWM_ConfigChannel(&tim3_handle, &pwm_config, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&tim3_handle, TIM_CHANNEL_1);
}
//msp函数
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM3)
    {
        GPIO_InitTypeDef gpio_initstruct;
        //打开时钟
        __HAL_RCC_GPIOA_CLK_ENABLE();                           // 使能GPIOB时钟
        __HAL_RCC_TIM3_CLK_ENABLE();
        
        //调用GPIO初始化函数
        gpio_initstruct.Pin = GPIO_PIN_6;                    // 两个LED对应的引脚
        gpio_initstruct.Mode = GPIO_MODE_AF_PP;             // 推挽输出
        gpio_initstruct.Pull = GPIO_PULLUP;                     // 上拉
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;           // 高速
		HAL_GPIO_Init(GPIOA, &gpio_initstruct);
		
		gpio_initstruct.Pin = GPIO_PIN_1;
		gpio_initstruct.Mode = GPIO_MODE_INPUT;
		gpio_initstruct.Pull = GPIO_PULLUP;           // 上拉
        gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速(可选)
		HAL_GPIO_Init(GPIOA, &gpio_initstruct);       // 初始化 GPIOA 引脚 1
    }
}
//修改CCR值的函数
void tim3_compare_set(uint16_t val)
{
    __HAL_TIM_SET_COMPARE(&tim3_handle, TIM_CHANNEL_1, val);
}

void sg90_init(void)
{
    tim3_init();
}

void sg90_angle_set(uint16_t angle)
{
    uint16_t CCRx = (1.0 / 9.0) * angle + 5.0;
    tim3_compare_set(CCRx);
}

uint8_t key(void)
{
    uint8_t KeyNum = 0;
   

    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET)
    {
        // 按键按下,等待去抖动
        delay_ms(20);
        if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET)  // 确认按键仍然按下
        {
            while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_SET);  // 等待按键释放
            delay_ms(20);  // 再次延时去抖动
            KeyNum = 1;  // 按键按下并释放,返回 1
        }
    }
	else
	{
		// PA1 是低电平
		delay_ms(20);
		if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET) 
		{
		while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1)==GPIO_PIN_RESET);
		delay_ms(20);
		KeyNum=2;
	}
}
	return KeyNum;
}

已知 PWM 信号的周期为20ms;高电平 0.5ms 指向 0° 位置,2.5ms 指向 180° 位置。如果我们要指向 angle°:

2.5-0.5=2ms,对应于180°

CCRx / (199 + 1) * 20 = 0.5 +(angle / 180)× 2

于是 CCRx =(1.0 / 9.0) * angle + 5.0

void SG_Control(uint16_t angle)
{
   float CCRx;
   CCRx =(1.0 / 9.0) * angle + 5.0;                             //占空比值 = 1/9 * 角度 + 5
   __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, (uint16_t )CCRx);
}

 

main.c

uint8_t KeyNum;			//定义用于接收键码的变量
float angle;
int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
    led_init();                         /* 初始化LED灯 */
    sg90_init();

    while(1)
    { 
		KeyNum=key();
		if(KeyNum==1)
		{
			angle+=30;
			if(angle>180)
				angle=0;
		}
       sg90_angle_set(angle);
    }
}

 delay.c

/**
  * @brief  微秒级延时
  * @param  nus 延时时长,范围:0~233015
  * @retval 无
  */
void delay_us(uint32_t nus)
{
    uint32_t temp;
    SysTick->LOAD = 72 * nus;                           /* 设置定时器重装值 */
    SysTick->VAL = 0x00;                                /* 清空当前计数值 */
    SysTick->CTRL |= 1 << 2;                            /* 设置分频系数为1分频 */
    SysTick->CTRL |= 1 << 0;                            /* 启动定时器 */
    do
    {
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16)));     /* 等待计数到0 */
    SysTick->CTRL &= ~(1 << 0);                         /* 关闭定时器 */
}

/**
  * @brief  毫秒级延时
  * @param  nms 延时时长,范围:0~4294967295
  * @retval 无
  */
void delay_ms(uint32_t nms)
{
    while(nms--)
        delay_us(1000);
}
 
/**
  * @brief  秒级延时
  * @param  ns 延时时长,范围:0~4294967295
  * @retval 无
  */
void delay_s(uint32_t ns)
{
    while(ns--)
        delay_ms(1000);
}

/**
  * @brief  重写HAL_Delay函数
  * @param  nms 延时时长,范围:0~4294967295
  * @retval 无
  */
void HAL_Delay(uint32_t nms)
{
    delay_ms(nms);
}

STM32HAL驱动舵机可以通过PWM信号实现。以下是一个简单的示例代码,用于STM32F4 Discovery板上的舵机控制。 ```c #include "stm32f4xx_hal.h" #define PWM_PERIOD 20000 // PWM period in microseconds #define PWM_STEPS 100 // Number of PWM steps TIM_HandleTypeDef htim; uint32_t pulse_width = 1500; // Pulse width in microseconds void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM_Init(); HAL_TIM_PWM_Start(&htim, TIM_CHANNEL_1); while (1) { __HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_1, pulse_width); HAL_Delay(20); pulse_width += 10; if (pulse_width > 2500) pulse_width = 1500; } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } } static void MX_TIM_Init(void) { TIM_MasterConfigTypeDef sMasterConfig; TIM_OC_InitTypeDef sConfigOC; htim.Instance = TIM4; htim.Init.Prescaler = (SystemCoreClock / 1000000) - 1; htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = PWM_PERIOD - 1; htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_PWM_Init(&htim) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = (uint32_t)(PWM_PERIOD / PWM_STEPS); sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_9; GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = GPIO_AF2_TIM4; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } void Error_Handler(void) { while (1) { } } ``` 在这个例子中,我们使用了STM32的定时器TIM4作为PWM输出。PWM周期为20毫秒,即50赫兹,每个周期分为100个步骤。在主循环中,我们将舵机的角度从0度到180度逐渐增加,然后从180度到0度逐渐减小,循环控制舵机的转动。 需要注意的是,舵机的角度控制范围通常是0度到180度(有些舵机可能不同),对应的脉冲宽度范围通常是500微秒到2500微秒。因此,在实际应用中,需要根据具体舵机的规格书来确定PWM周期和步数,以及脉冲宽度的范围。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

庆庆知识库

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值