最近有个项目要用到多达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