car004f/main.c

Fri, 27 Dec 2013 11:43:40 +0100

author
mbayer
date
Fri, 27 Dec 2013 11:43:40 +0100
changeset 153
80d869ac365b
parent 152
e787f47c084b
child 154
46822de910ff
permissions
-rw-r--r--

added accel and brake parameters to car firmware

#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>
#include <stdlib.h>
#include <stdint.h>
#include <avr/pgmspace.h>

#include "main.h"
#include "util/delay.h"


ISR ( USART_RXC_vect ) {
}

#define PULSE_PORT      PORTD
#define PULSE_BIT       PD2

typedef struct  {
    uint8_t calibration; // AVR Chip calibration byte written by avrdude
    uint8_t initialized; // if 0xff, reset config to defaults on first boot
    uint8_t slot;
    uint8_t light;
    uint8_t brake;
    uint8_t accel;
    uint8_t program; // 0xff = inactive ; programming mode active on slot X
} config_t;
config_t EEMEM eeconfig = {0,0xff,0,0,15,15,0};
config_t config;

volatile uint16_t data = 0;
volatile uint8_t data_len = 0;
volatile uint8_t bitbuf_len = 0;
volatile uint16_t bitbuf = 0;
volatile uint8_t car_speed[MAX_SLOTS];
volatile uint8_t car_switch[MAX_SLOTS];
volatile uint8_t car_act[MAX_SLOTS];

volatile uint8_t car_timeout[MAX_SLOTS];
volatile uint8_t timeout = 0;
volatile uint8_t brake_timeout = 0;

uint8_t old_switch[MAX_SLOTS];

uint8_t my_switch;
uint8_t my_speed;

ISR ( INT0_vect ) {
    GICR &= ~_BV(INT0) ; // Disable INT0
    // Startsignal erkannt, ab hier den Timer2 starten,
    // der liest dann alle 50µs den Zustand ein und schreibt das
    // empfangene Bit in den Puffer
    bitbuf = 0; // init
    bitbuf_len = 0b10000000; // init 1 pulse received

    //TCNT2 = 10;
    TCNT2 = 9;
    TIMSK |= _BV(OCIE2); //enable timer2 interrupt
}

ISR ( TIMER2_COMP_vect ) {
//    TCNT2 = 0;
    uint8_t clock;
    uint8_t state;
    uint8_t state2;
    if ((bitbuf_len & 0b10000000) == 0) clock = 0; else clock = 0xff;
    if ((bitbuf_len & 0b01000000) == 0) state = 0; else state = 0xff;
    if ((PIN(PULSE_PORT) & _BV(PULSE_BIT)) == 0) state2 = 0xff; else state2 = 0;

    if (clock) {
        bitbuf_len &= ~_BV(7); // switch clock to low
        // second pulse of bit
        if ((state==state2) & state2) {
            TIMSK &= ~_BV(OCIE2); //disable timer2 interrupt

            // two cycles high: packet end received
            data_len = (bitbuf_len & 0b00111111);

            //data = bitbuf; // output data
            // write data of controllers to array
            if (data_len == 10) { // controller data packet
                clock = (bitbuf >> 6) & 0b00000111;
                car_speed[clock] = (bitbuf >> 1) & 0x0F;
                car_switch[clock] = (bitbuf >> 5) & 1;
                // current response for this car?
                /*
                if (response != 0) {
                    if ( ((response & 0b00001110) >> 1) == clock) {
                        // add our ID to response:
                        send_response(response | self_id << 6);
                        response = 0;
                    }
                }
                */
            }

            GICR |= _BV(INT0) ; // Enable INT0

        } else {
            bitbuf_len++; // increment bit counter
            bitbuf = bitbuf << 1; // shift bits
            if (state2 == 0) bitbuf |= 1; // receive logic one
        }
    } else {
        bitbuf_len |= _BV(7); // switch clock to high
        // first pulse of bit
        if (state2) {
            bitbuf_len |= _BV(6); // store new state
        } else {
            bitbuf_len &= ~_BV(6); // store new state
        }
    }
}

