summaryrefslogtreecommitdiff
path: root/src/i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/i2c.c')
-rw-r--r--src/i2c.c130
1 files changed, 130 insertions, 0 deletions
diff --git a/src/i2c.c b/src/i2c.c
new file mode 100644
index 0000000..25b2d0f
--- /dev/null
+++ b/src/i2c.c
@@ -0,0 +1,130 @@
+#include <stdio.h>
+#include <avr/io.h>
+#include <util/twi.h>
+#include <util/delay.h>
+#include <stdbool.h>
+#include <string.h>
+#ifdef DEBUG_BUILD
+#include "debug.h"
+#endif
+
+#include "i2c.h"
+
+
+/** I2C notes
+ * TWDR: data address shift register
+ * contians address or data bytes to be transmitted/recieved
+ * ACK bit not accesible
+ * TWAR: matches recieved addresses
+ * TWCR: control register, sets settings
+ * TWINT: tw interrupt flag
+ * also is dependent on GIE in SREG
+ * TWSR: TWI status register (only matters if TWI interurupt flag asserted)
+
+ * Steps simplified:
+ * SEND START
+ * Send START with TWCR
+ * Set TWINT in TWCR to 1; as this clears the flag
+ * TWSR will then say start has begun
+ * Load SLA+W into TWDR (what is this? TODO)
+ * Write something to TWCR to initiate send? (make sure twint is set after)
+ * poll TWSR interrupt bit to see if we recieve a message (or figure out interrupts)
+ *
+ * Note: You may be wondering why I'm not handling i2c errors.
+ * I have limited time, and I don't think it's nessesary unless I start running into them.
+ *
+ * Some I may work on handling later.
+ *
+ */
+
+void error() {}
+
+void i2c_init() {
+ TWBR = 12;
+ TWSR &= 0b11111100;
+}
+
+
+void i2c_start(uint8_t addr, bool rw) {
+ TWCR = (_BV(TWSTA) | _BV(TWEN) | _BV(TWINT)); //send start signal
+ loop_until_bit_is_set(TWCR, TWINT);
+ if((TWSR & 0xf8) != TW_START) {
+ #ifdef DEBUG_BUILD
+ printf("Couldn't set start condition?\n");
+ #endif
+ error();
+ }
+
+ TWDR = (addr << 1) | rw;
+ TWCR = _BV(TWINT) | _BV(TWEN);
+ loop_until_bit_is_set(TWCR, TWINT);
+
+ if((TWSR & 0xf8) != TW_MT_SLA_ACK) {
+ #ifdef DEBUG_BUILD
+ printf("unhandled NACK in address transmission\
+ TWCR: 0x%x\
+ TWSR: 0x%x\n", TWCR, (TWSR & ~(0b11)));
+ #endif
+ error();
+ }
+
+}
+
+void i2c_stop() { TWCR = (_BV(TWSTO) | _BV(TWEN) | _BV(TWINT)); }
+
+void i2c_send(uint8_t byte) {
+ TWDR = byte; //fist packet is address
+ TWCR = _BV(TWINT) | _BV(TWEN); //send over the byte
+ loop_until_bit_is_set(TWCR, TWINT);
+
+ if((TWSR & 0xf8) != TW_MT_DATA_ACK) {
+ #ifdef DEBUG_BUILD
+ printf("unhandled NACK in data transmission\
+ TWCR: 0x%x\
+ TWSR: 0x%x\n", TWCR, (TWSR & ~(0b11)));
+ #endif
+ error();
+ }
+}
+
+uint8_t i2c_recv() {
+ uint8_t value;
+ loop_until_bit_is_set(TWCR, TWINT);
+ value = TWDR;
+ if((TWSR & 0xf8) != TW_MR_SLA_ACK) {
+#ifdef DEBUG_BUILD
+ printf("Error recieving byte from i2c device\n");
+#endif
+ error();
+ }
+ return value;
+}
+
+//does NOT control start/stop, simply reads TWDR
+
+uint8_t i2c_read_reg_addr16(uint8_t device, uint16_t addr) {
+ uint8_t value;
+
+ i2c_start(device, I2C_WRITE);
+ i2c_send((uint8_t)addr & 0xff);
+ i2c_send((uint8_t)addr >> 8);
+ i2c_start(device, I2C_READ);
+ value = i2c_recv();
+ i2c_stop();
+
+ return(value);
+}
+
+void i2c_write_reg(uint8_t device_addr, uint8_t device_reg, uint8_t value) {
+ i2c_start(device_addr, I2C_WRITE);
+ i2c_send(device_reg);
+ i2c_send(value);
+ i2c_stop();
+}
+
+void i2c_write_reg_multi(uint8_t device_addr, uint8_t device_reg, size_t len, uint8_t *values) {
+ i2c_start(device_addr, I2C_WRITE);
+ i2c_send(device_reg);
+ for(size_t on_byte = 0; on_byte < len; on_byte++) i2c_send(values[on_byte]);
+ i2c_stop();
+}