(2014.6.23 作成)
(2014.7.31 更新)
(2015.4.25 更新)
(2015.8.30 更新)
(2019.6.16 Update)
タイマー割り込みを使用して指定した時間間隔でカウンタ値を更新します。
たとえば1[us]間隔でカウントした場合、65535でTIMペリフェラルのカウンタがオーバーフローしますが、このオーバーフローした回数も32bit整数で数えています。何がいいたいかというと、1[us]間隔でカウントしたとしても 1[us] * 65536*(2^32-1) = 8年ぐらいはカウントできます。
STM32の場合使用するタイマー毎に割り込みで呼び出される関数名が固定されています。
Arduinoのようにユーザーから具体的なその関数名を隠蔽してクラスを作成することも可能でしたが、コードを無用に冗長にするだけなのであえてユーザー側に一手間かけてもらうことでシンプルにしています。
具体的には各マイコン種類ごとに定義されている以下の関数をユーザーコードとして作成し、その中でタイマのカウントアップルーチンを書き込んで使用してください。
具体的な関数名は以下の通りです。
STM32F303xC (STM32 F3Discovery) |
STM32F401xE (NucleoF401RE) |
|
TIM1 | TIM1_UP_TIM16_IRQHandler | TIM1_UP_TIM10_IRQHandler |
TIM2 | TIM2_IRQHandler | TIM2_IRQHandler |
TIM3 | TIM3_IRQHandler | TIM3_IRQHandler |
TIM4 | TIM4_IRQHandler | TIM4_IRQHandler |
TIM5 | - | TIM5_IRQHandler |
TIM6 | TIM6_DAC_IRQHandler | - |
TIM7 | TIM7_IRQHandler | - |
TIM8 | TIM8_UP_IRQHandler | - |
TIM9 | - | TIM1_BRK_TIM9_IRQHandler |
TIM10 | - | TIM1_UP_TIM10_IRQHandler |
TIM11 | - | TIM1_TRG_COM_TIM11_IRQHandler |
TIM15 | TIM1_BRK_TIM15_IRQHandler | - |
TIM16 | TIM1_UP_TIM16_IRQHandler | - |
TIM17 | TIM1_TRG_COM_TIM17_IRQHandler | - |
注意点としては一つの関数名に複数のタイマが関連付けられている場合があること、割り込みフラグをクリアすること、関数作成時に extern "C"を付け忘れないことぐらいでしょうか。
下にサンプルコードがあるので参考にしてください。
コンストラクタ |
||
プロトタイプ |
Timer( TIM_TypeDef *TIM, const DKS::TimeUint &timeUnit =
DKS::TimeUnit_MilliSec); |
|
戻り値 |
なし |
|
引数 |
TIM_TypeDef *TIM |
使用するタイマ |
DKS::TimeUint &timeUnit |
タイマの動作単位 usまたはms |
|
備考 |
|
カウンタの値を増やす |
|
プロトタイプ | CountUp(void) |
戻り値 |
なし |
引数 |
なし |
備考 |
カウンタを更新する。 ユーザーが定義する割り込みルーチン内に記載してください |
カウンタ値を読みに行く(readCounter)際にタイマーのレジスタ値と保存していたカウンタ値を足して返すのですが、ちょうどこのタイミングでタイマーの更新イベントが発生するとおかしな値を返します。このためこのプログラムでは以下のような対策を行っています。(読みやすくするため若干修正しています)
uint64_t DKS::Timer::readCounter()const { const uint32_t counter1 = m_counter; const uint32_t CNT1 = LL_TIM_GetCounter(m_TIM); const uint32_t counter2 = m_counter; const uint8_t m_counter_shift(16);// Update events occurs every 65536 counts. (1<<16) if (counter1 == counter2) //割込みが発生しなかった。 return ((counter1 << m_counter_shift) + CNT1) >> m_shift; else // counter1とcounter2の計測の間に割り込みが発生した return ((counter2 << m_counter_shift) + LL_TIM_GetCounter(m_TIM)) >> m_shift; }
すなわちカウンタ値を2回確認し、変化がなければその間で割り込みが発生しなかったと判断できるので、2回目のカウント値を取得する前のレジスタ値を加える。一方変化していればこの間で割り込みが発生したと考えられるので再度読み直したレジスタ値を加えています。
一旦割り込みを停止するなど、もう少しスマートなやり方があるのかもしれませんが、これよりよい方法が思いつきませんでした。
以下のコードをSTM32F103C8T6基板に記入するとで100us周期でLED4が点滅します。
#include "DKS_Common_F103xB.h" #include "DKS_Timer_F103xB.h" #include "DKS_F103C8T6.h" extern "C" { DKS::Timer tim; int main(void) { DKS::InitSystem(); DKS::STM32F103C8T6 board(DKS::BluePill); board.Init(); tim.Init(TIM3, DKS::TimeUnit_MicroSec); //引数なしだと1msでカウントアップ tim.Start(); volatile uint32_t countValueNew, countValueOld; while (1) { countValueNew = tim.ReadCounter(); if ((countValueNew - countValueOld) >= 100) { board.led.toggle(); countValueOld = countValueNew; } } } void TIM3_IRQHandler() { if (LL_TIM_IsActiveFlag_UPDATE(tim.m_TIM) == 1) { LL_TIM_ClearFlag_UPDATE(tim.m_TIM); tim.CountUp(); } } }