Code to run on the charger board (used to charge the car from the mains).

Dependencies:   mbed CUER_CAN

charger.cpp

Committer:
DasSidG
Date:
2017-07-27
Revision:
4:f6459580c312
Parent:
3:a7626dffb64a
Child:
5:756fae795d37

File content as of revision 4:f6459580c312:

#include "charger.h"
#include "mbed.h"
#include "CAN_Data.h"
#include "CAN_IDs.h"
#include "Data_types.h"
#include "CANParserCharger.h"

//TEST PUBLISH
using namespace CAN_IDs;

void car_interruptHandler();
void car_CANDataSentCallback();

void charger_interruptHandler();
void charger_CANDataSentCallback();

void init();
void get_CAN_data();

void calculate_current(float voltage_error, float temp_margin, float &current);
void check_timeouts();
void update_LEDS();
bool idAccepted(int id);



timeouts_t timeouts;

//CAN stuff. Not that there are two sets because there are two separate CAN buses here; one to communicate with the charger, and one to communicate with the rest of the car (importantly the BMS).
//The reason that there are two separate CAN buses is because the charger uses the extended CAN frame format, with a 29 bit ID, whereas the car uses the default 11 bit ID.
CAN car_can(CAR_CAN_READ_PIN, CAR_CAN_WRITE_PIN); //Create a CAN object to handle CAN comms
CANMessage car_buffer[CAN_BUFFER_SIZE]; //CAN receive buffer
bool car_safe_to_write[CAN_BUFFER_SIZE]; //Semaphore bit indicating that it's safe to write to the software buffer
bool car_CAN_data_sent = false;

CAN charger_can(CHARGER_CAN_READ_PIN, CHARGER_CAN_WRITE_PIN); //Create a CAN object to handle CAN comms
CANMessage charger_buffer[CAN_BUFFER_SIZE]; //CAN receive buffer
bool charger_safe_to_write[CAN_BUFFER_SIZE]; //Semaphore bit indicating that it's safe to write to the software buffer
bool charger_CAN_data_sent = false;

int main() {
    
    init();
    
    while(1) {
        
        //Reset error indicators
        comms_timeout = false;
        charger_failure = false;
        bms_error = false;
        
        //get the various data from the CAN packets
        get_CAN_data();

        check_timeouts();
        update_LEDS();
        
        if (min_cell_voltage > RISING_BALANCE_THRESHOLD || charge_finished) { 
            charge_finished = true;
            printf("Charge Finished\r\n");
            car_can.write(generate_charging_finished_msg());
            charger_control = 0; //set charger control bit to stop charging
        } 
        else {
            calculate_current(voltage_error, temp_margin, desired_current);
            desired_voltage = MAX_VOLTAGE;
            charge_finished = false;
            charger_control = 1; //set charger control bit to start charging
        }
        
        //send CAN data
        
        Timer t;
        t.start();
        charger_CAN_data_sent = false;
        charger_can.write(generate_charger_control_msg(desired_voltage, desired_current, charger_control)); //control message to charger
        while(!charger_CAN_data_sent && t.read_ms() < CAN_TIMEOUT_MS);
        
        t.reset();
        car_CAN_data_sent = false;
        car_can.write(generate_charger_info_msg(charger_voltage, charger_current, charger_status)); //charger info message for rest of car
        while(!car_CAN_data_sent && t.read_ms() < CAN_TIMEOUT_MS);
        
        printf("Voltage Error = %f\n", voltage_error);
        printf("Temperature Margin = %f\n", temp_margin);
        printf("Desired Voltage = %f\n", desired_voltage);
        printf("Desired Current = %f\n", desired_current);
        printf("Charger voltage = %f\n", charger_voltage);
        printf("Charger current = %f\n", charger_current);
        printf("Min cell voltage = %f\n", min_cell_voltage);
        printf("Max cell voltage = %f\n", max_cell_voltage);
        
    }
}

