STM32定时器输出多路可调频率方波实现细节记录

最近有个项目要用到多达16路的可调频率的方波,于是想到用stm32的定时器输出比较翻转模式(TIM_OCMode_Toggle),一个定时器可以产生4路的信号输出。仔细阅读手册,并且参考了官方的例程,大体过程实现过程是:打开一个TIMx计数器,再打开TIMx的一路或几路输出比较器(共4路),都配置好以后,计数器开始计数,当计数器里的值和比较寄存器里的值相等时,产生输出比较中断,在中断中将计数器中的值读出,与翻转周期相加再写道比较寄存器中。

先以timer3为例,上代码:

TIM3配置部分:

  1 /******************** (C) COPYRIGHT 2015**************************
  2  * ÎļþÃû  £ºhal_TIM.c
  3  * ÃèÊö    £º
  4  * ʵÑéƽ̨£ºTHE ONE 103
  5  * Ó²¼þÁ¬½Ó£º---------------------
  6  *          |  PA.06: (TIM3_CH1)  |
  7  *          |  PA.07: (TIM3_CH2)  |
  8  *          |  PB.00: (TIM3_CH3)  |
  9  *            |  PB.01: (TIM3_CH4)  |
 10  *           ---------------------
 11  * ¿â°æ±¾  £ºST3.5.0
 12  * ×÷Õß    £º´ú¾°¾©
 13 **********************************************************************************/
 14 #include "hal_TIM.h"
 15
 16 /* ƵÂʱ仯ȫ¾Ö±äÁ¿ */
 17 uint16_t CCR1_Val = 0x8000;
 18 uint16_t CCR2_Val = 0x4000;
 19 uint16_t CCR3_Val = 0x2000;
 20 uint16_t CCR4_Val = 0x1000;
 21
 22
 23 /*
 24  * º¯ÊýÃû£ºTIM3_GPIO_Config
 25  * ÃèÊö  £ºÅäÖÃTIM3¸´ÓÃÊä³öPWMʱÓõ½µÄI/O
 26  * ÊäÈë  £ºÎÞ
 27  * Êä³ö  £ºÎÞ
 28  * µ÷Óà  £ºÄÚ²¿µ÷ÓÃ
 29  */
 30 static void TIM3_GPIO_Config(void)
 31 {
 32   GPIO_InitTypeDef GPIO_InitStructure;
 33
 34     /* TIM3 clock enable */
 35     //PCLK1¾­¹ý2±¶Æµºó×÷ΪTIM3µÄʱÖÓÔ´µÈÓÚ72MHz
 36   RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
 37
 38   /* GPIOA and GPIOB clock enable */
 39   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
 40
 41   /*GPIOA Configuration: TIM3 channel 1 and 2 as alternate function push-pull */
 42   GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_6 | GPIO_Pin_7;
 43   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;            // ¸´ÓÃÍÆÍìÊä³ö
 44   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 45
 46   GPIO_Init(GPIOA, &GPIO_InitStructure);
 47
 48   /*GPIOB Configuration: TIM3 channel 3 and 4 as alternate function push-pull */
 49   GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_0 | GPIO_Pin_1;
 50
 51   GPIO_Init(GPIOB, &GPIO_InitStructure);
 52 }
 53
 54 /*
 55  * º¯ÊýÃû£ºTIM3_Mode_Config
 56  * ÃèÊö  £ºÅäÖÃTIM3Êä³öµÄÐźŵÄģʽ
 57  * ÊäÈë  £ºÎÞ
 58  * Êä³ö  £ºÎÞ
 59  * µ÷Óà  £ºÄÚ²¿µ÷ÓÃ
 60  */
 61 static void TIM3_Mode_Config(void)
 62 {
 63     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
 64     TIM_OCInitTypeDef  TIM_OCInitStructure;
 65
 66   /* Time base configuration */
 67   TIM_TimeBaseStructure.TIM_Period = 65535;
 68   TIM_TimeBaseStructure.TIM_Prescaler = 2;        //ÉèÖÃÔ¤·ÖƵ£º²»Ô¤·ÖƵ£¬¼´Îª72MHz
 69   TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1 ;    //ÉèÖÃʱÖÓ·ÖƵϵÊý£º²»·ÖƵ
 70   TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //ÏòÉϼÆÊýģʽ
 71   TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
 72
 73   /* PWM1 Mode configuration: Channel1 */
 74   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;        //ÅäÖÃΪ±È½ÏÊä³öģʽ·­×ªÄ£Ê½
 75     TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;  //µ±¶¨Ê±Æ÷¼ÆÊýֵСÓÚCCR1_ValʱΪµÍµçƽ
 76
 77     //ʹÄܱȽÏÊä³ö·­×ªÄ£Ê½Í¨µÀ1
 78   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
 79   TIM_OCInitStructure.TIM_Pulse = CCR1_Val;       //ÉèÖÃÌø±äÖµ£¬µ±¼ÆÊýÆ÷¼ÆÊýµ½Õâ¸öֵʱ£¬µçƽ·¢ÉúÌø±ä
 80   TIM_OC1Init(TIM3, &TIM_OCInitStructure);     //ʹÄÜͨµÀ1
 81   TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Disable);
 82
 83     //ʹÄܱȽÏÊä³ö·­×ªÄ£Ê½Í¨µÀ2
 84   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
 85   TIM_OCInitStructure.TIM_Pulse = CCR2_Val;      //ÉèÖÃͨµÀ2µÄµçƽÌø±äÖµ£¬Êä³öÁíÍâÒ»¸öÕ¼¿Õ±ÈµÄPWM
 86   TIM_OC2Init(TIM3, &TIM_OCInitStructure);      //ʹÄÜͨµÀ2
 87   TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Disable);
 88
 89     //ʹÄܱȽÏÊä³ö·­×ªÄ£Ê½Í¨µÀ3
 90     TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
 91   TIM_OCInitStructure.TIM_Pulse = CCR3_Val;    //ÉèÖÃͨµÀ3µÄµçƽÌø±äÖµ£¬Êä³öÁíÍâÒ»¸öÕ¼¿Õ±ÈµÄPWM
 92   TIM_OC3Init(TIM3, &TIM_OCInitStructure);     //ʹÄÜͨµÀ3
 93     TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Disable);
 94
 95     //ʹÄܱȽÏÊä³ö·­×ªÄ£Ê½Í¨µÀ4
 96   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
 97   TIM_OCInitStructure.TIM_Pulse = CCR4_Val;    //ÉèÖÃͨµÀ4µÄµçƽÌø±äÖµ£¬Êä³öÁíÍâÒ»¸öÕ¼¿Õ±ÈµÄPWM
 98   TIM_OC4Init(TIM3, &TIM_OCInitStructure);    //ʹÄÜͨµÀ4
 99   TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Disable);
100
101   TIM_ARRPreloadConfig(TIM3, ENABLE);             // ʹÄÜTIM3ÖØÔؼĴæÆ÷ARR
102
103   /* TIM3 enable counter */
104   TIM_Cmd(TIM3, ENABLE);                   //ʹÄܶ¨Ê±Æ÷3
105     TIM_ITConfig(TIM3, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 | TIM_IT_CC4, ENABLE);
106 }
107
108 /*********************************************************************
109   * @brief  Initialize the NCIC
110     * @param  None
111   * @retval None
112     * @date   20141205
113 ***********************************************************************/
114 void TIM_NVIC_Configuration(void)
115 {
116     NVIC_InitTypeDef NVIC_InitStructure;
117
118     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
119
120   NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
121   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
122   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
123   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
124   NVIC_Init(&NVIC_InitStructure);
125 }
126
127
128 /*
129  * º¯ÊýÃû£ºTIM3_PWM_Init
130  * ÃèÊö  £ºTIM3 Êä³öPWMÐźųõʼ»¯£¬Ö»Òªµ÷ÓÃÕâ¸öº¯Êý
131  *         TIM3µÄËĸöͨµÀ¾Í»áÓÐPWMÐźÅÊä³ö
132  * ÊäÈë  £ºÎÞ
133  * Êä³ö  £ºÎÞ
134  * µ÷Óà  £ºÍⲿµ÷ÓÃ
135  */
136 void TIM3_PWM_Init(void)
137 {
138     TIM3_GPIO_Config();
139     TIM3_Mode_Config();
140     TIM_NVIC_Configuration();
141 }
142
143 /******************* (C) COPYRIGHT 2015 *****END OF FILE************/

TIM3中断里面更新CCRx的值:

 1 extern uint16_t CCR1_Val;
 2 extern uint16_t CCR2_Val;
 3 extern uint16_t CCR3_Val;
 4 extern uint16_t CCR4_Val;
 5 /******************************************************************************/
 6 /*                                                                            */
 7 /*             ¶¨Ê±Æ÷3ÖжϷþÎñ³ÌÐò                                            */
 8 /******************************************************************************/
 9 void TIM3_IRQHandler(void)
10 {
11     u16 capture;
12
13     if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)  //¼ì²éTIM3¸üÐÂÖжϷ¢ÉúÓë·ñ
14     {
15         TIM_ClearITPendingBit(TIM3, TIM_IT_CC1 );
16         capture = TIM_GetCapture1(TIM3);
17 //        capture = TIM_GetCounter(TIM3);
18         TIM_SetCompare1(TIM3, capture + CCR1_Val );
19     }
20
21     if (TIM_GetITStatus(TIM3, TIM_IT_CC2) != RESET)  //¼ì²éTIM3¸üÐÂÖжϷ¢ÉúÓë·ñ
22     {
23         TIM_ClearITPendingBit(TIM3, TIM_IT_CC2 );
24         capture = TIM_GetCapture2(TIM3);
25 //        capture = TIM_GetCounter(TIM3);
26         TIM_SetCompare2(TIM3, capture+CCR2_Val );
27     }
28
29     if (TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET)  //¼ì²éTIM3¸üÐÂÖжϷ¢ÉúÓë·ñ
30     {
31         TIM_ClearITPendingBit(TIM3, TIM_IT_CC3 );
32         capture = TIM_GetCapture3(TIM3);
33 //        capture = TIM_GetCounter(TIM3);
34         TIM_SetCompare3(TIM3, capture + CCR3_Val );
35     }
36
37     if (TIM_GetITStatus(TIM3, TIM_IT_CC4) != RESET)  //¼ì²éTIM3¸üÐÂÖжϷ¢ÉúÓë·ñ
38     {
39         TIM_ClearITPendingBit(TIM3, TIM_IT_CC4 );
40         capture = TIM_GetCapture4(TIM3);
41 //        capture = TIM_GetCounter(TIM3);
42         TIM_SetCompare4(TIM3, capture + CCR4_Val );
43     }
44
45 }  

设置每个通道在输出比较匹配时产生中断,在中断中将比较寄存器的数值读出并加上新设置的数值,如果计算出的数值超过16位则舍弃超出的部分,再把这个新的数值写回相应的比较寄存器;这样下次比较成功将刚好发生在一个半波周期之后,对应的管脚将被翻转。

为什么还要加上上次的比较寄存器的值,这样岂不是每次的比较寄存器的值都不一样啊?直接设置最新的比较寄存器的值不行吗?当然不行,因为输出比较器比较的是当前的值与CNT的值,而不是比较的是CNT计数的个数。是绝对值,而不是相对值!计数器(CNT)的值一直是累加的,上面程序中设置的自动装载的值是65535,这样的目的是让计数器循环的进行累加,从而CCRx的值永远小于等于CNT,使输出比较寄存器匹配时,CNT的值不会是中间从0开始计数的,这样保证了方波占空比是固定的50%。当输出比较匹配时,进入中断,产生信号翻转,同时给CCRx赋新值,但是CNT此时有可能不是从0开始计数,所以必须把之前的CCRx中的值读出来。加上新设置的值然后再赋给CCRx。

时间: 2024-10-25 23:42:15

STM32定时器输出多路可调频率方波实现细节记录的相关文章

STM32定时器输出PWM频率和步进电机控制速度计算

1.STM32F4系列定时器输出PWM频率计算 第一步,了解定时器的时钟多少: 我们知道AHP总线是168Mhz的频率,而APB1和APB2都是挂在AHP总线上的. (1)高级定时器timer1, timer8以及通用定时器timer9, timer10, timer11的时钟来源是APB2总线(2)通用定时器timer2~timer5,通用定时器timer12~timer14以及基本定时器timer6,timer7的时钟来源是APB1总线 从STM32F4的内部时钟树可知: 当APB1和APB

