090901

|


筑波4戦エントリーしました。

ロガー続き。RTCを実装して、経過時間を表示できるようにした。これは富士に は必要ないのだけど。富士はコントロールブリッジに経過時間が表示されてい るので。あれは便利だ。
ディップスイッチでラップタイム、経過時間の表示の切り替え、1点、3点計測 の切り替えをできるようにした。
ロガーを入れた写真の3L4、これは3点計測、ラップタイム表示、ログ番号4。大 きい表示の8桁くらいのLCDで、できればI2C接続のとかあるといいのだけど。
今週末のMCFAJ3戦富士までに仕上げないと。


typedef void (irq_handler_t)(void);
irq_handler_t __rtc_intr;
// Application override interrupt handler.
irq_handler_t rtc_intr_counter_incr __attribute__ ((weak, alias ("__rtc_intr")));
irq_handler_t rtc_intr_alarm __attribute__ ((weak, alias ("__rtc_intr")));
irq_handler_t rtc_intr_subsecond __attribute__ ((weak, alias ("__rtc_intr")));

RTCは3つの割り込みを出せる。ymdhmsで指定したアラームと、ymdhmsのカウン
タの増分時、ソースクロックの16,32,64,128,256,512,1024,2048カウント毎だ。
ソースクロックは外部接続の発振子と内部のPCLKどっちかを選べる。PCLKの場
合はプリスケーラの設定ができる。

それぞれのハンドラはアプリケーションで実装。実装がない場合は__rtc_intr
が埋め草として使われる。

enum rtc_state
  {
    RTC_OFF,
    RTC_ON,
    RTC_START,
  };

enum rtc_state __rtc_state;

STATIC void rtc_reset ();
STATIC void rtc_alarm_clear (void);

void
rtc_init ()
{
  struct rtc_ymdhms date;
  // Power on (On reset, the RTC is enabled.)
RTCはデフォルトで電源が入っている。
  mcu_peripheral_power (PCONP_PCRTC, TRUE);

  // Reset all.
RTCは本体電源のON/OFFでレジスタがリセットされないので確実にリセット。
Interface2009/5付録基板の場合、VBATはVccに直結なのでなくてもいい。
  rtc_reset ();
  __rtc_state = RTC_ON;

  // RTC source clock.
Interface2009/5付録基板に32.768kHzの外部発振子をつけている。
  *RTC_CCR |= CCR_CLKSRC;	// 32.768kHz external oscillator.
  // If use PCLK, set RTC_PREINT and RTC_PREFRAC.
内部クロック(PCLK)を使うならRTC_PREINTとRTC_PREFRACを設定してプリスケーラを
設定しないといけない。

RTCを全部ゼロ。これはこの基板だから。
  memset (&date, 0, sizeof date);
  rtc_time_set (&date);
}

void
rtc_fini ()
{

  rtc_stop ();
  rtc_reset ();
  // Power off.
  mcu_peripheral_power (PCONP_PCRTC, FALSE);
  __rtc_state = RTC_OFF;
}

void
rtc_reset ()
{

  *RTC_CCR = 0;		// クロックストップ。
  *RTC_AMR = AMR_MASK;	// Alarm Mask. アラームの割り込みオフ。
  *RTC_CIIR = 0;	// Counter Increment Interrupt. カウンター割り込みオフ
  *RTC_CISS = 0;	// Subsecond interrupt. サブセカンド割り込みオフ
  *RTC_ILR = ILR_RTCCIF | ILR_RTCALF | ILR_RTSSF;	// Interrupt Location
前に残っていた割り込みをクリア

  // Clear alarm
  rtc_alarm_clear ();
}

void
rtc_start ()
{
  assert (__rtc_state == RTC_ON);
  // Start RTC clock
割り込みの有効化はこれだけ。3つそれぞれのイネーブルは
アラームはRTC_AMR, カウンターはRTC_CIIR、サブセカンドはRTC_CISS。

  *RTC_CCR |= CCR_CLKEN;
  __rtc_state = RTC_START;

  // Interrupt Enable.
  *VICIntSelect |= VIC_RTC; // FIQ
FIQなのでこのOSではこの割り込みでコンテキストスイッチなし。
  *VICIntEnable |= VIC_RTC;
}

