STM32を使用したデジタル制御を行う場合について,離散時間制御系を適切に実装するための設定方法を調査しました。ここではSTM32の評価ボードであるNUCLEO-G431KBを使用して検証を行った結果を公開します。詳細については
リファレンスマニュアル をご参照ください。
制御周期およびPWM出力周波数を制御するためにはタイマを使用します。タイマにはカウンタモードの設定自由度があり,Center-aligned mode 1~3, Edge-aligned mode (upcounting/downcounting)から選択することができます。3種類のCenter-aligned modeはそれぞれ割り込みタイミングが異なります。このタイマカウンタを参照値として比較器に渡すことでPWM出力を得ることができます。今回は,デジタル制御系の構築に向けた設定として,サンプリング点間でPWM出力波形が対称となるようにCenter-aligned mode 1を使用します。
タイマはサイズ(TIM->ARR + 1)のカウンタを持ち,入力クロックを分周比(TIM->PSC + 1)で分周したクロックをカウントします。ただし,ARRはAuto-reload register,PSCはPrescaler valueです。タイマの入力クロック周波数を
f c l k f_{\rm clk} f clk とした場合,タイマの周波数
f t i m e r f_{\rm timer} f timer は以下のようになります。
f d i v = 1 P S C + 1 f c l k T t i m e r = 1 f d i v ( A R R + 1 ) = 1 f c l k ( A R R + 1 ) ( P S C + 1 ) ∴ f t i m e r = 1 ( A R R + 1 ) ( P S C + 1 ) f c l k \begin{align}
f_{\rm div} &= \frac{1}{{\rm PSC} + 1}f_{\rm clk}\\
T_{\rm timer} &= \frac{1}{f_{\rm div}}({\rm ARR} + 1)\\
&= \frac{1}{f_{\rm clk}}({\rm ARR} + 1)({\rm PSC} + 1)\\
\therefore f_{\rm timer} &= \frac{1}{({\rm ARR} + 1)({\rm PSC} + 1)}f_{\rm clk}
\end{align} f div T timer ∴ f timer = PSC + 1 1 f clk = f div 1 ( ARR + 1 ) = f clk 1 ( ARR + 1 ) ( PSC + 1 ) = ( ARR + 1 ) ( PSC + 1 ) 1 f clk
Center-aligned modeではカウント値が
0 0 0 からスタートしてARRまでカウントアップし,その後
0 0 0 までカウントダウンします。そのため,制御周期
f s f_{\rm s} f s はタイマ周波数
f t i m e r f_{\rm timer} f timer の半分となります。
f s = 1 2 ( A R R + 1 ) ( P S C + 1 ) f c l k \begin{align}
f_{\rm s} &= \frac{1}{2({\rm ARR} + 1)({\rm PSC} + 1)}f_{\rm clk}
\end{align} f s = 2 ( ARR + 1 ) ( PSC + 1 ) 1 f clk
ここではNUCLEO-G431KBの内部クロック周波数を
170 M H z 170\ {\rm MHz} 170 MHz と設定し,タイマに入力する場合について考えます。制御周期を
1 k H z 1\ {\rm kHz} 1 kHz と設定する場合には,次のように設定することで達成可能です。
TIM1 -> PSC = 1 ;
TIM1 -> ARR = 42500 - 1 ;
本記事の検証はこの制御周期を使用して行います。
STM32のPWM出力では,Mode 1およびMode 2を選択することができます。PMW mode 1ではタイマカウンタより比較レジスタの値が大きい場合にHIGH,PWM mode 2ではその逆が出力されます。制御周期ごとに比較レジスタの値を更新することで,所望の制御入力を与えることができます。Duty比を指定して比較レジスタを設定する関数の実装例を以下に示します。
template < typename T >
static inline void SetCh1Duty ( T pDutyCycle ) {
volatile int CountPeriod = TIM1 -> ARR ;
volatile int CountVal = ( int ) ( pDutyCycle * ( T ) CountPeriod ) ;
TIM1 -> CCR1 = CountVal ;
return ;
}
ただし,ARRはAuto-reload register,CCRはCapture/compare registerを表します。Duty比を0.2と設定した場合のPWM出力を下図に示します。所望のDuty比のPWM出力を行いたい場合には,PWM mode 1を使用することで実現可能となります。
デジタル制御において相電流検出を行う場合,インバータの下アームに設置されたシャント抵抗の電圧から換算して取得する方法があります。この方式を使用する場合には,下アームのMOSFETが閉じた状態,すなわちPWM出力がオフの状態である時にA/D変換を行う必要があります。今回のタイマ設定を使用する場合には,タイマカウンタの山のタイミングでA/D変換を行う必要があります。この山の検知には,PWM mode 2と設定したPWM出力チャンネルを使用して実現することができます。以下にPWM mode 2としたチャンネル4に対して設定を行う例を示します。
uint16_t PULSE_WIDTH = 1 ;
TIM1 -> CCR4 = ( TIM1 -> ARR ) - PULSE_WIDTH ;
以下にPULSE_WIDTH = 300と設定した場合のPWMチャンネル4の出力を示します。PWMチャンネル4の出力は,タイマカウントの山からPULSE_WIDTHカウント前にHIGHとなり,山からPULSE_WIDTHカウント後にLOWとなります。このPWM出力をトリガとしてA/D変換を開始することで,シャント抵抗を使用した電流検出が可能となります。
デジタル制御を行う場合には,電流検出,制御演算,Duty比の一連の処理を逐次実行します。離散制御系の入出力サンプル点について考える場合,入力点はA/D変換のタイミングであるため,出力であるDuty比の反映もタイマカウンタの山とする必要があります。この出力サンプル点の調整には,TIM1->RCRを使用します。ただし,RCRはrepetition counter registerを表します。タイマカウンタはオーバーフローおよびアンダーフロー,すなわち山と谷のタイミングに反復回数カウンタをデクリメントします。反復回数カウンタが0となった場合にUpdate Eventが発行され,このタイミング以前に設定したDuty比が反映されます。また,この反復回数カウンタのリセット値はUpdate Eventベント発行時のRCRであり,次の山および谷のタイミングにて代入されます。
本節では実際の制御系の演算処理を想定し,A/D変換後に僅かな遅延を伴ってDuty比の参照値が与えられる場合について検証します。Duty比の参照値は0.2と0.8を交互に繰り返すものとします。この実装を以下に示します。
static bool flag = false ;
void ADC1_2_IRQHandler ( void )
{
/* USER CODE BEGIN ADC1_2_IRQn 0 */
float duty = ( flag == false ) ? 0.2 : 0.8 ;
flag = ! flag ;
// Caution! Only for a test: Pseudo delay for calculation
for ( int i = 0 ; i < 2000 ; i ++ ) asm ( "NOP" ) ;
SetCh1Duty ( duty ) ;
/* USER CODE END ADC1_2_IRQn 0 */
HAL_ADC_IRQHandler ( & hadc1 ) ;
/* USER CODE BEGIN ADC1_2_IRQn 1 */
/* USER CODE END ADC1_2_IRQn 1 */
}
初めに,以下の設定を行った場合の結果を示します。
//init tim1
TIM1 -> RCR = 1 ;
TIM1 -> EGR |= TIM_EGR_UG ;
EGRはEvent generation registerであり,強制的にUpdate Eventを発行するものとなります。UEVの発行時にタイマカウント値は0および反復回数カウンタは0にリセットされます。起動時の波形は次のようになりました。タイマカウンタは谷からスタートし,反復回数カウンタは山で1,谷で0を取ります。
下図は定常状態における各波形の拡大図です。この設定ではタイマカウンタの谷でDuty比の設定が反映されるため,谷と谷の間で対称かつ形状が凹な出力が確認されます。また,Duty比の更新は電流検出から半分のサンプリング時間またはその1サンプル後のタイミングとなっています。そのため,この設定によって得られるシステムは離散制御系として不適です。
上記の問題を解決するために,プログラムの初期化処理において以下の処理を実行します。
//init tim1
TIM1 -> RCR = 1 ;
TIM1 -> EGR |= TIM_EGR_UG ;
TIM1 -> CNT = TIM1 -> ARR ;
TIM1 -> CR1 &= ~ ( TIM_CR1_CMS ) ;
TIM1 -> CR1 |= TIM_CR1_DIR ;
TIM1 -> CR1 |= TIM_CR1_CMS ;
タイマカウンタにはインクリメント・デクリメントを決定するレジスタがTIM->CR1の4 bit目に存在します。ただし,CR1はControl register 1を表します。タイマカウンタのスタート位置を山に,かつカウント方向をデクリメントと設定してUEVを発行することで,反復回数カウンタは山で0,谷で1を取ります。上記の処理において,UEV発行時にタイマカウンタは0にリセットされるため,UEV発行後にタイマカウンタを山に移動させています。また,CR1[3]はカウンタモードがCenter-aligned modeの場合に変更不可であるため,一旦Center-aligned mode以外のモードを選択してCR1[3]を変更し,カウンタモードをCenter-aligned modeに戻しています。
この設定を与えた際のPWM出力を以下に示します。起動時の波形は次のようになりました。タイマカウンタは山からスタートし,反復回数カウンタは山で0,谷で1を取ります。そのため,Duty比の更新は山のタイミングで発生します。
下図は定常状態における各波形の拡大図です。上記の操作により,サンプリング点とDuty比更新点が重なり,PWM出力が山と山の間で対称かつ凸形状となりました。また,電流検出からDuty比設定までの時間猶予が1サンプルとなり,適切な離散時間処理を実現可能なシステムとなりました。