blackbox/main.c

Sat, 03 Dec 2011 09:16:39 +0100

author
Malte Bayer <mbayer@neo-soft.org>
date
Sat, 03 Dec 2011 09:16:39 +0100
changeset 26
4af697fa5ea9
parent 11
69c2a1408619
child 33
c261350bb394
permissions
-rw-r--r--

added some communication definitions

#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 <util/delay.h>

#include "driver/rs232.h"
#include "driver/adc.h"

#include "main.h"
#include "lowlevel.h"

volatile uint8_t program_count = 0;
volatile uint8_t program_id;
volatile uint8_t program_command;
volatile uint8_t program_parameter;

volatile uint8_t datalen = 0;
char data[10]; // 8 bytes data buffer + string termination

static char buffer[RS232_BUFSIZE+1];
static uint8_t buffer_len;


volatile uint16_t transmit_buffer;
volatile uint16_t transmit_buffer_queue;
volatile uint8_t  transmit_len;
volatile uint8_t  transmit_len_next;
volatile uint8_t  transmit_len_queue;

volatile uint16_t response;
volatile uint8_t timer0_delay;


ISR ( USART_RXC_vect ) {
    char c = UDR;

    // check for buffer overflow
    if (buffer_len==sizeof(buffer)) {
        buffer_len=0;
    }  else {
        // collect characters until end of line
        if ( (c==0x0A) ) {
            buffer[buffer_len]=0;

            // packet end received, parse the received packet
            switch (buffer[0]) {
                case 'P': // inject a program data word to the rails
                    // TODO: at the moment only parameters 0..9 supported
                    // needs to build a "human" parser
                    if (program_count == 0) {
                        program_id = buffer[3]-'0';
                        program_command = buffer[1]-'0';
                        if (program_command > 9)
                            program_command = buffer[1]-'A'+10;
                        program_parameter = buffer[2]-'0';
                        if (program_parameter > 9)
                            program_parameter = buffer[2]-'A'+10;
                        if (program_command < 4)
                            program_count = 0x02; // send commands twice (fuel, speed, brake)
                            else
                            program_count = 0x01;
                        RS232_puts_p(PSTR("OK\n"));
                    } else RS232_puts_p(PSTR("BUSY\n"));
                    break;

            }

            // wait for the next packet
            buffer_len=0;
        } else {
            buffer[buffer_len++]=c;
        }
    }
}



int insert_queue(uint16_t tmp, uint8_t len) {
    if (transmit_buffer_queue == 0) {
        transmit_buffer_queue = tmp;
        transmit_len_queue = len;
        return 1;
    }
    return 0;
}


int do_controller(uint8_t controller) {
    // read controller X speed & encode controller data packet
    uint16_t tmp;
    switch (controller) {
        case 0:
            tmp = ((getADC(CONTROLLER1_SPEED) / CONTROLLER_DIVISOR) & 0x0F) << 1;
            if ( (PIN(CONTROLLER_PORT) & _BV(CONTROLLER1_SW)) != 0) {
                tmp |= (1<<5);
                LED(1,0);
            } else LED(1,1);
            break;
        case 1:
            tmp = ((getADC(CONTROLLER2_SPEED) / CONTROLLER_DIVISOR) & 0x0F) << 1;
            if ( (PIN(CONTROLLER_PORT) & _BV(CONTROLLER2_SW)) != 0) {
                tmp |= (1<<5);
                LED(2,0);
            } else LED(2,1);
            break;
        case 2:
            tmp = ((getADC(CONTROLLER3_SPEED) / CONTROLLER_DIVISOR) & 0x0F) << 1;
            if ( (PIN(CONTROLLER_PORT) & _BV(CONTROLLER3_SW)) != 0) {
                tmp |= (1<<5);
                LED(3,0);
            } else LED(3,1);
            break;
        case 3:
            tmp = ((getADC(CONTROLLER4_SPEED) / CONTROLLER_DIVISOR) & 0x0F) << 1;
            if ( (PIN(CONTROLLER_PORT) & _BV(CONTROLLER4_SW)) != 0) {
                tmp |= (1<<5);
                LED(4,0);
            } else LED(4,1);
            break;
        case 4: tmp = (1<<5); break; // todo regler 5
        case 5: tmp = (1<<5); break; // todo regler 6
    }
    tmp |=  0b1000000000 | (controller << 6);
    if ( (PIN(SW_FUEL_PORT) & _BV(SW_FUEL)) != 0) tmp |= 1; // benzinstand aktiv - tankmodusschalter

    return insert_queue(tmp, 9);
}

