From 34be227b9784564940fa5821cb8d63ce7b898dd0 Mon Sep 17 00:00:00 2001 From: Savanni D'Gerinel Date: Mon, 5 Jun 2023 09:58:52 -0400 Subject: [PATCH] Figuring out the device state machine for i2c --- flake.nix | 23 ++++++ i2c-lights/main.c | 73 ++++++++++++++++++ i2c/device.c | 147 ++++++++++++++++++++++++++++++++++++ i2c/i2c.c | 31 -------- i2c/i2c.h | 44 ++++++++++- i2c_light_controller/main.c | 18 +++++ 6 files changed, 301 insertions(+), 35 deletions(-) create mode 100644 i2c-lights/main.c create mode 100644 i2c/device.c create mode 100644 i2c_light_controller/main.c diff --git a/flake.nix b/flake.nix index f6ab94e..18ca753 100644 --- a/flake.nix +++ b/flake.nix @@ -354,6 +354,29 @@ ]; }; + packages."x86_64-linux"."i2c_lights_" = + let + pkgs = import nixpkgs { system = "x86_64-linux"; }; + avr = pkgs.pkgsCross.avr.buildPackages; + in packages."x86_64-linux"."i2c_lights" { gcc = "${avr.gcc}/bin/avr-gcc"; cflags = mcu_cflags attiny85; avr = true; }; + packages."x86_64-linux"."i2c_lights" = + { gcc, cflags, avr }: + let + pkgs = import nixpkgs { system = "x86_64-linux"; }; + in mkProgram { + pkgs = pkgs; + gcc = gcc; + cflags = cflags; + pname = "i2c_lights"; + psrc = ./i2c-lights; + inherit avr; + + pbuildInputs = [ + (packages."x86_64-linux"."i2c" { inherit gcc cflags; }) + (packages."x86_64-linux"."dio" { inherit gcc cflags; }) + ]; + }; + devShell."x86_64-linux" = let pkgs = import nixpkgs { system = "x86_64-linux"; }; diff --git a/i2c-lights/main.c b/i2c-lights/main.c new file mode 100644 index 0000000..bf936d3 --- /dev/null +++ b/i2c-lights/main.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include + +typedef enum registers { + POWER_OFF = 0x00, + SET_LIGHTS = 0x01, +} registers_e; + +int main(void) { + i2c_device_t * i2c = i2c_device_init( + (dio_t){ .ddr = &DDRB, .port = &PORTB, .addr = 4 }, + (dio_t){ .ddr = &DDRB, .port = &PORTB, .addr = 3 }, + 0x93, + 3 + ); + + dio_t blue = (dio_t){ .ddr = &DDRB, .port = &PORTB, .addr = 0 }; + dio_t green = (dio_t){ .ddr = &DDRB, .port = &PORTB, .addr = 1 }; + dio_t red = (dio_t){ .ddr = &DDRB, .port = &PORTB, .addr = 2 }; + + dio_set_direction(&blue, LINE_OUT); + dio_set_direction(&green, LINE_OUT); + dio_set_direction(&red, LINE_OUT); + + set_sleep_mode(SLEEP_MODE_IDLE); + while (1) { + sei(); + sleep_mode(); + cli(); + + i2c_device_step(i2c_device_t *i2c); + + // Now, it's necessary to check whether the register has been set + + // /* SDA has changed. If it's low, let's listen for data */ + // if (!dio_read(i2c.sda)) { + // /* data incoming. Read the address */ + // uint8_t status = i2c_ok; + // uint8_t addr = i2c_client_read_byte(&i2c, &status); + // if (addr == 0x93) { + // /* Send the ack signal by pulling SDA low for one tick */ + // i2c_client_write_bit(&i2c, 0, &status); + + // uint8_t reg = i2c_client_read_byte(&i2c, &status); + // switch (reg) { + // case POWER_OFF: + // /* turn all of the lights off and put the device into lowest sleep mode */ + // break; + // case SET_LIGHTS: + // uint8_t light_status = i2c_client_read_byte(&i2c, &status); + // dio_set(&red, light_status & _BV(2)); + // dio_set(&green, light_status & _BV(1)); + // dio_set(&blue, light_status & _BV(0)); + // break; + // default: + // /* unrecognized register */ + // i2c_client_write(&i2c, 1, &status); + // } + // } else { + // /* The message was not for us, so wait until the bus goes idle */ + // bus_state_e state = ACTIVE; + // while (1) { + // sei(); + // sleep_mode(); + // cli(); + + // } + // } + // } + } +} diff --git a/i2c/device.c b/i2c/device.c new file mode 100644 index 0000000..02fed65 --- /dev/null +++ b/i2c/device.c @@ -0,0 +1,147 @@ + +i2c_device_t * i2c_device_init(dio_t clock, dio_t data, uint8_t address, uint8_t register_count) { + i2c_device_t *self = malloc(sizeof(i2c_device_t) + register_count); + + self->sda = data; + self->scl = clock; + self->addr = address; + self->bus_state_e = IDLE; + self->register_count = register_count; + self->target_register = -1; + for (int i = 0; i < register_count; i++) { + self->registers[i] = 0; + } + + return self +} + +/* +void i2c_device_write_bit(i2c_bus_t *bus, uint8_t value, i2c_error_e *error) { + if (*error != i2c_ok) return; + + while (read_scl(bus) != 0) { + if (value) { + set_sda(bus); + } else { + clear_sda(bus); + } + } +} + +void i2c_device_write_byte(i2c_bus_t *bus, uint8_t value, i2c_error_e *error) { + for (int i = 7; i >= 0; i--) { + i2c_write_bit(bus, value & _BV(i), error); + } +} + +uint8_t i2c_device_read_bit(i2c_bus_t *bus, i2c_error_e *error) { + if (*error != i2c_ok) return 0; + + while (read_scl(bus) == 0) { + i2c_delay(); + } + + return read_sda(bus); +} + +uint8_t i2c_device_read_byte(i2c_bus_t *bus, i2c_error_e *error) { + if (*error != i2c_ok) return 0; + + uint8_t value = 0; + for (int i = 0; i < 8; i++) { + value = (value << 1) | i2c_device_read_bit(bus, error); + } + + return value; +} + +void i2c_status_to_string(i2c_error_e status, char msg[20]) { + switch (status) { + case i2c_ok: + snprintf(msg, 20, "ok"); + break; + case i2c_arbitration_lost: + snprintf(msg, 20, "arbitration lost"); + break; + case i2c_timeout: + snprintf(msg, 20, "timeout"); + break; + case i2c_nak: + snprintf(msg, 20, "nak"); + break; + } +} + +void i2c_wait_for_idle(i2c_device_t *bus, i2c_error_e *error) { + switch (bus_state) { + case ACTIVE: + if (dio_read(&i2c.scl) && !dio_read(&i2c.sda)) { + state = INACTIVE_1; + } + break; + case INACTIVE_1: + if (dio_read(&i2c.scl) && dio_read(&i2c.sda)) { + state = IDLE; + } else { + state = ACTIVE; + } + break; + case IDLE: + break; + } +} + +bool i2c_is_idle(i2c_device_t *bus) { + return bus->state == IDLE, +} +*/ + + +void i2c_device_step(i2c_device_t *self) { + uint8_t sda = dio_read(self.sda); + uint8_t scl = dio_read(self.scl); + + i2c_error_e status = i2c_ok; + + switch (self->state) { + case IDLE: + if (!sda) self->state = START_SDA; + break; + case START_SDA: + if (sda) { + self->state = IDLE; + break; + } + if (!scl) self->state = START; + break; + case START: + uint8_t header = i2c_device_read_byte(self, &status); + uint8_t addr = header >> 1; + uint8_t mode = header & 0x01; + if addr == self->addr { + self->state = ACTIVE_LISTEN; + if (mode) { + self->direction = DEVICE_TO_HOST; + } else { + self->direction = HOST_TO_DEVICE; + } + i2c_device_write_bit(self, 0, &status); + } else { + self->state = ACTIVE_IGNORE; + } + break; + case ACTIVE_LISTEN: + // Do all of the stuff to read data from the host + break; + case ACTIVE_IGNORE: + break; + case INACTIVE: + break; + } +} + +/* +void i2c_device_release_clock(i2c_device_bus_t *bus) { + dio_set(bus->scl); +} +*/ diff --git a/i2c/i2c.c b/i2c/i2c.c index ff9eb2d..f892161 100644 --- a/i2c/i2c.c +++ b/i2c/i2c.c @@ -209,34 +209,3 @@ uint8_t i2c_read_byte(i2c_bus_t *bus, bool final_nak, i2c_error_e *error) { return value; } - -/* -void i2c_host_write(i2c_bus_t *bus, uint8_t address, uint8_t *data, size_t length, i2c_error_e *error) { - if (*error != i2c_ok) return; - - i2c_host_write_packet(bus, address << 1, error); - if (*error != ok) return; - - for (int i = 0; i < length; i++) { - i2c_host_write_packet(bus, data[i], error); - if (*error != ok) return; - } -} -*/ - -void i2c_status_to_string(i2c_error_e status, char msg[20]) { - switch (status) { - case i2c_ok: - snprintf(msg, 20, "ok"); - break; - case i2c_arbitration_lost: - snprintf(msg, 20, "arbitration lost"); - break; - case i2c_timeout: - snprintf(msg, 20, "timeout"); - break; - case i2c_nak: - snprintf(msg, 20, "nak"); - break; - } -} diff --git a/i2c/i2c.h b/i2c/i2c.h index 5241a76..8f5ed26 100644 --- a/i2c/i2c.h +++ b/i2c/i2c.h @@ -15,6 +15,20 @@ You should have received a copy of the GNU General Public License along with thi #include +typedef enum i2c_bus_state { + IDLE, + START_SDA, + START, + ACTIVE_LISTEN, + ACTIVE_IGNORE, + INACTIVE, +} i2c_bus_state_e; + +typedef enum message_direction { + HOST_TO_DEVICE, + DEVICE_TO_HOST, +} message_direction_e; + typedef enum { i2c_ok, i2c_arbitration_lost, @@ -30,15 +44,19 @@ typedef struct { dio_t scl; } i2c_bus_t; -/* typedef struct { dio_t sda; dio_t scl; uint8_t addr; -} i2c_client_t; -*/ + i2c_bus_state_e state; + message_direction_e direction; -i2c_bus_t * i2c_init(dio_t, dio_t); + uint8_t register_count; + int8_t target_register; + uint8_t registers[]; +} i2c_device_t; + +i2c_bus_t * i2c_init(dio_t clock, dio_t data); void i2c_start(i2c_bus_t *bus, i2c_error_e *error); void i2c_end(i2c_bus_t *bus, i2c_error_e *error); @@ -51,4 +69,22 @@ uint8_t i2c_read_byte(i2c_bus_t *bus, bool final_nak, i2c_error_e *error); void i2c_status_to_string(i2c_error_e status, char msg[20]); +i2c_device_t * i2c_device_init(dio_t clock, dio_t data, uint8_t address, uint8_t register_count); + +void i2c_device_step(i2c_device_t *); +void i2c_device_release_bus(i2c_device_t *); + +/* +void i2c_device_bus_state_update(i2c_device_bus_t *bus); + +void i2c_set_register(i2c_registers_t *regs, i2c_error_e *error) { +} + +void i2c_register_write(i2c_registers_t *regs, i2c_error_e *error) { +} + +void i2c_register_read(i2c_registers_t *regs, i2c_error_e *error) { +} +*/ + #endif diff --git a/i2c_light_controller/main.c b/i2c_light_controller/main.c new file mode 100644 index 0000000..7a119bf --- /dev/null +++ b/i2c_light_controller/main.c @@ -0,0 +1,18 @@ +#include +#include + +typedef enum registers { + POWER_OFF = 0x00, + SET_LIGHTS = 0x01, +} registers_e; + +int main(void) { + i2c_bus_t *bus = i2c_init( + (dio_t){ .ddr = &DDRB, .port = &PORTB, .addr = 4 }, + (dio_t){ .ddr = &DDRB, .port = &PORTB, .addr = 3 }, + ); + + while(1) { + } +} +