読者です 読者をやめる 読者になる 読者になる

ニキシー管時計のAVR-gccソースコード

電子工作

移転しました。

本体は内部RCで1 MHzで動作し、時計用タイマは外部クロック端子につながっている時計用水晶振動子の32.768 kHzで動作させています。24時間制です。


時計合わせ用にボタン2つ(←と↑)がついています。←を押すと順に1分の位、10分の位、1時間の位、10時間の位が点滅して、その位の数字が調整できます。↑を押すと、点滅している桁の数字が1つ増えます。10秒なにもボタンを押さないと元にもどります。

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define TIMEOUT 10//config timeout
#define S_IDOL 0 //idol state
#define S_ONEMIN 1
#define S_TENMIN 2
#define S_ONEHOUR 3
#define S_TENHOUR 4

volatile uint8_t sec = 0;//0-60
volatile uint16_t min = 0;//0-60*(24 + 1)
volatile uint8_t confsec = 0;//0-10+alpha
volatile uint8_t state = S_IDOL;//state
volatile uint8_t red_last = 1;
volatile uint8_t white_last = 1;

int8_t display(uint16_t num);

ISR(TIMER2_OVF_vect){ //int. per 1 sec
  //count and set time
  sec++;
  if (sec >= 60){
	min++;
	sec = 0;
	if (min >= 60*24){
		min = 0;
	}
  }

  display((min / 60)*100 + min % 60);

  //config timeout
  confsec++;
  if(confsec > TIMEOUT){
	state = S_IDOL;
	confsec = 0;
  }

}


ISR(TIMER2_COMPA_vect){ //int. per 1 sec + 1/2
	//turn off -> unavailable 74141
	if(state == S_ONEMIN){
	    //PB0,PD7	
		PORTB |= (1 << PORTB0);
		PORTD |= (1 << PORTD7); //| (1 << PORTD6) | (1 << PORTD5));
	}else if(state == S_TENMIN){
		//PB1,2
		PORTB |= ((1 << PORTB1) | (1 << PORTB2));
	}else if(state == S_ONEHOUR){
		//PD0,1
		PORTD |= ((1 << PORTD0) | (1 << PORTD1));
	}else if(state == S_TENHOUR){
		//PC0,1
		PORTC |= ((1 << PORTC0) | (1 << PORTC1));
	}

}


ISR(PCINT1_vect){
  _delay_ms(10);
  uint8_t red_now = PINC & (1 << PINC4);
  uint8_t white_now = PINC & (1 << PINC5);

  if ((white_last != 0) & (white_now == 0)){//white pull down
	//shift state 
	state++;
	if(state > S_TENHOUR){
		state = S_IDOL;
	}
	confsec = 0;
  }
  white_last = white_now;

  if ((red_last != 0) & (red_now == 0)){//red pull down
	confsec = 0;
	if(state == S_ONEMIN){
		min++;
		if(min % 10 == 0){
			min -= 10;
		}
	}else if(state == S_TENMIN){
		min += 10;
		if(min % 60 < 10){
			min -= 60;
		}
	}else if(state == S_ONEHOUR){
		min += 60;
		if((min / 60) / 10 < 2){
			if((min / 60) % 10 == 0){
				min -= 60*10;
			}
		}else{
			if((min / 60) % 10 == 4){
				min -= 60*4;
			}
		}
	}else if(state == S_TENHOUR){
		min += 60*10;
		if((min / 60) % 10 >= 4){
			if((min / 60) / 10 == 2){
				min -= 60*20;
			}
		}else{
			if((min / 60) / 10 == 3){
				min -= 60*30;
			}
		}
	}

	display((min / 60)*100 + min % 60);
  }
  red_last = red_now;
}

int8_t display(uint16_t num){
  uint8_t one = num % 10;
  uint8_t ten = (num / 10) % 10;
  uint8_t hund = (num / 100) % 10;
  uint8_t thou = (num / 1000) % 10;

  //4one DCBA -> PB0,PD7,6,5
  //3ten DCBA -> PB1,2,3,4
  //2hundDCBA -> PD0,1,2,3
  //1thouDCBA -> PC0,1,2,3
  PORTB = (((ten & 0b0001)?1:0) << 4)
	| (((ten & 0b0010)?1:0) << 3)
	| (((ten & 0b0100)?1:0) << 2)
	| (((ten & 0b1000)?1:0) << 1)
	| (((one & 0b1000)?1:0) << 0);

  PORTC = (((thou & 0b0001)?1:0) << 3)
	| (((thou & 0b0010)?1:0) << 2)
	| (((thou & 0b0100)?1:0) << 1)
	| (((thou & 0b1000)?1:0) << 0)
	| (1 << PORTC4)
	| (1 << PORTC5);

  PORTD = (((hund & 0b0001)?1:0) << 3)
	| (((hund & 0b0010)?1:0) << 2)
	| (((hund & 0b0100)?1:0) << 1)
	| (((hund & 0b1000)?1:0) << 0)
	| (((one & 0b0001)?1:0) << 5)
	| (((one & 0b0010)?1:0) << 6)
	| (((one & 0b0100)?1:0) << 7);

  return 0;
}

int8_t init(void){

//pc4(PCINT12) red, pc5(PCINT13) white
  DDRB = 0xFF;
  DDRC = 0b11001111;
  DDRD = 0xFF;
  PORTB = 0x00;
  PORTC = 0b00110000;
  PORTD = 0x00;


  ASSR   = 0b00100000; // use crystal.
  TCCR2B = 0b00000101; // 128 prescaler, timer on; 1 sec
  //TCCR2B = 0b00000100; // 64 prescaler, timer on; 1/2 sec
  //TCCR2B = 0b00000011; // 32 prescaler, timer on; 1/4 sec

  TIMSK2 |= (1 << TOIE2) | (1 << OCIE2A); // overflow and A interrupts on
  OCR2A = 0x80;

  //interrupt.
  PCICR |= (1 << PCIE1);
  PCMSK1 |= (1 << PCINT12) | (1 << PCINT13);//PCMSK1 = 0x30

  GTCCR  = 0b00000010; //timer reset
  sei(); // allow interrupts

  return 0;
}

int main(void){
  init();

  while(1){

  }
  return 0;
}