国民技术N32G031使用高级定时器TIM1输出互补PWM
高级定时器介绍
高级定时器具有互补输出功能、死区插入和刹车功能。适用于电机控制。
16 位自动装载计数器。(可实现向上计数、向下计数、向上/下计数)
16 位可编程预分频器。(分频系数可配置为1 到65536 之间的任意值)
可编程重复计数器
TIM1 最多6 个通道,TIM8 最多6 个通道
4 个捕获/比较通道,工作模式为:PWM输出、输出比较、单脉冲模式输出、输入捕获
如下事件发生时产生中断/DMA:
更新事件
触发事件
输入捕获
输出比较
刹车信号输入
死区时间可编程的互补输出
对于TIM1、TIM8,通道1、2、3 支持此功能
可通过外部信号控制定时器
多个定时器内部连接在一起,以实现定时器的同步或链接
TIM1_CC5 和 TIM8_CC5 用于比较器消隐
TIM1_CC6 用于 OPAMP1 和 OPAMP2 的输入通道切换
增量(正交)编码器接口:用于追踪运行轨迹和解析旋转方位
霍尔传感器接口:用于三相电机控制
结构框图
- 详细介绍请看国名技术官方指导手册,此处不细讲。
代码讲解
- 目标:使用TIM1产生一组带有死区的互补PWM波形,此处使用寄存器来编写。使用库来编写的话,请参考官方的实例即可。
PWM.h
#ifndef __PWM_H__
#define __PWM_H__
#include "n32g031.h"
#include <stdint.h>
void PWMInit_ds(uint16_t PrescalerVal, uint16_t period);
#endif
PWM.c
#include "pwm.h"
/*
* 使用高级定时器产生互补PWM输出
*/
void PWMInit_ds(uint16_t PrescalerVal, uint16_t period)
{
uint32_t temp;
OCInitType TIM_OCInitStructure;
RCC->APB2PCLKEN |= (1<<12);//开启高级定时器1时钟
RCC->APB2PCLKEN |=(1<<2);//使能GPIOA的时钟
//gpio口配置
//PA4 CH1
GPIOA->PMODE &= (~(3 << 8));//clear PA4的模式
GPIOA->PMODE |= (2<<8); //PA4设置复用功能模式
GPIOA->POTYPE &=(~(1<<4)); //PA4设置为推挽输出
GPIOA->SR &=(~(1<<4)); //PA4快速翻转
GPIOA->PUPD &=(~(3<<8));//PA4无上下拉
GPIOA->AFL &=(~(0X0F<<16));
GPIOA->AFL |=(0X03<<16);//AF3: TIM1_CH1
GPIOA->DS |=(1<<4);//PA4设为低驱动能力(8mA(5V)/4mA(3.3V)/2mA(1.8V)
//PA7 CH1N
GPIOA->PMODE &= (~(3 << 14));//clear PA7的模式
GPIOA->PMODE |= (2<<14); //PA7设置复用功能模式
GPIOA->POTYPE &=(~(1<<7)); //PA7设置为推挽输出
GPIOA->SR &=(~(1<<7)); //PA7快速翻转
GPIOA->PUPD &=(~(3<<14));//PA7无上下拉
GPIOA->AFL &=(~(0X0F<<28));
GPIOA->AFL |=(0X05<<28);//AF5: TIM1-CHIN
GPIOA->DS |=(1<<7);//PA7设为低驱动能力(8mA(5V)/4mA(3.3V)/2mA(1.8V)
//timer1配置
TIM1->CNT=0;//计数器
TIM1->PSC = PrescalerVal;//f=fck_psc/(PrescalerVal+1)
TIM1->AR = period;//自动重装载的值
TIM1->REPCNT=10;//重复计数器
temp=TIM1->CTRL1;
temp |=(1<<17);//PVD作为BRK启用。0:禁止;1:使能
temp &=~(1<<16);//LockUp锁存作为BRK使能。0:禁止;1-使能
temp &=~(1<<10);//IOM作为BRK使能。0:使能。选择外部刹车信号(来自IOM);1:禁止。选择内部刹车信号(来自COMP)
temp &=(~(3<<8));
temp |=((0<<8));//CLKD[1:0] 表示 CK_INT(定时器时钟)和 DTS(用于死区时间发生器和数字滤波器(ETR、TIx)的时钟)之间的分频比。
//00:tDTS = tCK_INT;01:tDTS = 2 x tCK_INT;10:tDTS = 4 x tCK_INT;11:保留,不要使用这个配置
temp |=(1<<7);//自动重装载预装载允许位。1:TIMx_AR 寄存器的影子寄存器使能
temp &=(~(1<<5));//选择中央对齐模式。00:边缘对齐模式。 TIMx_CTRL1.DIR 指定向上计数或向下计数。
temp &=(~(1<<4));//0:计数器向上计数;1:计数器向下计数。
temp &=(~(1<<3));//0:禁用单脉冲模式,发生更新事件时不影响计数器计数。
temp |=(1<<2);//更新请求源:1:如果更新中断或 DMA 请求使能,只有计数器上溢/下溢会产生更新中断或 DMA请求。
temp &=(~(1<<1));//更新禁用: 0-启用更新事件;1-UEV 禁用
TIM1->CTRL1 =temp;
/*******************pwm输出配置部分 start***********************/
TIM1->CCEN &= (uint32_t)(~(uint32_t)TIM_CCEN_CC1EN);
temp=TIM1->CTRL2;
temp &=~(1<<9);//输出空闲状态1(OC1N输出) 0:当MOEN=0时,死区后OC1N=0;1:当MOEN=0时,死区后OC1N=1。
temp &=~(1<<8);//输出空闲状态1(OC1输出) 0:当MOEN=0时,如果实现了OC1N,则死区后OC1=0;1:当MOEN=0时,如果实现了OC1N,则死区后OC1=1。
temp &=(~(0X07<<4));
temp |=(0X05<<4);//101:比较 - OC2REF 信号用作触发输出(TRGO)。
temp |=(1<<0);//1:CCxEN,CCxNEN和OCxMD位是预装载的;设置该位后,它们只在设置了CCUDGN位后被更新。
TIM1->CTRL2 =temp;
//捕获/比较模式寄存器
temp=TIM1->CCMOD1;
temp &=(~(0x07<<12));
temp |=(0x06<<12);//110:PWM 模式 1 - 在向上计数模式下,如果 TIMx_CNT < TIMx_CCDAT2,则通道2 的 OC2REF 信号为高电平,否则为低电平。
//在向下计数模式下,如果TIMx_CNT > TIMx_CCDAT2,则通道 2 的 OC2REF 信号为低电平,否则为高电平。
temp |=(1<<11);//0:禁用 TIMx_CCDAT2 寄存器的预加载功能。 支持随时对TIMx_CCDAT2寄存器进行写操作,写入的值立即生效。
//1:使能 TIMx_CCDAT2 寄存器的预加载功能。 仅对预加载寄存器进行读写操作。当更新事件发生时,TIMx_CCDAT2的值被加载到影子寄存器中。
temp &=(~(3<<8));//00:CC2通道被配置为输出;
temp &=(~(1<<7));//输出比较1清’0’使能。0:OC1REF 不受ETRF输入的影响;1:一旦检测到ETRF输入高电平,清除OC1REF=0。
temp &=(~(0x07<<4));
temp |=(0x06<<4);//110:PWM 模式 1 - 在向上计数模式下,如果 TIMx_CNT < TIMx_CCDAT1,则通道1 的 OC1REF 信号为高电平,否则为低电平。
//在向下计数模式下,如果TIMx_CNT > TIMx_CCDAT1,则通道 1 的 OC1REF 信号为低电平,否则为高电平。
temp |=(1<<3);//0:禁用 TIMx_CCDAT1 寄存器的预加载功能。 支持随时对TIMx_CCDAT1寄存器进行写操作,写入的值立即生效。
//1:使能 TIMx_CCDAT1 寄存器的预加载功能。 仅对预加载寄存器进行读写操作。当更新事件发生时,TIMx_CCDAT1 的值被加载到影子寄存器中。
temp &=(~(3<<0));//00:CC1通道被配置为输出;
TIM1->CCMOD1 =temp;
TIM1->CCMOD2=0;
//捕获/比较使能寄存器
temp=TIM1->CCEN;
temp &=~(1<<3);//捕获/比较1互补输出极性 0:OC1N高电平有效;1:OC1N低电平有效。
temp |=(1<<2);//0:禁用 - 禁用输出 OC1N 信号。1:使能 - 使能输出 OC1N 信号。
temp &=(~(1<<1));//捕获/比较1输出极性 0:OC1高电平有效;1:OC1低电平有效。
temp |=(1<<0);//0:禁用 - 禁用输出 OC1 信号。1:使能 - 使能输出 OC1信号。
TIM1->CCEN =temp;
TIM1->CCDAT1=period>>1;//比较器CC1的比较值设置为period/2,即输出占空比为50%。
TIM1->EVTGEN |=(1<<5);//CCUDGN - 捕获/比较事件,产生控制更新。该位由软件设置。 如果此时 TIMx_CTRL2.CCPCTL = 1,则允许更新 CCxEN、CCxNEN 和 OCxMD 位。 该位由硬件自动清零。0:无动作 1:产生一个COM事件
/*******************pwm输出配置部分 end***********************/
//DMA/中断使能寄存器
temp=TIM1->DINTEN;
temp &=(~(1<<7));//1:允许刹车中断。0:禁止刹车中断;
temp &=(~(1<<6));//1:使能触发中断。0:禁止触发中断;
temp &=(~(1<<5));//1:允许COM中断。0:禁止COM中断;
temp &=(~(1<<0));//1:允许更新中断。0:禁止更新中断;
TIM1->DINTEN =temp;
TIM1->SMCTRL=0;//从模式控制寄存器
TIM1->STS=0;//状态标志位
TIM1->EVTGEN=0;//事件产生寄存器
temp=TIM1->BKDT;
temp &=(~(1<<14));//0:只有软件可以设置TIMx_BKDT.MOEN;1:软件设置TIMx_BKDT.MOEN; 或者如果刹车输入未激活,则在下一次更新事件发生时,硬件自动设置 TIMx_BKDT.MOEN。
temp &=(~(1<<13));//刹车输入极性:0:刹车输入低电平有效;1:刹车输入高电平有效。
temp &=(~(1<<12));//刹车功能使能:0:禁止刹车输入(BRK及CCS时钟失效事件);1:开启刹车输入(BRK及CCS时钟失效事件)。
temp &=(~(1<<11));//0:当定时器不工作时,禁止OC/OCN输出(OC/OCN使能输出信号=0);1:当定时器不工作时,一旦CCxEN=1或CCxNEN=1,首先开启OC/OCN并输出无效电平,然后置OC/OCN使能输出信号=1。
temp &=(~(1<<10));//0:当定时器不工作时,禁止OC/OCN输出(OC/OCN使能输出信号=0);1:当定时器不工作时,一旦CCxEN=1 或CCxNEN=1,OC/OCN首先输出其空闲电平,然后OC/OCN使能输出信号=1。
temp &=(~(0X03<<8));
temp |=((0X01<<8));//锁定设置 (Lock configuration) 该位为防止软件错误而提供写保护。此处设置锁定级别为 1级。
temp &=(~(0Xff<<0));
temp |=(12<<0);//死区发生器设置:
//DTGN[7:5]=0xx => DT=DTGN[7:0] × Tdtgn,Tdtgn = TDTS;
//DTGN[7:5]=10x => DT=(64+DTGN[5:0]) × Tdtgn,Tdtgn = 2 × TDTS;
//DTGN[7:5]=110 => DT=(32+DTGN[4:0]) × Tdtgn,Tdtgn = 8 × TDTS;
//DTGN[7:5]=111 => DT=(32+DTGN[4:0])× Tdtgn,Tdtgn = 16 × TDTS;
TIM1->BKDT =temp;
TIM1->EVTGEN |=(1<<0);//产生更新事件
TIM1->CTRL1 |=1;//0:禁止计数器;1:使能计数器。
TIM1->BKDT |=(1<<15);//主输出使能 0:OC 和 OCN 输出被禁用或强制进入空闲状态。1:如果设置了 TIMx_CCEN.CCxEN 或 TIMx_CCEN.CCxNEN 位,则使能 OC 和 OCN输出。
}
main.c
#include "main.h"
#include "sys.h"
#include "pwm.h"
int main(void)
{
uint8_t dir=0;
uint16_t pwm=180;
/* SystemInit() function has been called by startup file startup_n32g031.s */
/* Configure the system clock to 48 MHz */
PWMInit_ds(48-1,999);//48MHz/((48)*(999+1))=1KHz
while (1)
{
//以下是呼吸灯的实例代码。
if(pwm<150)
{
dir=0;
}
else if(pwm>850)
{
dir=1;
}
if(dir)pwm -=50;else pwm +=50;
Systick_delayMs(100);//延时100ms
TIM1->CCDAT1 = pwm;//修改占空比
}
}
结尾
把PA4或PA7接上LED灯,可以看到呼吸灯效果。使用示波器观察PA4、PA7脚波形,可以观察到一组互补PWM波,占空比在不断修改。