CH32VのオレオレArduino環境を作ろう その5 SysTickタイマー

概要

前回はSerialクラスを作りましたが今回はSysTickタイマーを使って時間関係の処理を作っていきたいと思います。

SysTickタイマーとは?

CH32Vシリーズでは複数のタイマーを搭載しています。

上記はCH32V103のものですが、4種類のタイマーがあります。

名前内容
TMI1Advanced-control Timer
TMI2General-purpose
TMI3General-purpose
TMI4General-purpose
IWDGIndependent Watchdog
WWDGWindow Watchdog
SysTickSystem Time Base Timer

区分的には上記になっていました。今回利用するのはSysTickになります。このタイマーはどのボードでも1つだけ搭載されている基礎的なタイマーとなります。RTOSなどでタスクの切り替えで利用しているタイマーで、切り替え間隔をTickと呼びます。Tickの間隔はいろいろあるのですがarduino-esp32のFreeRTOSだと1ms間隔になっています。

当初はFreeRTOSを搭載しようとも考えていたのですが、省メモリでシンクルコアだとあまりメリットが無いかもしれないのでまずはRTOSなしのArduino環境を準備しています。

チップSysTickの仕様
CH32V00332Bitのincremental counter
CH32V00632Bitのincremental counter
CH32V10364Bitのincremental counter
CH32V20x64Bitのincremental or decremental counter
CH32V30764Bitのincremental or decremental counter
CH32X03564Bitのincremental or decremental counter
CH32L10364Bitのincremental or decremental counter

チップごとのSysTickタイマーの仕様を調べたところ、上記の区分で3種類ありました。ビット数は精度でどれだけ長いタイマーを作れるのかなのでいいとして、インクリメンタルの増加とデクリメンタルの減少があります。

タイマーは0から始まって既定値まで増えると割り込みがかかるインクリメンタルと、既定値にセットして減らしていって0になると割り込みがかかるデクリメンタルの2種類があります。今回はインクリメンタルで統一したほうが良さそうですね。

EVTのサンプルをnoneOSで動かす

void SYSTICK_Init_Config(u_int64_t ticks)
{
    SysTick->CTLR = 0x0000;//購液狼由柴方匂

    SysTick->CNTL0 = 0;
    SysTick->CNTL1 = 0;
    SysTick->CNTL2 = 0;
    SysTick->CNTL3 = 0;
    SysTick->CNTH0 = 0;
    SysTick->CNTH1 = 0;
    SysTick->CNTH2 = 0;
    SysTick->CNTH3 = 0;

    SysTick->CMPLR0 = (u8)(ticks & 0xFF);
    SysTick->CMPLR1 = (u8)(ticks >> 8);
    SysTick->CMPLR2 = (u8)(ticks >> 16);
    SysTick->CMPLR3 = (u8)(ticks >> 24);
    SysTick->CMPHR0 = (u8)(ticks >> 32);
    SysTick->CMPHR1 = (u8)(ticks >> 40);
    SysTick->CMPHR2 = (u8)(ticks >> 48);
    SysTick->CMPHR3 = (u8)(ticks >> 56);

    NVIC_SetPriority(SysTicK_IRQn, 15);
    NVIC_EnableIRQ(SysTicK_IRQn);
    SysTick->CTLR = (1<<0);
}

void setup() {
    SystemCoreClockUpdate();
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n",SystemCoreClock);
    printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID());
    SYSTICK_Init_Config(SystemCoreClock/8-1);//1s
}

void loop() {
}

uint32_t counter;

#ifdef __cplusplus
extern "C" {
#endif

void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
void SysTick_Handler(void)
{
    printf("welcome to WCH\r\n");
    SysTick->CNTL0 = 0;
    SysTick->CNTL1 = 0;
    SysTick->CNTL2 = 0;
    SysTick->CNTL3 = 0;
    SysTick->CNTH0 = 0;
    SysTick->CNTH1 = 0;
    SysTick->CNTH2 = 0;
    SysTick->CNTH3 = 0;

    counter++;
    printf("Counter:%d\r\n",counter);
}

#ifdef __cplusplus
}
#endif

上記のコードでCH32V103で動かしてみました。SYSTICK_Init_Config関数でSysTickタイマーの設定をすると、割り込みでSysTick_Handler関数が呼ばれるようになります。気をつけないといけないのはextern “C”でC言語から呼び出せる関数にする必要があります。この2つの関数はボードによって違うので個別関数として切り出せばよさそうですね。

