历史上的今天
返回首页

历史上的今天

今天是:2025年06月12日(星期四)

正在发生

2020年06月12日 | STM32基于固件库学习笔记(9)TM3产生PWM调LED亮度

2020-06-12 来源:eefocus

PWM

  脉冲宽度调制模式可以产生一个由TIMx_ARR寄存器确定频率、由TIMx_CCRx寄存器确定占空比的信号,也就是说对脉冲宽度的控制。


  STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出! 


  在TIMx_CCMRx寄存器中的OCxM位写入’110’(PWM模式1)或’111’(PWM模式2),能够独立地设置每个OCx输出通道产生一路PWM。必须设置TIMx_CCMRx寄存OCxPE位以使能相应的预装载寄存器,最后还要设置TIMx_CR1寄存器的ARPE位,(在向上计数或中心对称模式中)使能自动重装载的预装载寄存器。


  仅当发生一个更新事件的时候,预装载寄存器才能被传送到影子寄存器,因此在计数器开始计数之前,必须通过设置TIMx_EGR寄存器中的UG位来初始化所有的寄存器。


  OCx的极性可以通过软件在TIMx_CCER寄存器中的CCxP位设置,它可以设置为高电平有效或低电平有效。TIMx_CCER寄存器中的CCxE位控制OCx输出使能。详见TIMx_CCERx寄存器的描述。


  在PWM模式(模式1或模式2)下,TIMx_CNT和TIMx_CCRx始终在进行比较,(依据计数器的计数方向)以确定是否符合TIMx_CCRx≤TIMx_CNT或者TIMx_CNT≤TIMx_CCRx。然而为了与OCREF_CLR的功能(在下一个PWM周期之前,ETR信号上的一个外部事件能够清除OCxREF)一致,OCxREF信号只能在下述条件下产生:


  ● 当比较的结果改变,或通用定时器(TIMx)

  ● 当输出比较模式(TIMx_CCMRx寄存器中的OCxM位)从“冻结”(无比较,OCxM=’000’)切换到某个PWM模式(OCxM=’110’或’111’)。


  这样在运行中可以通过软件强置PWM输出。


  根据TIMx_CR1寄存器中CMS位的状态,定时器能够产生边沿对齐的PWM信号或中央对齐的PWM信号。


PWM 边沿对齐模式


向上计数配置

  当TIMx_CR1寄存器中的DIR位为低的时候执行向上计数。


  下面是一个PWM模式1的例子。当TIMx_CNT

  如果比较值为0,则OCxREF保持为’0’。 下图为TIMx_ARR=8时边沿对齐的PWM波形实例。


- 向下计数配置

  当TIMx_CR1寄存器的DIR位为高时执行向下计数。

  在PWM模式1,当TIMx_CNT>TIMx_CCRx时参考信号OCxREF为低,否则为高。如果TIMx_CCRx中的比较值大于TIMx_ARR中的自动重装载值,则OCxREF保持为’1’。该模式下不能产生0%的PWM波形。


PWM 中央对齐模式

  当TIMx_CR1寄存器中的CMS位不为’00’时,为中央对齐模式(所有其他的配置OCxREF/OCx信号都有相同的作用)。根据不同的CMS位设置,比较标志可以在计数器向上计数时被置’1’、在计数器向下计数时被置’1’、或在计数器向上和向下计数时被置’1’。TIMx_CR1寄存器中的计数方向位(DIR)由硬件更新,不要用软件修改它。


下图给出了一些中央对齐的PWM波形的例子

 ● TIMx_ARR=8

 ● PWM模式1

 ● TIMx_CR1寄存器中的CMS=01,在中央对齐模式1时,当计数器向下计数时设置比较标志。


使用中央对齐模式的提示:

● 进入中央对齐模式时,使用当前的向上/向下计数配置;这就意味着计数器向上还是向下计数取决于TIMx_CR1寄存器中DIR位的当前值。此外,软件不能同时修改DIR和CMS位。

