Subversion Repositories Code-Repo

Rev

Rev 277 | Blame | Compare with Previous | Last modification | View Log | RSS feed

// <editor-fold defaultstate="collapsed" desc="Configuration Bits">
// PIC16F1825 Configuration Bit Settings

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT software controlled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON       // PLL Enable (4x PLL enabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
// </editor-fold>

#include "defines.h"
#include "INTERRUPTS.h"
#include "IO.h"
#include "I2C1.h"
#include "I2C2.h"
#include "CPS.h"
#include "TLC59116.h"
#include "MCP23009.h"

persistent uint8_t op_state;

void Reset_Board(uint8_t next_state) {
    op_state = next_state;
    RESET();
}

uint8_t Get_Last_Reset(void) {
    uint8_t ret;
    if (PCONbits.nPOR == 0) {
        ret = RESET_POR;
    } else if (PCONbits.nBOR == 0) {
        ret = RESET_BOR;
    } else if (STATUSbits.nTO == 0) {
        ret = RESET_WDT;
    } else if (PCONbits.nRMCLR == 0) {
        ret = RESET_MCLR;
    } else if (PCONbits.STKOVF || PCONbits.STKUNF) {
        ret = RESET_STK;
    } else if (PCONbits.nRI == 0) {
        ret = RESET_RST;
    } else {
        ret = RESET_POR;
    }

    PCON = 0x0F;
    STATUSbits.nPD = 1;
    STATUSbits.nTO = 1;
    return ret;
}

uint8_t Read_Address(void) {
    uint8_t ret = I2C1_SLAVE_PREFIX;
    if (!I2C_ADDR_3_PORT)
        ret |= 0x08;
    if (!I2C_ADDR_2_PORT)
        ret |= 0x04;
    if (!I2C_ADDR_1_PORT)
        ret |= 0x02;
    if (!I2C_ADDR_0_PORT)
        ret |= 0x01;

    return ret;
}

BTN_STATUS btns;
LED_VALUES leds = {0x00};