void SYSTICK_Init_Config(u_int64_t ticks)
{
    SysTick->SR &= ~(1 << 0);//clear State flag
    SysTick->CMP = ticks;
    SysTick->CNT = 0;
    SysTick->CTLR = 0xF;

    NVIC_SetPriority(SysTicK_IRQn, 15);
    NVIC_EnableIRQ(SysTicK_IRQn);
}

ちなみにCH32V307だと上記のようにかなりシンプルです。CH32V103は64ビットレジスタを8ビットを8つに分割しているので結構面倒な書き方にしています。SRレジスタなどもCH32V103にはなかったので微妙に違うので注意してください。

1msのSysTickタイマーに改造

  SYSTICK_Init_Config((SystemCoreClock / 8 / 1000) - 1);  //1ms

上記のタイマーセットしている場所を/8で1秒だったので/8/1000で1ミリ秒に変更するだけです。ただこのままだと1ミリ秒にシリアル通信でのprintfが終わらないので少しコードを整理します。

void SYSTICK_Init_Config(u_int64_t ticks) {
  SysTick->CTLR = 0x0000;

  SysTick->CNTL0 = 0;
  SysTick->CNTL1 = 0;
  SysTick->CNTL2 = 0;
  SysTick->CNTL3 = 0;
  SysTick->CNTH0 = 0;
  SysTick->CNTH1 = 0;
  SysTick->CNTH2 = 0;
  SysTick->CNTH3 = 0;

  SysTick->CMPLR0 = (u8)(ticks & 0xFF);
  SysTick->CMPLR1 = (u8)(ticks >> 8);
  SysTick->CMPLR2 = (u8)(ticks >> 16);
  SysTick->CMPLR3 = (u8)(ticks >> 24);
  SysTick->CMPHR0 = (u8)(ticks >> 32);
  SysTick->CMPHR1 = (u8)(ticks >> 40);
  SysTick->CMPHR2 = (u8)(ticks >> 48);
  SysTick->CMPHR3 = (u8)(ticks >> 56);

  NVIC_SetPriority(SysTicK_IRQn, 15);
  NVIC_EnableIRQ(SysTicK_IRQn);

  SysTick->CTLR = (1 << 0);
}

void setup() {
  SystemCoreClockUpdate();
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  USART_Printf_Init(115200);
  SYSTICK_Init_Config((SystemCoreClock / 8 / 1000) - 1);  //1ms
}

unsigned int counter;

void loop() {
  if (counter % 100 == 0) {
    printf("counter = %d\n", counter);
  }
}

#ifdef __cplusplus
extern "C" {
#endif

  void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
  void SysTick_Handler(void) {
    SysTick->CNTL0 = 0;
    SysTick->CNTL1 = 0;
    SysTick->CNTL2 = 0;
    SysTick->CNTL3 = 0;
    SysTick->CNTH0 = 0;
    SysTick->CNTH1 = 0;
    SysTick->CNTH2 = 0;
    SysTick->CNTH3 = 0;
    counter++;
  }

#ifdef __cplusplus
}
#endif

これで1ミリ秒単位で起動してからの経過時間がわかるカウンタができました。この値がmillis関数の戻り値になり、delay関数などの待機時間として利用します。

ただこのままだとまだ足りません。Arduinoではマイクロ秒を返すmicros関数があります。1ミリ秒はSysTickタイマーのカウントでわかるようになりましたが、1ミリ秒未満はこのままだとわかりません。そこでSysTickタイマーの中のカウンタを直接確認するようにします。

インクリメンタルタイマーですので、0からスタートして既定値まで増えていきます。どれだけ増えたのかを確認することで1ミリ秒未満の時間経過もわかります。

ミリ秒未満の値確認

