STM32—TIMx输出PWM信号驱动MG996R舵机
文章目录
一.前言
利用STM32的TIM3的通道1、通道2,输出俩路PWM信号,驱动MG996R舵机。
涉及到:TIM定时器基本原理,TIM定时中断、TIM输出PWM信号、MG996R舵机驱动原理
二.MG996R舵机简介
MG996R舵机单线驱动,是一款360°舵机,180°舵机与360°舵机的区别就是:180°舵机可以直接控制舵机旋转的角度,但舵机只能够旋转180°;360°舵机无法直接控制其旋转角度,只能控制其转动方向和速度。
舵机的驱动信号由周期为20ms的脉冲来控制:
当高电平持续时间为0.5~1.5ms时,舵机正转,时间越小转动越快
当高电平持续时间为1.5~2.5ms时,舵机反转,时间越大转动越快
当高电平持续时间为1.5ms或者其他时间时,舵机停止转动
三.TIM定时器简介
STM32F1系列中,有8个定时器,分别为基本定时器(2个)、通用定时器(2个)、高级定时器(2个),如图:
这些定时器的相同点:
1.计数器的分辨率都是16位;
2.预分频系数都是16位(2的16次方),1-65535;
3.都可以产生DMA请求;
各自的特点:
1.基本定时器只可以向上计数,而通用定时器和高级定时器既可以向上计数也可以向下计数;
2.基本定时器没有输入捕获、输出比较功能;
3.高级定时器支持互补输出;
一般输出PWM信号只用通用定时器即可。
四.通用定时器TIMx
1.TIMx主要功能
通用TIMx定时器(TIM2、TIM3、TIM4和TIM5)功能主要包括如下:
- 16位向上、向下、向上/向下自动装载计数器
- 16位可编程(可以实时修改)预分频器,分频系数为1~65535
- 四种独立通道功能:
1.输入捕获
2.输出比较
3.PWM生成
4.单脉冲输出 - 使用外部信号控制定时器和定时器互连的同步电路
- 可以由如下事件触发中断或者DMA:
1.更新,即计数器溢出,或者计数器初始化
2.特定的触发事件,比如:计数器启动、停止、初始化等等
3.输入捕获
4.输出比较 - 支持针对定位的增量(正交)编码器和霍尔传感电路
- 触发输入作为外部时钟或者按周期的电流管理
看起来功能很多,实际做项目的时候都是一条龙服务,就是一套流水线操作就完成了TIMx的全部功能配置。
2.TIMx框图
TIMx的框图如下:
按照我个人的理解和实践中积累的经验,可以将框图从逻辑上分为四部分:
橙色:时基部分,负责选择时钟源
蓝色:计数部分,负责根据预分频后的时钟进行计数、自动装载工作
绿色:输入捕获部分
紫色:输出比较部分
3.计数单元
可编程通用定时器的主要部分是一个16位计数器和其相关的自动装载寄存器,前面也说了,计数器可以向上、向下、双向计数。既然要计数,那就必须要知道计数的多少和每一次计数的时间。计数器的时钟由预分频器对时钟源分频得到。
时基单元包括:
- 计数器寄存器(TIMx_CNT)
- 预分频器寄存器(TIMx_PSC)
- 自动装载寄存器(TIMx_ARR)
计数器寄存器中存储的是当前计数的值,自动装载寄存器中存储的是目标计数值,当计数器溢出后,会重新装填目标计数值,而预分频器寄存器中的是对时钟的分频系数。
4.时钟选择
计数器的时钟可以由如下时钟源提供:
- 内部时钟(CK_INT)
- 外部时钟模式1:外部输入引脚(TIx)
- 外部时钟模式2:外部触发输入(ETR)
- 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器
我们主要使用内部时钟CK_INT,CK_INT是从APB1倍频来的,当APB1的时钟分频数为1时(36MHz),TIMx的时钟就是APB1的2倍,即72MHz。
5.输出比较PWM
有专门的三个寄存器来控制PWM:
- 捕获/比较模式寄存器(TIMx_CCMR1/2)
- 捕获/比较使能寄存器(TIMx_CCER)
- 捕获/比较寄存器(TIMx_CCR1~4)
五.TIM3输出双路PWM信号代码详解
1.TIMx初始化结构体详解
输出PWM用到的TIMx初始化结构体有:
1.时基初始化结构体TIM_TimeBaseInitTypeDef
2.输出比较初始化结构体TIM_OCInitTypeDef
1.时基结构体TIM_TimeBaseInitTypeDef
用于定时器基本参数的设置,使用void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)
函数进行初始化:
typedef struct {
uint16_t TIM_Prescaler; // 预分频器
uint16_t TIM_CounterMode; // 计数模式
uint32_t TIM_Period; // 定时器周期
uint16_t TIM_ClockDivision; // 时钟分频
uint8_t TIM_RepetitionCounter; // 重复计算器
} TIM_TimeBaseInitTypeDef;
TIM_Prescaler:预分频器设置,只有经过预分频器后的时钟才是CK_CNT,计数器时钟频率 (fCK_CNT) 等于fCK_PSC / (PSC[15:0] + 1),可实现 1 至 65536 分频。
TIM_CounterMode:定时器计数模式,可设置向上计数、向下计数和中心对齐计数三种模式。
TIM_Period:设置的是自动重装寄存器ARR的值,ARR为要装载到影子寄存器的值,可设置 1 至 65536 。
TIM_ClockDivision:时钟分频,设置定时器时钟 CK_INT 频率与死区发生器以及数字滤波器采样时钟频率分频比。可以选择 1、 2、 4 分频。
TIM_RepetitionCounter:重复计数器,只有八位,只存在与高级定时器。
一般来讲,我们只需要设置
uint16_t TIM_Prescaler; // 预分频器
uint16_t TIM_CounterMode; // 计数模式
uint32_t TIM_Period; // 定时器周期
这三个成员就可以实现定时器的基本参数设置。
2.输出比较初始化结构体TIM_OCInitTypeDef
使用输出比较模式时就需要配置此结构体,使用void TIM_OCxInit(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct)
函数进行初始化:
typedef struct {
uint16_t TIM_OCMode; // 比较输出模式
uint16_t TIM_OutputState; // 比较输出使能
uint16_t TIM_OutputNState; // 比较互补输出使能
uint32_t TIM_Pulse; // 脉冲宽度
uint16_t TIM_OCPolarity; // 输出极性
uint16_t TIM_OCNPolarity; // 互补输出极性
uint16_t TIM_OCIdleState; // 空闲状态下比较输出状态
uint16_t TIM_OCNIdleState; // 空闲状态下比较互补输出状态
} TIM_OCInitTypeDef;
TIM_OCMode:比较输出模式选择,总共有八种,常用的为 PWM1/PWM2。它设定
CCMRx 寄存器 OCxM[2:0]位的值。
TIM_OutputState:比较输出使能,决定最终的输出比较信号 OCx 是否通过外部引脚输
出。它设定 TIMx_CCER 寄存器 CCxE/CCxNE 位的值。
TIM_OutputNState:比较互补输出使能,决定 OCx 的互补信号 OCxN 是否通过外部引脚
输出。它设定 CCER 寄存器 CCxNE 位的值。
TIM_Pulse:比较输出脉冲宽度,实际设定比较寄存器 CCR 的值,决定脉冲宽度。可
设置范围为 0 至 65535。
TIM_OCPolarity:比较输出极性,可选 OCx 为高电平有效或低电平有效。它决定着定
时器通道有效电平。它设定 CCER 寄存器的 CCxP 位的值。
TIM_OCNPolarity:比较互补输出极性,可选 OCxN 为高电平有效或低电平有效。它
设定 TIMx_CCER 寄存器的 CCxNP 位的值。
TIM_OCIdleState:空闲状态时通道输出电平设置,可选输出 1 或输出 0,即在空闲状
态(BDTR_MOE 位为 0)时,经过死区时间后定时器通道输出高电平或低电平。它设定
CR2 寄存器的 OISx 位的值。
TIM_OCNIdleState:空闲状态时互补通道输出电平设置,可选输出 1 或输出 0,即在
空闲状态(BDTR_MOE 位为 0)时,经过死区时间后定时器互补通道输出高电平或低电
平,设定值必须与 TIM_OCIdleState 相反。它设定是 CR2 寄存器的 OISxN 位的值。
当要输出PWM信号时只需要配置如下成员即可:
uint16_t TIM_OCMode; // 比较输出模式
uint16_t TIM_OCPolarity; // 输出极性
uint16_t TIM_OutputState; // 比较输出使能
PWM的模式:
PWM1:向上计数时CNT<CCR 时为有效电平,向下计数时CNT>CCR 时为有效电平
PWM2:向上计数时CNT<CCR 时为无效电平,向下计数时CNT>CCR 时为无效电平
输出极性决定了有效电平是高电平还是低电平。
2.TIM3输出俩路PWM初始化代码
/* 利用TIM3通道2输出PWM PA6、PA7 输出极性高——高电平有效
ARR:预装载值(决定频率)
CCR:设定的比较值(决定占空比)
CNT:计数值
PWM1:向上计数时CNT<CCR 时为有效电平,向下计数时CNT>CCR 时为有效电平
PWM2:向上计数时CNT<CCR 时为无效电平,向下计数时CNT>CCR 时为无效电平
psc:时钟预分频数
*/
void TIM3_PWM_Init( uint16_t arr,uint16_t psc )
{
/* 初始化结构体 */
TIM_OCInitTypeDef TIM_OCInitStruct;
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
RCC_APB1PeriphClockCmd( RCC_APB1Periph_TIM3, ENABLE );
/* PA7:复用推挽输出 */
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 ;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStruct );
/* PA6:复用推挽输出 */
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 ;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStruct );
/* 初始化TIM3 基本配置 */
TIM_DeInit( TIM3 );
TIM_TimeBaseInitStruct.TIM_Period = arr;
TIM_TimeBaseInitStruct.TIM_Prescaler = psc;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit( TIM3, &TIM_TimeBaseInitStruct );
/* 初始化TIM3通道2 PWM配置 */
/* TIM_OutputNState, TIM_OCNPolarity, TIM_OCIdleState 和 TIM_OCNIdleState 是
高级定时器 TIM1 和 TIM8 才用到的。 */
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;//PWM模式1
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性——高极性
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;//输出使能
TIM_OCInitStruct.TIM_Pulse = 199;//比较值CCR,可以不用配置,因为后期肯定要该
TIM_OC2Init( TIM3, &TIM_OCInitStruct );//PA7
TIM_OC1Init( TIM3, &TIM_OCInitStruct );//PA6
/* 使能预装载寄存器 */
TIM_OC2PreloadConfig( TIM3, TIM_OCPreload_Enable );
TIM_OC1PreloadConfig( TIM3, TIM_OCPreload_Enable );
/* 使能自动装载的预装载寄存器 */
TIM_ARRPreloadConfig( TIM3, ENABLE );
/* 使能TIM3 */
TIM_Cmd( TIM3, ENABLE );
/*
从这里开始TIM3已经开始输出PWM了
此时PWM输出的频率和占空比都是固定的,可以通过
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
函数来调整比较值,从而调整占空比(通道2)
*/
}
传入函数的参数uint16_t arr,uint16_t psc
确定了PWM信号的周期,使用void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
函数改变ccr,从而改变PWM信号的占空比。
3.主函数
int main(void)
{
// uint8_t k=5;
// NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
// TIM4_Delay_Init( 9999,7199 );
TIM3_PWM_Init( 199,7199 );// 频率为72000000/14400=5000Hz 20ms周期脉冲
// USART1_Config();
SysTick_Init();
/*
20ms脉冲
5-14 正转,值越小,转的越快
16-25 反转,值越大,转的越快
*/
TIM_SetCompare1( TIM3, 14 );
TIM_SetCompare2( TIM3, 14 );
SysTick_Delay_Ms( 500);
TIM_SetCompare2( TIM3, 16 );
TIM_SetCompare2( TIM3, 16 );
SysTick_Delay_Ms( 500);
TIM_SetCompare1( TIM3, 50);
TIM_SetCompare2( TIM3, 50);
}
可以控制俩个舵机的转动,因为是360°舵机,所以我采用控制舵机转动的速度和转动的时间来控制转动的角度,代码是我亲测可以使用的,有什么疑问可以交流一下,大家共同进步!
STM32—TIMx输出PWM信号驱动MG996R舵机的更多相关文章
- STM32 精确输出PWM脉冲数控制电机(转)
STM32 精确输出PWM脉冲数控制电机 发脉冲两种目的1)速度控制2)位置控制 速度控制目的和模拟量一样,没有什么需要关注的地方发送脉冲方式为PWM,速率稳定而且资源占用少 stm32位置控制需要获 ...
- STM32定时器输出PWM频率和步进电机控制速度计算
1.STM32F4系列定时器输出PWM频率计算 第一步,了解定时器的时钟多少: 我们知道AHP总线是168Mhz的频率,而APB1和APB2都是挂在AHP总线上的. (1)高级定时器timer1, t ...
- 基于STM32F030F4P9和STM32 CUBEMX 输出PWM波形
STM32F030F4P9定时器功能比较丰富,在此记录项目中使用其自动输出PWM波形(频率:50HZ).CubeMX配置定时器如下图说明. 在此定时器基础时钟为48MHZ,配置中不做分频处理,预分频系 ...
- STM32F103定时器输出PWM波控制直流电机
这个暑假没有回家,在学校准备九月份的电子设计竞赛.今天想给大家分享一下STM32高级定时器输出PWM波驱动直流电机的问题.. 要想用定时器输出的PWM控制直流电机,,首先要理解“通道”的概念..一个定 ...
- 张高兴的 .NET Core IoT 入门指南:(五)PWM 信号输出
什么是 PWM 在解释 PWM 之前首先来了解一下电路中信号的概念,其中包括模拟信号和数字信号.模拟信号是一种连续的信号,与连续函数类似,在图形上表现为一条不间断的连续曲线.数字信号为只能取有限个数值 ...
- 【春节歌曲回味 | STM32小音乐盒 】PWM+定时器驱动无源蜂鸣器(STM32 HAL库)
l STM32通过PWM与定时器方式控制无源蜂鸣器鸣响 l STM32小音乐盒,歌曲进度条图形显示与百分比显示,歌曲切换 l 编程使用STM32 HAL库 l IIC OLED界面编程,动画实 ...
- 基于Arduino、STM32进行红外遥控信号接收
catalogue . 遥控器原理简介 . 红外遥控原理 . 常见红外遥控器红外线信号传输协议 . 遙控器的发展 . 实验过程 . 攻击面 . 基于STM32实现红外信号解码 1. 遥控器原理简介 0 ...
- STM32中的PWM的频率和占空比的设置
转于http://blog.csdn.net/liming0931/article/details/8491468 下面的这个是stm32的定时器逻辑图,上来有助于理解: TIM3的ARR寄存器和 ...
- STM32F103 TIM1输出PWM设置
//TIM1 分频 #define TIM1_DIV1 (1-1) #define TIM1_DIV2 (2-1) #define TIM1_DIV4 (4-1) #define TIM1_DIV8 ...
随机推荐
- 注解+AOP实现redis遍历缓存
1.注解 package com.yun.smart.annotation; import java.lang.annotation.ElementType; import java.lang.ann ...
- 将Acunetix与CircleCI集成
如果要在DevSecOps中包含Acunetix ,则需要将其与CI / CD系统集成.Acunetix具有针对最受欢迎的CI / CD系统Jenkins的现成集成.但是,您可以使用Acunetix ...
- scrapy-redis的搭建 分布式爬虫 去重
master:一.spider文件1.需要更改继承的类from scrapy_redis.spiders import RedisSpider 2.注释掉start_urls 3.在爬虫目录下新创建一 ...
- python3.7验证码识别MuggleOCR,为什么总是报错
先来看看MuggleOCR简介(白嫖)这是一个为麻瓜设计的本地OCR模块只需要简单几步操作即可拥有两大通用识别模块,让你在工作中畅通无阻. 这套模型是基于 https://github.com/ker ...
- 安装GLPI
Centos7安装GLPI资产管理软件 系统信息 环境说明 下面的命令可以查看系统的版本信息,本次使用的是centos7 cat /etc/redhat-release uname -a IP地址信息 ...
- 浅淡fhq_Treap
浅淡 \(fhq\_Treap\) 前言 fhq_Treap \(yyds\)! \(sto\ FHQ\ orz\) 机房大佬们都打的 \(Splay\) 只有蒟蒻打的 \(fhq\) (防火墙)(范 ...
- RPC远程协议之原理分析
在近几年工作中发现,功能服务化或微服务化越来越流行,逐渐成为实现中大型分布式系统架构的主要方式,而在分布式系统中的不同节点应用间的通信中,RPC远程协议扮演关键作用.实际上,在日常工作中,我们也多多少 ...
- Map集合笔记
一.Map集合的特点 Map集合是一个双列集合 Map中的元素,key和value的数据类型可以相同,也可以不同. Map中的元素,key是允许重复的,value是可以重复的 Map中的元素,key和 ...
- Day11继承、封装、多态-面向对象编程(2)
封装 我们设计程序要追求 高内聚,低耦合 . 高内聚:类的内部数据操作细节自己完成,不允许外部干涉 低耦合:仅暴露少量方法给外部使用 封装(数据的隐藏) 通常,应禁止直接访问一个对象中数据的实际表示, ...
- mybatis-1-hellomybatis
一.先创建一个mybatis的数据表 USE `mybatis`; CREATE TABLE tbl_employee( id INT(11) PRIMARY KEY AUTO_INCREMENT, ...