はじめに
「PicoでベアメタルのLチカ(RAMバージョン) その2」として。
これまで作ってきたLチカは、適当な値をforでビジーループするだけだったので、タイミングはいい加減だった。
Cortex-M0+ではコアにSysTickを持っていて、これは難しい設定することもなく使用可能なので、これを使ってLチカのタイミングを正確にする。
SysTickはPico SDKの中では基本的には使用されていない。
SysTick
RP2040 Datasheetの「2.4.5.1.1. SysTick timer」によると、
The SysTick timer uses a 1μs pulse as a clock enable. This is generated in the watchdog block as timer_tick.
watchdogのtimer_tick
で生成される1usのパルスを使用するとのこと。
SYST_CSRのCLKSOURCE
の初期値が0になっており外部のクロックを使用するとのこと。これがtimer_tick
のことかと思われる。
WATCHDOGのTICKレジスタのRUNNING
とENABLE
が動いているので、timer_tickの1usのパルスは初期状態で動作している。
Lチカ(RAMバージョン)を改造
Lチカ(RAMバージョン)をSysTickを使用するように変更してみる。
regs.h
regs.h
にSYSTICK関連のレジスタ定義を追加する。
diff --git a/blink_ram/regs.h b/blink_ram/regs.h index dc0b11b..2d650ec 100644 --- a/blink_ram/regs.h +++ b/blink_ram/regs.h @@ -30,5 +30,9 @@ #define IO_GPIO25_CTRL (IO_BANK0_BASE + IO_BANK_GPIO25_CTRL) +#define SYSTICK_CSR (0xE000E010) +#define SYSTICK_RVR (0xE000E014) +#define SYSTICK_CVR (0xE000E018) + #endif //REGS_H
main.c
main.c
にinit_systick()
とdelay_us()
を追加する。
初期状態ではSysTickでは動作していないので、init_systick()
で動かすようにする。その際CLKSOURCEは0に設定し、timer_tickを使用するようにする。
delay_us()
では、RVRに任意のディレイ時間をus単位で設定する。次にCVRに0を設定しRVRにリセットされるまで待つ。
次に、CVRが0になるまで待つことで設定した時間の分だけビジーループする。
diff --git a/blink_ram/main.c b/blink_ram/main.c index 47f4935..f4b4135 100644 --- a/blink_ram/main.c +++ b/blink_ram/main.c @@ -14,6 +14,22 @@ void write_reg_op(uint32_t addr, uint32_t value, uint32_t op) { write_reg(addr | op, value); } +void init_systick() { + write_reg(SYSTICK_CSR, 0x1); +} + +// us is up to max of 24bit (0xffffff) +void delay_us(uint32_t us) { + write_reg(SYSTICK_RVR, us); + write_reg(SYSTICK_CVR, 0); + + // wait for 0 to RVR + while (!read_reg(SYSTICK_CVR)) __asm__ __volatile__(""); + + // wait for RVR counting down to 0. + while (read_reg(SYSTICK_CVR)) __asm__ __volatile__(""); +} + int main(void) { ///////////////////// @@ -39,10 +55,13 @@ int main(void) { write_reg(IO_GPIO25_CTRL, 0x5); uint32_t v = read_reg(IO_GPIO25_CTRL); + // SysTick init + init_systick(); + // Blink write_reg(SIO_GPIO_OE_SET, (1ul<<25)); while (1) { - for (int i = 100000; i != 0; i--) ; + delay_us(100*1000); //100ms write_reg(SIO_GPIO_OUT_XOR, (1ul<<25)); } return 0;
波形を見る
ちゃんと100ms幅になっている。
まとめ
今回のような単純な使い方では、SysTickは24ビットしかないのであまり長い時間待たせられない。
例えば割り込みを使用してTickカウンタを作って、その差分で待つようにすれば24ビットの壁は超えられると思うが、ちゃんとしたタイマー処理を使用したい場合は、やはりタイマーを使用するほうが良いと思われる。