0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Nucleo 8S208RB タイマーを使う

Last updated at Posted at 2023-09-03

TIM4で ticks_ms() のかわり

経過時間を表す ticks_ms() のかわりを作ってみます。

割り込みハンドラ

stm8s_it.c(抜粋)
INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
{
  extern uint32_t ticks_ms;

  TIM4_ClearFlag(TIM4_FLAG_UPDATE);
  ticks_ms++;
  return;
}

使用例

main.c(抜粋)
  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // 16MHz 動作

  uint32_t ticks_ms = 0;

  TIM4_DeInit();
  TIM4_TimeBaseInit(
    TIM4_PRESCALER_128, // プリスケーラー div 128
    124 // 再ロード値
  ); 
  TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
  TIM4_Cmd(ENABLE);

  enableInterrupts();

  while (1)
  {
    if (! ((GPIOE->IDR) & 1<<4)) {
      printf("Ticks_ms = %lu\n", ticks_ms);
    }
  }

Nucleoではボタンを押すたびにリセットからの経過時間がmsec単位で表示されます。

補足

RM0016によれば,基本タイマー TIM4 の プリスケーラー 1,2,4,8,16,32,64,128 から選べ,
カウンターリロード値は8ビットという,STM32に慣れているとかなり割り切った,前向きに書けばシンプルでわかりやすい仕様ですね。

TIM4周波数 = f[master] / PSCR / (ARR + 1)

動作周波数は16MHzで譲れないとして,PSCR = 128, ARR=124で

16MHz / 128 / ( 124 + 1 ) = 1kHz

のタイマーとしてみました。

TIM2 で PWM

image.png

main.c(抜粋)

  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // 16MHz / 1 = 16MHz

  TIM2_DeInit();
  TIM2_TimeBaseInit( // 16MHz / 1 / (15999+1) = 1kHz
    TIM2_PRESCALER_1, 
    15999
    );

  TIM2_OC2Init(  // for TIM2_CH2 PD3
    TIM2_OCMODE_PWM1, 
    TIM2_OUTPUTSTATE_ENABLE, 
    4800, // 4800 / (15999+1) = 30%
    TIM2_OCPOLARITY_HIGH
    );
  TIM2_Cmd(ENABLE);
  TIM2->CR1 |= TIM2_CR1_CEN;

ほぼこちらの抜粋です。

  • 動作周波数は16MHz
  • 1kHz, ON DUTY = 30% の PWM です。コメントのとおり,プリスケーラーは 1 再ロード値は 15999 比較値は 4800 です。

16MHz / 1 / (15999+1) = 1kHz
4800/(15999+1) = 30%

  • TIM2_CH2に出力するようにします。PD3 に出てきます。

補足

最低限ですと,これだけでいいようです。(プリスケーラー 16 リロード値 999)

main.c(抜粋)
  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // 16MHz / 1 = 16MHz

  TIM2->PSCR = TIM2_PRESCALER_16;

  const uint16_t tim2_auto_reload = 999;
  TIM2->ARRH = (tim2_auto_reload >> 8);
  TIM2->ARRL = (tim2_auto_reload & 0xFF);

  const uint16_t tim2_compare_reg1 = 300;
  TIM2->CCR2H = (tim2_compare_reg1 >> 8);
  TIM2->CCR2L = (tim2_compare_reg1 & 0xFF);

  TIM2->CCER1 = TIM2_CCER1_CC2E; // Enable compare channel 2 output
  TIM2->CCMR2 = TIM2_OCMODE_PWM1;

  TIM2->EGR |= TIM2_EGR_UG; // Generate an update event to register new settings
  TIM2->CR1 = TIM2_CR1_CEN; // Enable the counter

以下を参考にしました。

-- https://gist.github.com/stecman/f748abea0332be1e41640fd25b5ca861

TIM2 で One Pulse Mode

image.png

ロジアナで見やすいように,タイマー開始時にPC5(=Nucleo LED)をH タイマー終了時に L にしています。

main.c(抜粋)
  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // 16MHz 動作

  GPIO_Init(GPIOC, GPIO_PIN_5, GPIO_MODE_OUT_PP_HIGH_FAST);

  GPIO_WriteLow(GPIOC, GPIO_PIN_5);

  TIM2_DeInit();
  TIM2_SelectOnePulseMode(TIM2_OPMODE_SINGLE);
  TIM2_TimeBaseInit( // 16MHz / 1 / (15999+1) = 1kHz
    TIM2_PRESCALER_1, 
    15999
    );

/*
  MODE POLARITY result
    1    HIGH    (start) HHH LLLLLLL (end) HHHH...
    1    LOW     (start) LLL HHHHHHH (end) LLLL...
    1    HIGH    (start) LLL HHHHHHH (end) LLLL...
    2    LOW     (start) HHH LLLLLLL (end) HHHH...
*/

  TIM2_OC2Init(  // for TIM2_CH2 PD3
    TIM2_OCMODE_PWM1, 
    TIM2_OUTPUTSTATE_ENABLE, 
    4800, // 4800 / (15999+1) = 30%
    TIM2_OCPOLARITY_HIGH
    );

  TIM2_Cmd(ENABLE);
  TIM2->CR1 |= TIM2_CR1_CEN;
  GPIO_WriteHigh(GPIOC, GPIO_PIN_5); // for debug

  while (TIM2->CR1 & TIM2_CR1_CEN) {} // タイマー終了を待つ

  GPIO_WriteLow(GPIOC, GPIO_PIN_5); // for debug

PWMに TIM2_SelectOnePulseMode(TIM2_OPMODE_SINGLE); が加わっただけですね。終了したかどうかはここではCENレジスタを見ています。