void
rtc_stop ()
{

  // Interrupt Disable
  *VICIntEnable &= ~VIC_RTC;
  // Stop RTC clock.
  *RTC_CCR &= ~CCR_CLKEN;
  // Clear pending interrupt.
  *RTC_ILR = ILR_RTCCIF | ILR_RTCALF | ILR_RTSSF;
  __rtc_state = RTC_ON;
}

bool
rtc_time_set (struct rtc_ymdhms *date)
{

  *RTC_SEC = date->sec;
  *RTC_MIN = date->min;
  *RTC_HOUR = date->hour;
  *RTC_DOM = date->day;
  *RTC_DOW = date->wday;
  *RTC_DOY = 0; //XXX
  *RTC_MONTH = date->mon;
  *RTC_YEAR = date->year;

  return TRUE;
}

bool
rtc_time_get (struct rtc_ymdhms *date)
{

  date->sec = *RTC_SEC;
  date->min = *RTC_MIN;
  date->hour = *RTC_HOUR;
  date->day = *RTC_DOM;
  date->wday = *RTC_DOW;
  date->mon = *RTC_MONTH;
  date->year = *RTC_YEAR;

  return TRUE;
}

bool
rtc_alarm (struct rtc_ymdhms *date, bool on)
{
  // Clear Pending interrupt.
  *RTC_ILR |= ILR_RTCALF;

  if (on)
    {
      *RTC_ALSEC = date->sec;
      *RTC_ALMIN = date->min;
      *RTC_ALHOUR = date->hour;
      *RTC_ALDOM = date->day;
      *RTC_ALDOW = date->wday;
      *RTC_ALDOY = 0;//XXX
      *RTC_ALMON = date->mon;
      *RTC_ALYEAR = date->year;
      // Alarm mask.
      *RTC_AMR = 0;	// compare all.
    }
  else
    {
      *RTC_AMR = AMR_MASK;	// mask all.
      rtc_alarm_clear ();
    }

  return TRUE;
}

void
rtc_alarm_clear ()
{
  // Clear alarm
  *RTC_ALSEC = 0;
  *RTC_ALMIN = 0;
  *RTC_ALHOUR = 0;
  *RTC_ALDOM = 0;
  *RTC_ALDOW = 0;
  *RTC_ALDOY = 0;
  *RTC_ALMON = 0;
  *RTC_ALYEAR = 0;
  *RTC_AMR = AMR_MASK;	// mask all;
}

bool
rtc_counter_incr (enum ymdhms counter, bool on)
{
  uint32_t bit[] = { CIIR_IMYEAR, CIIR_IMMON, CIIR_IMDOM, CIIR_IMDOW,
		     CIIR_IMHOUR, CIIR_IMMIN, CIIR_IMSEC };

  // Clear Pending interrupt.
  *RTC_ILR |= ILR_RTCCIF;
  if (on)
    {
      *RTC_CIIR = bit[counter];
この実装だと、どれか一つのカウンタの増分時しか割り込みが起きない。
本来は全ての組み合わせでいい。
    }
  else
    {
      *RTC_CIIR = 0;
    }

  return TRUE;
}

bool
rtc_subsecond (uint32_t subsecond_select, bool on)
{
  // SubSelSub-Second Select.
  *RTC_CISS = subsecond_select;

   // Clear Pending interrupt.
  *RTC_ILR |= ILR_RTSSF;

  // Enable/Disable interrupt.
  if (on)
    *RTC_CISS |= CISS_SUBSECENA;
  else
    *RTC_CISS &= ~CISS_SUBSECENA;

  return TRUE;
}

void
rtc_print ()
{
  struct rtc_ymdhms date;

  rtc_time_get (&date);

  iprintf ("%d %d %d %d %d %d %d\n", date.year, date.mon, date.day,
	   date.wday, date.hour, date.min, date.sec);
}

void
rtc_intr ()
{
  uint32_t r = *RTC_ILR;	// interrupt cause.

  *RTC_ILR |= (r & ILR_MASK);	// Clear interrupt.
RTC_ILRの予約ビットの読み込みは不定、書き込み時は0じゃないといけない。

  // Dispatch application defined handler.
  if (r & ILR_RTCCIF)
    rtc_intr_counter_incr ();
  if (r & ILR_RTCALF)
    rtc_intr_alarm ();
  if (r & ILR_RTSSF)
    rtc_intr_subsecond ();
}

void
__rtc_intr ()	// Place holder.
{
ユーザ定義のハンドラがなければここ。
  rtc_print ();
}