ATtiny13

Published on 2016-11-28 in Nyan Board.

I’m still waiting for my chips, but @davedarko has a bunch of ATtiny13s handy, and he reports that there is a small problem: ATtiny13 doesn’t have Timer1. A quick look at the datasheet confirms that – so I will need to use Timer0 instead. However, there are two problems with that. First, Timer0 cannot directly control the PB4 pin that I used for the piezo on the boards, and second, its prescaler is not nearly as accurate, so I can’t use it for selecting octaves. But let’s try and see what we can do.

The first problem is simple: I undusted my old trusty time machine, went back a week, and modified the original board design, to make one of the cats (the middle right one) different from others – it has piezo on pin PB0, and separate LEDs on PB3 and PB4, so it can blink its eyes independently.

The second one is a bit worse – we will have to compromise. I’m setting the prescaler to a static value, and I’m shifting the timer counters right by the octave number – this way the higher the octave, the smaller the numbers – so higher frequency. (You may remember from your music lessons, that each octave has twice the frequency of the previous one). The problem with this is that I lose accuracy at the higher frequencies. Oh well. Also, I had to switch the chip to 1Mhz, because then I can get the prescaler at the right spot.

The end result isn’t bad, and it’s only 2 bytes larger than the ATtiny85 version (because I now blink the other eye too, otherwise it would have been smaller). Here it is:

void play(const byte *notes, byte length) {
    union {
        struct {
            unsigned int tone: 3;
            unsigned int octave: 2;
            unsigned int length: 1;
        } note;
        byte raw;
    } note;

    while (length --> 0) { // "Goes to 0" operator.
        note.raw = pgm_read_byte_near(notes++);
        if (note.note.octave) {
            TCCR0B = 2; // Pre-scaler /64
            OCR0A = pgm_read_byte_near(FREQ + note.note.tone) >> (note.note.octave - 1);
        }
        if (note.note.length) {
            _delay_ms(220);
            TCCR0B = 0;
            _delay_ms(30);
        } else {
            _delay_ms(110);
            TCCR0B = 0;
            _delay_ms(15);
        }
        PORTB ^= 1<<3 | 1<<4;
    }
}

int main() {
    DDRB = 1<<0 | 1<<3 | 1<<4; // Output pins PB0, PB3, PB4.
    TCCR0A |= 1<<COM0A0 | 1<<WGM01; // Enable output on PB0 and set CTC mode.
    PORTB ^= 1<<4; // Close one eye, so they blink one at a time.
    play(INTRO_NOTES, 26);
    while(1) {
        play(MELODY_NOTES, 216);
    }
}