前言:

        本篇将实现通过TIM1高级定时器使用互补输出的方式驱动一个310直流减速电机,同时本篇会涉及到Keil5在线仿真来观测管脚输出PWM


高级定时器:

        STM32分为3种定时器,功能由低到高可以分为基本定时器(TIM6,TIM7)通用定时器(TIM2,TIM3,TIM4,TIM5)和高级定时器(TIM1,TIM8),这里呢,由于小车的硬件设计方面将TIM1和TIM8用来驱动310直流减速电机,TIM2,TIM3,TIM4,TIM5分别使用其输入捕获功能来计算小车4个电机的转速,以便后续进行PID的调节功能。

        在这里我给大家简单介绍下高级定时器的功能。如下图所示,高级定时器其实就比通用定时器多了一项,就是死区。其余部分可以说与通用定时器一模一样。其本质就是一个16位计数器和与其相关的自动装载寄存器。只不过这个计数器可以向上计数、向下计数或者向上向下双向计数
 

 高级定时器的时钟来源如下图所示,其时钟来源可以由内部时钟(CK_INT)提供,也可以由外部时钟模式1:外部输入引脚的方式提供,还有外部时钟模式2:外部触发输入ETR的方式提供,还可以由内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器来提供,不过对于我们而言,我们选择的是第一种

 这里呢,我们以TIM1为例,主要介绍以TIM1的CH1和CH1N通道以互补输出的方式来驱动一个310直流减速电机。(这里给大家提醒一点,互补输出不能配置为TIM的输入引脚,所以不能通过增量编码方式完成计数功能,其可以输出PWM,但不能用来捕获)在此之前,我们先给大家简单讲述一下STM32定义的与定时器有关的结构体的具体含义

先给大家介绍的是时基单元这个结构体,我们一般在这里要完成的就是定时时长的计算,这一点很重要。

定时器的周期就为:

f = \frac{72M}{(TIM_Prescaler+1)(TIM_ClockDivision+1)}

而这个TIM_Period就是说定时器计数计到你预设的值之后就会自动清零。

    typedef struct
    {
      uint16_t TIM_Prescaler;                //预分频,此值+1为分频的除数 

      uint16_t TIM_CounterMode;         //计数方式,如向上计数,向下计数

      uint16_t TIM_Period;                    //计数值

      uint16_t TIM_ClockDivision;         //时钟分频因子可选择1、2、4分频。配置死区时用到

      uint8_t TIM_RepetitionCounter;   //配置重复计数器,一般用不到,设为0就好
    } TIM_TimeBaseInitTypeDef;   

接下来给大家介绍的就是TIM_OCInitTypeDef结构体,在这里由很多要点我会一一给大家罗列

    typedef struct

    {

      uint16_t TIM_OCMode;

      uint16_t TIM_OutputState;          //输出状态

      uint16_t TIM_OutputNState;       //互补通道的输出状态

      uint16_t TIM_Pulse;                    //占空比(或者说叫预设值)      

      uint16_t TIM_OCPolarity;           //输出极性

      uint16_t TIM_OCNPolarity;        //互补通道的输出极性

      uint16_t TIM_OCIdleState;         //空闲状态

      uint16_t TIM_OCNIdleState;      //互补通道的空闲状态

    } TIM_OCInitTypeDef;

首先就是TIM_OCMode,定时器的PWM有两种模式,一种是TIM_OCMode_PWM1,一种是TIM_OCMode_PWM2,这里我以TIM_OCMode_PWM1为例

假如我这时候选用 TIM_OCMode_PWM1,并且配置TIM_OutputState = TIM_OCPolarity_High

那么当计数器内加载的值小于我的预设值(TIM_Pulse)时,这时候,输出的是高电平,大于我的预设值的时候,输出的就是低电平。

其次,这里要注意的一点是我们这里没有配置死区时间,只设置了互补输出,TIM1_CH1N是TIM1_CH1的反相,在代码里我们配置好了CH1通道的话,一定要注意,CH1N通道是会自动带着一个取反的,也就是说,为了产生互补波形,我们必须要将TIM_OCPolarity与TIM_OCNPolarity配置成一样的,就像下图所示的那样


正确的:

 错误的:

正篇开始:

        在这里,我们的硬件电路设计上采用了AS4950芯片作为电机芯片来控制电机转动,这里我将硬件设计电路给大家展示一下。在硬件设计方面,我们不存在H桥,也就意味着我们不需要使用死区时间来规避电流对元器件造成的损坏。这里给大家浅谈一下死区:死区就是在上半桥关断后,延迟一段时间再打开下半桥或在下半桥关断后,延迟一段时间再打开上半桥,从而避免功率元件烧毁。这段延迟时间就是死区。以TIM1_CH1和TIM1_CH1N为例,两者用于驱动上下两个功率管。如果死区时间为0,则 TIM1_CH1N是TIM1_CH1的反相。如果死区时间不为0,则在TIM1_CH1N上插入了Deadtime,防止上下功率管同时导通。

 这个AS4950的数据手册里比较重要的部分,我也一并给大家放上,方便下面对代码的构思与讲解

 根据我用方框框起来的部分,大家就可以看出来,我们这里通过对 IN1 和 IN2 管脚的输入来决定电机的正反转。在硬件上,我们用PA8作为Motor1,用PB13作为Motor1_N。也就是TIM1的CH1通道和CH1N通道。

        接下来我们就要开始对代码的规划了,硬件设计大家可以去我的博客获取原理图从零开始制作STM32F103RCT6小车(一)_孙启尧的博客-CSDN博客

好了,话不多说,直接上代码


代码部分

        这里有几个坑要避开一下,首先是TIM_ARRPreloadConfig(TIM1, ENABLE);这句代码的作用只是允许或禁止在定时器工作时向ARR的缓冲器中写入新值,以便在更新事件发生时载入覆盖以前的值。后续如果需要更改电机转速,这句话必须有

        其次是TIM_CtrlPWMOutputs(TIM1, ENABLE);最开始的时候,我也没注意,直到实际操作的时候,才发现这个问题,百度了一下,原来对于高级定时器,我们必须要允许PWM输出,这样才会有波形产生。

下面是motor.c文件里面的代码

#include "stm32f10x.h"
#include "motor.h"
 
void Motor_Init()
{
	//PA8 	    -> TIM1_CH1
	//PB13      -> TIM1_CH1N
	//PC6		-> TIM8_CH1
	//PA7		-> TIM8_CH1N
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseStructure;
	TIM_OCInitTypeDef	TIM_OCInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//PA8
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//PB13
	
	TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period = 9999;
	TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (SystemCoreClock / 1000000) - 1;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;//空闲状态
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;//互补通道的空闲状态
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;//互补通道的输出极性
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 1999;//其实初始化该设为0,这个大家可以自行更改
	
	TIM_OC1Init(TIM1, &TIM_OCInitStructure); 
	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
	TIM_ARRPreloadConfig(TIM1, ENABLE);//允许后续更改ARR寄存器里面的值,这个必须有
	TIM_Cmd(TIM1, ENABLE);//使能定时器1
	TIM_CtrlPWMOutputs(TIM1, ENABLE);//使能PWM输出,对于高级定时器来说,这句话必须有
}
 
void Motor1_forward(unsigned int pulse)
{
	TIM_SetCompare1(TIM1, pulse); 
}
//小于4999时,电机向前转,大于4999时,电机向后转,等于4999时,小车停止转动

下面是motor.h里面的代码

#ifndef __MOTOR_H
#define __MOTOR_H
 
void Motor_Init(void);
void Motor1_forward(unsigned int pulse);
	
#endif

 

使用Keil5完成软件仿真观测管脚输出PWM

        这里我主要给大家说下,暑假在家,没有示波器怎么办呢?软件仿真里面有一个类似于示波器的东西可以帮助我们观看PWM波形

        首先,点击魔法棒,选中Debug选项,点击箭头所指的部分,在左下角一定要检查一下自己的STM32型号,比如这一批小车的底层板所用的芯片全部都是STM32F103RCT6,那么我们这里所选就要和我框起来的部分一致。最后点击OK

 再进入调试界面

 点击箭头所指的地方

 点击左侧的Set up

 这时,会弹出一个新界面,点击箭头所指的部分,增加新的观测管脚,比如要观察PA8,向里面输入PORTA.8就可以了,记得要按照右侧图所指的那样,将选项改为Bit,同时还可以更改颜色

小车驱动代码补发:

main.c

#include "stm32f10x.h"
#include "led.h"
#include "sysclock.h"
#include "motor.h"
 
