Some old AVR projects
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

437 lines
7.9 KiB

//Imitace casovane bomby, Ondrej Hruska (c) 2010-2012
//-------------------------------------------------
// Zapojeni:
// +------u------+
// reset --+ /RST Vcc +-- napajeni +5V
// Anoda jednotek --+ PD0 PB7 +-- segment D
// Anoda desitek --+ PD1 PB6 +-- sedment E
// (nezapojeno) --+ XT2 PB5 +-- segment C
// (nezapojeno) --+ XT1 PB4 +-- segment H
// drat --+ PD2 PB3 +-- segment G
// drat --+ PD3 PB2 +-- segment A
// drat --+ PD4 PB1 +-- segment F
// drat --+ PD5 PB0 +-- segment B
// GND --+ GND PD6 +-- output signal (vybuch)
// +-------------+
//
// Nazvy segmentu na displeji:
// ---A---
// | |
// F B
// | |
// ---G---
// | |
// E C
// | |
// ---D--- H
//
/*
Ports:
PORTB - segments
PD0 anode L
PD1 anode H
PD2 w0
PD3 w1
PD4 w2
PD5 w3
PD6 BOMB
PD7 -nc-
*/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include <stdlib.h>
#include <util/delay_basic.h>
#include <avr/eeprom.h>
#include <avr/sleep.h>
void updateDisplayBuffer();
void ports_init();
void timer_init();
void multiplex(uint8_t times);
void boom();
void halt();
void init_animation();
/*FUSES =
{
.low = 0xE4,
.high = 0xDF
};*/
/** initial number of intervals */
#define INIT_MINS 60
/** seconds in one interval */
#define INIT_MAX 60
/** seconds in one interval */
#define RAPID_MAX 8
/** seconds in one interval */
#define RAPID_COUNT 5
/* MACROS */
/** set one hertz interrupt */
#define timer_enable() TIMSK=(1<<OCIE1A)
/** disable timer interrupt */
#define timer_disable() TIMSK=0
// segments DEChGAFB
#define B (1<<0)
#define F (1<<1)
#define A (1<<2)
#define G (1<<3)
#define H (1<<4)
#define C (1<<5)
#define E (1<<6)
#define D (1<<7)
// anodes
#define AL 1
#define AH 0
// wires
#define W0 2
#define W1 3
#define W2 4
#define W3 5
//bomb pin
#define BM 6
// pin groups
#define WIRES ((1<<W0)|(1<<W1)|(1<<W2)|(1<<W3))
#define ANODES ((1<<AL)|(1<<AH))
#define BOMB (1<<BM)
#define SEGMENTS 0xFF
uint8_t num2seg[10]; // array for num -> 7seg translation
#define BLANK 0
/* VARIABLES */
uint8_t EEMEM wireset;
uint8_t volatile cnt; // time counter (countdown)
uint8_t volatile cnt_interval; // current interval counter in seconds
uint8_t volatile cnt_interval_max; // length of current interval in seconds
uint8_t disp_H; // display buffer - ones
uint8_t disp_L; // tens
uint8_t volatile wires;
uint8_t volatile last_wires;
uint8_t is_one_digit;
uint8_t wire_shutdown; //absolute mask
uint8_t wire_boom;
//remaining two are for RAPID COUNTDOWN (30 s)
/* INTERRUPT VECTORS */
/** one second interrupt */
ISR(TIMER1_COMPA_vect){
cnt_interval++;
if(!is_one_digit) disp_H ^= H;
disp_L ^= H;
if(cnt_interval >= cnt_interval_max){
cnt_interval=0;
cnt--;
//to tens and ones
updateDisplayBuffer();
}
//time over?
if(cnt==0){
boom();
}
}
/** MAIN */
int main()
{
cnt = INIT_MINS;
cnt_interval = 0;
cnt_interval_max = INIT_MAX;
is_one_digit = 0;
disp_H = 0;
disp_L = 0;
last_wires = WIRES;
num2seg[0] = A|B|C|D|E|F;
num2seg[1] = B|C;
num2seg[2] = A|B|G|E|D;
num2seg[3] = A|B|G|C|D;
num2seg[4] = F|G|B|C;
num2seg[5] = A|F|G|C|D;
num2seg[6] = A|C|D|E|F|G;
num2seg[7] = A|B|C;
num2seg[8] = A|B|C|D|E|F|G;
num2seg[9] = A|B|C|D|F|G;
timer_disable();
timer_init();
timer_reset();
ports_init();
init_animation();
last_wires = ~(PIND & WIRES);
//wire set
//read wireset number
uint8_t wireset_r = eeprom_read_byte((uint8_t*)&wireset);
//go to next one, reset if >11
wireset_r++;
if(wireset_r>=12) wireset_r=0;
//store new value
eeprom_write_byte((uint8_t*)&wireset,wireset_r);
//select wires for wireset
uint8_t ws_boom[12] = {(1<<W1), (1<<W0), (1<<W2), (1<<W0), (1<<W3), (1<<W0), (1<<W1), (1<<W1), (1<<W3), (1<<W2), (1<<W2), (1<<W3)};
uint8_t ws_shutdown[12] = {(1<<W0), (1<<W3), (1<<W1), (1<<W2), (1<<W2), (1<<W1), (1<<W3), (1<<W2), (1<<W1), (1<<W0), (1<<W3), (1<<W0)};
wire_boom = ws_boom[wireset_r];
wire_shutdown = ws_shutdown[wireset_r];
updateDisplayBuffer();
timer_enable();
sei();
uint8_t diff;
while(1){
multiplex(50);
//check wire
wires = ~(PIND & WIRES);
diff = wires ^ last_wires;
//wire changed
if(diff != 0){
//shutdown wire was connected last time
if( (last_wires & wire_shutdown) != 0 && (diff & wire_shutdown) != 0){
//shutdown wire!
halt();
exit(0);
}else if( (last_wires & wire_boom) != 0 && (diff & wire_boom) != 0){
//bomb launched!
boom();
exit(0);
}else{
//rapid
if(cnt_interval_max > RAPID_MAX){
cnt_interval_max = RAPID_MAX;
if(cnt>RAPID_COUNT){
cnt = RAPID_COUNT;
cnt_interval = 0;
timer_reset();
updateDisplayBuffer();
}
}
}
}
last_wires = wires;
}
cli();
return 0;
}
/** put corrent segments into display buffer (from cnt) */
void updateDisplayBuffer(){
div_t foo = div((int)cnt,10);
//keep decimal dots
disp_H = (disp_L & H) | num2seg[foo.quot];
disp_L = (disp_L & H) | num2seg[foo.rem];
if(foo.quot == 0){
disp_H = 0;
is_one_digit = 1;
}else{
is_one_digit = 0;
}
}
/** show display, repeat "times" x */
void multiplex(uint8_t times){
for(; times>0; times--){
PORTB = ~disp_L; // TENS segments; common anode, needs invert
PORTD &= ~ANODES; // reset anodes
PORTD |= (1<<AL); // turn TENS anode on
_delay_loop_1(255); // wait
PORTB = ~disp_H; // ONES segments; common anode, needs invert
PORTD &= ~ANODES; // reset anodes
PORTD |= (1<<AH); // turn ONES anode on
_delay_loop_1(255); // wait
}
PORTB = ~BLANK; //display off
}
void multiplex_pwm(uint8_t times, uint8_t delay){
for(; times>0; times--){
PORTB = ~disp_L; // TENS segments; common anode, needs invert
PORTD &= ~ANODES; // reset anodes
PORTD |= (1<<AL); // turn TENS anode on
_delay_loop_1(255-delay); // wait
PORTD &= ~(1<<AL);
_delay_loop_1(delay);
PORTB = ~disp_H; // ONES segments; common anode, needs invert
PORTD &= ~ANODES; // reset anodes
PORTD |= (1<<AH); // turn ONES anode on
_delay_loop_1(255-delay); // wait
PORTD &= ~(1<<AH);
_delay_loop_1(delay);
}
PORTB = ~BLANK; //display off
}
/** the boom */
void boom(){
cli();
PORTD |= BOMB; //activate bomb
PORTD &= ~WIRES; // turn off wire pullups
disp_H = 0b01010101;
disp_L = 0b10101010;
while(1){
disp_H ^= 0xff;
disp_L ^= 0xff;
longmpx();
}
// PORTD |= ANODES; // turn both anodes on
// PORTB = 0x00; // all segments on
// __asm__("cli");
exit(0);
}
/** the boom */
void halt(){
PORTD &= ~WIRES;
for(uint8_t volatile pause=0; pause < 0xff; pause++){
multiplex_pwm(40,pause);
}
//animation
PORTD = (1<<AL); // higher part
PORTB = ~H; // show one dot
timer_disable();
cli();
for(;;){}
exit(0);
}
/** set up ports */
void ports_init(){
DDRB = SEGMENTS; // segments to output
PORTB = SEGMENTS; // turned off (cathodes!)
DDRD = ANODES|BOMB; // set outputs
PORTD = WIRES; // enable wire pullups
}
/** set timer1 to one hertz */
void timer_init(){
TCCR1A = 0;
TCCR1B=(1<<WGM12)|(1<<CS12);
OCR1AH=0b01111010;
OCR1AL=0b00010010;
}
/** clear timer */
void timer_reset(){
TCNT0 = 0;
}
void longmpx(){
for(uint8_t i=2; i>0; i--){
multiplex(250);
}
}
/** animation on startup */
void init_animation(){
disp_H = BLANK;
disp_L = BLANK;
for(uint8_t i=0; i<10; i++){
disp_H ^= 0xff;
disp_L ^= 0xff;
longmpx();
}
disp_H = BLANK;
disp_L = BLANK;
_delay_loop_1(255);
}