diff --git a/code/main.c b/code/main.c index fdec727..943cc26 100644 --- a/code/main.c +++ b/code/main.c @@ -8,20 +8,43 @@ #include "i2c_master.h" -#define EEPROM_SIZE 512 +#define EEPROM_SIZE 256 +#define EEPROM_IDX_SIZE 4 + +#define BATTERY_REG_MODE 0x00 +#define BATTERY_REG_CTRL 0x01 +#define BATTERY_READ_ADDR 0x02 +#define BATTERY_READ_ADDR_END 0x0B +#define BATTERY_BUFF_SIZE (BATTERY_READ_ADDR_END - BATTERY_READ_ADDR + 1) + +#define BATTERY_CHARGE 0 +#define BATTERY_VOLT 6 +#define BATTERY_TEMP 8 + +#define BATTERY_MILLIOHM 30 + +#define BATTERY_CHARGE_MULT 67 +#define BATTERY_CHARGE_DIV (10 * BATTERY_MILLIOHM) +#define BATTERY_VOLT_MULT 244 +#define BATTERY_VOLT_DIV 100 +#define BATTERY_TEMP_MULT 125 +#define BATTERY_TEMP_DIV 1000 #define CHAR_SIZE 5 -#define OLED_ADDRESS (0x3C << 1) +#define OLED_ADDRESS (0x3C << 1) +// #define OLED_ADDRESS (0x3D << 1) +#define EEPROM_ADDRESS (0x50 << 1) +#define BATTERY_ADDRESS (0x70 << 1) #define OLED_X_SIZE 128 #define OLED_Y_SIZE 64 // Time(ms) to keep the display on before sleep -#define DISPLAY_DELAY 3000 +#define DISPLAY_DELAY 3000 // Time(ms) to assume long button press -#define LONG_PRESS 500 +#define LONG_PRESS 500 // Rotary Encoder Parameters #define ROT_PULSE_COUNT 12 @@ -29,11 +52,6 @@ #define ROT_WHEEL_RAD 13.875 //#define ROT_REVERSE -// Input masks -#define BUTTON_V 0x01 -#define BUTTON_S 1 -#define BUTTON_M (BUTTON_V << BUTTON_S) - // Event queue macros #define MAX_EVENT_COUNT 64 #define PTR_INC(x) ((x) = events + ((((x) - events) + 1) % MAX_EVENT_COUNT)) @@ -59,12 +77,6 @@ enum btn_state_e BTN_DOWN14 }; -enum state_e -{ - STATE_COUNTING, - STATE_SETTING -}; - // These are set on hardware init uint8_t old_btn_pin_state; uint8_t old_rot_pin_state; @@ -133,7 +145,8 @@ const uint8_t PROGMEM unfold_table[16] = 0xAA }; -const uint8_t PROGMEM symbols[80] = +// Bottom -> Top (In Byte); Left -> Right (In Row) +const uint8_t PROGMEM symbols[90] = { 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 @@ -145,35 +158,80 @@ const uint8_t PROGMEM symbols[80] = 0x03, 0x71, 0x09, 0x05, 0x03, // 7 0x36, 0x49, 0x49, 0x49, 0x36, // 8 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 - // A - // B + 0x7E, 0x11, 0x11, 0x11, 0x7E, // A + 0x7F, 0x49, 0x49, 0x49, 0x36, // B + 0x3E, 0x41, 0x41, 0x41, 0x22, // C 0x1F, 0x20, 0x40, 0x20, 0x1F, // V - // h + 0x7F, 0x08, 0x04, 0x04, 0x78, // h 0x7C, 0x04, 0x78, 0x04, 0x78, // m 0x00, 0x60, 0x60, 0x00, 0x00, // . - // < - // > + 0x41, 0x22, 0x14, 0x08, 0x00, // > + 0x0E, 0x11, 0x11, 0x0E, 0x00 // deg }; +// Init direct hardware void simple_init(); + +// OLED send cmd helper void display_send_cmd(uint8_t cmd); + +// OLED send data helper void display_send_data(const uint8_t *data, uint16_t buflen); + +// OLED init void display_init(); + +// OLED On/Off void display_enable(uint8_t en); + +// Symbol printing helper void get_symbol16(uint8_t index, uint8_t *out); + +// Print symbol on screen +// Symbol idx in table, +// Start X pixel - 0 -- 117 +// Y row - 0 -- 3 +// Char size - 10x16 void print_symbol(uint8_t symbol_idx, uint8_t x, uint8_t y, uint8_t invert); + +// Print the length left with mm and highlight the required digit +// 0 - no highlight, 0xFF - clears everything void print_mm(uint32_t value, uint8_t highlight); -void print_direction(uint8_t is_up, uint8_t invert); -void print_voltage(uint16_t value); -uint16_t adc_measure(); -uint16_t get_voltage(); -uint16_t find_eeprom_idx(); -void read_eeprom_val(uint16_t idx, uint32_t *value, uint8_t *dir); -void write_eeprom_val(uint16_t idx, uint32_t value, uint8_t dir); + +// Print the direction of the filament +// 0xFF - clears everything +void print_direction(uint8_t is_A, uint8_t highlight); + +// Find the current eeprom data idx of the attached spool +int16_t find_eeprom_idx(); + +// Read value and direction of current spool +int8_t read_eeprom_val(int8_t idx, uint32_t *value, uint8_t *direction); + +// Write value and direction of current spool +int8_t write_eeprom_val(int8_t idx, uint32_t value, uint8_t direction); + +// Reset battery counter stats +int8_t reset_battery(); + +// Read battery stats +// temp is in celsius +int8_t read_battery(int16_t *mAh, int16_t *mV, int16_t *temp); + +// I2C write helper +int8_t i2c_write_buff(uint8_t i2c_addr, uint8_t data_addr, uint8_t *buf, uint8_t len); + +// I2C read helper +int8_t i2c_read_buff(uint8_t i2c_addr, uint8_t data_addr, uint8_t *buf, uint8_t len); + +// Do the big sleep void do_sleep(); -void update_display(uint32_t value, uint32_t highlight, uint8_t dir, uint8_t dir_highlight); + +// Extracts digit from value +// 1 - ones, 2 - tens, 3 - hundreds ... uint8_t extract_digit(uint32_t value, uint8_t digit_num); +// Updates the ms counter ISR(TIMER1_OVF_vect) { cli(); @@ -796,8 +854,8 @@ void print_symbol(uint8_t symbol_idx, uint8_t x, uint8_t y, uint8_t invert) void print_mm(uint32_t value, uint8_t highlight) { - static uint32_t old_value = 0; - static uint8_t old_highlight = 0; + static_uint8_t old_symbols[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + static uint8_t old_highlight = 0xFF; uint8_t x; uint8_t y; uint8_t symbol; @@ -822,7 +880,12 @@ void print_mm(uint32_t value, uint8_t highlight) invert = 0; } - if (is_first && (0 == highlight) && (0 == symbol) && (1 != i)) + // Leading zero removal + // There's no highlight, this is the first digit to write, it's 0 + // and it's not the last digit + // or everything is removed + if ((is_first && (0 == highlight) && (0 == symbol) && (1 != i)) || + (0xFF == highlight)) { symbol = 0xFF; } @@ -831,216 +894,296 @@ void print_mm(uint32_t value, uint8_t highlight) is_first = 0; } - if ((0 == old_value) || (symbol != extract_digit(old_value, i)) || (old_highlight != highlight)) + // Update only necessary digits + // On first run, on highlight change or on symbol change + if ((0xFF == old_highlight) || (old_highlight != highlight) || + (symbol != old_symbols[6 - i])) { print_symbol(symbol, x, y, invert); + old_symbols[6 - i] = symbol; } x += (CHAR_SIZE + 1) * 2; } - // Print 'mm' - if (0 == old_value) + // Print 'mm' (only on first run) + if (0xFF == old_highlight) { - print_symbol(10, x, y, 0); + print_symbol(15, x, y, 0); x += (CHAR_SIZE + 1) * 2; - print_symbol(10, x, y, 0); + print_symbol(15, x, y, 0); + } + + if (0xFF == highlight) + { + print_symbol(0xFF, x, y, 0); + x += (CHAR_SIZE + 1) * 2; + print_symbol(0xFF, x, y, 0); } - old_value = value; old_highlight = highlight; } -void print_direction(uint8_t is_up, uint8_t invert) +void print_direction(uint8_t is_A, uint8_t highlight) { - uint8_t symbol_index[2]; - static uint8_t old_is_up = 0; - static uint8_t old_invert = 1; + uint8_t symbol; + static uint8_t old_is_A = 0; + static uint8_t old_highlight = 0xFF; - if (is_up) + if (is_A) { - symbol_index[0] = 14; - symbol_index[1] = 13; + symbol = 10; } else { - symbol_index[0] = 13; - symbol_index[1] = 15; + symbol = 11; } - if ((old_is_up != is_up) || (old_invert != invert)) + // Clear it all + if ((0xFF == highlight) && (old_highlight != highlight)) { - print_symbol(symbol_index[0], OLED_X_SIZE - CHAR_SIZE * 2, 0, invert); - print_symbol(symbol_index[1], OLED_X_SIZE - CHAR_SIZE * 2, 1, invert); + print_symbol(0xFF, OLED_X_SIZE - (CHAR_SIZE + 1) * 2 * 2, 0, 0); + print_symbol(0xFF, OLED_X_SIZE - (CHAR_SIZE + 1) * 2, 0, 0); } - old_is_up = is_up; - old_invert = invert; -} - -void print_voltage(uint16_t value) -{ - static uint16_t old_value = 0; - uint8_t x; - uint8_t y; - uint8_t symbol; - uint8_t i; - - x = 0; - y = 1; - - // Print 3 digits - for (i = 3; i > 0; --i) + else { - symbol = extract_digit(value, i); - if ((0 == old_value) || (symbol != extract_digit(old_value, i))) + // Print '>' only when display is comming from clear + if (0xFF == old_highlight) { - print_symbol(symbol, x, y, 0); + print_symbol(symbol_index[0], OLED_X_SIZE - (CHAR_SIZE + 1) * 2 * 2, 0, 0); } - if (0 == x) + // Print symbol on change or highlight change + if ((old_is_A != is_A) || (old_highlight != highlight)) { - x += (CHAR_SIZE + 1) * 2; + print_symbol(symbol, OLED_X_SIZE - (CHAR_SIZE + 1) * 2, 0, highlight); } - - x += (CHAR_SIZE + 1) * 2; } - - if (0 == old_value) - { - // Print 'V' - print_symbol(11, x, y, 0); - - // Print '.' - x = (CHAR_SIZE + 1) * 2; - print_symbol(12, x, y, 0); - } - old_value = value; + old_is_A = is_A; + old_highlight = highlight; } -uint16_t adc_measure() +int16_t find_eeprom_idx() { - uint16_t result; - - // Enable and start ADC - ADCSRA |= (1 << ADEN) | (1 << ADSC); - - // Wait for conversion - while (ADCSRA & (1 << ADSC)); - - // Read ADC value - result = ADC; - - // Redo to get a better reading - ADCSRA |= (1 << ADSC); - while (ADCSRA & (1 << ADSC)); - result = ADC; - - // Disable ADC - ADCSRA &= ~(1 << ADEN); - - return result; -} - -uint16_t measure_voltage() -{ - uint32_t val; - uint16_t result; - - val = 110; - val *= 1023; - val /= adc_measure(); - result = val; - - return result; -} - -uint16_t find_eeprom_idx() -{ - uint16_t idx = 0; + uint8_t err; + int16_t idx = 0; uint8_t found = 0; + uint32_t value = 0xFFFFFFFF; - // FF is the erased byte value and is used as prefix - for (idx = 0; idx < EEPROM_SIZE; ++idx) + // FF is the erased byte value + for (idx = 0; idx < EEPROM_SIZE / EEPROM_IDX_SIZE; ++idx) { - if (0xFF != eeprom_read_byte((uint8_t *) idx)) + err = read_eeprom_val(idx, &value); + if (0 != err) + { + return -1; + } + + if (value != 0xFFFFFFFF) { found = 1; break; } } + // Not found = new EEPROM if (!found) { - return EEPROM_SIZE; + return 0; } - // FF XX ... - if (1 == idx) - { - // Check Last byte for value - // FF XX ... FF ?? - if (0xFF != eeprom_read_byte((uint8_t *) (EEPROM_SIZE - 1))) - { - idx = EEPROM_SIZE - 1; - } - } - - // XX ... - if (0 == idx) - { - // XX ... FF ?? ?? - for (idx = EEPROM_SIZE - 2; idx < EEPROM_SIZE; ++idx) - { - if (0xFF != eeprom_read_byte((uint8_t *) idx)) - { - break; - } - } - } - - // idx here is the byte that's not the FF prefix so reverse it by 1 - idx += EEPROM_SIZE; - --idx; - idx %= EEPROM_SIZE; - return idx; } -void read_eeprom_val(uint16_t idx, uint32_t *value, uint8_t *dir) +int8_t read_eeprom_val(int8_t idx, uint32_t *value, uint8_t *direction) { - uint8_t curr_byte; - uint8_t i; + int8_t err; + uint8_t buf[4]; - (*value) = 0; - for (i = 0; i < 4; ++i) + if (idx < 0) { - curr_byte = eeprom_read_byte((uint8_t *) idx); - (*value) <<= 8; - (*value) |= curr_byte; - ++idx; - idx %= EEPROM_SIZE; + return -1; + } + idx %= (EEPROM_SIZE / EEPROM_IDX_SIZE); + + err = i2c_read_buff(EEPROM_ADDRESS, idx * EEPROM_IDX_SIZE, buf, 4); + if (4 != err) + { + return -1; } - *dir = ((*value) & 0x00800000) >> 23; - (*value) &= 0x007FFFFF; + (*direction) = buf[0]; + buf[0] = 0; + (*value) = 0; + + for (uint8_t i = 0; i < 4; ++i) + { + (*value) <<= 8; + (*value) |= buf[i]; + } + + return 0; } -void write_eeprom_val(uint16_t idx, uint32_t value, uint8_t dir) +int8_t write_eeprom_val(int8_t idx, uint32_t value, uint8_t direction) { - uint8_t curr_byte; - uint8_t i; + int8_t err; + uint8_t buf[4]; - value |= 0xFF000000; - if (0 != dir) + if (idx < 0) { - value |= 0x00800000; + return -1; + } + idx %= (EEPROM_SIZE / EEPROM_IDX_SIZE); + + for (uint8_t i = 0; i < 4; ++i) + { + buf[i] = ((value >> ((3 - i) * 8)) & 0xFF); } - for (i = 0; i < 4; ++i) + buf[0] = direction; + + err = i2c_write_buff(EEPROM_ADDRESS, idx * EEPROM_IDX_SIZE, buf, 4) + if (4 != err) { - curr_byte = (value >> (24 - 8 * i)) & 0xFF; - eeprom_update_byte((uint8_t *) idx, curr_byte); - ++idx; - idx %= EEPROM_SIZE; + return -1; } + + return 0; +} + +int8_t reset_battery() +{ + int8_t err; + uint8_t val; + + // Reset All Battery Counters + val = 0x02; + err = i2c_write_buff(BATTERY_ADDRESS, BATTERY_REG_CTRL, &val, 1); + if (1 != err) + { + return -1; + } + + // Set Running Mode + val = 0x10; + err = i2c_write_buff(BATTERY_ADDRESS, BATTERY_REG_MODE, &val, 1); + if (1 != err) + { + return -1; + } + + return 0; +} + +int8_t read_battery(int16_t *mAh, int16_t *mV, int16_t *temp) +{ + // Reading from 0x02 to 0x0B inclusive + uint8_t buf[BATTERY_BUFF_SIZE]; + int8_t err; + int32_t val; + + err = i2c_read_buff(BATTERY_ADDRESS, BATTERY_READ_ADDR, buf, BATTERY_BUFF_SIZE); + if (err != BATTERY_BUFF_SIZE) + { + return -1; + } + + // Calc mAh + (*mAh) = buf[BATTERY_CHARGE + 1]; + (*mAh) <<= 8; + (*mAh) |= buf[BATTERY_CHARGE]; + + val = (*mAh); + val *= BATTERY_CHARGE_MULT; + val /= BATTERY_CHARGE_DIV; + (*mAh) = val; + + // Calc mV + (*mV) = buf[BATTERY_VOLT + 1]; + (*mV) <<= 8; + (*mV) |= buf[BATTERY_VOLT]; + + val = (*mV); + val *= BATTERY_VOLT_MULT; + val /= BATTERY_VOLT_DIV; + (*mV) = val; + + // Calc temp + (*temp) = buf[BATTERY_TEMP + 1]; + (*temp) <<= 8; + (*temp) |= buf[BATTERY_TEMP]; + + val = (*temp); + val *= BATTERY_TEMP_MULT; + val /= BATTERY_TEMP_DIV; + (*temp) = val; + + return 0; +} + +int8_t i2c_write_buff(uint8_t i2c_addr, uint8_t data_addr, uint8_t *buf, uint8_t len) +{ + uint8_t err; + + err = i2c_start(i2c_addr | I2C_WRITE); + if (0 != err) + { + i2c_stop(); + return -1; + } + + err = i2c_write(data_addr); + if (0 != err) + { + i2c_stop(); + return -1; + } + + for (uint8_t i = 0; i < len; ++i) + { + err = i2c_write(buf[i]); + if (0 != err) + { + i2c_stop(); + return i; + } + } + + i2c_stop(); + return i; +} + +int8_t i2c_read_buff(uint8_t i2c_addr, uint8_t data_addr, uint8_t *buf, uint8_t len) +{ + uint8_t err; + + err = i2c_start(i2c_addr | I2C_WRITE); + if (0 != err) + { + i2c_stop(); + return -1; + } + + err = i2c_write(data_addr); + if (0 != err) + { + i2c_stop(); + return -1; + } + + err = i2c_start(i2c_addr | I2C_READ); + if (0 != err) + { + i2c_stop(); + return -1; + } + + for (uint8_t i = 0; i < len; ++i) + { + buf[i] = i2c_read(i == len - 1); + } + + i2c_stop(); + return i; } void do_sleep() @@ -1066,13 +1209,6 @@ void do_sleep() PRR &= ~(1 << PRUSI); } -void update_display(uint32_t value, uint32_t highlight, uint8_t dir, uint8_t dir_highlight) -{ - print_mm(value, highlight); - print_direction(dir, dir_highlight); - print_voltage(measure_voltage()); -} - uint8_t extract_digit(uint32_t value, uint8_t digit_num) { while (1 != digit_num)