summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2016-01-25 23:29:43 +0100
committerDaniel Friesel <derf@finalrewind.org>2016-01-25 23:29:43 +0100
commitf4fe825650e1eb30ad57963232047606ac034e5c (patch)
tree08f25b5ad0deacf77f7e2a9c51b17b79cd3e4240
parent55da79c69f302cd86f0776e127daae0a12f557d4 (diff)
refactor I2C class into Storage class. Work-in-progress.
-rw-r--r--src/i2c.cc65
-rw-r--r--src/i2c.h14
-rw-r--r--src/main.cc16
-rw-r--r--src/storage.cc172
-rw-r--r--src/storage.h23
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;