1、TIMER 输出 PWM 基本概念
脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的
数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。一般用来控
制步进电机的速度等等。STM32 的定时器除了 TIM6 和 TIM7 之外,其他的定时器都可以用来产生 PWM
输出,其中高级定时器 TIM1 和 TIM8 可以同时产生 7 路的 PWM 输出,而通用定时器也能同时产生 4 路
的 PWM 输出。
1.1 PWM 输出模式
STM32 的 PWM 输出有两种模式,模式 1 和模式 2,由 TIMx_CCMRx 寄存器中的 OCxM 位确定的(“110”
为模式 1,“111”为模式 2)。模式 1 和模式 2 的区别如下:110:PWM 模式 1-在向上计数时,一旦
TIMx_CNTTIMx_CCR1 时通道 1 为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)。111:PWM 模
式 2-在向上计数时,一旦 TIMx_CNTTIMx_CCR1 时通道 1 为有效电平,否则为无效电平。由此看来,模式 1 和模式 2 正
好互补,互为相反,所以在运用起来差别也并不太大。而从计数模式上来看,PWM 也和 TIMx 在作定时
器时一样,也有向上计数模式、向下计数模式和中心对齐模式,关于 3 种模式的具体资料,可以查看《STM32
参考手册》的“14.3.9 PWM 模式”一节,在此就不详细赘述了。
1.2 PWM 输出管脚
PWM 的输出管脚是确定好的,具体的引脚功能可以查看《STM32 参考手册》的“8.3.7 定时器复用
功能重映射”一节。在此需要强调的是,不同的 TIMx 有分配不同的引脚,但是考虑到管脚复用功能,STM32
提出了一个重映像的概念,就是说通过设置某一些相关的寄存器,来使得在其他非原始指定的管脚上也能
输出 PWM。但是这些重映像的管脚也是由参考手册给出的。比如说 TIM3 的第 2 个通道,在没有重映像
的时候,指定的管脚是 PA.7,如果设置部分重映像之后,TIM3_CH2 的输出就被映射到 PB.5 上了,如果
设置了完全重映像的话,TIM3_CH2 的输出就被映射到 PC.7 上了。
1.3 PWM 输出信号
PWM 输出的是一个方波信号,信号的频率是由 TIMx 的时钟频率和 TIMx_ARR 预分频器所决定的,
具体设置方法在前面一个学习笔记中有详细的交代。而输出信号的占空比则是由 TIMx_CRRx 寄存器确定
的。其公式为“占空比=(TIMx_CRRx/TIMx_ARR)*100%”,因此,可以通过向 CRR 中填入适当的数来输
出自己所需的频率和占空比的方波信号。
2、TIMER 输出 PWM 源代码
#include "stm32f10x.h"
/* Private typedef -----------------------------------------------------------*/
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
u16 TimerPeriod = 7200;
u16 DutyFactor = 50;
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
void RCC_Configure();
void GPIO_Configure();
void TIM_Configure();
void PWM_Configure();
void NVIC_Configure();
/**
* @brief Main program.
* @param None
* @retval None
*/
void RCC_Configure()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA
|
RCC_APB2Periph_GPIOB
|
RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4 | RCC_APB1Periph_TIM3,ENABLE);
}
void GPIO_Configure()
{
/* GPIOA 配置:通道 PA.6 和 PA.7 为 输出引脚*/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void TIM_Configure()
{
/* Time Base configuration 这里配置的就是 PWM 的周期,pwm 还真是比 AVR 的强劲*/
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
//计数方
式
TIM_TimeBaseStructure.TIM_Period = TimerPeriod - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
//这边是普通定时器的初始化和中断申请
TIM_DeInit(TIM4);
TIM_TimeBaseStructure.TIM_Period=1;
TIM_TimeBaseStructure.TIM_Prescaler= 3599;
TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; //采样分频
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
//自动重装载寄存器的值
//时钟预分频数
TIM_ClearFlag(TIM4, TIM_FLAG_Update); //清除溢出中断标志
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);
// 计数溢出时触发中断
}
void PWM_Configure()
{
/* 通道 1,2 和 3 配置在 PWM 模式 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = DutyFactor * 7200 / 100;//设置占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//这里的 4 行代码就是设置 PWM
的空闲电平、波形方式的!一开始自己一不小心搞成了都高的死区,这里是都低电平的死区~
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC2Init(TIM3, &TIM_OCInitStructure);
PWM
//初始化两组互补的
/* 自动输出使能,中断,死区相关的设置*/
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
TIM_BDTRInitStructure.TIM_DeadTime = 12;//死区时间为 12/SYSTEMCLK (ns)
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;//关闭外部 break 功能,当然在产品中最
好加入这个保护,蛮好用的。
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;
TIM_BDTRConfig(TIM3, &TIM_BDTRInitStructure);
TIM_CtrlPWMOutputs(TIM3, ENABLE);
/* 主输出启用 */
}
void NVIC_Configure()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //通道 TIM3
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //副优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
int main(void)
{
RCC_Configure();
GPIO_Configure();
TIM_Configure();
PWM_Configure();
NVIC_Configure();
TIM_Cmd(TIM3, ENABLE);
TIM_Cmd(TIM4, ENABLE);
while (1);
}
/* TIM3 的计数器使能 */
/* TIM4 的计数器使能 */
放在 system32_it.c 里执行
void TIM4_IRQHandler(void)
{
static vu16 sign = 0;
static vu16 Counter_sine=0;
static
vu16
Duty_Cycle_sinewavetable[128]={0x0,0x83,0x105,0x187,0x209,0x28A,0x30A,0x38A,0x409,0x486,0x502,0x57
D,0x5F7,0x66F,0x6E5,0x75A,0x7CC,0x83D,0x8AB,0x917,0x981,0x9E8,0xA4D,0xAAF,0xB0E,0xB6B,0xBC4,
0xC1B,0xC6E,0xCBE,0xD0B,0xD54,0xD9A,0xDDD,0xE1C,0xE57,0xE8F,0xEC3,0xEF3,0xF1F,0xF48,0xF6C,0
xF8D,0xFA9,0xFC2,0xFD6,0xFE6,0xFF3,0xFFB,0xFFF,0xFFF,0xFFB,0xFF3,0xFE6,0xFD6,0xFC2,0xFA9,0xF8
D,0xF6C,0xF48,0xF1F,0xEF3,0xEC3,0xE8F,0xE57,0xE1C,0xDDD,0xD9A,0xD54,0xD0B,0xCBE,0xC6E,0xC1
B,0xBC4,0xB6B,0xB0E,0xAAF,0xA4D,0x9E8,0x981,0x917,0x8AB,0x83D,0x7CC,0x75A,0x6E5,0x66F,0x5F7,
0x57D,0x502,0x486,0x409,0x38A,0x30A,0x28A,0x209,0x187,0x105,0x83,0x0};
if ( TIM_GetITStatus(TIM4 , TIM_IT_Update) == SET)
{
if(sign == 0)
{
TIM_SetCompare1(TIM3,Duty_Cycle_sinewavetable[Counter_sine]);
TIM_SetCompare2(TIM3,0);
Counter_sine++;
if(Counter_sine==100)
{
//半周期采样 100 个点
Counter_sine=0;
sign = 1;
}
}else{
TIM_SetCompare1(TIM3,0);
TIM_SetCompare2(TIM3,Duty_Cycle_sinewavetable[Counter_sine]);
Counter_sine++;
if(Counter_sine==100)
{
Counter_sine=0;
sign = 0;
}
}
}
TIM_ClearITPendingBit(TIM4 , TIM_FLAG_Update);
}