void loop() {
  unsigned int cntl = SysTick->CNTL0 + (SysTick->CNTL1 << 8) + (SysTick->CNTL2 << 16) + (SysTick->CNTL3 << 24);
  unsigned int cnth = SysTick->CNTH0 + (SysTick->CNTH1 << 8) + (SysTick->CNTH2 << 16) + (SysTick->CNTH3 << 24);
  unsigned int cmpl = SysTick->CMPLR0 + (SysTick->CMPLR1 << 8) + (SysTick->CMPLR2 << 16) + (SysTick->CMPLR3 << 24);
  unsigned int cmph = SysTick->CMPHR0 + (SysTick->CMPHR1 << 8) + (SysTick->CMPHR2 << 16) + (SysTick->CMPHR3 << 24);
  printf("counter = %d, cntl = %d, cnth = %d, cmpl = %d, cmph = %d\n", counter, cntl, cnth, cmpl, cmph);
}

loop関数を変更して、中身のデータを確認してみました。

counter = 91581, cntl = 4833, cnth = 0, cmpl = 8999, cmph = 0
counter = 91587, cntl = 4101, cnth = 0, cmpl = 8999, cmph = 0
counter = 91593, cntl = 3373, cnth = 0, cmpl = 8999, cmph = 0
counter = 91599, cntl = 2640, cnth = 0, cmpl = 8999, cmph = 0
counter = 91605, cntl = 1909, cnth = 0, cmpl = 8999, cmph = 0
counter = 91611, cntl = 1176, cnth = 0, cmpl = 8999, cmph = 0
counter = 91617, cntl = 445, cnth = 0, cmpl = 8999, cmph = 0
counter = 91622, cntl = 7860, cnth = 0, cmpl = 8999, cmph = 0

上記のようなデータになりました。cntlが内部カウンターの下位32ビットで、cnthが上位32ビットです。同じくcmplが対象値の下位32ビットで、cmphが上位32ビットになります。printfは比較的重い処理なので上記の出力をするだけで6ミリ秒程度かかっています。

  SYSTICK_Init_Config((SystemCoreClock / 8 / 1000) - 1);  //1ms

さて、1ミリ秒未満の値ですが、上記でcmpを設定しているので-1した値が入っています。そのため9000が最大値で、cntの値との割合を確認すれば経過時間がわかりそうです。

void loop() {
  unsigned int cnt = SysTick->CNTL0 + (SysTick->CNTL1 << 8) + (SysTick->CNTL2 << 16) + (SysTick->CNTL3 << 24);
  unsigned int cmp = SysTick->CMPLR0 + (SysTick->CMPLR1 << 8) + (SysTick->CMPLR2 << 16) + (SysTick->CMPLR3 << 24) + 1;
  unsigned int micros = (counter * 1000) + (cnt * 1000 / cmp);
  printf("counter = %d, micros = %d, cnt = %d, cmpl = %d\n", counter, micros, cnt, cmp);
}

とりあえず下位32ビットだけ処理をして、cmpは1を足してからmicrosを計算してみます。

counter = 6189, micros = 6189010, cnt = 91, cmpl = 9000
counter = 6194, micros = 6194356, cnt = 3205, cmpl = 9000
counter = 6199, micros = 6199893, cnt = 8039, cmpl = 9000
counter = 6205, micros = 6205429, cnt = 3867, cmpl = 9000
counter = 6210, micros = 6210966, cnt = 8701, cmpl = 9000
counter = 6216, micros = 6216504, cnt = 4536, cmpl = 9000
counter = 6222, micros = 6222040, cnt = 365, cmpl = 9000
counter = 6227, micros = 6227482, cnt = 4338, cmpl = 9000
counter = 6233, micros = 6233018, cnt = 168, cmpl = 9000

出力的にも良さそうですね。

時間巻き戻りチェック

void loop() {
  static unsigned int old = 0;
  unsigned int cnt = SysTick->CNTL0 + (SysTick->CNTL1 << 8) + (SysTick->CNTL2 << 16) + (SysTick->CNTL3 << 24);
  unsigned int cmp = SysTick->CMPLR0 + (SysTick->CMPLR1 << 8) + (SysTick->CMPLR2 << 16) + (SysTick->CMPLR3 << 24) + 1;
  unsigned int micros = (counter * 1000) + (cnt * 1000 / cmp);
  if (old < micros) {
    old = micros;
  } else {
    printf("counter = %d, old = %d, micros = %d, cnt = %d, cmpl = %d\n", counter, old, micros, cnt, cmp);
  }
}

念の為前回の時間を保存しておいて、ちゃんと毎回値が増えているのかを確認してみます。もちろん出力されないのを期待していますが。。。

