您的位置:首页 > 其它

STM32F10X PWM配置例程详解,测试无误

2016-01-31 23:42 330 查看
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
}


欢迎讨论,共同进步
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: