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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/moses1994/article/details/50616512
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
}


欢迎讨论,共同进步
--------------------- 
作者:moses1994 
来源:CSDN 
原文:https://blog.csdn.net/moses1994/article/details/50616512 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值