STM32学習と開発ノート8:正確なパルス出力のためのマスタースレーブタイマー

  デバイス制御のためのパルス信号は、非常に一般的ですが、いくつかのケースでは、我々は、制御パルスの正確な数は、運動の正確な制御を実現することを願っています。そこおそらく達成するための様々な方法がありますが、このタイプの動作は、人々が考える可能性が高いということです達成するためにタイマーを使用しています。

1 概説、原理

  私たちは、STM32プラットフォーム上で、PWM動作は非常に一般的な用法である達成するためにタイマーを使用することを知っています。我々は正確に主二つのタイマーから生成されたPWM波により出力パルスの数を制御することができるという事実を用いて、単一のタイマーを使用。その後、我々は、タイマ出力からのパルスの正確な数を使用しての主な原則で簡単に見てみましょう。

  STM32プラットフォームの場合、一般的にTIM1とTIM8 2つの高度なタイマー、TIM2、TIM3、TIM、TIM5および他のいくつかの汎用タイマを持っています。STM32は、これらのタイマーが起動され、別のタイマに一定の条件により活性化することができます。タイマーが満了したときにここで、特定の条件は、より多くの成功やその他の条件のタイミングかもしれません。このトリガーは、別のタイマーは、作業モードで起動タイマーからのトリガ信号を受け、問題のマスターモードトリガ信号にタイマー、タイマーアライメントタイマーによって呼び出されます。これらのタイマは、タイマとして使用することができる、タイマーは、異なるソースに対応する主トリガは、それらが主従関係を任意に配置設定することができない従わなければならないようです。具体的な位置関係は次のとおりです。

 

  もちろん、出力パルスの正確な制御を達成するために、要件上記のリストから、タイマーに従って主要な構成を達成するために必要です。マスタークロックのため、出力はPWM出力として構成されており、トリガ・モードからメイン出力を有効にします。タイマ、スレーブモードを有効にする必要があり、及びゲート・モードに設定するために、トリガ・ソースが選択され、上記の表に記載しました。

       しかし、なぜ主は、タイマー、それから出力されるパルスの正確な数を達成することができるでしょうか?私たちは、問題を説明するために、以下の簡単な図を使用します。

 

       まず、基本的な要件である配置前述のマスターとスレーブタイマー、から主請求項のタイマーに従って。マスタクロックパルスは、カウンタ制御からのパルス出力の出力周波数と出力パルス数を設定する責任があります。再びオンにするまで、特にこのプロセスでは、カウントが規定値に達したマスタークロックのトリガパルスカウント出力、によって開始されたタイマからのマスタタイマ信号からメインプロセスを開始するには、割り込みはタイマのメイン出力、メインプロセスを停止し、このプロセス。

2、系统设计

  我们已经了解了通过主从计时器实现精确数量脉冲输出的基本原理。那究竟如何实际做呢?接下来我们就设计一个简单的系统实现它。

  在这一系统中,我是使用STM32F407作为实现平台,以TIM1作为主计时器,TIM4作为从计时器,同时输出四路脉冲信号。四路的频率是相同的,但每一路的输出数量是可以设定的。具体的操作结构如下图所示:

 

  主进程轮询控制计时器TIM1和TIM4工作,而TIM1主计时器给TIM4从计时器输出触发信号,而从计时器到达指定脉冲数后输出中断信号控制TIM1的输出通道停止。我们人为规定TIM4的通道1、2、3、4与TIM1的输出通道1、2、3、4相对应。

3、代码实现

  我们已经说明了使用主从计时器实现精确输出脉冲数的原理,也设计了我们的我们想要实现的系统结构,接下来我们实现这一系统。

3.1、主计时器的配置

  首先我们来看一看主计时器的配置,具体代码如下:

 1 /*TIM1初始化配置*/
 2 static void TIM1_Init_Configuration(uint32_t period)
 3 {
 4   TIM_MasterConfigTypeDef sMasterConfig = {0};
 5   TIM_OC_InitTypeDef sConfigOC = {0};
 6   TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
 7  
 8   htim1.Instance = TIM1;
 9   htim1.Init.Prescaler = 1;
10   htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
11   htim1.Init.Period = (period-1);
12   htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
13   htim1.Init.RepetitionCounter = 0;
14   if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
15   {
16     Error_Handler();
17   }
18   sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
19   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
20   if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
21   {
22     Error_Handler();
23   }
24   sConfigOC.OCMode = TIM_OCMODE_PWM1;
25   sConfigOC.Pulse = (period/2);
26   sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
27   sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
28   sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
29   sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
30   sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
31   if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
32   {
33     Error_Handler();
34   }
35   if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
36   {
37     Error_Handler();
38   }
39   if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
40   {
41     Error_Handler();
42   }
43   if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
44   {
45     Error_Handler();
46   }
47   sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
48   sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
49   sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
50   sBreakDeadTimeConfig.DeadTime = 0;
51   sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
52   sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
53   sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
54   if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
55   {
56     Error_Handler();
57   }
58  
59   HAL_TIM_MspPostInit(&htim1);
60  
61 }

3.2、从计时器的配置

   接着我们再来看一看从计时器的配置,具体代码如下:

 1 /*TIM4初始化配置*/
 2 static void TIM4_Init_Configuration(void)
 3 {
 4   TIM_ClockConfigTypeDef sClockSourceConfig = {0};
 5   TIM_SlaveConfigTypeDef sSlaveConfig = {0};
 6   TIM_MasterConfigTypeDef sMasterConfig = {0};
 7  
 8   htim4.Instance = TIM4;
 9   htim4.Init.Prescaler = 0;
10   htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
11   htim4.Init.Period = 0xFFFF;
12   htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
13   if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
14   {
15     Error_Handler();
16   }
17   sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
18   if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
19   {
20     Error_Handler();
21   }
22   sSlaveConfig.SlaveMode = TIM_SLAVEMODE_GATED;
23   sSlaveConfig.InputTrigger = TIM_TS_ITR0;
24   if (HAL_TIM_SlaveConfigSynchronization(&htim4, &sSlaveConfig) != HAL_OK)
25   {
26     Error_Handler();
27   }
28   sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
29   sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
30   if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
31   {
32     Error_Handler();
33   }
34  
35 }

3.3、主轮询函数实现

   主轮询函数控制着主从计时器的启动,是实现脉冲输出的控制者,包括设置脉冲数并开启从计数器的计数和中断以及启动主计时器的输出。具体代码如下:

 1 /*实现通讯数据的处理*/
 2 void HgraDataProcess(void)
 3 {
 4   TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);  // 捕获比较1中断使能
 5   TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_2,TIM_ICPOLARITY_RISING);  // 捕获比较2中断使能
 6   TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_3,TIM_ICPOLARITY_RISING);  // 捕获比较3中断使能
 7   TIM_SET_CAPTUREPOLARITY(&htim1,TIM_CHANNEL_4,TIM_ICPOLARITY_RISING);  // 捕获比较4中断使能
 8  
 9   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_1,6400);     // 输入通道1的捕获比较值CCR1
10   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_2,6400);     // 输入通道2的捕获比较值CCR2
11   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_3,6400);     // 输入通道3的捕获比较值CCR3
12   __HAL_TIM_SET_COMPARE(&htim4,TIM_CHANNEL_4,6400);     // 输入通道4的捕获比较值CCR4
13  
14   HAL_TIM_OC_Start_IT(&htim4,TIM_CHANNEL_1);    //开启定时器4通道1的输入捕获中断
15   HAL_TIM_OC_Start_IT(&htim4,TIM_CHANNEL_2);    //开启定时器4通道2的输入捕获中断
16   HAL_TIM_OC_Start_IT(&htim4,TIM_CHANNEL_3);    //开启定时器4通道3的输入捕获中断
17   HAL_TIM_OC_Start_IT(&htim4,TIM_CHANNEL_4);    //开启定时器4通道4的输入捕获中断
18  
19   HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_1);  //开启定时器1通道1的PWM输出中断        
20   HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_2);  //开启定时器1通道2的PWM输出中断
21   HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_3);  //开启定时器1通道3的PWM输出中断
22   HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_4);  //开启定时器1通道4的PWM输出中断
23 }

