Has base BMU code but sends dummy temperature and voltage readings to test CAN
Dependencies: CUER_CAN DS1820 LTC2943 LTC6804 mbed
Fork of BMS_BMUCore_Max by
main.cpp
- Committer:
- DasSidG
- Date:
- 2017-07-02
- Revision:
- 15:e901aff1f5b3
- Parent:
- 14:e0e88a009f4c
File content as of revision 15:e901aff1f5b3:
#include "mbed.h" #include "CANParserBMU.h" #include "Data_Types_BMU.h" #include "CAN_Data.h" #include "CAN_IDs.h" #include "EEPROM_I2C.h" #include "Temperature.h" #include "LTC2943_Read.h" #include "Cell_Voltage.h" #include "SPI_I2C_Parser.h" using namespace CAN_IDs; // Function definitions void transmit_data(BMU_data measurements,uint32_t status); void read_temperature_sensors(BMU_data &measurements); void update_SOC(); void init(); void interruptHandler(); void CANDataSentCallback(); void write_SOC_EEPROM(BMU_data &measurements,uint16_t start_address); uint16_t read_EEPROM_startup(BMU_data &measurements); uint32_t check_measurements(BMU_data &measurements); void take_measurements(BMU_data &measurements); void test_read_CAN_buffer(); bool test_read_voltage_CAN(uint16_t readings[], int can_ids[]); void test_CAN_send(); void test_CAN_read(); CAN can(CAN_READ_PIN, CAN_WRITE_PIN); //Create a CAN object to handle CAN comms CANMessage buffer[CAN_BUFFER_SIZE]; //CAN receive buffer bool safe_to_write[CAN_BUFFER_SIZE]; //Semaphore bit indicating that it's safe to write to the software buffer bool CAN_data_sent = false; uint16_t eeprom_start_address; //the initial address where we store/read SoC values Timeout loop_delay; bool delay_finished = false; void loop_delay_callback(void) { delay_finished = true; } int main() { BMU_data measurements; uint16_t current_EEPROM_address; uint32_t status; uint16_t volt_readings[36]; int can_ids[9]; /**while(true) { for (int i = 0; i < 9; ++i) { while(!test_read_voltage_CAN(&volt_readings[(i*4)], &can_ids[i])); } for (int i = 0; i < 36; ++i) { printf("Cellvoltage %d = %d, CAN ID is %d \r\n", i, volt_readings[i], can_ids[i/4]); volt_readings[i] = -1; can_ids[i/4] = 0; } printf("\r\n"); } */ init(); //current_EEPROM_address = read_EEPROM_startup(measurements); // Read from the eeprom at startup to fill in the values of SoC //ltc2943.accumulatedCharge(measurements.percentage_SOC); // Initialise the LTC2943 with the current state of charge while (true) { // Take measurements from the sensors take_measurements(measurements); /*// Dont want to read the temperature sensors during each iteration of the loop if (c == 0) { read_temperature_sensors(measurements); } else if(c >= 4) { c = -1; } c++; // Check data for errors status = check_measurements(measurements); // Update the SOC update_SOC(); //Store data in the eeprom write_SOC_EEPROM(measurements, current_EEPROM_address); */ // CAN bus CAN_data_sent = false;//Currently does nothing, adding this line in more places then using //while(!CAN_data_sent); in order to ensure sending completes transmit_data(measurements,status); //test_read_CAN_buffer(); /* // Conserve power - enter a low powered mode delay_finished = false; loop_delay.attach(loop_delay_callback, LOOP_DELAY_S); while (!delay_finished) sleep(); */ //test_CAN_send(); //test_CAN_read(); wait(1); } } void transmit_data(BMU_data measurements, uint32_t status) { CANMessage msg; /* Place all of the collected data onto the CAN bus */ // Send cell voltages //voltages sent in sets of 4 + one cmu data set int repeating_unit_length = NO_READINGS_PER_CMU /4 + 1; for(uint16_t i= 0; i < NO_CMUS; i++) { //input id is offset, data structure is info, voltage, voltage, ...... //This is a slightly modified version of the Tritium BMS datasheet, to add an extra voltage reading set. msg = createVoltageTelemetry(repeating_unit_length*i+2, measurements.cell_voltages[i].voltages); can.write(msg); wait(0.1); //CONSIDER WAITS JUST IN CASE //+4 - 4 cell voltages sent per measurement msg = createVoltageTelemetry(repeating_unit_length*i+3, measurements.cell_voltages[i].voltages + 4); can.write(msg); wait(0.1); msg = createVoltageTelemetry(repeating_unit_length*i+4, measurements.cell_voltages[i].voltages + 8); can.write(msg); wait(0.1); //printf("Message id: %d \r\n", msg.id); } //Transmitting all of the individual probes: //for(uint8_t i = 0; i < devices_found; i++) REPLACED BY DUMMY LOOP for(uint8_t i = 0; i < 10; i++) { individual_temperature tempreading = measurements.temperature_measurements[i]; msg = createTemperatureTelemetry(i, &tempreading.ROMID[0], tempreading.measurement); if(can.write(msg)) printf("Message sent succesfully \r\n"); else printf("message failed to send \r\n"); individual_temperature testOut = decodeTemperatureTelemetry(msg); printf("ID[6] is %d and temp is %f \r\n",testOut.ROMID[6],testOut.measurement); wait(0.1); } // Create SOC CAN message createPackSOC(measurements.SOC, measurements.percentage_SOC); // Min/max cell voltages createCellVoltageMAXMIN(measurements.max_cell_voltage, measurements.min_cell_voltage); // Min/Max cell temperature, Currently the meaning of temp max/min is a ambiguous // due to changes to Temperature reading (namely the CMU ID portion of it), @TODO change MAXMIN createCellTemperatureMAXMIN(measurements.min_cell_temp,measurements.max_cell_temp); // Battery voltage and current // @TODO add the voltage createBatteryVI(measurements.battery_voltage,measurements.battery_current); //Extended battery pack status createExtendedBatteryPackStatus(status); } uint16_t read_EEPROM_startup(BMU_data &measurements) { /* The first page of the EEPROM, specifically the first 2 addresses store a pointer of the first memory location of measurement data. The EEPROM only has a finite number of read/write cycles which is why we aren't writing to the same location throughout */ uint16_t start_address; char start_address_array[2]; char SOC_out[8]; // 4 bytes for the 2 floats one is SOC and the other % charge float *fp1,*fp2; // temporary storage for float conversion // Get a pointer to the start address for the data stored in the eeprom i2c_page_read(0x0000,2,start_address_array); // Read the data from this address start_address = (start_address_array[1]<< 8) | start_address_array[0]; // mbed little endian follow this convention i2c_page_read(start_address, 8,SOC_out); // Convert the SOC_out values back into floats fp1 = (float*)(&SOC_out[0]); fp2 = (float*)(&SOC_out[4]); measurements.SOC = *fp1; measurements.percentage_SOC = *fp2; // Select the next address to write to start_address += 0x0040; if(start_address > MAX_WRITE_ADDRESS) { start_address = START_WRITE_ADDRESS; // Loop to the start of the eeprom } /*@TODO need to include a CRC check for the address pointer for the scenario when power is removed and we are writing to the eeprom*/ // write the new address to location 0x0000 start_address_array[0] = start_address | 0x00FF; start_address_array[1] = start_address >> 8; i2c_page_write(0x0000, 2, start_address_array); return start_address; } void write_SOC_EEPROM(BMU_data &measurements,uint16_t start_address) { char data_out[8]; float *fp1,*fp2; fp1 = (float*)(&measurements.SOC); fp2 = (float*)(&measurements.percentage_SOC); for(int i = 0; i < 4; i++ ) { data_out[i] = *fp1; fp1++; } for(int j = 4; j < 7; j++ ) { data_out[j] = *fp2; fp2++; } i2c_page_write(start_address, 8,data_out); } void read_temperature_sensors(BMU_data &measurements) { float min_temperature; float max_temperature; DigitalOut isotherm_12V_pin(ISOTHERM_12V_PIN); isotherm_12V_pin = 1; probe[0]->convert_temperature(DS1820::all_devices); isotherm_12V_pin = 0; min_temperature = probe[0]->temperature('C'); max_temperature = min_temperature; // Initially set the max and min temperature equal /**for (int i=0; i<devices_found; i++) { for(int j = 0; j < 7; j++) measurements.temperature_measurements[i].ROMID[j] = probe[i]->ROM[j]; measurements.temperature_measurements[i].measurement = probe[i] ->temperature('C'); if(measurements.temperature_measurements[i].measurement > max_temperature) { max_temperature = measurements.temperature_measurements[i].measurement; } else if (measurements.temperature_measurements[i].measurement < min_temperature) { min_temperature = measurements.temperature_measurements[i].measurement; } printf("Device %d temperature is %3.3f degrees Celcius.\r\n",i+1 ,probe[i]->temperature('C')); }ABOVE BLOCK REPLACED BY DUMMY MEASUREMENTS */ //Dummy data goes here for(int i = 0; i < 10; i++) { for(int j = 0; j < 7; j++) measurements.temperature_measurements[i].ROMID[j] = j; measurements.temperature_measurements[i].measurement = -5 + (float) i; if(measurements.temperature_measurements[i].measurement > max_temperature) { max_temperature = measurements.temperature_measurements[i].measurement; } else if (measurements.temperature_measurements[i].measurement < min_temperature) { min_temperature = measurements.temperature_measurements[i].measurement; } } //There is also a CMU # component of this struct, currently unfilled, perhaps not needed at all. measurements.max_cell_temp.temperature = max_temperature; measurements.min_cell_temp.temperature = min_temperature; } void update_SOC() { // Update the SOC value } uint32_t check_measurements(BMU_data &measurements) { uint32_t status; if(measurements.max_cell_voltage.voltage > MAX_CELL_VOLTAGE) { status = status | CELL_OVER_VOLTAGE; } else if (measurements.min_cell_voltage.voltage < MIN_CELL_VOLTAGE) { status = status | CELL_UNDER_VOLTAGE; } else if (measurements.max_cell_temp.temperature > MAX_CELL_TEMPERATURE) { status = status | CELL_OVER_TEMPERATURE; } /* @TODO also include errors for: *untrusted measurement *CMU timeout *SOC not valid */ return status; } void take_measurements(BMU_data &measurements) { uint16_t cellvoltages[NO_CMUS][12]; //TODO Use LTC6804_acquireVoltage to fill this array, and then properly format //it to be sent over CAN //LTC6804_acquireVoltage(cellvoltages); REPLACED BY DUMMY DATA for(int i = 0; i < NO_CMUS; i++) { for(int j = 0; j < 12; j++) cellvoltages[i][j] = i*j; //Just to have some data variation } for(int i=0; i<NO_CMUS; i++){ for(int j=0; j<12; j++){ measurements.cell_voltages[i].voltages[j] = cellvoltages[i][j]; // / 10; REMOVED FOR DUMMY DATA //printf("Cellvoltage[%d][%d] = %d \r\n",i,j,cellvoltages[i][j]); // SAME AS ABOVE /10); } } //Add code to take all temperature measurements and add it to measurements struct. read_temperature_sensors(measurements); //Current, SoC measurements.battery_current = (uint32_t) ltc2943.current()*1000; //*1000 to convert to mA measurements.percentage_SOC = ltc2943.accumulatedCharge(); measurements.SOC = (measurements.percentage_SOC /100) * BATTERY_CAPACITY; } void init() { temperature_init(); // Initialise the temperature sensors LTC2943_initialise(); //Initialises the fixed parameters of the LTC2943 LTC6804_init(MD_FAST, DCP_DISABLED, CELL_CH_ALL, AUX_CH_VREF2); //Initialises the LTC6804s for(int i=0; i<CAN_BUFFER_SIZE; i++) { buffer[i].id = BLANK_ID; //("%d",buffer[i].id); safe_to_write[i]= true; } //Initialise CAN stuff, attach CAN interrupt handlers can.frequency(CAN_BIT_RATE); //set transmission rate to agreed bit rate (ELEC-006) can.reset(); // (FUNC-018) can.attach(&interruptHandler, CAN::RxIrq); //receive interrupt handler can.attach(&CANDataSentCallback, CAN::TxIrq); //send interrupt handler } void CANDataSentCallback(void) { CAN_data_sent = true; printf("Some CAN was sent \r\n"); } void interruptHandler() { CANMessage msg; if(can.read(msg)) printf("interrupt reached with id %d \r\n",msg.id); else printf("reading failed \r\n"); for(int i=0; i<CAN_BUFFER_SIZE; i++) { if((buffer[i].id == msg.id || buffer[i].id==BLANK_ID) && safe_to_write[i]) { //("id %d added to buffer \r\n", msg.id); 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 test_read_CAN_buffer() { //Import the data from the buffer into a non-volatile, more usable format CAN_Data can_data[CAN_BUFFER_SIZE]; //container for all of the raw data int 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) { safe_to_write[i] = false; can_data[i].importCANData(buffer[i]); received_CAN_IDs[i] = buffer[i].id; safe_to_write[i] = true; } for(int i = 0; i < CAN_BUFFER_SIZE; i++) { uint16_t readings[4]; individual_temperature testpoint; if(!(buffer[i].id == BLANK_ID) && buffer[i].id < 10) { testpoint = decodeTemperatureTelemetry(buffer[i]); printf("Temperature is %f and ID[5] is %d \r\n", testpoint.measurement, testpoint.ROMID[5]); }else if(buffer[i].id == 1538) printf("id 1538 was recieved\r\n"); if(buffer[i].id > 0x600 && buffer[i].id < 0x800) { for(int i =0; i < 4; i++) { readings[i] = (buffer[i].data[2 * i]) + (buffer[i].data[2*i+1] << 8); //Since data is 8 8bit ints not 4 16 bit ones printf("readings[i] = %d \r\n", readings[i]); } } } } bool test_read_voltage_CAN(uint16_t readings[], int can_ids[]) { CANMessage msg; int can_id; int offset; int first_index; int second_index; if(can.read(msg)) { for(int i =0; i < 4; i++) { readings[i] = (msg.data[2 * i]) + (msg.data[2*i+1] << 8); //Since data is 8 8bit ints not 4 16 bit ones } can_id = msg.id; can_ids[0] = msg.id; offset = can_id - 1536; //1536 = 0x600 first_index = (offset - 1)/4; //offset of 2,3,4 is CMU 1; 6,7,8, is CMU 2; etc. second_index = ((offset - 1) % 4) - 1; //Makes it so 0,1,2 represent each voltage set //SID: subtracted 1 to make it work return true; } else return false; } void test_CAN_send() { CANMessage msg; char value = 142; msg = CANMessage(1, &value,1); if(can.write(msg)) printf("Succesfully sent %d \r\n", value); else printf("Sending Failed \r\n"); } void test_CAN_read() { CANMessage msg; if(can.read(msg)) printf("Successfully recieved %d \r\n", msg.data[0]); else printf("Reading Failed \r\n"); }