STM32F10X PWM配置例程详解,测试无误
硬件平台:STM32F10X PWM模块 + JLink + 示波器
软件平台:Keil 4
一、基础知识
首先,根据芯片的型号,STM32小容量、中容量产品和STM32F105xx/STM32F107xx的互联型产品,包含一个高级控制定时器(TIM1)。大容量产品的STM32F103xx包含有二个高级控制定时器(TIM1和TIM8)。
一个高级定时器可以输出七路PWM波,而一个通用定时器则最多只能输出四路互补PWM波。
通用定时器和高级定时器相互独立,互不影响,可同时操作。
如果需要的PWM 较多,比如控制六轴的话,可以自行选取不同的定时器,一个不够的话可选两个。
其次,每个通用的定时器一般只有4路通道,每个通道有一个比较寄存器,初始化的时候设置不同的值后,可以生成4路PWM信号,不过这4路的PWM频率相同,占空比可以不一样。
最后,有任何关于引脚复用、及相关寄存器的具体问题,以相应的数据手册为准。
PWM的实质还是定时器TIMER模块的使用。
二、相应模块
程序涉及的模块有:
RCC:复位及时钟控制模块,用于初始化STM32 USART外设时钟及IO口复用时钟;
GPIO:通用输入输出口复用配置模块;
Delay:利用系统时钟SysTick,也号称“滴答”,写的延时模块;
Led:系统运行提示模块;
Timer:定时器模块配置,PWM配置也在其中。
三:代码
RCC
#include "Rcc.h"
void RCC_Init(void)
{
ErrorStatus HSEStartUpStatus;
//定义枚举类型错误状态变量
RCC_DeInit();//复位系统时钟设置
RCC_HSEConfig(RCC_HSE_ON);
//打开外部高速时钟晶振,使能HSE
/*RCC_HSE_ON 开
_off 关 _bypass hse晶振被外部时钟旁路*/
HSEStartUpStatus = RCC_WaitForHSEStartUp();
/*RCC_WaitForHSEStartUp()返回一个ErrorStatus枚举值,
success好,error未好*/
if(HSEStartUpStatus == SUCCESS)//HES就绪
{
RCC_HCLKConfig(RCC_SYSCLK_Div1);
//AHB时钟(HCLK)=系统时钟
RCC_PCLK1Config(RCC_HCLK_Div2);
//设置低速AHB时钟(APB1)为HCLK的2分频
RCC_PCLK2Config(RCC_HCLK_Div1);
//设置高速AHB时钟(APB2)=HCLK时钟
FLASH_SetLatency(FLASH_Latency_2);
//设置FLASH延时周期数为2
//使能领取指缓存
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
//设置PLL时钟源及倍频系数,为HSE的9倍频 8MHz * 9 = 72MHz
/*void RCC_PLLConfig(u32 RCC_PLLSource, u32 RCC_PLLMul)
RCC_PLLSource_HSI_Div2 pll输入时钟=hsi/2;
RCC_PLLSource_HSE_Div1 pll输入时钟 =hse
RCC_PLLSource_HSE_Div2 pll输入时钟=hse/2
RCC_PLLMul_2 ------_16 pll输入时钟*2---16
pll输出时钟不得超过72MHZ*/
RCC_PLLCmd(ENABLE);
//ENABLE / DISABLE
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);//等待PLL输出稳定
/*FlagStatus RCC_GetFlagStatus(u8 RCC_FLAG) 检查指定RCC标志位
返回SET OR RESET
RCC_FLAG_HSIRDY HSI晶振就绪
RCC_FLAG_HSERDY
RCC_FLAG_PLLRDY
RCC_FLAG_LSERDY
RCC_FLAG_LSIRDY.......*/
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
//设置PLL为系统时钟源
/*void RCC_SYSCLKConfig(u32 RCC_SYSCLKSource) 设置系统时钟
RCC_SYSCLKSource_HSI
RCC_SYSCLKSource_HSE
RCC_SYSCLKSource_PLLCLK 选HSI HSE PLL 作为系统时钟*/
while(RCC_GetSYSCLKSource() != 0x08);
//判断PLL是否是系统时钟
/*u8 RCC_GetSYSCLKSource(void) 返回用作系统时钟的时钟源
0x00:HSI 0x04:HSE 0x08:PLL */
}
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |
RCC_APB2Periph_AFIO |
RCC_APB2Periph_GPIOB , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//U2 U3 时钟在APB1
//打开GPIO时钟,复用功能,串口1的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟
//好奇怪,是因为官方的库函数更新?
//不是说F10X系列只有一个CAN,而F4有CAN1 CAN2 吗?
//怎么他的系统配置文件里面是can1?????
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //时钟使能
/*void RCC_APB2PeriphClockCmd(u32 RCC_APB2Periph, FunctionalState NewState)
enable 或 disable apb2 外设时钟
RCC_APB2Periph_AFIO 功能复用IO 时钟
RCC_APB2Periph_GPIOA/B/C/D/E GPIOA/B/C/D/E 时钟
RCC_APB2Periph_ADC1/ADC2 ADC1/2 时钟
RCC_APB2Periph_TIM1
RCC_APB2Periph_SPI1
RCC_APB2Periph_USART1
RCC_APB2Periph_ALL 全部APB2外设时钟*/
}
GPIO
#include "GPIO.h"
void MYGPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO_InitStructure初始化结构体为GPIO_InitTypeDef结构
GPIO_DeInit(GPIOA);
GPIO_StructInit(&GPIO_InitStructure);
//函数:指向结构GPIO_InitTypeDef的指针,待初始化
//CAN TX : A12
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
//CAN TX : A111
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO
// USART TX :A9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
//2、GPIO_SPEED:GPIO_SPEED_10MHz/_2MHz/_50MHz 最高输出速率
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
/*Mode,工作状态:GPIO_MODE_AIN ----- 模拟输入
_IN_FLOATING ----- 浮空输入
_IPD ----- 上拉输出
_IPU ----- 上拉输入
_OUT_OD ----- 开漏输出
_OUT_PP ----- 推挽输出
_AF_OD ----- 复用开漏输出
_AF_PP ----- 复用推挽输出*/
GPIO_Init(GPIOA , &GPIO_InitStructure);
// USART RX :A10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
//IO浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
//初始化
/************pwm2 pa1**********************/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM_CH2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO
}
Delay
#include "delay.h"
static u8 fac_us=0; //us延时倍乘数
static u16 fac_ms=0; //ms延时倍乘数,在ucos下,代表每个节拍的ms数
//初始化延迟函数
//SYSTICK的时钟固定为HCLK时钟的1/8
//SYSCLK:系统时钟
void delay_init()
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //选择外部时钟 HCLK/8
fac_us=SystemCoreClock/8000000; //为系统时钟的1/8
fac_ms=(u16)fac_us*1000; //非OS下,代表每个ms需要的systick时钟数
}
//延时nus
//nus为要延时的us数.
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
//延时nms
//注意nms的范围
//SysTick->LOAD为24位寄存器,所以,最大延时为:
//nms<=0xffffff*8*1000/SYSCLK
//SYSCLK单位为Hz,nms单位为ms
//对72M条件下,nms<=1864
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms; //时间加载(SysTick->LOAD为24bit)
SysTick->VAL =0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
Led
#include "led.h"
//初始化PB12和13为输出口.并使能这两个口的时钟
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB,PE端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_12|GPIO_Pin_13);
}
Timer
#include "timer.h"
#include "led.h"
//定时器3中断服务程序
void TIM2_IRQHandler(void) //TIM2中断
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
//检查指定的TIM中断发生与否:TIM 中断源
//不等于RESET 即为 SET,就是发生了
//(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update );
//清除TIMx的中断待处理位:TIM 中断源
LED0=!LED0;
}
}
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//TIMX X:1----4
//TIM2 PWM部分初始化
//PWM输出初始化
//arr:自动重装值
//psc:时钟预分频数
void TIM2_PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/*typedef struct
{
u16 TIM_Period;
自动重装寄存器周期的值,0x00000-----0xFFFF
u16 TIM_Prescaler;
TIMX 时钟频率除数的预分频值 0x0000----0xFFFF
u8 TIM_ClockDivision;
时钟分割 TIM_CKD_DIV1 T DTS = Tck_tim
TIM_CKD_DIV2 T DTS = 2Tck_tim
TIM_CKD_DIV4 T DTS = 4Tck_tim
u16 TIM_CounterMode;
计数器模式 TIM_CounterMode_Up TIM 向上计数模式
TIM_CounterMode_Down 向下计数模式
TIM_CounterMode_CenterAligned1 -----3 中央对齐模式1--3计数模式
} TIM_TimeBaseInitTypeDef;*/
TIM_OCInitTypeDef TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//初始化TIM2
TIM_TimeBaseStructure.TIM_Period = arr;
//设置在下一个更新事件装入活动的自动重装载寄存器 周期的值 就是周期 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc;
//设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//设置时钟分割:T DTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
//TIM向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
//根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE ); //使能指定的TIM2中断,允许更新中断
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM2, ENABLE); //使能TIMx外设
//GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2, ENABLE); 怎么用????
//改变指定管脚的映射 Timer3部分重映射 TIM2_CH2->PB5
//初始化TIM2 Channel2 PWM模式
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
//选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
//比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
//输出极性:TIM输出比较极性高
//TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
//TIM_Pulse 待装入比较寄存器的脉冲值 0x0000----0xFFFF
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
//根据T指定的参数初始化外设TIM2 OC2
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
//使能TIM2在CCR2上的预装载寄存器
//TIM_ARRPreloadConfig(TIM2, ENABLE);
//使能TIM2在ARR上的预装载寄存器
TIM_Cmd(TIM2, ENABLE); //使能TIM2
}
欢迎讨论,共同进步