Shrinking

Published on 2016-11-26 in Mechatronic Ears.

OK, now that I’ve shrunk the code for Nyan Board to 470 bytes, it’s time to look at the code for the ears. This time I’m going to got straight into plain C. All I need to do is to figure out how to do analogRead()… Turns out it’s super-simple!

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


#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))


int16_t adc(int8_t pin) {
    ADMUX = 0<<ADLAR | 0<<REFS1 | 0<<REFS0 | pin & 0x03;
    ADCSRA |= 1<<ADSC;
    while (ADCSRA & 1<<ADSC) {
        // Pass.
    }
    return ADC - 512;
}

void update_servos(uint8_t left_position, uint8_t right_position) {
    static uint8_t current_left = 0;
    static uint8_t current_right = 0;

    uint8_t left = 17 + MIN(left_position, 15);
    if (current_left < left) {
        ++current_left;
        OCR0A = current_left;
    } else if (current_left > left) {
        --current_left;
        OCR0A = current_left;
    } else {
        OCR0A = 255;
    }
    uint8_t right = 33 - MIN(right_position, 15);
    if (current_right < right) {
        ++current_right;
        OCR0B = current_right;
    } else if (current_right > right) {
        --current_right;
        OCR0B = current_right;
    } else {
        OCR0B = 255;
    }
}

int running_average(int16_t *buffer, uint8_t *cursor,
                    int16_t *total, int16_t value) {
    *total -= buffer[*cursor];
    buffer[*cursor] = value;
    *total += value;
    *cursor = (*cursor + 1) & 0x0F;
    return *total >> 4;
}

int main () {
    static uint8_t left = 7;
    static uint8_t right = 7;
    static int16_t x_buffer[16] = {};
    static int16_t y_buffer[16] = {};
    static int16_t z_buffer[16] = {};
    static uint8_t x_cursor = 0;
    static uint8_t y_cursor = 0;
    static uint8_t z_cursor = 0;
    static int16_t x_total = 0;
    static int16_t y_total = 0;
    static int16_t z_total = 0;

    // Init ADC.
    ADCSRA = 1<<ADEN | 1<<ADPS2 | 1<<ADPS1 | 1<<ADPS0; // Pre-scaler 128.

    // Output pins PB0 and PB1.
    DDRB = 1<<0 | 1<<1;

    // Setup the PWM clock to ~62.5Hz for the servos.
    TCCR0A = 2<<COM0A0 | 2<<COM0B0 | 3<<WGM00;
    TCCR0B = 0<<WGM02 | 1<<CS00 | 1<<CS01 | 0<<CS02;

    OCR0A = 0;
    OCR0B = 0;

    while (1) {
        int16_t z = running_average(x_buffer, &x_cursor, &x_total, adc(1) - 16);
        int16_t y = running_average(y_buffer, &y_cursor, &y_total, adc(2));
        int16_t x = running_average(z_buffer, &z_cursor, &z_total, adc(3));

        if (x > 20) {
            left = 15;
            right = 15;
        } else if (x < -20) {
            left = 0;
            right = 0;
        } else if (y > 20) {
            left = 15;
            right = 0;
        } else if (y < -20) {
            left = 0;
            right = 15;
        } else {
            left = 7;
            right = 7;
        }

        update_servos(left, right);
        _delay_ms(60);
    }
}

The new code is not as minimalistic as in the case of Nyan Board – in particular, I’m using much more memory here, for all the running average buffers – but it’s nice and small.

AVR Memory Usage
----------------
Device: attiny85

Program:     484 bytes (5.9% Full)
(.text + .data + .bootloader)

Data:        109 bytes (21.3% Full)
(.data + .bss + .noinit)

\