counter = 3972, old = 3972511, micros = 3972484, cnt = 4363, cmpl = 9000
counter = 3979, old = 3979739, micros = 3979712, cnt = 6411, cmpl = 9000
counter = 3987, old = 3987113, micros = 3987086, cnt = 779, cmpl = 9000
counter = 3993, old = 3993910, micros = 3993883, cnt = 7947, cmpl = 9000
counter = 4001, old = 4001426, micros = 4001399, cnt = 3595, cmpl = 9000
counter = 4009, old = 4009511, micros = 4009484, cnt = 4363, cmpl = 9000
counter = 4016, old = 4016881, micros = 4016854, cnt = 7691, cmpl = 9000
counter = 4025, old = 4025511, micros = 4025484, cnt = 4363, cmpl = 9000

思ったより多くの出力がありました。8ミリ秒間隔ぐらいで時間が巻き戻っていますね、、、

時間巻き戻り調査

void loop() {
  const int cnt = 100;
  unsigned int data[4][cnt] = {};

  for (int i = 0; i < cnt; i++) {
    data[0][i] = counter;
    data[1][i] = SysTick->CNTL0 + (SysTick->CNTL1 << 8) + (SysTick->CNTL2 << 16) + (SysTick->CNTL3 << 24);
    data[2][i] = SysTick->CMPLR0 + (SysTick->CMPLR1 << 8) + (SysTick->CMPLR2 << 16) + (SysTick->CMPLR3 << 24) + 1;
    data[3][i] = (data[0][i] * 1000) + (data[1][i] * 1000 / data[2][i]);
  }

  unsigned int old = 0;
  for (int i = 0; i < cnt; i++) {
    if (old < data[3][i]) {
      old = data[3][i];
    } else {
      old = data[3][i];
      printf("Error\n");
    }
    printf("counter = %d, micros = %d, cnt = %d, cmpl = %d\n", data[0][i], data[3][i], data[1][i], data[2][i]);
  }
}

printfを実行すると遅くなるのでとりあえず100回変数に保存してからゆっくり出力してみます。

counter = 5982, micros = 5982995, cnt = 8962, cmpl = 9000
counter = 5982, micros = 5982996, cnt = 8971, cmpl = 9000
counter = 5982, micros = 5982997, cnt = 8981, cmpl = 9000
counter = 5982, micros = 5982998, cnt = 8990, cmpl = 9000
Error
counter = 5982, micros = 5982000, cnt = 4, cmpl = 9000
counter = 5982, micros = 5982001, cnt = 13, cmpl = 9000
counter = 5982, micros = 5982002, cnt = 23, cmpl = 9000
counter = 5982, micros = 5982003, cnt = 32, cmpl = 9000

発見しました。上記はカウントのリセットは入ってもcounterの値が更新されていないですね。

volatile unsigned int counter;

上記に変更してみました。Arduinoだとvolatileを使っていることが多い気がするのですがマイコンの場合には__IOを使う例が多いです。Arduino Uno R3やESP32はvolatileですが、Arduino UNO R4(renesas_uno)は__IOでした。どっちを指定しても中身は両方volatileになります。

counter = 5973, micros = 5973996, cnt = 8972, cmpl = 9000
counter = 5973, micros = 5973998, cnt = 8982, cmpl = 9000
counter = 5973, micros = 5973999, cnt = 8992, cmpl = 9000
counter = 5974, micros = 5974000, cnt = 6, cmpl = 9000
counter = 5974, micros = 5974001, cnt = 16, cmpl = 9000
counter = 5974, micros = 5974002, cnt = 26, cmpl = 9000

ちゃんと反映するようになりました。これでハッピーかと思いきや、、、

counter = 4323, micros = 4323167, cnt = 1506, cmpl = 9000
counter = 4323, micros = 4323168, cnt = 1516, cmpl = 9000
counter = 4323, micros = 4323169, cnt = 1526, cmpl = 9000
counter = 4323, micros = 4323199, cnt = 1791, cmpl = 9000
Error
counter = 4323, micros = 4323171, cnt = 1545, cmpl = 9000
counter = 4323, micros = 4323172, cnt = 1555, cmpl = 9000
counter = 4323, micros = 4323173, cnt = 1565, cmpl = 9000

カウントが巻き戻っている場所を発見。