PWMのモード,Polarity設定で信号極性が変わります。コメントに書いてあるとおりでした。

再スタートするには

もう一度 TIM1->CR1 |= TIM1_CR1_CEN; すればいいようです。

image.png

指定時間だけ H にしてタイマーが終了したら L になっていてほしい

1msecだけONにすることを考えます。

image.png

main.c(抜粋)
  CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1); // 16MHz 動作

  TIM2_DeInit();
  TIM2_SelectOnePulseMode(TIM2_OPMODE_SINGLE);
  TIM2_TimeBaseInit(
    TIM2_PRESCALER_1, 
    16000 // 再ロード値
    );

  TIM2_OC2Init(  // for TIM2_CH2 PD3
    TIM2_OCMODE_PWM2, 
    TIM2_OUTPUTSTATE_ENABLE, 
    1, // ON duration 比較値
    TIM2_OCPOLARITY_HIGH
    );

  TIM2_Cmd(ENABLE);
  TIM2->CR1 |= TIM2_CR1_CEN;

OnePulseモードを構成すると,

  • タイマースタート
  • ON状態
  • OFF状態
  • タイマー終了
  • ON状態が続く

となります。なので,ON状態がL OFF状態がHとなるようにして,ON状態を極力短くなるようにすればいいということになります。

比較値は0にすることはできないので,1が最小値となります。正しくは 1 / (F_MASTER / プリスケーラー) の時間だけ L となりますが,プリスケーラーを小さくすれば実質問題ないでしょう。

上記設定値の例

  • 動作周波数 16MHz
  • プリスケーラー値 1
  • TIM2のカウンター周波数は 16MHz/1 = 16MHz
  • 再ロード値 16000 -->> H区間は 16000/16MHz = 1msec
  • 比較値 1 -->> 事前のL区間は 1/16MHz = 62.5nsec

TIM1で One Shot Timer を使う

参考記事

  • 反転してCH1Nにも出す相補出力もできるとのことですが,TIM1_CH1Nをポートに割り当てるためにはオプションバイトの変更が必要です。

250kbpsでポートを読んでみる

タイマーを使って定期的にGPIOを読むことを考えます。
25kHz DUTY=30% の矩形波を読むことをPWMでつくり,別のタイマーで250kbpsでサンプリングします。

 *  実行するタスク: platformio device monitor 

--- Terminal on /dev/cu.usbmodem14103 | 9600 8-N-1
--- Available filters and text transformations: colorize, debug, default, direct, hexlify, log2file, nocontrol, printable, send_on_enter, time
--- More details at https://bit.ly/pio-monitor-filters
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H
TIM2 PWM started
TIM4 ready for sample
TIM4 finished sample
00001110000000111000000011100000001110000000111000000011100000001110000000111000000011100000001110000000111000000011100000001110

今までの組み合わせです。基本タイマー TIM4 で 250kHz で割り込みを作り,グローバルな配列に代入します。

stm8s_it.c(抜粋)
uint8_t buffer_count;
uint8_t buffer[128];

INTERRUPT_HANDLER(TIM4_UPD_OVF_IRQHandler, 23)
{

  //GPIO_WriteReverse(GPIOC, GPIO_PIN_5);
  if (buffer_count < 128) {
    buffer[buffer_count++]=GPIOD->IDR;
  }
  
  TIM4_ClearFlag(TIM4_FLAG_UPDATE);

  return;
}

バッファーは 1ビット * サンプリング数 ですが,スピード優先で GPIOD の8ビット分まるごと読むようにしてみました。
buffer_countは128未満であればサンプリングし,128であれば何もせずに抜けるようにしました。
グローバル変数はヘッダーで宣言します。

stm8s_it.h(抜粋)
extern uint8_t buffer_count;
extern uint8_t buffer[128];

複数のグローバル変数は構造体で持つのが主流のようですが,とりあえず簡単に書きました。

TIM4を作成,サンプリングを開始して,停止したことを判定して書き出します。PD3だけ知りたいので buffer[] の3ビット目だけを書いています。

main.c(抜粋)
  buffer_count=128; // 念のためサンプリングを停止

  TIM4_DeInit();
  TIM4_TimeBaseInit( // 16MHz / 16 / (3+1) = 250kHz
    TIM4_PRESCALER_16,
    3
  ); 
  TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
  TIM4_Cmd(ENABLE);

  enableInterrupts();

  printf("TIM4 ready for sample\n");

  buffer_count=0; // start sampling

  while( buffer_count<128 ) {
    GPIO_WriteReverse(GPIOC,GPIO_PIN_5);
  } // wait end of sampling

  printf("TIM4 finished sample\n");

  for (int i=0; i<128; i++) {
    printf("%d",(buffer[i]>>3) & 1);
    //printf("%2x\n",buffer[i]);
  }
  printf("\n");

サンプリング終了待ちのwhileループ,最適化の影響でしょうか,何もないとなぜかbuffer_countが更新されません。苦し紛れにLチカさせました。

PWMは上にある通りTIM2_CH2に構成しました。

main.c(抜粋)
  TIM2_DeInit();
  TIM2_TimeBaseInit( // 16MHz / 16 / (39+1) = 25kHz
    TIM2_PRESCALER_16, 
    39
    );

  TIM2_OC2Init(  // for TIM2_CH2 PD3
    TIM2_OCMODE_PWM1, 
    TIM2_OUTPUTSTATE_ENABLE, 
    12, // 5 / (19+1) = 25%
    TIM2_OCPOLARITY_HIGH
    );
  TIM2_Cmd(ENABLE);
  TIM2->CR1 |= TIM2_CR1_CEN; // start TIM2 PWM

割り込み処理中は軽くしないとだめですね。今回の例では 500kbps では動きませんでした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?