ISR (INT1_vect) {
}

ISR (TIMER0_OVF_vect) {
    TCNT0  = 100;            // TIMER0 vorladen mit 100
    if (brake_timeout > 1) brake_timeout--;
    if (timeout > 1) timeout--;
    for (uint8_t i=0; i<MAX_SLOTS; i++)
        if (car_timeout[i] > 1) car_timeout[i]--;
}

#define LIGHT_PORT      PORTC
#define LIGHT_FRONT     2
#define LIGHT_BRAKE     4

#define IR_PORT         PORTB
#define IR_LED          3

#define LIGHT_MODES             1       // anzahl der lichtmodi (ohne den modus "aus")
#define BRAKE_OFF_TIMEOUT       60      // value * 10ms

//#define CAR_DEBUG       1
#define EE_CONFIG_ADDR  64
#define DOUBLE_CLICK_TIMEOUT    50 // 500ms

void config_save(void) {
    eeprom_write_block( &config, &eeconfig, sizeof(config_t) );
}


void brake_on(void) {
    OCR1A = (int) ((float)0xff * (float)((float)config.brake / (float)15));
    LIGHT_PORT |= _BV(LIGHT_BRAKE); // brake light on
    DDRB |= _BV(1);                  // PB1 PWM Output enable
    brake_timeout = BRAKE_OFF_TIMEOUT;
}

void brake_off(void) {
    OCR1A = 0;
    LIGHT_PORT &= ~_BV(LIGHT_BRAKE); // brake light off
    DDRB &= ~_BV(1);                  // PB1 PWM Output disable
    brake_timeout = 0;
}

uint8_t set_id(void) {
    _delay_ms(100); // short wait
    uint8_t temp;
    timeout = 1;
    brake_timeout = 0xff;
    // wait for key press and assign on double click
    while ((car_speed[config.slot] == 0) && (brake_timeout > 1)) {
        temp = car_switch[config.program];
        if (temp == 0) {
            // wait for second key press within timeout period to assign successfully
            brake_timeout = DOUBLE_CLICK_TIMEOUT;
            timeout = 1;
            while (brake_timeout > 1) {
                if (temp != car_switch[config.program]) {
                    temp = car_switch[config.program];
                    if (temp == 0) {
                        config.slot = config.program;
                        return 1;
                    }
                }
                // toggle lights if timeout
                if (timeout == 1) {
                    LIGHT_PORT ^= _BV(LIGHT_FRONT);
                    timeout = 5;
                }
            }
            return 0;
        }

        // toggle lights if timeout
        if (timeout == 1) {
            LIGHT_PORT ^= _BV(LIGHT_FRONT);
            timeout = 10;
        }
    }
    return 0;
}