void loop() {
  const int cnt = 100;
  unsigned int data[4][cnt] = {};

  for (int i = 0; i < cnt; i++) {
    data[0][i] = counter;
    data[1][i] = SysTick->CNTL0 + (SysTick->CNTL1 << 8) + (SysTick->CNTL2 << 16) + (SysTick->CNTL3 << 24);
    data[2][i] = SysTick->CMPLR0 + (SysTick->CMPLR1 << 8) + (SysTick->CMPLR2 << 16) + (SysTick->CMPLR3 << 24) + 1;
    data[3][i] = (data[0][i] * 1000) + (data[1][i] * 1000 / data[2][i]);
  }

  unsigned int old = 0;
  for (int i = 0; i < cnt; i++) {
    if (old < data[3][i]) {
      old = data[3][i];
    } else {
      old = data[3][i];
      printf("Error\n");
      printf("counter = %d, micros = %d, cnt = %d, cmpl = %d\n", data[0][i-2], data[3][i-2], data[1][i-2], data[2][i-2]);
      printf("counter = %d, micros = %d, cnt = %d, cmpl = %d\n", data[0][i-1], data[3][i-1], data[1][i-1], data[2][i-1]);
      printf("counter = %d, micros = %d, cnt = %d, cmpl = %d\n", data[0][i], data[3][i], data[1][i], data[2][i]);
    }
  }
}

全体の傾向がわかったのでエラーの場所のみ表示するように変更しました。範囲外を参照する可能性がありますがデバッグなのでとりあえず目をつむります。

Error
counter = 133222, micros = 133222994, cnt = 8950, cmpl = 9000
counter = 133222, micros = 133223023, cnt = 9215, cmpl = 9000
counter = 133222, micros = 133222996, cnt = 8969, cmpl = 9000

9000を超えているのにカウントはリセットされていませんね。16進数にして見てみます。

Error
counter = 3132, micros = 3132198, cnt = 06f6, cmpl = 9000
counter = 3132, micros = 3132227, cnt = 07ff, cmpl = 9000
counter = 3132, micros = 3132200, cnt = 0709, cmpl = 9000

cntのみ16進数にしたところ、上位8ビットのカウントアップの方が早いですね。06から07にカウントアップしてからffから09になっています。

    //data[1][i] = SysTick->CNTL0 + (SysTick->CNTL1 << 8) + (SysTick->CNTL2 << 16) + (SysTick->CNTL3 << 24);
    data[1][i] = *(uint32_t*)&SysTick->CNTL0;

8ビット単位で取得するとやはり取得タイミングがずれて不整合が起こります。32ビットを一度に取ってくるように修正しました。

修正版

void SYSTICK_Init_Config(u_int64_t ticks) {
  SysTick->CTLR = 0x0000;

  SysTick->CNTL0 = 0;
  SysTick->CNTL1 = 0;
  SysTick->CNTL2 = 0;
  SysTick->CNTL3 = 0;
  SysTick->CNTH0 = 0;
  SysTick->CNTH1 = 0;
  SysTick->CNTH2 = 0;
  SysTick->CNTH3 = 0;

  SysTick->CMPLR0 = (u8)(ticks & 0xFF);
  SysTick->CMPLR1 = (u8)(ticks >> 8);
  SysTick->CMPLR2 = (u8)(ticks >> 16);
  SysTick->CMPLR3 = (u8)(ticks >> 24);
  SysTick->CMPHR0 = (u8)(ticks >> 32);
  SysTick->CMPHR1 = (u8)(ticks >> 40);
  SysTick->CMPHR2 = (u8)(ticks >> 48);
  SysTick->CMPHR3 = (u8)(ticks >> 56);

  NVIC_SetPriority(SysTicK_IRQn, 15);
  NVIC_EnableIRQ(SysTicK_IRQn);

  SysTick->CTLR = (1 << 0);
}

void setup() {
  SystemCoreClockUpdate();
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
  USART_Printf_Init(115200);
  SYSTICK_Init_Config((SystemCoreClock / 8 / 1000) - 1);  //1ms
}

volatile unsigned int _millis;

unsigned long millis(void) {
  return _millis;
}

unsigned long _micros(void) {
  unsigned int cnt = *(uint32_t*)&SysTick->CNTL0;
  unsigned int cmp = *(uint32_t*)&SysTick->CMPLR0 + 1;
  unsigned int micros = (millis() * 1000) + (cnt * 1000 / cmp);
  return micros;
}

unsigned long micros(void) {
  unsigned int micro = _micros();

  while ((micro % 1000) < 5) {
    micro = _micros();
  }

  return _micros();
}

void loop() {
  unsigned int micro = micros();

  static unsigned int old = 0;
  static int count = 0;
  count++;
  if (old < micro) {
    old = micro;
    if ((count % 1000000) == 0) {
      printf("OK count = %d, _millis = %d, old = %d, micro = %d\n", count, millis(), old, micro);
    }
  } else {
    printf("NG count = %d, _millis = %d, old = %d, micro = %d\n", count, millis(), old, micro);
  }
}

#ifdef __cplusplus
extern "C" {
#endif

  void SysTick_Handler(void) __attribute__((interrupt("WCH-Interrupt-fast")));
  void SysTick_Handler(void) {
    SysTick->CNTL0 = 0;
    SysTick->CNTL1 = 0;
    SysTick->CNTL2 = 0;
    SysTick->CNTL3 = 0;
    SysTick->CNTH0 = 0;
    SysTick->CNTH1 = 0;
    SysTick->CNTH2 = 0;
    SysTick->CNTH3 = 0;
    _millis++;
  }

#ifdef __cplusplus
}
#endif

いろいろ調整してこんなコードになりました。まずSysTickの読み出しは桁がずれることがあるので8バイト単位で読み込んではいけません。しかし書き込みは8バイト単位で書き込まないと書き込み時のリセットがうまく動かなくなりました。書き込みはEVTのサンプルどおりにしたほうが安全です。

そして巻き戻り対策ですが、下三桁が005以下の場合にはリトライするようなコードにしました。ちょっと無駄なコードなのですがタイミング系は難しいですね。

他のボードにも対応しつつArduino化

他のボードのEVTを参考にしながらどんどん移植をすすめていきます。基本的にCH32V103が一番変わっていて、それ以外はほぼ同じようなコードになりました。CH32V003は少しだけ簡略化されていたり、カウンタが32ビットなので差分があります。

add SysTick · ch32-riscv-ug/arduino_core_ch32_riscv_arduino@10d94a5
CH32 Risc-V for Arduino IDE. Contribute to ch32-riscv-ug/arduino_core_ch32_riscv_arduino development by creating an acco...

移植自体はすぐに終わりましたが試験しないといけないボードが6種類あるのでその準備だけで結構たいへんです。WCH社のボードは種別の表示が小さいのでチップの特定と、シリアルポートなどもボードごとに違うので細かい確認をする必要があります。

openwchの実装を確認

arduino_core_ch32/cores/arduino/ch32/clock.c at 5d8952b9d05aece5e0c23ea3ab6e86063a7f89df · openwch/arduino_core_ch32
Core library for CH32duino. Contribute to openwch/arduino_core_ch32 development by creating an account on GitHub.

上記の処理になります。

  uint64_t m0 = GetTick();
  uint64_t u0 = *((__IO uint32_t *)SYSTICK_CNTH);  
           u0 = (u0 << 32) + *((__IO uint32_t *)SYSTICK_CNTL);

CH32V103ですが、読み込みは32ビット単位でやっていますね。

  if (m1 != m0) {
    return (m1 * 1000 + ((tms - u1) * 1000) / tms);
  } else {
    return (m0 * 1000 + ((tms - u0) * 1000) / tms);
  }

値は2回取得してミリ秒が違うときには古い方を採用するようにしているようです。ただミリ秒自体の計算がデクリメンタルのタイマーで実装していますね。。。つまりマイクロ秒はかなりおかしな値が帰ってきていると思います。

CH32V103

CH32V103はインクリメンタルしか設定できないのでレジスタの項目が少ないです。

CH32V307

CH32V307だとMODEのビットが増えてインクリメンタルとデクリメンタルの指定ができるようになっています。

まとめ

1ミリ秒でSysTickの設定をしてタイマー割り込みでカウントアップするところまでは検証していたので、すぐに終わると思ったのですがやっぱりハマりました。

マイクロ秒の部分はどうしてもきれいにいかなかったので力技になってしまいました。割り込み禁止とか排他制御をすればよいのかもしれませんが、まずはこれで進めたいと思います。

コメント