uint8_t mirror( uint8_t n ) {
    n = ((n >> 1) & 0x55) | ((n << 1) & 0xaa);
    n = ((n >> 2) & 0x33) | ((n << 2) & 0xcc);
    n = ((n >> 4) & 0x0f) | ((n << 4) & 0xf0);
    return n;
}

int do_program(uint8_t controller, uint8_t command, uint8_t parameter) {
    // send program data packet
    uint16_t tmp;
    parameter = mirror(parameter);
    controller = mirror(controller);
    command = mirror(command);
    tmp = 0b1000000000000 | (parameter << 4) | command | (controller >> 5);
    return insert_queue(tmp, 12);
}

int do_active(void) {
    // send controller active data packet
    uint16_t tmp = 0b10000000;
    if ((getADC(CONTROLLER1_SPEED) / CONTROLLER_DIVISOR) > 0) tmp |= 0b11000001;
    if ((getADC(CONTROLLER2_SPEED) / CONTROLLER_DIVISOR) > 0) tmp |= 0b10100001;
    if ((getADC(CONTROLLER3_SPEED) / CONTROLLER_DIVISOR) > 0) tmp |= 0b10010001;
    if ((getADC(CONTROLLER4_SPEED) / CONTROLLER_DIVISOR) > 0) tmp |= 0b10001001;
    // todo: regler 5 und 6
    // todo: wenn Daten enpfangen wurden hier eine Quittierung senden anstatt dem Active Word

    return insert_queue(tmp, 7);
}

int do_pace_ghost(void) {
    // send ghost and pacecar data packet
    // todo: at the moment, both disabled!
    uint16_t tmp = 0b1111100000;
    if ( (PIN(SW_FUEL_PORT) & _BV(SW_FUEL)) != 0) tmp |= 1; // benzinstand aktiv - tankmodusschalter
    // todo: PC, NH, TK, (KFR, FR)

    return insert_queue(tmp, 9);
}

ISR ( TIMER1_COMPA_vect ) {
    // trigger packet transfer:
    transmit_len = transmit_len_next;
    //LED(2,2);

    // here is some more time to do something else...
}

ISR ( TIMER2_COMP_vect ) {
    //OCR2 = TIMER2_50US; // make sure that timer2 is 50µs !!!
    // data packet timer 100µs pro bit...
    if (transmit_len >= 0xFE) {
        if (transmit_len != 0xFF) {
            RAIL_POWER_PORT |= _BV(RAIL_POWER); // end of transmission
            transmit_len = 0xFF;
            transmit_buffer = transmit_buffer_queue;
            transmit_buffer_queue = 0;
            transmit_len_next = transmit_len_queue;

            // start the response receiver timer
            // TODO: only on 8 timeslots, not on every transmission
            // TODO: give slot number to timer - then store the transmission to 8 slots array
            TCNT0 = TIMER0_250US;
            timer0_delay = TIMER0_2300NS;
            response = 0;
            TIMSK |= _BV(TOIE0);

        }
    } else {
        uint16_t bit = (1<<(transmit_len & 0b01111111));
        uint16_t clock;
        if ((transmit_len & 0b10000000) == 0) clock = 0; else clock = 0xffff;
        if ( ((transmit_buffer ^ clock) & bit) != 0 )
            RAIL_POWER_PORT |= _BV(RAIL_POWER); else
            RAIL_POWER_PORT &= ~_BV(RAIL_POWER);
        if ( (transmit_len & 0b10000000) == 0 ) {
            // block 0
            //if (transmit_len == 0) transmit_len = 0xFF; else transmit_len |= 0b10000000; // set clock
            transmit_len |= 0b10000000; // set clock
        } else {
            // block 1, output the current bit
            transmit_len &= 0b01111111; // reset clock
            //if (transmit_len != 0) transmit_len--; // next bit
            if (transmit_len == 0) transmit_len = 0xFE; else transmit_len--; // next bit
        }
    }

    //LED(3,2);
}