【转】STM32定时器输出比较模式中的疑惑

OCx与OCxREF和CCxP之间的关系 初学STM32,我这个地方卡了很久,现在终于有些明白了,现在把我的理解写下与大家共享,如果有不对的地方,还请指出. OCxREF就是一个参考信号,并且约定: OCxREF=1,称OCxREF有效.反之,OCxREF=0,称OCxREF无效: ‘1’电平(高电平)称为OCxREF的有效电平,‘0’ 电平(低电平)称为OCxREF的无效电平. ——依据参考手册:The output stage generates an intermediate wavefo

STM32F4_TIM输出PWM波形(可调频率、占空比)

Ⅰ.概述 上一篇文章关于STM32基本的计数原理明白之后,该文章是在其基础上进行拓展,讲述关于STM32比较输出的功能,以输出PWM波形为实例来讲述. 提供实例工程中比较实用的函数:只需要调用该函数,参数为频率和占空比 void TIM2_CH2_PWM(uint32_t Freq, uint16_t Dutycycle); 先看一下实例中1KHz.20%占空比波形图 TIM2_CH2_PWM(1000, 20); 关于本文的更多详情请往下看. Ⅱ.实例工程下载 笔者针对于初学者提供的例程都是去

关于STM32 定时器 PWM 实时调节占空比时,预装载特性

最近在调试项目的时候遇到一个奇怪的现象:在调试状态下,给定时器捕获比较寄存器赋不同值,能产生不同占空比的波形(图1).反映到器件上也有不同的电压显示,但是在设备运行的时候,就不行了(图2). 图1 图2 纠结了N天后,也没有办法解决,只好乖乖的看STM32 控制器手册找找看了,结果还真找到了. 从图中可以看到,如果使能预装载特性,则数据会立即写入寄存器中,如果没有使能,那就得等到有事件(?)发生了.这我就明白了,在调试状态下,给寄存器赋值,不会产生什么影响,因为人的反应速度很慢,但是在运行的时候

第八章:STM32定时器

时间:2014年8月15日 一.定时器种类:      1.四个可同步运行的通用定时器(TIM2~TIM5),每个均有一个16位的自动加载递增/递减计数器.一个16位预分频器和4个独立的通道.       2.两个16位高级控制定时器(TIM1和TIM8)         特点:由一个可编程预分频器驱动的16为自动装载 计数器组成,与通用定时器有共同处,但功能更强大.       3.两个基本定时器(TIM6和TIM7)        特点:主要用于产生DAC触发信号,也可以当作通用的16为时基

[原创] STM32 定时器TIMx 编码器应用 函数 TIM_EncoderInterfaceConfig 分析

今天把STM32 定时器输入作为 编码器接口相关的 函数   TIM_EncoderInterfaceConfig ,好好分析了一遍 因为网上不少人对这个函数有问题 1 void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode, 2 uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity) 3 { 4 uint16_t tmpsmcr = 0; 5 uint

STM32定时器的预装载寄存器与影子寄存器之间的关系【转】

首先转载:   STM32定时器的预装载寄存器与影子寄存器之间的关系 本文的说明依据STM32参考手册(RM0008)第10版:英文:http://www.st.com/stonline/products/literature/rm/13902.pdf中译文:http://www.stmicroelectronics.com.cn/stonline/mcu/images/STM32_RM_CH_V10_1.pdf 在STM32参考手册的第13.14章中,都有一张定时器的框图,下面是第14章中定时

STM32 定时器用于外部脉冲计数(转)

源:STM32 定时器用于外部脉冲计数 STM32 定时器(一)——定时器时间的计算 STM32的定时器是灰常NB的,也是灰常让人头晕的(当然是对于白菜来说的). STM32中的定时器有很多用法: (一)系统时钟(SysTick) 设置非常简单,以下是产生1ms中断的设置,和产生10ms延时的函数: void RCC_Configuration(void) { RCC_ClocksTypeDef RCC_ClockFreq; SystemInit();//源自system_stm32f10x.c

STM32定时器 TIM3之PWM输出

void GPIO_TimPWM(void){ GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable , ENABLE); GPIO