diff options
-rw-r--r-- | src/i2c.cc | 65 | ||||
-rw-r--r-- | src/i2c.h | 14 | ||||
-rw-r--r-- | src/main.cc | 16 | ||||
-rw-r--r-- | src/storage.cc | 172 | ||||
-rw-r--r-- | src/storage.h | 23 |
5 files changed, 197 insertions, 93 deletions
diff --git a/src/i2c.cc b/src/i2c.cc deleted file mode 100644 index cc10b19..0000000 --- a/src/i2c.cc +++ /dev/null @@ -1,65 +0,0 @@ -#include <avr/io.h> -#include <avr/interrupt.h> -#include <util/delay.h> -#include <stdlib.h> - -#include "i2c.h" - -I2C i2c; - -void I2C::enable() -{ - /* - * Set I2C clock frequency to 100kHz. - * freq = F_CPU / (16 + (2 * TWBR * TWPS) ) - * let TWPS = "00" = 1 - * -> TWBR = (F_CPU / 100000) - 16 / 2 - */ - TWSR = 0; // the lower two bits control TWPS - TWBR = ((F_CPU / 100000UL) - 16) / 2; -} - -// TODO Everything[tm] (error handling and generic code) -// Also TODO: Use interrupts instead of polling -/* - * This method encapsules both, transmit and read into one function. - * Although it could be split into read/write, all reads require a transmit. - * Hence this approach reduces code duplication. - * - * num_tx, num_rx = number of byte to transmit/receive - * txbuf, rxbuf = pointer to the uint8_t array for tx/rx - */ -void I2C::xmit(int num_tx, int num_rx, uint8_t *txbuf, uint8_t *rxbuf) -{ - int i; - - if (num_tx) { - TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); - while (!(TWCR & _BV(TWINT))); - TWDR = (I2C_EEPROM_ADDR << 1) | 0; - TWCR = _BV(TWINT) | _BV(TWEN); - while (!(TWCR & _BV(TWINT))); - for (i = 0; i < num_tx; i++) { - TWDR = txbuf[i]; - TWCR = _BV(TWINT) | _BV(TWEN); - while (!(TWCR & _BV(TWINT))); - } - } - if (num_rx) { - TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); - while (!(TWCR & _BV(TWINT))); - TWDR = (I2C_EEPROM_ADDR << 1) | 1; - TWCR = _BV(TWINT) | _BV(TWEN); - while (!(TWCR & _BV(TWINT))); - for (i = 0; i < num_rx; i++) { - if (i == num_rx-1) { - TWCR = _BV(TWINT) | _BV(TWEN); - } else { - TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA); - } - while (!(TWCR & _BV(TWINT))); - rxbuf[i] = TWDR; - } - } - TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); -} diff --git a/src/i2c.h b/src/i2c.h deleted file mode 100644 index 818db23..0000000 --- a/src/i2c.h +++ /dev/null @@ -1,14 +0,0 @@ -#include <avr/io.h> -#include <avr/interrupt.h> -#include <stdlib.h> - -#define I2C_EEPROM_ADDR 0x50 - -class I2C { - public: - I2C() {}; - void enable(); - void xmit(int num_tx, int num_rx, uint8_t *txbuf, uint8_t *rxbuf); -}; - -extern I2C i2c; diff --git a/src/main.cc b/src/main.cc index 0fb68ff..8ca3e21 100644 --- a/src/main.cc +++ b/src/main.cc @@ -6,16 +6,12 @@ #include "display.h" #include "font.h" -#include "i2c.h" +#include "storage.h" #include "modem.h" #include "system.h" int main (void) { - char testbuf[] = "..Olol I2C extra lang bla foo wololo moep "; - char testbuf2[32]; - testbuf[0] = 1; - testbuf[1] = 0; // disable ADC to save power PRR |= _BV(PRADC); @@ -29,17 +25,9 @@ int main (void) display.enable(); modem.enable(); - i2c.enable(); + storage.enable(); sei(); - //display.setString("tx"); - - i2c.xmit(64, 0, (uint8_t *)testbuf, (uint8_t *)testbuf); - - //display.setString("rx"); - - i2c.xmit(2, 64, (uint8_t *)testbuf, (uint8_t *)testbuf2); - display.setString(testbuf2); while (1) { // nothing to do here, go to idle to save power diff --git a/src/storage.cc b/src/storage.cc new file mode 100644 index 0000000..f755e0b --- /dev/null +++ b/src/storage.cc @@ -0,0 +1,172 @@ +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/delay.h> +#include <stdlib.h> + +#include "storage.h" + +Storage storage; + +/* + * EEPROM data structure ("file system"): + * + * Organized as 64B-pages, all animations/texts are page-aligned. Byte 0 .. + * 255 : storage metadata. Byte 0 contains the number of animations, byte 1 the + * byte offset of the first animation, byte 2 of the second, and so on. + * Byte 256+: texts/animations without additional storage metadata, aligned + * to 64B. So, a maximum of 256-(256/64) = 252 texts/animations can be stored, + * and a maximum of 255 * 64 = 16320 Bytes (almost 16 kB / 128 kbit) can be + * addressed. To support larger EEPROMS, change the metadate area to Byte 2 .. + * 511 and use 16bit page pointers. + * + * The text/animation size is not limited by this approach. + * + * Example: + * Byte 0 = 3 -> we've got a total of three animations + * Byte 1 = 4 -> first text/animation starts at byte 64*4 = 256 + * Byte 2 = 8 -> second starts at byte 64*8 = 512 + * Byte 3 = 9 -> third starts at 64*9 * 576 + * Byte 4 = whatever + * . + * . + * . + * Byte 256ff = first text/animation. Has a header encoding its length in bytes. + * Byte 512ff = second + * Byte 576ff = third + * . + * . + * . + */ + +void Storage::enable() +{ + /* + * Set I2C clock frequency to 100kHz. + * freq = F_CPU / (16 + (2 * TWBR * TWPS) ) + * let TWPS = "00" = 1 + * -> TWBR = (F_CPU / 100000) - 16 / 2 + */ + TWSR = 0; // the lower two bits control TWPS + TWBR = ((F_CPU / 100000UL) - 16) / 2; +} + + +/* + * Send an I2C (re)start condition and the EEPROM address in read mode. Returns + * after it has been transmitted successfully. + */ +void Storage::i2c_start_read() +{ + TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); + while (!(TWCR & _BV(TWINT))); + + // Note: The R byte ("... | 1") causes the TWI momodule to switch to + // Master Receive mode + TWDR = (I2C_EEPROM_ADDR << 1) | 1; + TWCR = _BV(TWINT) | _BV(TWEN); + while (!(TWCR & _BV(TWINT))); +} + +/* + * Send an I2C (re)start condition and the EEPROM address in write mode. + * Returns after it has been transmitted successfully. + */ +void Storage::i2c_start_write() +{ + TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); + while (!(TWCR & _BV(TWINT))); + + TWDR = (I2C_EEPROM_ADDR << 1) | 0; + TWCR = _BV(TWINT) | _BV(TWEN); + while (!(TWCR & _BV(TWINT))); +} + +/* + * Send an I2C stop condition. + */ +void Storage::i2c_stop() +{ + TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); +} + +/* + * Sends len bytes to the EEPROM. Note that this method does NOT + * send I2C start or stop conditions. + */ +// TODO Everything[tm] (error handling and generic code) +int8_t Storage::i2c_send(uint8_t len, uint8_t *data) +{ + uint8_t pos = 0; + + for (pos = 0; pos < len; pos++) { + TWDR = data[pos]; + TWCR = _BV(TWINT) | _BV(TWEN); + while (!(TWCR & _BV(TWINT))); + } + + return pos + 1; +} + +/* + * Receives len bytes from the EEPROM into data. Note that this method does + * NOT send I2C start or stop conditions. + */ +// TODO dito +int8_t Storage::i2c_receive(uint8_t len, uint8_t *data) +{ + uint8_t pos = 0; + + for (pos = 0; pos < len; pos++) { + if (pos == len-1) { + // Don't ACK the last byte + TWCR = _BV(TWINT) | _BV(TWEN); + } else { + // Automatically send ACK + TWCR = _BV(TWINT) | _BV(TWEN) | _BV(TWEA); + } + while (!(TWCR & _BV(TWINT))); + data[pos] = TWDR; + } + + return pos + 1; +} + +/* + * Writes len bytes of data into the EEPROM, starting at byte number pos. + * Does not check for page boundaries yet. + * Includes a complete I2C transaction. + */ +int8_t Storage::i2c_write(uint16_t pos, uint8_t len, uint8_t *data) +{ + uint8_t addr_buf[2]; + + addr_buf[0] = pos >> 8; + addr_buf[1] = pos & 0xff; + + i2c_start_write(); + i2c_send(2, addr_buf); + i2c_send(len, data); + i2c_stop(); + + return 0; // TODO proper return code to indicate write errors +} + +/* + * Reads len bytes of data from the EEPROM, starting at byte number pos. + * Does not check for page boundaries yet. + * Includes a complete I2C transaction. + */ +int8_t Storage::i2c_read(uint16_t pos, uint8_t len, uint8_t *data) +{ + uint8_t addr_buf[2]; + addr_buf[0] = pos >> 8; + addr_buf[1] = pos & 0xff; + + i2c_start_write(); + i2c_send(2, addr_buf); + i2c_start_read(); + i2c_receive(len, data); + i2c_stop(); + + return 0; // TODO proper return code to indicate read/write errors +} diff --git a/src/storage.h b/src/storage.h new file mode 100644 index 0000000..75998bb --- /dev/null +++ b/src/storage.h @@ -0,0 +1,23 @@ +#include <avr/io.h> +#include <avr/interrupt.h> +#include <stdlib.h> + +#define I2C_EEPROM_ADDR 0x50 + +class Storage { + private: + void i2c_start_write(void); + void i2c_start_read(void); + void i2c_stop(void); + int8_t i2c_send(uint8_t len, uint8_t *data); + int8_t i2c_receive(uint8_t len, uint8_t *data); + int8_t i2c_read(uint16_t addr, uint8_t len, uint8_t *data); + int8_t i2c_write(uint16_t addr, uint8_t len, uint8_t *data); + // TODO "file system" housekeeping (index of first free page) + public: + Storage() {}; + void enable(); + // TODO load / save methods for animations +}; + +extern Storage storage; |