/* SMBus Control for SMBus 1.1 Battery Controller */ #include #include #include #include #include #include // project headers #include "setup.h" // Peter Fleury's LCD Lib #include "lcd.h" // Peter Fleury's I2C Lib #include "i2cmaster.h" /* -----C o n s t a n t s ------*/ #define CONTROL_ADDRESS 0x16 /* Memo from Arduino Project SBS::commandSet commands[38]; SBS::SBS(uint8_t address, char sda, char scl) { // Slave Function Address,Writable?, byte count commands[0] = {"ManufacturerAccess", 0x00, true, 1, "word"}; commands[1] = {"RemainingCapacityAlarm", 0x01, true, 2, "mAh"}; commands[2] = {"RemainingTimeAlarm", 0x02, true, 2, "minutes"}; commands[3] = {"BatteryMode", 0x03, true, 2, "bit flags"}; commands[4] = {"AtRate", 0x04, true, 2, "mA"}; commands[5] = {"AtRateTimeToFull", 0x05, false, 2, "minutes"}; commands[6] = {"AtRateTimeToEmpty", 0x06, false, 2, "minutes"}; commands[7] = {"AtRateOK", 0x07, false, 1, "Boolean"}; commands[8] = {"Temperature", 0x08, false, 2, "0.1K"}; commands[9] = {"Voltage", 0x09, false, 2, "mV"}; commands[10] = {"Current", 0x0a, false, 2, "mA"}; commands[11] = {"AverageCurrent", 0x0b, false, 2, "mA"}; commands[12] = {"MaxError", 0x0c, false, 2, "percent"}; commands[13] = {"RelativeStateOfCharge", 0x0d, false, 2, "percent"}; commands[14] = {"AbsoluteStateOfCharge", 0x0e, false, 2, "percent"}; commands[15] = {"RemainingCapacity", 0x0f, false, 2, "mAh"}; commands[16] = {"FullChargeCapacity", 0x10, false, 2, "mAh"}; commands[17] = {"RunTimeToEmpty", 0x11, false, 2, "minutes"}; commands[18] = {"AverageTimeToEmpty", 0x12, false, 2, "minutes"}; commands[19] = {"AverageTimeToFull", 0x13, false, 2, "minutes"}; commands[20] = {"ChargingCurrent", 0x14, false, 2, "mA"}; commands[21] = {"ChargingVoltage", 0x15, false, 2, "mV"}; commands[22] = {"BatteryStatus", 0x16, false, 2, "bit flags"}; commands[23] = {"CycleCount", 0x17, false, 2, "count"}; commands[24] = {"DesignCapacity", 0x18, false, 2, "mAh"}; commands[25] = {"DesignVoltage", 0x19, false, 2, "mV"}; commands[26] = {"SpecificationInfo", 0x1a, false, 2, "unsigned int"}; commands[27] = {"ManufactureDate", 0x1b, false, 2, "unsigned int"}; commands[28] = {"SerialNumber", 0x1c, false, 2, "number"}; //reserved 0x1d - 0x1f commands[29] = {"ManufacturerName", 0x20, false, 0, "string"}; commands[30] = {"DeviceName", 0x21, false, 1, "string"}; commands[31] = {"DeviceChemistry", 0x22, false, 1, "string"}; commands[32] = {"ManufacturerData", 0x23, false, 1, "data"}; commands[33] = {"OptionalMfgFunction5", 0x2f, false, 1, "data"}; commands[34] = {"VoltageCellFour", 0x3c, false, 1, "mV"}; commands[35] = {"VoltageCellThree", 0x3d, false, 1, "mV"}; commands[36] = {"VoltageCellTwo", 0x3e, false, 1, "mV"}; commands[37] = {"VoltageCellOne", 0x3f, false, 1, "mV"}; */ const char PROGMEM commands[0x40][16]= { "ManufacturerAcc", //0x00 "RemainingCapaci", "RemainingTimeAl", "BatteryMode ", "AtRate ", "AtRateTimeToFul", "AtRateTimeToEmp", "AtRateOK ", "Temperature ", "Voltage ", "Current ", "AverageCurrent ", "MaxError ", "RelativeChgStat", "AbsoluteChgStat", "RemainingCapaci", "FullChargeCapac", // 0x10 "RunTimeToEmpty ", "AverageTimeToEm", "AverageTimeToFu", "ChargingCurrent", "ChargingVoltage", "BatteryStatus ", "CycleCount ", "DesignCapacity ", "DesignVoltage ", "SpecInfo ", "ManufactureDat ", "SerialNumber ", "reserved ", "reserved ", "reserved ", "ManufacturerNam", // 0x20 "DeviceName ", "DeviceChemistry", "ManufacturerDat", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "OptionalMfgFunc", "reserved ", // 0x30 "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "reserved ", "Voltage Cell 4 ", "Voltage Cell 3 ", "Voltage Cell 2 ", "Voltage Cell 1 " // 0x3F }; /* ------------- Global variables ----*/ volatile SMBFlags_t theFlags; uint8_t param = 0; // I2C Props uint8_t theData2=0; uint8_t theData=0; uint8_t crc,theAck = 0; uint16_t theValue; /* ----- SUB Programs ---*/ /*! \brief Initializes I/O port directions and pull-up resistors * * This function initializes all I/O ports with correct * direction and pull-up resistors, if needed. */ static void PortsInit(void) { // Port B is LCD Data Output // the LCD lib does this on its own, but here for completeness DDRB = 0b01111111; // I2C_PORT is the data,clock and CE output I2C_DIR |= (1 << BIT_CLK)|(1 << BIT_DATA)|(1<> 4]); lcd_putc(hextable[data & 0x0f]); } static void printword(const uint16_t data) { printbyte(data >> 8); printbyte(data); } static void printlong(const uint32_t data) { printword(data >> 16); printword(data); } static void printdouble(const uint64_t data) { printlong(data >> 32); printlong(data); } // output a number in decimal style char dectable[16] = "0000000000000000"; char a; static void printdec(int16_t number) { uint8_t n,i; /* yeah, yeah warning "pointer to integer without a cast" * Am i tired of this or what */ a = itoa(number,dectable,10); i = strlen(dectable); for (n=0;n < i;n++) { if (n == i-1) lcd_putc('.'); lcd_putc(dectable[n]); } } static void printnum(int32_t number) { uint8_t n,i; a = ltoa(number,dectable,10); i = strlen(dectable); for (n=0;n < i;n++) { lcd_putc(dectable[n]); } } // the CRC8 routine for consecutive processing // polynom is X^8+^X^2+X^1+1 // uint8_t crc8_update(uint8_t crc,const uint8_t data) { uint8_t i; crc = crc ^ data; for (i = 0; i < 8; i++) { if (crc & 0x01) crc = (crc >> 1) ^ 0x83; else crc >>= 1; } return crc; } /* custom I2C Routine for Battery controller access */ void send_I2C(uint8_t isRead) { uint8_t ret = 0; crc = crc8_update(0,CONTROL_ADDRESS+I2C_WRITE); ret = i2c_start(CONTROL_ADDRESS+I2C_WRITE); // set device address and write mode if ( ret ) { /* failed to issue device address, possibly no device found */ i2c_stop(); theAck = 0; } else { /* issuing start condition ok, device accessible */ /* read a Battery register in 16 bit theData is the register to access a repeated start condition with the read address then 16 bits are read and arranged in little endian */ if (isRead) { crc = crc8_update(crc, theData); i2c_write(theData); crc = crc8_update(crc,CONTROL_ADDRESS+I2C_READ); i2c_rep_start(CONTROL_ADDRESS+I2C_READ); theData2 = i2c_readAck(); crc = crc8_update(crc, theData2); theValue = theData2 + (256 * i2c_readNak()); // i2c_stop(); // set stop conditon = release bus theAck = 1; } /* write 16 bits to battery register */ else { crc = crc8_update(crc,(uint8_t) theValue & 0xFF); i2c_write((uint8_t) theValue & 0xFF); crc = crc8_update(crc,(uint8_t) (theValue >> 8) & 0xFF); i2c_write((uint8_t) (theValue >> 8) & 0xFF); i2c_write(crc); i2c_stop(); } } } // Cursorpositions for selectable parameters uint8_t cursorpos[3] = { 0,5,13 }; void showPars(void) { lcd_gotoxy(cursorpos[0],0); lcd_puts_P("R");printbyte(theData);printspc(); lcd_gotoxy(cursorpos[1],0); lcd_puts_P("V");printnum(theValue);printspc(); lcd_gotoxy(cursorpos[2],0); if (theAck) { lcd_puts_P("A"); } else { lcd_puts_P("N"); } printbyte(crc); lcd_gotoxy(0,1); lcd_puts_p(commands[theData]); lcd_gotoxy(cursorpos[param],0); _delay_ms(5); } /* repeatedly execute this code part. Here we scan the buttons, set params and display stuff * Corner * Button0 Button1 Button2 7 6 5 incr. decr. param change */ static void execCommand(void) { char n; static uint8_t i; n = keyin(); switch (n) { // increment selected parameter case 0b10000000 : switch (param) { case 0: if (theData < 0x3F) theData++; send_I2C(TRUE); // read break; case 1: if (theValue < 0xFFFF) theValue++; else theValue = 0; break; case 2: send_I2C(FALSE); // write break; default : break; } break; // decrement selected parameter case 0b01000000 : switch (param) { case 0: if (theData > 0) theData--; send_I2C(TRUE); // read break; case 1: if (theValue > 0) theValue--; else theValue = 0xFFFF; break; case 2: send_I2C(TRUE); // read break; default : break; } break; // cycle through parameters case 0b00100000 : if (param < 2) param++; else param = 0; theFlags.PanelWait = TRUE; i = 0; break; default: i = 0; break; } // switch // show to user on LCD showPars(); // autorepeat if (theFlags.PanelWait) { if (keyin() > 0) {}; } if (i < 10) { _delay_ms(150); i++; } else { _delay_ms(15); } theFlags.PanelWait = FALSE; } /* *************************************************************************************** *! \brief Main function / initialization * * Lets initialize all variables here - even if its only for completeness *******************************************************************************************+ */ int main(void) { cli(); // variables theFlags.PanelWait = FALSE; param = 0; //Initialize peripherals. PortsInit(); _delay_ms(100); i2c_init(); // init I2C interface /* initialize display, cursor off */ lcd_init(LCD_DISP_ON_CURSOR); lcd_home(); lcd_puts_P("Battery SMBus\n"); lcd_putc('0'); // diagnostic outputs _delay_ms(1000); lcd_clrscr(); /*************************** MAIN LOOP *************************/ //the main loop handles user interface // no ISRs in this program for(;;) { execCommand(); } } /*************************** END MAIN LOOP *************************/ PROGMEM const char VersionandCopyright[36] = "1.1 (c)2009-2019 Matthias Schoeldgen"; PROGMEM const char EndofFile[22] = "Ende der Fahnenstange ";