● 不推荐当运行在中央对齐模式时改写计数器,因为这会产生不可预知的结果。特别地:

 ─ 如果写入计数器的值大于自动重加载的值(TIMx_CNT>TIMx_ARR),则方向不会被更新。


例如,如果计数器正在向上计数,它就会继续向上计数。

 ─ 如果将0或者TIMx_ARR的值写入计数器,方向被更新,但不产生更新事件UEV。

● 使用中央对齐模式最保险的方法,就是在启动计数器之前产生一个软件更新(设置

TIMx_EGR 位中的UG位),不要在计数进行过程中修改计数器的值。


通过库函数来配置该功能的步骤

注:本次学习的使用TIM3的向上计数模式(TIM3_CH2 通道将重映射到 PB5 上)产生PWM。


1. 开启TIM3时钟以及复用功能时钟,配置PB5为复用输出。


  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  //使能定时器3时钟

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //复用时钟使能

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //使能GPIOB时钟

  

  GPIO_InitTypeDef GPIO_ITDef_PB5;

  GPIO_ITDef_PB5.GPIO_Pin = GPIO_Pin_5;

  GPIO_ITDef_PB5.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出

  GPIO_ITDef_PB5.GPIO_Speed = GPIO_Speed_2MHz;

  GPIO_Init(GPIOB,&GPIO_ITDef_PB5);


2. 设置TIM3_CH2重映射到 PB5上。


默认条件下,TIM3_REMAP[1:0]为 00,是没有重映射的,所以 TIM3_CH1~TIM3_CH4 分别是接在 PA6、PA7、PB0 和 PB1 上的,而我们想让 TIM3_CH2 映射到 PB5 上,则需要设置TIM3_REMAP[1:0]=10,即部分重映射,这里需要注意,此时 TIM3_CH1 也被映射到 PB4 上了。


GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //重映射 TIM3_CH2->PB5


3. 初始化TIM3, 设置TIM3的ARR和PSC 。

注:向上计数模式


  TIM_TBIStruct_TIM3.TIM_Period = 100; //设置自动重装载值 ARR

  TIM_TBIStruct_TIM3.TIM_Prescaler = 7199; //设置预分频值 PSC

  TIM_TBIStruct_TIM3.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

  TIM_TBIStruct_TIM3.TIM_ClockDivision = TIM_CounterMode_Up; //TIM 向上计数模式  

  TIM_TimeBaseInit(TIM3,&TIM_TBIStruct_TIM3);


4. 设置TIM3_CH2的PWM 模式 使能TIM3的CH2输出。

注:在固件库"stm32f10x.h"和"stm32f10x.c"里能查到。


  void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

//主要还是TIM_OCInitTypeDef结构体的定义内容。

typedef struct

{

  uint16_t TIM_OCMode;//选择定时器模式。

  /*

  TIM_OCMode_Timing  TIM 输出比较时间模式

 TIM_OCMode_Active   TIM 输出比较主动模式

 TIM_OCMode_Inactive TIM 输出比较非主动模式

 TIM_OCMode_Toggle   TIM 输出比较触发模式

 TIM_OCMode_PWM1     TIM 脉冲宽度调制模式 1

 TIM_OCMode_PWM2     TIM 脉冲宽度调制模式 2

  */      

  uint16_t TIM_OutputState; //设置比较输出使能

  uint16_t TIM_OutputNState; //指定TIM的互补输出比较状态。 

  //设置了待装入捕获比较寄存器的脉冲值。它的取值必须在 0x0000 和 0xFFFF 之间。

  uint16_t TIM_Pulse; 

  uint16_t TIM_OCPolarity;//输出极性

  /*

  TIM_OCPolarity_High TIM 输出比较极性高

  TIM_OCPolarity_Low TIM 输出比较极性低

  */

  uint16_t TIM_OCNPolarity;//指定互补的输出极性。  

  uint16_t TIM_OCIdleState; //指定空闲状态下的TIM输出比较引脚状态。

  uint16_t TIM_OCNIdleState; //指定空闲状态下的TIM输出比较引脚状态。

} TIM_OCInitTypeDef;


 //初始化 TIM3 Channel2 PWM 模式

  TIM_OCInitTypeDef TIM_OCITDef_TIM3; 

  TIM_OCITDef_TIM3.TIM_OCMode = TIM_OCMode_PWM2; //选择 PWM 模式 2

  TIM_OCITDef_TIM3.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能

  TIM_OCITDef_TIM3.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高  

  TIM_OC2Init(TIM3, &TIM_OCITDef_TIM3); //初始化外设 TIM3 OC2


