单片机
返回首页

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

2018-07-06 来源:eefocus

硬件平台: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

}


进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • PIC单片机控制的遥控防盗报警器电路

  • 用数字电路CD4069制作的万能遥控轻触开关

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 开关电源的基本组成及工作原理

  • 如何构建一个触摸传感器电路

  • 基于ICL296的大电流开关稳压器电源电路

    相关电子头条文章