電音の工場ブログ

趣味の電子工作を中心としたブログです.音モノの工作が多いです.

avr-gcc で工作(その4)

avr-gcc で工作(その4)

もうちょっと色気を出していじってみました。

普通に電源投入ならLEDが点灯、電源ONのときにRESETスイッチでリセットをかけるとLEDが点滅します。AVRマイコンの電源にはスーパーキャパシタを使って cold start と warm start を判別しながら動かしてみようと思っていたのでこの実験をしてみました。まだこのあたりの話は回路図にはあらわしてありません。

現状の回路図は (その2) id:Chuck:20040912#p1 と同じものです。

20040912回路図

状態の特定には MCUSR (MCU Status Register) を使います。.initN section*1を使うと main() に入る前の部分にコードを置くことができます。my_init_mcusr() で MCUSRの値をcpu_statに覚えておいて、MCUSRはクリアしておきます*2。ioinit() の中で、External Reset であれば Timer2 を動かし、Power-On-Reset であればポートに出力するだけで終わります。

.initN section を使わなくても MCUSR の値は壊れない*3と思われるのですが、まぁ使ってみたかったので。.init3 に置くと cpu_stat の値を壊してくれちゃいました*4ので、.init8 に配置しています。

さて、次は何をしようかな。もう少しハードウェアをいじる必要があるかもしれません。

以下ソースコード

/*
	PORTA LED blinking
*/
#include 
#include 
#include 
#include 

#define _COUNTER 5

enum { ON, OFF };

volatile uint8_t cnt = _COUNTER;
volatile uint8_t led = ON;
volatile uint8_t cpu_stat;

void my_init_mcusr (void) __attribute__ ((naked)) \
    __attribute__ ((section (".init8")));

void my_init_mcusr (void)
{
  cpu_stat = MCUSR;
  MCUSR = 0;
}

SIGNAL(SIG_OVERFLOW2)
{
  if(!(--cnt)) {
    cnt = _COUNTER;
    switch (led) {
    case ON:
      led = OFF;
      cbi(PORTA, PA0);
      break;
    case OFF:
      led = ON;
      sbi(PORTA, PA0);
      break;
    }
  }
}

void ioinit (void)
{
  uint8_t rst = cpu_stat & (_BV(EXTRF) | _BV(PORF));

  /* enable PA0 as output */
  DDRA = _BV (PA0);
  /* tmr2 is prescaled by 1024 */
  TCCR2 = _BV (CS22) | _BV (CS21) | _BV (CS20);

  switch (rst) {
  case 2:		// external reset -> blink
    timer_enable_int (_BV (TOIE2));
    /* enable interrupts */
    sei ();
    break;
  case 1:
  case 3:
    sbi(PORTA, PA0);
    break;
  default:
    cbi(PORTA, PA0);
  }
}

int main(void)
{
  ioinit();
  for (;;) ;
  return 0;
}

*1:reserved もあるけれど Nは0から9まであります。

*2:Errataによれば MCUSRの値をbitごとにクリアするのはバグのために状態が変化してしまってダメだそうです。クリアするなら一括で 0x00 を代入せよ、とのこと。

*3Watch Dog を使っていなければ。

*4:いや、単に初期化してくれただけなのですが。