注:TIM_OutputNState、TIM_OCNPolarity、TIM_OCIdleState 和 TIM_OCNIdleState 是高级定时器 TIM1 和 TIM8 才用到的


5. 使能TIM3。


TIM_Cmd(TIM3, ENABLE); //使能 TIM3


6. 修改TIM3_CCR2来控制占空比。

注:修改Compare2便可以修改占空比。


void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);


完整程序

#include "stm32f10x.h"  

/*

   TIM3输出比较模式

   LED  PB5

   KEY0   

*/

u16 data = 0;//0-1000

u8 temp = 2;//1-250 2-500 3-1000

u8 dir = 1;

void delay_ms(u16 time)

{    

   u16 i = 0;  

   while(time--)

   {

      i = 12000;  

      while(i--);    

   }

}


void LED_Init_PE5(void)

{

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);  

  GPIO_InitTypeDef GPIO_ITDef_PE5;

  GPIO_ITDef_PE5.GPIO_Pin = GPIO_Pin_5;

  GPIO_ITDef_PE5.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出

  GPIO_ITDef_PE5.GPIO_Speed = GPIO_Speed_2MHz;

  GPIO_Init(GPIOE,&GPIO_ITDef_PE5);

  GPIO_SetBits(GPIOE,GPIO_Pin_5);//1

}

void KEY0_Init_PE4(void)

{

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_AFIO,ENABLE);

  GPIO_InitTypeDef GPIO_ITDef_PE4;

  GPIO_ITDef_PE4.GPIO_Pin = GPIO_Pin_4;

  GPIO_ITDef_PE4.GPIO_Mode = GPIO_Mode_IPU;//上拉输入

  GPIO_Init(GPIOE,&GPIO_ITDef_PE4); 

  

  //外部中断

  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);

  

  EXTI_InitTypeDef EXTI_ITDef_PE4;

  EXTI_ITDef_PE4.EXTI_Line = EXTI_Line4;

  EXTI_ITDef_PE4.EXTI_Mode =  EXTI_Mode_Interrupt;//为中断请求

  EXTI_ITDef_PE4.EXTI_Trigger = EXTI_Trigger_Falling;//输入线路下升沿中断

  EXTI_ITDef_PE4.EXTI_LineCmd = ENABLE;  

  EXTI_Init(&EXTI_ITDef_PE4);

  

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置 NVIC 中断分组 2:2 位抢占优先级,2 位响应优先级

  

  NVIC_InitTypeDef NVIC_ITDef;

  

  NVIC_ITDef.NVIC_IRQChannel = EXTI4_IRQn; //使能按键外部中断通道

  NVIC_ITDef.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级 2,

  NVIC_ITDef.NVIC_IRQChannelSubPriority = 0x02; //子优先级 2

  NVIC_ITDef.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道

  

  NVIC_Init(&NVIC_ITDef);

   

}

void TIM3_Init(void)

