STM32F303K8 ミリ秒遅延の実装(mdelay)
はじめに
STM32 マイコンで他デバイスと通信を行い, 結果を取得するまでに遅延処理が必要であったため Basic Timer (TIM6 / TIM7) を用いて遅延処理を実装.
nop
命令を使用して遅延を実装した方法 stm32:wait,delay処理 - Qiita や HAL_Delay
(SysTick割り込み)を使用した方法 HAL_Delay 関数の注意点 などが非常に参考になる.
開発環境
HAL ライブラリや STM32CubeMX などの環境の使用は想定していないため, 使用するレジスタのアドレス及び値を事前に定義する必要がある.
名称 | 備考 | |
---|---|---|
コンパイラ | arm-none-eabi-gcc | 11.2.0 |
評価ボード | NUCLEO-F303K8 | STM32F303K8 |
遅延処理の実装
RCC レジスタ設定
システムクロック設定と TIM6 の有効化
はじめに, STM32F303K8 のシステムクロックを HSI(8MHz) に設定. 以下, この値を順にしてプリスケーラ, クロック数等の値を設定.
#define RCC_ADDR (0x40021000)
#define RCC ((volatile struct rcc_t *)(RCC_ADDR))
// 内部クロック 8Mhz を使用
if((RCC->CFGR & RCC_CFGR_SWS_HSI) != RCC_CFGR_SWS_HSI) {
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_HSI;
while((RCC->CFGR & RCC_CFGR_SWS_HSI) == 0);
}
// TIM6 タイマクロックの有効化
RCC->APB1ENR |= RCC_APB1ENR_TIM6EN | RCC_APB1ENR_TIM6EN;
IRQ 割り込みの有効化と優先順位の指定
上記の HAL_Delay 関数の注意点
で記載されているのと同様に, この実装方法でも割り込みの優先順位に注意する必要がある.
// TIM6, DAC2 割り込み
NVIC_enable_irq(TIM6DAC1_IRQn);
// 割り込みの優先順位を指定
NVIC_set_priority(TIM6DAC1_IRQn, 0x00);
遅延処理
遅延処理は, 指定時間が経過するまでポーリングすることで実現している.
TIM6->RSC = 7999
このプリスケーラの値で, 1クロック辺りに必要とする時間を指定. 今回の場合 システムクロックを 8[MHz] としているため, カウンタクロック周波数 $f_{\rm{CK\_CNT}}$ は,
f_{\rm{CK\_CNT}} = \frac{f_{\rm{CK\_PSC}}}{7999 + 1} = \frac{8.0 \times 10^6}{8.0 \times 10^3} = 1.0 \times 10^3 [\rm{Hz}] = 1.0 [\rm{kHz}]
であり, その逆数である周期 $T$ は
T = \frac{1}{f} = \frac{1}{1.0 \times 10^6} = 1.0 \times 10^{-3} [\rm{s}] = 1.0 [\rm{ms}]
となる. 周期 $T$ とカウントするクロックは同じであるため, 自動再ロードレジスタ TIM6->ARR
に遅延させたい秒数 [ms] を代入すれば指定時間後に割り込みが発生する.
volatile static uint8_t _f_timeout;
void TIM6_DAC1_handler(void) {
uint32_t sr = TIM6->SR;
if((sr & TIM67_SR_UIF) == TIM67_SR_UIF) {
// TIM6 割り込み
TIM6->SR = 0;
_f_timeout = 1;
}
}
void mdelay(uint16_t msec) {
if(msec == 0) {
return;
}
_f_timeout = 0;
// ms
TIM6->PSC = 7999;
TIM6->ARR = msec;
TIM6->CNT = 0;
TIM6->CR1 =
TIM67_CR1_OPM |
TIM67_CR1_URS;
NVIC_enable_irq(TIM6DAC1_IRQn);
NVIC_set_priority(TIM6DAC1_IRQn, 0);
TIM6->DIER = TIM67_DIER_UIE;
TIM6->CR1 |= TIM67_CR1_CEN;
while(! _f_timeout) {
NOP();
}
NVIC_disable_irq(TIM6DAC1_IRQn);
TIM6->CR1 &= TIM67_CR1_CEN;
TIM6->DIER = 0;
}
実行結果
実行したコード(PB3 の LED 点滅)とHantek DSO5072P
で計測したデータを下図に示す.
uint8_t _f_switch = 0;
while(1) {
// 20[ms] の遅延. 1周期当たり 40[ms]
mdelay(20);
if(_f_switch) {
GPIOB->BRR |= (1 << 3);
} else {
GPIOB->ODR |= (1 << 3);
}
_f_switch ^= 1;
10[ms], 20[ms] 以上であれば十分使用できる結果ではないだろうか.
1[ms] のように極端に小さい値にすると, if
分岐やレジスタへの代入等の影響で誤差が大きくなる.
また, プリスケーラの値を変更して, $10^{-6}$ [s] で udelay
の実装を行ったが, こちらの場合は設定や呼び出しなどで 30[us] 程かかってしまうため, システムクロック数を上げたり処理を最小限に最適化する必要がある.
Appendix
補足コード等
NVIC 有効化, 無効化, 優先順位指定
void NVIC_enable_irq(irq_type_t type) {
if(type >= 0) {
uint8_t offset = type / 32,
shift_size = type % 32;
*(NVIC->ISER + offset) = 1 << shift_size;
}
}
void NVIC_disable_irq(irq_type_t type) {
if(type >= 0) {
uint8_t offset = type / 32,
shift_size = type % 32;
*(NVIC->ICER + offset) = 1 << shift_size;
}
}
void NVIC_set_priority(
irq_type_t type,
uint8_t priority) {
if(type >= 0) {
uint8_t offset = type / 4,
shift_size = type % 4;
*(NVIC->IPR + offset) |= ((uint32_t)priority << (8 * shift_size + 4));
} else {
// TODO SysTick などの割り込み優先順位を設定
}
}
Author And Source
この問題について(STM32F303K8 ミリ秒遅延の実装(mdelay)), 我々は、より多くの情報をここで見つけました https://qiita.com/Livenga/items/b13f6cd4e8b257e4877c著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .