Electronic Dice with ATtiny13


This electronic dice is powered from a CR2032 3V battery and an ATtiny13A microcontroller is controlling the LEDs. No button and no switch is needed to operate it. A piezo buzzer is used as a preassure sensor. I took it out from an old telephone. As the dice is dropped from at least 1 cm height to the table the dice shuffles through the numbers and after a few seconds it shows the rolled number. 

 Working principle of the dice

If you drop the dice, the piezo buzzer creates a voltage spike. This spike is trimmed by the 1N4148 diode so the microcontroller doesn't get damaged. This trimmed spike creates a pin change interrupt and resets a timer. In that moment another, always running timer is devided by 6 and the remainder of this division gives a number which is always between 0 and 5. 0 means 1 on the dice, 1 means 2 on the dice and so on. 5 means 6. So this number is the starting position of the dice. This number could be any number from 0 to 5, so basically it's a random number. From now on the numbers cycles through 1 to 6 fast and getting slower and slower, finally it stops on a number and this is the number you rolled with the dice. After a pin change interrupt has occured the pin change interrupt is disabled so you can not roll another number in the process. 



The microcontroller has very few I/O pins, exatly 5. 1 is for the piezo input, so we have 4 I/O pins left. It is a challange to drive 7 LED-s with only 4 pins. The trick is that all 6 numbers of the dice can be put together with 4 patterns. Each number with maximum of 3 patterns. 

Part list:

470R resistor                          x3 (R1, R3, R4)
1k resistor                               x1 (R2)
10k resistor                             x1 (R5)
1M resistor                              x1 (R6)
10 mm diffused red LED        x7 (LED1, LED2, LED3, LED4, LED5, LED6, LED7)
1N4148 diode                         x1 (D2)
CR2032 battery holder           x1
CR2032 battery                      x1
piezo buzzer                           x1
DIP-8 IC socket                        x1
ATtiny13A-PU                          x1 (IC1)
Single sided PCB 55x55 mm  x2
15 mm M3 spacer                   x8
M3 8 mm pan head screw      x4
M3 nut                                      x4
M3 washer                                x4
9 mm plywood 55x55 mm     x1
resistor leg for jumper wire    x2
some wire

all resistors are 1/4W
Schematic diagram

PCB Layout
PCB

Component placement top
Drill 3 mm holes in the plywood

Glue the piezo to the plywood with a hot glue gun

Insert the battery






Programming the ATtiny13

Connecting the USBasp programmer to the uC.




It is the same process as I described in the previous post except here we put "t13" instead of "t84" in the codes:
test:
avrdude -c usbasp -p t13 -B 250

compile:
avr-g++ -g -Os -Wall -mcall-prologues -mmcu=attiny13 -fno-exceptions -o main_t13.obj main_t13.c

upload:
avr-g++ -g -Os -Wall -mcall-prologues -mmcu=attiny13 -fno-exceptions -o main_t13.obj main_t13.c
avr-objcopy -R .eeprom -O ihex main_t13.obj main_t13.hex
avrdude -c usbasp -p t13 -B 250 -U flash:w:main_t13.hex


THE CODE:

/*
Electronic Dice code for ATtiny13A-PU

fuse bits: (default fuse setting)
fuse high byte: 0xFF
fuse low byte:  0x6A

PINOUT

ATtiny13A

pin 1 RESET
pin 8 Vcc
pin 4 GND

pin 5 PB0
pin 6 PB1
pin 7 PB2
pin 2 PB3
pin 3 PB4

O..
... -> pin 3 PB4 pattern 1
..O

..O
... -> pin 6 PB1 pattern 2
O..

...
O.O -> pin 5 PB0 pattern 3
...

...
.O. -> pin 7 PB2 pattern 4
...

piezo -> pin 2 PB3

dice:
1 -> pat4
2 -> pat2
3 -> pat2 + pat4
4 -> pat1 + pat2
5 -> pat1 + pat2 + pat4
6 -> pat1 + pat2 + pat3
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

#define F_CPU 1200000UL   //cpu speed 1.2 MHz

#include <util/delay.h>


#define PAT1 1<<PB4
#define PAT2 1<<PB1
#define PAT3 1<<PB0
#define PAT4 1<<PB2


void setup_timer0(void);
void setup_io(void);
void en_pinchange(void);
void dis_pinchange(void);
void powerDown(void);
void display(uint8_t);

uint8_t msec=0, sleep=0, mix=0, num=6, tim=0;


/* MAIN ****************** MAIN *********************************/


int main(void)
{  
  cli();    //disable interrupts globally
  
  setup_io();
  setup_timer0();
  en_pinchange();

  sei();    //enable interrupts globally
  
  PORTB &= 0b01000;

  while(1)
  {
    if(mix==1)
    {
      if(sleep<2) num=tim%6;
      else if((sleep>=2) && (sleep<4)) num=(tim/2)%6;
      else if((sleep>=4) && (sleep<5)) num=(tim/4)%6;
      else if((sleep>=5) && (sleep<6)) num=(tim/8)%6;
      
      display(num);
    }
    
    
    if(sleep>8)
    {
      num=6;
      PORTB &= 0b01000;
      
      mix=0;
      powerDown();
    }
  }
  return 0;
}

/* MAIN ENDED *************** MAIN ENDED *************************/



/* ISR RUTINES ************* INTERRUPT ***************************/


ISR(PCINT0_vect)
{
  sleep=0;
  setup_io();
  mix=1;
  dis_pinchange();
}


ISR(TIM0_COMPA_vect)
{
  msec++;
  tim++;
  if(tim>=192) tim=0;
  
  if(msec>=20)
  {
    sleep++;
    msec=0;
  }
}


/* POWER DOWN ***************************************************/


void powerDown(void)
{
  PORTB=0;                  //Set PORTB pins LOW
  TIMSK0 &= ~(1<<OCIE0A);   //Disable Timer0 OCMA interrupt
  GIFR |= (1<<PCIF);        //Clear eventually occurred previous pin change interrupts

  en_pinchange();

  MCUCR = (MCUCR | (1<<SM1)) & ~(1<<SM0);
  MCUCR |= (1<<SE);       //Enable Power-Down
  
  sleep_cpu();            //CPU sleep!
  
  MCUCR &= ~(1<<SE);      //Disable Power-Down

  sleep=0;
  TIMSK0 |= (1<<OCIE0A);  //Enable Timer0 OCMA interrupt
  setup_io();
}


/* SETUP ******************************************************/

void setup_io(void)
{
  DDRB = 0b10111;   //Set PB0,1,2,4 pins to output, PB3 to input
}

void setup_timer0(void)
{
  TCNT0 = 0;
  TCCR0A |= (1 << WGM01);     //CTC mode
  TCCR0B |= (1<<CS02) | (1<<CS00);  //Timer speed prescaler 1,2MHz/1024=1.172 kHz
  OCR0A = 58;     //interrupt every ~50ms
  TIMSK0 |= (1<<OCIE0A);            //enable Timer0 OCMA interrupt
}


/* PINCHANGE ******************************************************/

void en_pinchange(void)
{
  GIMSK |= (1<<PCIE);
  PCMSK |= (1<<PCINT3);      // Set the pinchange input to PB3/PCINT3
}

void dis_pinchange(void)
{
  GIMSK &= ~(1<<PCIE);
  PCMSK &= ~(1<<PCINT3);
}


/* Numbers Subroutines *******************************************/

void one(void)
{
  PORTB = PAT4;
  _delay_ms(1);
}

void two(void)
{
  PORTB = PAT2;
  _delay_ms(1);
}

void three(void)
{  
  PORTB = PAT2;
  _delay_ms(1);
  PORTB = PAT4;
  _delay_ms(1);
}

void four(void)
{
  PORTB = PAT1;
  _delay_ms(1);
  PORTB = PAT2;
  _delay_ms(1);
}


void five(void)
{
  PORTB = PAT1;
  _delay_ms(1);
  PORTB = PAT2;
  _delay_ms(1);
  PORTB = PAT4;
  _delay_ms(1);
}


void six(void)
{
  PORTB = PAT1;
  _delay_ms(1);
  PORTB = PAT2;
  _delay_ms(1);
  PORTB = PAT3;
  _delay_ms(1);
}



void display( uint8_t number)
{
  if(number==0) one();
  else if (number==1) two();
  else if (number==2) three();
  else if (number==3) four();
  else if (number==4) five();
  else if (number==5) six();
  else PORTB &= 0b01000;    //clear output pins
}

Youtube video:

Comments

Popular posts from this blog

How to program AVR microcontrollers (ATmega8, ATmega328p, ATtiny13, ATtiny84 ...) on Linux operating system (OpenSuse Leap 15.2)