{

  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //复用时钟使能

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); 

  

  GPIO_InitTypeDef GPIO_ITDef_PB5;

  GPIO_ITDef_PB5.GPIO_Pin = GPIO_Pin_5;

  GPIO_ITDef_PB5.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出

  GPIO_ITDef_PB5.GPIO_Speed = GPIO_Speed_2MHz;

  GPIO_Init(GPIOB,&GPIO_ITDef_PB5);


  TIM_DeInit(TIM3);//复位TIM3(可要可不要)

  

  GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE); //重映射 TIM3_CH2->PB5

 

  TIM_TimeBaseInitTypeDef TIM_TBIStruct_TIM3;

  

  TIM_TBIStruct_TIM3.TIM_Period = 100; //设置自动重装载值

  TIM_TBIStruct_TIM3.TIM_Prescaler = 7199; //设置预分频值

  TIM_TBIStruct_TIM3.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim

  TIM_TBIStruct_TIM3.TIM_ClockDivision = TIM_CounterMode_Up; //TIM 向上计数模式  

  TIM_TimeBaseInit(TIM3,&TIM_TBIStruct_TIM3);

  

  //初始化 TIM3 Channel2 PWM 模式

  TIM_OCInitTypeDef TIM_OCITDef_TIM3;  

  TIM_OCITDef_TIM3.TIM_OCMode = TIM_OCMode_PWM2; //选择 PWM 模式 2

  TIM_OCITDef_TIM3.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能

  TIM_OCITDef_TIM3.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高  

  TIM_OC2Init(TIM3, &TIM_OCITDef_TIM3); //初始化外设 TIM3 OC2

  

  TIM_OC2PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能预装载寄存器

  

  TIM_Cmd(TIM3,ENABLE); //使能 TIM3

}


int main(void)

{

  LED_Init_PE5();

  KEY0_Init_PE4();

  TIM3_Init();

  while(1)

  {

   

    //按键处理(通过按键改变占空比)

    switch(temp)

    {

      case 0:data=0;break;

      case 1:data=3;break;

      case 2:data=5;break;

      case 3:data=10;break;

      case 4:data=50;break;

      case 5:data=90;break;

      case 6:data=100;break;

    }

/*    data从0-200-0 来修改占空比

     delay_ms(10);

     if(dir)data++;

       else data--;

     if(data>200)dir=0;

     if(data==0)dir=1;

*/

     TIM_SetCompare2(TIM3,data);

  }

     

}

//按键中断处理函数,按键每按一下,PE5的状态翻转一次(LED亮灭翻转)

void EXTI4_IRQHandler(void)

{

    if(EXTI_GetITStatus(EXTI_Line4)!=RESET)//判断某个线上的中断是否发生

   {

     delay_ms(10);     //按键延时消斗

     if(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)))//再次判断是否按键按下

     {    

       temp++;//按下一次,temp+1,

       if(temp>6)temp=0;//这里可以根据实际需求修改

       

       if(GPIO_ReadOutputDataBit(GPIOE,GPIO_Pin_5))//是灭的

       {

         GPIO_ResetBits(GPIOE,GPIO_Pin_5); //BRR  0 亮

       }else 

       {

         GPIO_SetBits(GPIOE,GPIO_Pin_5);  //BSRR  1  灭

       }          

     }   

    EXTI_ClearITPendingBit(EXTI_Line4); 

推荐阅读

史海拾趣

全智景(Allvision)公司的发展小趣事

全智景公司自成立以来,一直致力于电子视觉技术的研发。早期,公司面临激烈的市场竞争和技术瓶颈,但全智景团队凭借坚韧不拔的精神,成功研发出了一款具有划时代意义的高清摄像头芯片。这款芯片以其出色的图像质量和稳定性,迅速获得了市场的认可,并推动了整个电子视觉行业的进步。这一技术突破不仅让全智景公司在市场上站稳了脚跟,也为公司后续的发展奠定了坚实的基础。

Califia Lighting公司的发展小趣事

随着产品质量的不断提升和市场份额的逐步扩大,Califia Lighting开始积极寻求与国际市场的合作。他们与多个国家和地区的合作伙伴建立了稳定的合作关系,将产品出口到全球各地。同时,公司还参加了多个国际电子照明展会,与全球同行交流学习,进一步提升了公司的国际影响力。