void calculate_current(float voltage_error, float temp_margin, float &current){
        
    float Idot, I;
    static bool balancing = false;
    I = current;
    if (I < 800 && voltage_error < 100 || balancing) {
            balancing = true;
            printf("balancing\r\n");
            Idot = voltage_error*KI_BALANCE;
        } 
    else {
        Idot = voltage_error*KI_CHARGE;
    }
    I += Idot*TIME_STEP/1000.0;

    if(I > MAX_CURRENT) {
        I = MAX_CURRENT;
    }
    if(I < 0) {
        I = 0;
    }
    /*
    //Reduce current if temperature is too high
    if (temp_margin > TEMP_RAMP_START) {
        I = 1 - ((temp_margin - TEMP_RAMP_START) / (TEMP_RAMP_FINISH - TEMP_RAMP_START));
    }
    if (temp_margin > TEMP_RAMP_FINISH) {
        I *= 0;
    }
    */
    current = I;
}

void init()
{    
    for(int i=0; i<CAN_BUFFER_SIZE; i++) 
    {
        car_buffer[i].id = BLANK_ID;
        //("%d",buffer[i].id);
        car_safe_to_write[i]= true;
        
        charger_buffer[i].id = BLANK_ID;
        //("%d",buffer[i].id);
        charger_safe_to_write[i]= true;
    }
    
    //Initialise CAN stuff, attach CAN interrupt handlers
    car_can.frequency(CAN_BIT_RATE); //set transmission rate to agreed bit rate
    car_can.reset(); 
    car_can.attach(&car_interruptHandler, CAN::RxIrq); //receive interrupt handler
    car_can.attach(&car_CANDataSentCallback, CAN::TxIrq); //send interrupt handler
    
    charger_can.frequency(CHARGER_CAN_BIT_RATE); //set transmission rate to agreed bit rate
    charger_can.reset();
    charger_can.attach(&charger_interruptHandler, CAN::RxIrq); //receive interrupt handler
    charger_can.attach(&charger_CANDataSentCallback, CAN::TxIrq); //send interrupt handler
    
    //Start comms timeout timers
    
    timeouts.BMS_timeout.start();
    timeouts.charger_timeout.start();

}

void car_CANDataSentCallback(void) {
    car_CAN_data_sent = true;
}

void charger_CANDataSentCallback(void) {
    charger_CAN_data_sent = true;
}

void car_interruptHandler()
{
    CANMessage msg;
    car_can.read(msg);
    //if(DEBUG) printf("id %d incoming \r\n", msg.id);
    if(idAccepted(msg.id)) {
        for(int i=0; i<CAN_BUFFER_SIZE; i++) {
            if((car_buffer[i].id == msg.id || car_buffer[i].id==BLANK_ID) && car_safe_to_write[i]) {
                //("id %d added to buffer \r\n", msg.id);
                car_buffer[i] = msg;
                //return required so that only first blank buffer entry is converted to incoming message ID each time new message ID is encountered
                return;
            }
        }
    }
}

void charger_interruptHandler()
{
    CANMessage msg;
    charger_can.read(msg);
    //if(DEBUG) printf("id %d incoming \r\n", msg.id);
    if(idAccepted(msg.id)) {
        for(int i=0; i<CAN_BUFFER_SIZE; i++) {
            if((charger_buffer[i].id == msg.id || charger_buffer[i].id==BLANK_ID) && charger_safe_to_write[i]) {
                //("id %d added to buffer \r\n", msg.id);
                charger_buffer[i] = msg;
                //return required so that only first blank buffer entry is converted to incoming message ID each time new message ID is encountered
                return;
            }
        }
    }
}

bool idAccepted(int id)
{
    switch(id) {
        case BMS_BASE_ID:
            timeouts.BMS_timeout.reset();
            return true;
            
        case BMS_BASE_ID + CHARGER_CONTROL_INFO_ID:
            return true;
            
        case BMS_BASE_ID + MAX_MIN_VOLTAGE:
            return true;
            
        case BMS_BASE_ID + BATTERY_STATUS_ID:
            return true;
            
        case CHARGER_VI_INFO_ID:
            timeouts.charger_timeout.reset();
            return true;   
        default:
            return false;
    }
}   


