智能小车之舵机控制
Posted 旭日初扬
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能小车之舵机控制相关的知识,希望对你有一定的参考价值。
目录
一、输出比较功能分析
TIM5_PWM_Init(9999,143,TIM5);
IM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
设置CCR寄存器
void SetJointAngle(float angle)
angle=(u16)(50.0*angle/9.0+249.0);
// 设置捕获比较寄存器1的值
TIM_SetCompare1(TIM5,angle);
定义:自动重装载值ARR=9999,预分频系数psc=143
设置自动重装值,当计数器的计数值=arr发生定时器中断,且计数器重新开始计数。
CNT计数器计数值被捕获/比较寄存器获取,当CNT的值=arr时,OCxREF信号极性发生反转,当指定OCx通道的高电平为有效电平时,OCxREF=1为有效电平,OCxREF=0为无效电平,并且会产生比较中断 CCxI,相应的标志位 CCxIF(SR 寄存器中)会置 位。然后 OCxREF 再经过一系列的控制之后就成为真正的输出信号 OCx/OCxN。
二、PWM封装
/**********************************************************************************
PWM初始化
PWM 信号的频率的计算公式为:F = TIM_CLK/(ARR+1)*(PSC+1)。
其中 TIM_CLK 等于 72MHZ,ARR 即自动重装载寄存器的值。PSC 即计数器时钟的分频因子。
PWM 输出就是对外输出脉宽(即占空比)可调的方波信号,信号频率由自动重装寄存器 ARR 的值决定,占空比由比较寄存器 CCR 的值决定。
PWM 信号主要都是用来控制电机,一般的电机控制用的都是边沿对齐模式,FOC 电机一般用中心对齐模式
CNT 工作在递增模式为例,在中,ARR=8,CCR=4,CNT 从 0 开始计数,当 CNT<CCR 的值时,OCxREF为有效的高电平于此同时,比较中断寄存器 CCxIF 置位。
当CCR=<CNT<=ARR 时,OCxREF 为无效的低电平。然后 CNT 又从 0 开始计数并生成计数器上溢事件,以此循环往复。
中心对齐模式:ARR=8,CCR=4。第一阶段计数器 CNT 工作在递增模式下,从 0 开始计数,当 CNT<CCR 的值时,OCxREF 为有效的高电平,当
CCR=<CNT<<ARR 时,OCxREF 为无效的低电平。
第二阶段计数器 CNT 工作在递减模式从 ARR 的值开始递减,当 CNT>CCR 时,OCxREF 为无效的低电平,当 CCR=>CNT>=1时,OCxREF 为有效的高电平
模式 计数器 CNT 计算方式 说明
PWM1 递增 CNT<CCR,通道 CH 为有效,否则为无效
递减 CNT>CCR,通道 CH 为无效,否则为有效
PWM2 递增 CNT<CCR,通道 CH 为无效,否则为有效
递减 CNT>CCR,通道 CH 为有效,否则为无效
当使用 PWM 输入模式的时候,因为一个输入通道(TIx)会占用两个捕获通道(ICx),所以一个定时器在使用 PWM 输入的时候最多只能使用两个输入通道(TIx)。
**********************************************************************************/
void TIMx_PWM_Init(uint16_t OCMode,uint16_t OCPolarity,st_u8 CHx,TIM_TypeDef* TIMx)
// 定时器比较输出初始化结构体 TIM_OCInitTypeDef 用于输出比较模式,与 TIM_OCxInit 函数配合使用完成指定定时器输出通道初始化配置。高级控制定时器有四个定时器通道,使用时都必须单独设置。
TIM_OCInitTypeDef TIM_OCInitStructure;
//*--------------------输出比较结构体初始化-------------------*/
TIM_OCInitStructure.TIM_OCMode = OCMode;//TIM_OCMode_PWM1;
// 输出通道电平极性配置 它决定着定时器通道有效电平
TIM_OCInitStructure.TIM_OCPolarity = OCPolarity;//TIM_OCPolarity_High;
// 脉冲值,即输出都是低电平
TIM_OCInitStructure.TIM_Pulse = 0;
// 输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
// 互补输出使能
// TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
if(CHx==1)
TIM_OC1Init(TIMx, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIMx,ENABLE); //MOE 主输出使能
TIM_OC1PreloadConfig(TIMx, TIM_OCPreload_Enable); //CH1预装载使
if(CHx==2)
TIM_OC2Init(TIMx, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIMx,ENABLE); //MOE 主输出使能 当使用的是通用定时器时,这句不需要
TIM_OC2PreloadConfig(TIMx, TIM_OCPreload_Enable);
if(CHx==3)
TIM_OC3Init(TIMx, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIMx,ENABLE); //MOE 主输出使能
TIM_OC3PreloadConfig(TIMx, TIM_OCPreload_Enable);
if(CHx==4)
TIM_OC4Init(TIMx, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIMx,ENABLE); //MOE 主输出使能
TIM_OC4PreloadConfig(TIMx, TIM_OCPreload_Enable);
//使能ARR、MOE及定时器
TIM_ARRPreloadConfig(TIMx, ENABLE); //使能TIMx在ARR上的预装载寄存器 占空比
TIM_Cmd(TIMx, ENABLE);
三、定时器封装
/******************************************************************************
定时器初始化函数
STM32总共有8个定时器,分别是2个高级定时器(TIM1、TIM8),
4个通用定时器(TIM2、TIM3、TIM4、TIM5)和2个基本定时器(TIM5、TIM6)
定时器时钟经过psc分频后得到驱动计数器计数的计数器时钟(CK_CNT)
CK_CNT=TIMxCLK/(PSC+1).
计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达
到自动重装载寄存器的时候产生更新事件,并清零从头开始计数.
自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数
值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断.
定时器的定时时间等于计数器的中断周期乘以中断的次数。
计数器在 CK_CNT 的驱动下,计一个数的时间则是 CK_CLK 的倒数,等于:1/(TIMxCLK/(PSC+1))
产生一次中断的时间则等于:1/(CK_CLK * ARR)
中断服务程序中设置一个变量TIME,记录中断次数,则定时时间:1/CK_CLK * (ARR+1)*time
*******************************************************************************/
void TIMx_Init(st_u32 RCC_APB1Periph,st_u16 per,st_u16 psc,st_u16 clk_div,st_u16 Count_mode,TIM_TypeDef* TIMx)
/*uint32_t RCC_APB1Periph = RCC_APB1Periph_TIM2|RCC_APB1Periph_TIM3|RCC_APB1Periph_TIM4
|RCC_APB1Periph_TIM5|RCC_APB1Periph_TIM6|RCC_APB1Periph_TIM7;*/
// 定时器结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//使能定时器时钟 TIM5
RCC_APB1PeriphClockCmd(RCC_APB1Periph, ENABLE);
// 设置自动重装载寄存器周期的值 计数到5000为500ms
// 计数一次的时间1/(TIMxCLK/(PSC+1))=1/1000
// 定时器周期,实际就是设定自动重载寄存器的值,在事件生成时更新到影子寄存器。可设置范围为 0 至 65535。
TIM_TimeBaseInitStructure.TIM_Period = per;
// 设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
// 定时器预分频器设置,时钟源经该预分频器才是定时器时钟,它设定
// TIMx_PSC 寄存器的值。可设置范围为 0 至 65535,实现 1 至 65536 分频。
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
// 时钟分割 设置时钟分割:TDTS = Tck_tim
// 时钟分频,设置定时器时钟 CK_INT 频率与数字滤波器采样时钟
// 频率分频比,基本定时器没有此功能,不用设置。
TIM_TimeBaseInitStructure.TIM_ClockDivision =clk_div ;
// 计数模式 TIM向上计数模式
// 可是在为向上计数(TIM_CounterMode_Up)、向下计数(TIM_CounterMode_Down)以及三种中心对齐模式。TIM_CounterMode_CenterAligned1 TIM_CounterMode_CenterAligned2 TIM_CounterMode_CenterAligne3
// 基本定时器只能是向上计数,即 TIMx_CNT 只能从 0 开始递增,并且无需初始化。
TIM_TimeBaseInitStructure.TIM_CounterMode = Count_mode;
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseInitStructure);
//TIM_Cmd(TIMx, ENABLE);
四、初始化定时器与舵机配置
#include "public.h"
void PWM_Init(uint16_t arr,uint16_t psc)
#if 0
/*************************引脚初始化***************************************/
// PA0 DJ TIM5_CH1
GPIOInit(DJ_PORT,DJ_PIN,GPIO_Mode_AF_PP,DJ_RCC);
// PB6 DJ2 TIM4_CH1
// GPIOInit(DJ2_PORT,DJ2_PIN,GPIO_Mode_AF_PP,DJ2_RCC);
// PA6 DJ3 TIM3_CH1
// GPIOInit(DJ3_PORT,DJ3_PIN,GPIO_Mode_AF_PP,DJ3_RCC);
/**************************定时器配置***************************************/
TIMx_Init(RCC_APB1Periph_TIM5,arr, psc,0,TIM_CounterMode_Up,TIM5);
// TIMx_Init(RCC_APB1Periph_TIM4,arr, psc,0,TIM_CounterMode_Up,TIM4);
// TIMx_Init(RCC_APB1Periph_TIM3,arr, psc,0,TIM_CounterMode_Up,TIM3);
/***************************PWM输出比较引脚**********************************/
TIMx_PWM_Init(TIM_OCMode_PWM1,TIM_OCPolarity_High,CH1,TIM5);
TIMx_PWM_Init(TIM_OCMode_PWM1,TIM_OCPolarity_High,CH2,TIM5);
//TIMx_PWM_Init(TIM_OCMode_PWM1,TIM_OCPolarity_High,1,TIM4);
//TIMx_PWM_Init(TIM_OCMode_PWM1,TIM_OCPolarity_High,1,TIM3);
//TIMx_PWM_Init(TIM_OCMode_PWM1,TIM_OCPolarity_High,2,TIM5);
//TIMx_PWM_Init(TIM_OCMode_PWM1,TIM_OCPolarity_High,2,TIM4);
//TIMx_PWM_Init(TIM_OCMode_PWM1,TIM_OCPolarity_High,2,TIM3);
#else
//定义初始化结构体
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
//使能定时器时钟 TIM5
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
//使能GPIOA外设时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//初始化GPIO
//设置服用输出功能TIM5
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM5 PA0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//初始化时基
//设置在下一个更新事件装入活动的自动重装载寄存器周期值50HZ
TIM_TimeBaseStructure.TIM_Period = arr;
//设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_Prescaler =psc;
//设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
//TIM向上计数模式
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
//根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);
//输出模式配置
//选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
//比较输出使能
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;//设置待装入捕获比较寄存器的脉冲值
//输出极性:TIM输出比较级性高
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
//根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC1Init(TIM5, &TIM_OCInitStructure);
TIM_CtrlPWMOutputs(TIM5,ENABLE); //MOE 主输出使能
TIM_OC1PreloadConfig(TIM5, TIM_OCPreload_Enable); //CH1预装载使能
TIM_OC2Init(TIM5, &TIM_OCInitStructure);
//使能ARR、MOE及定时器
TIM_ARRPreloadConfig(TIM5, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM5, ENABLE); //使能TIM4
#endif
// 设置CCR寄存器的值
void SetJointAngle(st_u8 ID, float angle)
switch(ID)
case 0: //-90°~90°
// 设置TIMx Capture Compare1寄存器值
angle=(u16)(50.0*angle/9.0+249.0);
TIM_SetCompare1(TIM5,angle);
break;
//0°~180°
case 1:
angle=(u16)(4.175*angle+409.25);
TIM_SetCompare2(TIM3,angle);
break;
case 2: //-150°~0°
angle=-angle;
angle=(u16)(4.175*angle+480.0);
TIM_SetCompare1(TIM4,angle);
break;
case 3:
angle=-180-angle;
angle=-angle;
angle=(u16)(4.175*angle+315.0);
TIM_SetCompare2(TIM4,angle);
break;
//-90°~90°
case 4:
angle=90.0+angle;
angle=(u16)(249.0+50.0*angle/9.0);
TIM_SetCompare3(TIM4,angle);
break;
default: break;
五、初始化配置
/*
* File : board.c
* This file is part of RT-Thread RTOS
* COPYRIGHT (C) 2006, RT-Thread Development Team
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rt-thread.org/license/LICENSE
*
* Change Logs:
* Date Author Notes
* 2017-07-24 Tanek the first version
*/
#include <rthw.h>
#include <rtthread.h>
#include "board.h"
#ifdef __CC_ARM
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE 1024
//从内部SRAM里面分配一部分静态内存来作为rtt的堆空间,这里配置为4KB
static uint32_t rt_heap[RT_HEAP_SIZE];
RT_WEAK void *rt_heap_begin_get(void)
return rt_heap;
RT_WEAK void *rt_heap_end_get(void)
return rt_heap + RT_HEAP_SIZE;
#endif
#endif
extern uint8_t OSRunning;
/**
* This function will initial your board.
*/
void rt_hw_board_init()
SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
SysTick_Init(72);
LED_Init();
uart_init(115200);
/*
PWM频率=72/(arr+1)(psc+1)=50Hz T=1/F=0.02s=20ms=20 000us 每秒50次
计数10000 用20ms 计数一次0.002ms
舵机参数:
工作电压:4.8V-6V
位置等级:1024级
脉冲控制精度为2us
// 设置高电平的持续时间
TIM_SetCompare1(TIM5,angle);
软件
0.5ms-------------0度; 2.5% 计数250次 249(arr+1) 250/10000=2.5% 0.5ms=20ms/10000次*angle
1.0ms------------45度; 5.0% 计数500次 499
1.5ms------------90度; 7.5% 计数750次 749
2.0ms-----------135度; 10.0% 计数2000次
2.5ms-----------180度; 12.5% 计数2500次
调节占空比 angle/9999+1
*/
//TIM2_Init(5000,7199);
//TIM4_PWM_Init(7199,0);
PWM_Init(9999,143);
OSRunning=1;
/* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#if defined(RT_USING_CONSOLE) && defined(RT_USING_DEVICE)
rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
void SysTick_Handler(void)
/* enter interrupt */
rt_interrupt_enter();
/* 更新时基 */
rt_tick_increase();
/* leave interrupt */
rt_interrupt_leave();
//重映射串口1到rt_kprintf
void rt_hw_console_output(const char *str)
/* 进入临界段 */
rt_enter_critical();
/* 直到字符串结束 */
while(*str!='\\0')
if(*str=='\\n')
USART1->DR = (u8) '\\r';
while((USART1->SR&0X40)==0);
USART1->DR =*str++;
while((USART1->SR&0X40)==0);
/* 退出临界段 */
rt_exit_critical();
六、main函数
#define ROOT
#include "public.h"
/*************************其他控制量******************************/
#if 0
void ST_MCU(void)
#ifdef TIM
RCC_PCLK1Config(RCC_HCLK_Div4); // HCLK/4 = 72/4
RCC_PCLK2Config(RCC_HCLK_Div4);
TIM2Init();
#else
TIM4_Init(1000,36000-1); //定时500ms
#endif
#endif
/*************************定义线程控制块******************************/
static rt_thread_t led1_thread=RT_NULL;
static rt_thread_t test_thread=RT_NULL;
/*************************线程主体函数******************************/
static void led1_thread_entry(void*parameter);
static void test_thread_entry(void*parameter);
int main(void)
#ifdef LED_DEBUG
// 创建led线程
led1_thread = rt_thread_create("led",
led1_thread_entry,
RT_NULL,
512,
3,
20);
// 启动线程,开启调度
if(led1_thread!=RT_NULL)
rt_thread_startup(led1_thread);
else
return -1;
#endif
test_thread = rt_thread_create("test",test_thread_entry,RT_NULL,512,3,30);
// 启动线程,开启调度
if(test_thread!=RT_NULL)
rt_thread_startup(test_thread);
else
return -1;
//LED1线程
static void led1_thread_entry(void* parameter)
while(1)
LED1=~LED1;
rt_thread_delay(500); /* 延时200个tick */
LED0=~LED0;
rt_thread_delay(500); /* 延时200个tick */
static void test_thread_entry(void*parameter)
// ultrasonic_test();
while(1)
SetJointAngle(0,90);
delay_ms(300);
// TIM_SetCompare1(TIM5,angle);
SetJointAngle(0,5);
delay_ms(300);
SetJointAngle(0,175);
delay_ms(300);
以上是关于智能小车之舵机控制的主要内容,如果未能解决你的问题,请参考以下文章