int main(void) {
    uint8_t buffer[32];
    uint8_t result, length;
    uint8_t i2c_slave_addr;
//    uint8_t op_state;
    uint8_t i;

    // Set internal oscillator speed to 32MHz
    OSCCONbits.SPLLEN = 1;  // 4x PLL enable (overwritten by config bits)
    OSCCONbits.IRCF = 0xE;  // Base frequency @ 8MHz
    OSCCONbits.SCS = 0b00;  // System clock determined by config bits

    // Initialize I/O
    IO_Init();

    uint8_t last_reset = Get_Last_Reset();
    if (last_reset == RESET_POR || last_reset == RESET_BOR ||
            last_reset == RESET_MCLR || last_reset == RESET_WDT ||
            last_reset == RESET_STK) {
        op_state = OP_STATE_IDLE;
    }
//    if (last_reset == RESET_RST) {
//        op_state = OP_STATE_ACTIVE;
//    }

    i2c_slave_addr = Read_Address();

    // Delay a bit to allow I2C lines to stabilize
    __delay_ms(10);

    // Initialize I2C1 in slave mode
    I2C1_DATA i2c1_data;
    I2C1_Init(&i2c1_data);
    I2C1_Configure_Slave(i2c_slave_addr);

    // Initialize I2C2 in master mode
    I2C2_DATA i2c2_data;
    I2C2_Init(&i2c2_data);
    I2C2_Configure_Master(I2C_1MHZ);

    CPS_DATA cps_data;
    CPS_Init(&cps_data);

    // Initialize interrupts
    Interrupt_Init();
    Interrupt_Enable();

    MCP23009_Init(&btns);
    MCP23009_Query();

    TLC59116_Init();
//    TLC59116_Write_All(&leds);

    if (op_state == OP_STATE_IDLE) {
//        IO_IOC_Enable();
        Idle_Animation();
    } else if (op_state == OP_STATE_ACTIVE) {
        uint8_t button_R = 0, button_L = 0;
        uint8_t dir_state = DIR_S;
        leds.single.LED_S = DIR_BRIGHTNESS;
        TLC59116_Write_All(&leds);
        while (1) {
            // Check if an I2C message was received
            do {
                result = I2C1_Get_Status();
            } while (!result);
            length = I2C1_Read_Buffer(buffer);
            if (length == 1 && buffer[0] == CMD_RESET) {
                Reset_Board(OP_STATE_IDLE);
            } else if (length == 1 && buffer[0] == CMD_ACTIVE) {
                Reset_Board(OP_STATE_ACTIVE);
            } else if (length == 17 && buffer[0] == CMD_SET_LEDS) {
                for (i = 0; i < 16; i++) {
                    leds.w[i] = buffer[i + 1];
                }
                // Reset the direction LEDs
                if (dir_state == DIR_S)
                    leds.single.LED_S = DIR_BRIGHTNESS;
                else if (dir_state == DIR_W)
                    leds.single.LED_W = DIR_BRIGHTNESS;
                else if (dir_state == DIR_N)
                    leds.single.LED_N = DIR_BRIGHTNESS;
                else if (dir_state == DIR_E)
                    leds.single.LED_E = DIR_BRIGHTNESS;
                TLC59116_Write_All(&leds);
            }
            

            // Check if either capacitive buttons were pressed
            if (cps_data.btn_pressed[0] && button_R == 0) {
                // Right button went from unpressed -> pressed
                dir_state = Direction_Rotate_CClockwise(dir_state);
                TLC59116_Write_All(&leds);
            }
            if (cps_data.btn_pressed[1] && button_L == 0) {
                // Left button went from unpressed -> pressed
                dir_state = Direction_Rotate_Clockwise(dir_state);
                TLC59116_Write_All(&leds);
            }
            // Save the previous button state
            button_R = cps_data.btn_pressed[0];
            button_L = cps_data.btn_pressed[1];

            // Poll the hardware buttons
            I2C2_Master_Restart(MCP23009_ADDR, MCP23009_GPIOA, 1);
            do {
                result = I2C2_Get_Status();
            } while (!result);
            length = I2C2_Read_Buffer(buffer);
            btns.w = buffer[0];
            // Change the direction according to the user's perspective
            if (dir_state == DIR_W) {
                uint8_t tmp = btns.BTN_R_S;
                btns.BTN_R_S = btns.BTN_R_W;
                btns.BTN_R_W = btns.BTN_R_N;
                btns.BTN_R_N = btns.BTN_R_E;
                btns.BTN_R_E = tmp;
            } else if (dir_state == DIR_E) {
                uint8_t tmp = btns.BTN_R_S;
                btns.BTN_R_S = btns.BTN_R_E;
                btns.BTN_R_E = btns.BTN_R_N;
                btns.BTN_R_N = btns.BTN_R_W;
                btns.BTN_R_W = tmp;
            } else if (dir_state == DIR_N) {
                uint8_t tmp = btns.BTN_R_S;
                btns.BTN_R_S = btns.BTN_R_N;
                btns.BTN_R_N = tmp;
                tmp = btns.BTN_R_E;
                btns.BTN_R_E = btns.BTN_R_W;
                btns.BTN_R_W = tmp;
            }
        }
    }
}

uint8_t Direction_Rotate_Clockwise(uint8_t dir_state) {
    uint8_t ret;
    switch (dir_state) {
        case DIR_S:
            leds.single.LED_S = 0x00;
            leds.single.LED_W = DIR_BRIGHTNESS;
            ret = DIR_W;
            break;
        case DIR_W:
            leds.single.LED_W = 0x00;
            leds.single.LED_N = DIR_BRIGHTNESS;
            ret = DIR_N;
            break;
        case DIR_N:
            leds.single.LED_N = 0x00;
            leds.single.LED_E = DIR_BRIGHTNESS;
            ret = DIR_E;
            break;
        case DIR_E:
            leds.single.LED_E = 0x00;
            leds.single.LED_S = DIR_BRIGHTNESS;
            ret = DIR_S;
            break;
    }
    return ret;
}

uint8_t Direction_Rotate_CClockwise(uint8_t dir_state) {
    uint8_t ret;
    switch (dir_state) {
        case DIR_S:
            leds.single.LED_S = 0x00;
            leds.single.LED_E = DIR_BRIGHTNESS;
            ret = DIR_E;
            break;
        case DIR_E:
            leds.single.LED_E = 0x00;
            leds.single.LED_N = DIR_BRIGHTNESS;
            ret = DIR_N;
            break;
        case DIR_N:
            leds.single.LED_N = 0x00;
            leds.single.LED_W = DIR_BRIGHTNESS;
            ret = DIR_W;
            break;
        case DIR_W:
            leds.single.LED_W = 0x00;
            leds.single.LED_S = DIR_BRIGHTNESS;
            ret = DIR_S;
            break;
    }
    return ret;
}