EVERLIGHT公司的发展小趣事

在电子产品领域,品质是企业生存和发展的关键。EVERLIGHT一直将品质管理放在首位,建立了严格的质量控制体系。公司从原材料采购到生产过程,再到产品出厂,都进行严格的品质检测和监控,确保每一颗LED都符合高标准的质量要求。这种对品质的坚持让EVERLIGHT在市场上赢得了良好的口碑,也为企业赢得了更多客户的信任。

Custom Components Inc公司的发展小趣事

随着全球环保意识的不断提高,电子行业对绿色环保和可持续发展的要求也日益严格。CCI积极响应这一趋势,将绿色环保理念融入公司的战略规划和日常运营中。

公司不仅采用了环保材料和绿色生产工艺来降低产品对环境的影响,还通过优化生产流程和资源利用等方式降低能耗和排放。此外,CCI还积极参与行业环保组织和公益活动,推动整个行业向更加绿色、环保的方向发展。

上海国芯(Gcore)公司的发展小趣事
首先检查电源电压是否正常,电源是否稳定。
Aristo-Craft/ L M P Inc公司的发展小趣事

随着科技的进步,Aristo-Craft/L M P Inc公司意识到要想在行业中保持领先地位,必须不断进行技术创新和产品升级。公司投入大量资源进行研发,成功推出了一系列具有创新性的电子产品,如高性能的集成电路、先进的通信设备等。这些产品不仅提高了公司的市场竞争力,还为客户带来了更好的使用体验。

问答坊 | AI 解惑

求助!光耦 延时 继电器电路

最近接一活,想了几天没理出思路,请大家帮帮忙 流程:SW1切换控制电机(强电)正反转,如:电机正常运转,SW1切换,K2动作,电机停止;K1延时2秒动作,电机换向,同时K2断开。 (时间继电器买的是标准品,改造了一下,里面的时间控制芯片型号为 ...…

查看全部问答>

提一个关于C8051f系列中断借口设置的问题

在C8051f020中,为INT0和INT1分配了端口之后,程序中除了对XBR1进行设置外还需要设置写什么?谢谢…

查看全部问答>

请教2440 nand flash驱动的一些问题

//今天看了一下flash驱动的一些代码,发现有些不解。 //平台描述2440+64M nand  flash -------------------------------------------- /* 写扇区函数     @func   BOOL | FMD_WriteSector | Writes the specified ...…

查看全部问答>

请问CE系统启动时如何自动运行在SD Card里的应用程序?

自动运行在SD Card里的应用程序与自动运行系统中的应用程序是不一样的,不需要制定到NK.bin里,并且要先要系统识别了SD Card后,再运行里面的程序。 试过了配置platform.reg或者common.reg,在[HKEY_LOCAL_MACHINE\\init]段添加如下内容: \"La ...…

查看全部问答>

M0例子这一句没看明白

请问:  LPC_SYSCON->SYSAHBCLKCTRL |= (1 << 16);                             /* ...…

查看全部问答>

STR91x编程参考手册中文版V1.0

STR91x编程参考手册 V1.0   由于时间仓促及水平所限,以及ST的技术文档本身也在不断修正,错误及不妥之处在所难免,欢迎各位读者批评指正。同时大家在开发过程中请下载ST发布的最新芯片手册,如果有技术问题,欢迎访问我 ...…

查看全部问答>

Cosmic

我下了个CosMIC for STM8 但不能仿真,是没有LICENSE的原因吗? 从COsmic申请一个免费的LICENSE,怎么不给我呀…

查看全部问答>

MSP430F123

最近因为项目中,初次使用MSP430F123,发现有点问题,是不是该系列的SPI和UART,不能同时使用,郁闷中,简直难用的要死。…

查看全部问答>