int main()
{
	LED_init();
	Motor1_Init();
	Motor2_Init();
	Motor3_Init();
	Motor4_Init();
	GPIO_WriteBit(GPIOC, GPIO_Pin_12, Bit_RESET);
	while(1)
	{
		Motor1_forward(1999);
		Motor2_forward(7999);
		Motor3_forward(1999);
		Motor4_forward(1999);
	}
}


motor.c

#include "stm32f10x.h"
#include "motor.h"
 
void Motor1_Init()
{
	//PA8 	    -> TIM1_CH1
	//PB13      -> TIM1_CH1N
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseStructure;
	TIM_OCInitTypeDef	TIM_OCInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//PA8
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//PB13
	
	TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period = 9999;
	TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (SystemCoreClock / 1000000) - 1;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;
	
	TIM_OC1Init(TIM1, &TIM_OCInitStructure); 
	TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
	TIM_ARRPreloadConfig(TIM1, ENABLE);
	TIM_Cmd(TIM1, ENABLE);
	TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
 
void Motor2_Init()
{
	//PC6				-> TIM8_CH1
	//PA7				-> TIM8_CH1N
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseStructure;
	TIM_OCInitTypeDef	TIM_OCInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8|RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//PA7
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//PC6
	
	TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period = 9999;
	TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (SystemCoreClock / 1000000) - 1;
	TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;
	
	TIM_OC1Init(TIM8, &TIM_OCInitStructure); 
	TIM_OC1PreloadConfig(TIM8, TIM_OCPreload_Enable);
	TIM_ARRPreloadConfig(TIM8, ENABLE);
	TIM_Cmd(TIM8, ENABLE);//?????1
	TIM_CtrlPWMOutputs(TIM8, ENABLE);
}
 
void Motor3_Init()
{
	//PC7 	    -> TIM8_CH2
	//PB0       -> TIM8_CH2N
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseStructure;
	TIM_OCInitTypeDef	TIM_OCInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//PC7
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//PB0
	
	TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period = 9999;
	TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (SystemCoreClock / 1000000) - 1;
	TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;
	
	TIM_OC2Init(TIM8, &TIM_OCInitStructure); 
	TIM_OC2PreloadConfig(TIM8, TIM_OCPreload_Enable);
	TIM_ARRPreloadConfig(TIM8, ENABLE);
	TIM_Cmd(TIM8, ENABLE);
	TIM_CtrlPWMOutputs(TIM8, ENABLE);
}
 
void Motor4_Init()
{
	//PC8 	    -> TIM8_CH3
	//PB1       -> TIM8_CH3N
	GPIO_InitTypeDef GPIO_InitStructure;
	TIM_TimeBaseInitTypeDef	TIM_TimeBaseStructure;
	TIM_OCInitTypeDef	TIM_OCInitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOC, &GPIO_InitStructure);//PC8
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//PB1
	
	TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseStructure.TIM_Period = 9999;
	TIM_TimeBaseStructure.TIM_Prescaler = (uint16_t) (SystemCoreClock / 1000000) - 1;
	TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);
	
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
	TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;
	
	TIM_OC3Init(TIM8, &TIM_OCInitStructure); 
	TIM_OC3PreloadConfig(TIM8, TIM_OCPreload_Enable);
	TIM_ARRPreloadConfig(TIM8, ENABLE);
	TIM_Cmd(TIM8, ENABLE);
	TIM_CtrlPWMOutputs(TIM8, ENABLE);
}
 
 
void Motor1_forward(unsigned int pulse)
{
	TIM_SetCompare1(TIM1, pulse); 
}
void Motor2_forward(unsigned int pulse)
{
    unsigned int media;
	media = 10000 - pulse - 2;
	TIM_SetCompare1(TIM8, media); 
	TIM_SetCompare1(TIM8, pulse); 
}
void Motor3_forward(unsigned int pulse)
{
	TIM_SetCompare2(TIM8, pulse); 
}
 
void Motor4_forward(unsigned int pulse)
{
	TIM_SetCompare3(TIM8, pulse); 
}

motor.h

#ifndef __MOTOR_H
#define __MOTOR_H
 
void Motor1_Init(void);
void Motor2_Init(void);
void Motor3_Init(void);
void Motor4_Init(void);
 
void Motor1_forward(unsigned int pulse);
void Motor2_forward(unsigned int pulse);
void Motor3_forward(unsigned int pulse);
void Motor4_forward(unsigned int pulse);
 
#endif