int main(void)
{
    // config (from eeprom!)
    eeprom_read_block( &config, &eeconfig, sizeof(config_t) );

    // set the internal calibration byte
    OSCCAL = config.calibration;
    // TODO: Vielleicht den internen Takt des AVR anhand der Bitclock auf den Schienen synchronisieren???
    // Das Calibration byte scheint nicht zu stimmen

    if (config.initialized == 0xff) {
        config.slot = 0;
        config.light = 0;
        config.brake = 15;
        config.accel = 15;
        config.program = 0xff;
        config.initialized = 0;
        config_save();
    }

    uint8_t temp;

    // setup data bit timer2
    TCCR2 = (1<<CS21) | (1<<WGM21); //divide by 8, set compare match
    OCR2 = TIMER2_50US;


    // enable both external interrupts
    // int 0 = data RX
    MCUCR = _BV(ISC00) | _BV(ISC01) | _BV(ISC10) | _BV(ISC11); // INT0/1 rising edge
    GICR = _BV(INT0) | _BV(INT1) ; // Enable INT0 + INT1


    DDR(LIGHT_PORT) |= _BV(LIGHT_FRONT) | _BV(LIGHT_BRAKE);

    TCCR1A = (1<<WGM10)|(1<<COM1A1)   // Set up the two Control registers of Timer1.
            |(1<<COM1B1);             // Wave Form Generation is Fast PWM 8 Bit,

    TCCR1B = (1<<WGM12)|(1<<CS10);     // OC1A and OC1B are cleared on compare match
                                       // and set at BOTTOM. Clock Prescaler is 1.


    //OCR1A = 63;                       // Dutycycle of OC1A = 25%
    //OCR1B = 127;                      // Dutycycle of OC1B = 50%
    OCR1A = 0; // brake PWM!
    OCR1B = 0; // Motor drive PWM
    DDRB &= ~_BV(2);                  // PB2 PWM Output disable
    DDRB &= ~_BV(1);                  // PB1 PWM Output disable

    // configure TIMER0 to overflow every 10ms at 4 MHz
    TIMSK = _BV(TOIE0);         // Timer0 Overflow INT erlauben
    TCNT0  = 100;            // TIMER0 vorladen mit 100
    TCCR0 = _BV(CS02) ;         // Vorteiler auf 256, ab hier läuft der TIMER0

    sei();


    if ((config.program != 0xff) || (config.slot > 5 )) {
        temp = set_id();
        config.program = 0xff;
        config_save();
        if (temp == 1) {
            // acknowledge with the engine
            OCR1B = 25;
            DDRB &= ~_BV(2);                  // PB2 PWM Output disable
            for (temp = 128; temp > 0; temp--) {
                DDRB ^= _BV(2);                   // PB2 PWM Output toggle
                _delay_ms(2);
                DDRB ^= _BV(2);                   // PB2 PWM Output toggle
                _delay_ms(3);
            }

        }
        timeout = 0;
    }


    float my_accel;
    my_switch = car_switch[config.slot]; // initialize
    my_speed = car_speed[config.slot]; // initialize
    my_accel =  (float)config.accel / (float)15;

    while (1) {
        // main loop

        if (brake_timeout == 1) brake_off();

        if (my_speed != car_speed[config.slot]) {
            my_speed = car_speed[config.slot];
            OCR1B = (int) ((float)0xff * (float)((float)my_speed / (float)15) * my_accel );
            if (my_speed == 0) {
                DDRB &= ~_BV(2);                  // PB2 PWM Output disable
                brake_on();
            } else {
                brake_off();
                DDRB |= _BV(2);                   // PB2 PWM Output enable
            }
        }

        // Light cycle if switch pressed without speed
        if (car_speed[config.slot] == 0) {
            if (my_switch != car_switch[config.slot]) {
                my_switch = car_switch[config.slot];
                if (my_switch == 0) {
                    // cycle light
                    if (config.light >= LIGHT_MODES) config.light = 0; else config.light++;
                    config_save();
                }
            }
        }

        // check any car switch for a double click and speed = 0
        for (temp = 0; temp<MAX_SLOTS; temp++) if (car_switch[temp] != old_switch[temp]) {
            old_switch[temp] = car_switch[temp];
            if ((car_speed[temp] == 0) && (old_switch[temp] == 0)) {
                // key pressed
                if (car_timeout[temp] > 1) {
                    // second key press within timeout, enter program mode for this key
                    config.program = temp;
                    config_save();
                    car_timeout[temp] = 0xff; // the car has to be reset within this timeout
                } else {
                    car_timeout[temp] = DOUBLE_CLICK_TIMEOUT;
                }
            }

            if (car_timeout[temp] == 1) {
                if (config.program == temp)  {
                    // cancel ID programming mode
                    config.program = 0xff;
                    config_save();
                }
                car_timeout[temp] = 0;
            }
        }

        switch (config.light) {
            case 0:
                LIGHT_PORT &= ~_BV(LIGHT_FRONT); // switch lights off
                break;
            case 1:
                LIGHT_PORT |= _BV(LIGHT_FRONT); // switch lights on
                break;
        }


        // timeout reset
        if (timeout == 1) timeout = 0;
    } // main loop end
};

mercurial