void check_timeouts() //Check if it's been too long since any of the other devices in the car have communicated
{
    
    if (timeouts.BMS_timeout.read_ms() > BMS_MSG_TIMEOUT_MS) 
    {
        printf("Error: BMS comms timeout");
        comms_timeout = true;
    }
    
    if (timeouts.charger_timeout.read_ms() > CHARGER_MSG_TIMEOUT_MS) 
    {
        printf("Error: Charger comms timeout");
        comms_timeout = true;
    }
}

void update_LEDS() {
    if (charger_failure || bms_error) {
        desired_current = 0;    
        red_led = 1;
    } 
        
    else if (comms_timeout) {
        desired_current = 0;
        yellow_led = 1;
    } 
    
    else if (!charge_finished) {
        green_led = 1;
    } 
    
    else {
        red_led = 0;
        yellow_led = 0;
        green_led = 0;
    }  

}

void get_CAN_data() {
    
    //Import the data from the buffer into a non-volatile, more usable format
    CANMessage car_msgArray[CAN_BUFFER_SIZE]; //Same as above but some functions take message as their parameter
    int car_received_CAN_IDs[CAN_BUFFER_SIZE]; //needed to keep track of which IDs we've received so far
    for (int i = 0; i<CAN_BUFFER_SIZE; ++i) 
    {
        car_safe_to_write[i] = false;
        car_received_CAN_IDs[i] = car_buffer[i].id;
        car_msgArray[i] = car_buffer[i];
        car_safe_to_write[i] = true;
        
        //Now actually import the data from the CAN packets into the global variables
        switch (car_received_CAN_IDs[i]) {
            
            case BMS_BASE_ID:
                break;
                
            case BMS_BASE_ID + CHARGER_CONTROL_INFO_ID:
                get_charger_control_info(car_msgArray[i], voltage_error, temp_margin, discharge_error, pack_capacity);
                break;
                
            case BMS_BASE_ID + MAX_MIN_VOLTAGE:
                get_max_min_voltage(car_msgArray[i], min_cell_voltage, max_cell_voltage);
                break;
                
            case BMS_BASE_ID + BATTERY_STATUS_ID:
                get_battery_status(car_msgArray[i], bms_error); 
                break;
            case BLANK_ID: //This means we haven't received this type of message yet, so do nothing
                break;
            default:
                break;
        }
    }
    
    //Import the data from the buffer into a non-volatile, more usable format
    CANMessage charger_msgArray[CAN_BUFFER_SIZE]; //Same as above but some functions take message as their parameter
    int charger_received_CAN_IDs[CAN_BUFFER_SIZE]; //needed to keep track of which IDs we've received so far
    for (int i = 0; i<CAN_BUFFER_SIZE; ++i) 
    {
        charger_safe_to_write[i] = false;
        charger_received_CAN_IDs[i] = charger_buffer[i].id;
        charger_msgArray[i] = charger_buffer[i];
        charger_safe_to_write[i] = true;
        
        //Now actually import the data from the CAN packets into the global variables
        switch (charger_received_CAN_IDs[i]) {
            
            case CHARGER_VI_INFO_ID:
                get_charger_VI_info(charger_msgArray[i], charger_voltage, charger_current, charger_status);
                if(charger_status bitand 1 == 1) {
                    printf("Charger status: Hardware Failure\r\n");
                    charger_failure = true;
                }
                
                if(charger_status bitand 2 == 2) {
                    printf("Charger status: Over Temperature\r\n");
                    charger_failure = true; 
                }
                
                if(charger_status bitand 4 == 4) {
                    printf("Charger status: Input Voltage Wrong\r\n");
                    charger_failure = true; 
                }
                
                if(charger_status bitand 8 == 8) {
                    printf("Charger status: Reverse Polarity\r\n");
                    charger_failure = true;
                }
                
                if(charger_status bitand 16 == 16) {
                    printf("Charger status: communication timeout\r\n");
                    charger_failure = true; 
                }
                break;
            case BLANK_ID: //This means we haven't received this type of message yet, so do nothing
                break;
            default:
                break;
        }
    }
}