3.4、中断处理函数的实现

  从计时器产生中断后,会根据不同的中断调用不同的中断处理函数,这些回调函数是需要我们实现的,在这里要实现主计时器PWM输出的停止以及中断标志的复位等处理。具体实现代码如下:

 1 /*PWM中断轮询回调函数*/
 2 static void TIM1_PWM_PulseFinished(TIM_HandleTypeDef *htim)
 3 {
 4   if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET)                         //判断是否生成中断标志位SR
 5   {
 6     if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) !=RESET)              //定时器中断使能是否开启
 7     {
 8       __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_CC1);                   //清除中断标志位SR
 9       if(HAL_TIM_PWM_Stop_IT(&htim1, TIM_CHANNEL_1)==HAL_OK)         //关闭定时器1的通道1的PWM输出
10       {
11         HAL_TIM_OC_Stop_IT(&htim4,TIM_CHANNEL_1) ;                   //关闭定时器4的通道1的输入中断捕获
12         flagStop[0] = 1;                                                //关闭标志置1
13       }
14     }
15   }                                                                          //下面的通道2同理如此
16   if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2) != RESET)
17   {
18     if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC2) !=RESET)
19     {
20       __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_CC2);                       //清除标志位
21      
22       if(HAL_TIM_PWM_Stop_IT(&htim1, TIM_CHANNEL_2)==HAL_OK)
23       {    
24         HAL_TIM_OC_Stop_IT(&htim4,TIM_CHANNEL_2) ;   
25         flagStop[1] = 1;           
26       }
27     }
28   }
29   if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3) != RESET)
30   {
31     if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC3) !=RESET)
32     {
33       __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_CC3);                       //清除标志位
34      
35       if(HAL_TIM_PWM_Stop_IT(&htim1, TIM_CHANNEL_3)==HAL_OK)
36       {    
37         HAL_TIM_OC_Stop_IT(&htim4,TIM_CHANNEL_3) ;   
38         flagStop[2] = 1;           
39       }
40     }
41   }
42   if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4) != RESET)
43   {
44     if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC4) !=RESET)
45     {
46       __HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_CC4);                       //清除标志位
47      
48       if(HAL_TIM_PWM_Stop_IT(&htim1, TIM_CHANNEL_4)==HAL_OK)
49       {    
50         HAL_TIM_OC_Stop_IT(&htim4,TIM_CHANNEL_4) ;   
51         flagStop[3] = 1;            
52       }
53     }
54   }
55    
56   if((flagStop[0]== 1)&&(flagStop[1] == 1)&&(flagStop[2] == 1)&&(flagStop[3] == 1))
57   {
58     flagStop[0]= 0;
59     flagStop[1]= 0;
60     flagStop[2]= 0;
61     flagStop[3]= 0;
62  
63     __HAL_TIM_SET_COUNTER(&htim4,0);
64   }
65 }

4、小结

 

  我们设计了一个四路输出的脉冲输出,每一路的输出数量可以精确单独控制,在输出的频率相对较低而且数量不大的情况下我们验证是没有问题的。当然在数量特别多时,是否有偏差我们没有测试。而在我们使用的平台,时钟为168MHz,根据我们的简单测试在输出8MHz的脉冲时还是比较精确的,不过这已经完全满足一般的应用需求。

  其实从STM32的手册我可以知道,输出指定脉冲数的方法有多种,但使用主从计时器方式是比较好的一种。这种方式虽然多用了一个定时器,但因为不需要频繁中断大大减少了CPU的处理资源。

欢迎关注:

おすすめ

転載: www.cnblogs.com/foxclever/p/12275486.html