ISR ( TIMER0_OVF_vect ) {
// TODO: last bit should be set by the sender, not from us!
    TCNT0 = TIMER0_250US;
    //LED(1,2);
    if (timer0_delay == 0) {
        RAIL_POWER_PORT &= ~_BV(RAIL_POWER); // pull rails low
        _delay_us(28); // wait some cycles
        if ((PIN(RAIL_DETECT_PORT) & _BV(RAIL_DETECT)) != 0) { // check for logic zero
            if (response == 0) {
                // there is no start bit, so stop the timer and cancel response receiving
                TIMSK &= ~_BV(TOIE0);
            } else {
                // we received a bit (logic low)
                response = response << 1;
            }
        } else {
            // okay, we have logic high
            response = response << 1;
            response |= 1;
            //debug:
            RS232_puts("ANSWER RX\n");
        }
        _delay_us(20); // wait some cycles
        RAIL_POWER_PORT |= _BV(RAIL_POWER); // restore rails power
    } else timer0_delay--; // 2.3 ms delay not reached yet
}

ISR (INT2_vect) {
    // Lap counter Interrupt
    LED(5,2);
}


int main(void)
{

    unsigned char s[30];
    uint16_t tmp;
    uint8_t  packet_index = 1;

    uint8_t  btn_start = _BV(SW_START);
    uint8_t  old_start = btn_start;
    uint8_t  mode = 0;
    // valid race modes:
    // 0: free drive
    // 1: race countdown initiated
    // 2: countdown 5
    // 3: countdown 4
    // 4: countdown 3
    // 5: countdown 2
    // 6: countdown 1
    // 7: race start initiated (next mode will be zero = free drive)



    init_hardware();

    // switch on rails power
    RAIL_POWER_PORT |= _BV(RAIL_POWER);

    while (1) {
        // check for short circuit on the rails
        check_rails_shortcut();

        // read in button presses
        btn_start = (PIN(SW_START_PORT) & _BV(SW_START));
        if (old_start != btn_start) {
            // start button changed
            if (btn_start == 0) {
                // start button press active
                if (mode == 0) {
                    // Initiate race countdown
                    mode = 0; // todo: set to 1
                    // issue reset command to lap counter
                    program_command = 6;
                    program_parameter = 9;
                    program_id = 0;
                    program_count = 1;
                    LAP_COUNTER_PORT |= _BV(LAP_COUNTER);
                } else {
                    // do a reset, switch back to free drive mode 0
                    mode = 0;
                    // issue reset command
                    program_command = 19;
                    program_parameter = 0;
                    program_id = 7;
                    program_count = 1;
                    LAP_COUNTER_PORT &= ~_BV(LAP_COUNTER);
                }
            }
            //LAP_COUNTER_PORT |= _BV(LAP_COUNTER);
        } else old_start = btn_start;
        // LAP_COUNTER_PORT &= ~_BV(LAP_COUNTER);

        switch (packet_index) {
            case 1:
                if (program_count > 0) {
                    // command in queue
                    if (do_program(program_id, program_command, program_parameter)) {
                        packet_index++;
                        program_count--;
                    }
                } else {
                    // output idle command
                    if (do_program(7, 19, 0)) packet_index++; // reset
                    //if (do_program(7, 20, 15)) packet_index++; // reset / pitstop detect
                    //if (insert_queue(0, 0)) packet_index++; // null command
                }
                break;
            case 2:
                if (do_pace_ghost()) packet_index++;
                break;
            case 3:
                if (do_active()) packet_index++;
                break;
            case 4:
                if (do_controller(0)) packet_index++;
                break;
            case 5:
                if (do_controller(4)) packet_index++;
                break;
            case 6:
                if (do_controller(1)) packet_index++;
                break;
            case 7:
                if (do_controller(5)) packet_index++;
                break;
            case 8:
                if (do_controller(2)) packet_index++;
                break;
            case 9:
                if (do_active()) packet_index++;
                break;
            case 10:
                if (do_controller(3)) packet_index = 1;
                // last packet, so reset packet index
                break;
        }


    } // main loop end
};

mercurial