void Check_I2C_Idle(void) {
    uint8_t buffer[32];
    uint8_t result, length, i;

    // Check if an I2C message was received
    result = I2C1_Get_Status();
    if (result) {
        length = I2C1_Read_Buffer(buffer);
        if (length == 1 && buffer[0] == CMD_RESET) {
            Reset_Board(OP_STATE_IDLE);
        } else if (length == 1 && buffer[0] == CMD_ACTIVE) {
            Reset_Board(OP_STATE_ACTIVE);
        }
    }
}

void Idle_Animation(void) {
    LED_VALUES leds = {0};
    uint8_t led_direction_bar[8] = {1,0,0,0,0,0,0,0};
    uint8_t led_direction_dir[8] = {1,0,0,0};
    uint8_t led_direction_ind[8] = {1,0,0,0};
    uint8_t led_8_high_thres = 0x80;    // Max brightness of the middle section
    uint8_t led_8_next_thresh = 0x40;   // Threshold to start the next LED
    uint8_t led_4_high_thres = 0x80;    // Max brightness of the side sections
    uint8_t led_4_next_thresh = 0x74;   // Threshold to start the next LED
    uint8_t i, next_led;

    for (i = 0; i < 16; i++) leds.w[i] = 0x00;
    
    while (1) {

        // Check to see if a new message was received
        Check_I2C_Idle();

        // Update the LEDs in the middle section
        for (i = 0; i < 8; i++) {
            // Change the LED brightness depending on its direction
            if (led_direction_bar[i] == 1) {
                leds.w[i]++;
            } else if (led_direction_bar[i] == 2) {
                leds.w[i]--;
            }

            // Change direction if peak brightness is reached
            // When the brightness reaches a middle threshold, start
            //   increasing the brightness of the next LED
            if (led_direction_bar[i] == 1 && leds.w[i] == led_8_high_thres) {
                led_direction_bar[i] = 2;
            } else if (led_direction_bar[i] == 1 && leds.w[i] == led_8_next_thresh) {
                next_led = (i == 7) ? 0 : i + 1;
                led_direction_bar[next_led] = 1;
            } else if (led_direction_bar[i] == 2 && leds.w[i] == 0x00) {
                led_direction_bar[i] = 0;
            }
        }

        // Update the LEDs in the right section
        for (i = 0; i < 4; i++) {
            // Change the LED brightness depending on its direction
            if (led_direction_dir[i] == 1) {
                leds.w[i+8]++;
            } else if (led_direction_dir[i] == 2) {
                leds.w[i+8]--;
            }

            // Change direction if peak brightness is reached
            // When the brightness reaches a middle threshold, start
            //   increasing the brightness of the next LED
            if (led_direction_dir[i] == 1 && leds.w[i+8] == led_4_high_thres) {
                led_direction_dir[i] = 2;
            } else if (led_direction_dir[i] == 1 && leds.w[i+8] == led_4_next_thresh) {
                next_led = (i == 3) ? 0 : i + 1;
                led_direction_dir[next_led] = 1;
            } else if (led_direction_dir[i] == 2 && leds.w[i+8] == 0x00) {
                led_direction_dir[i] = 0;
            }
        }

        // Update the LEDs in the left section
        for (i = 0; i < 4; i++) {
            // Change the LED brightness depending on its direction
            if (led_direction_ind[i] == 1) {
                leds.w[i+12]++;
            } else if (led_direction_ind[i] == 2) {
                leds.w[i+12]--;
            }

            // Change direction if peak brightness is reached
            // When the brightness reaches a middle threshold, start
            //   increasing the brightness of the next LED
            if (led_direction_ind[i] == 1 && leds.w[i+12] == led_4_high_thres) {
                led_direction_ind[i] = 2;
            } else if (led_direction_ind[i] == 1 && leds.w[i+12] == led_4_next_thresh) {
                next_led = (i == 3) ? 0 : i + 1;
                led_direction_ind[next_led] = 1;
            } else if (led_direction_ind[i] == 2 && leds.w[i+12] == 0x00) {
                led_direction_ind[i] = 0;
            }
        }

        // Write the LED values to the controller
        TLC59116_Write_All(&leds);

        // Delay a bit to slow down the animation
        __delay_ms(1);
    }
}