aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKevin O'Connor <kevin@koconnor.net>2018-11-23 23:49:26 -0500
committerKevin O'Connor <kevin@koconnor.net>2018-11-23 23:49:26 -0500
commit729a2e830677acc460514ccb827d79bcb88ba4d6 (patch)
tree8236968f71875066d6ffa8aeee7f90eb5252b806 /src
parente5150fe187b9f980ea9148f5087c1994595d56c3 (diff)
downloadkutter-729a2e830677acc460514ccb827d79bcb88ba4d6.tar.gz
kutter-729a2e830677acc460514ccb827d79bcb88ba4d6.tar.xz
kutter-729a2e830677acc460514ccb827d79bcb88ba4d6.zip
avr: Add support for sending I2C messages
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'src')
-rw-r--r--src/avr/Kconfig1
-rw-r--r--src/avr/Makefile1
-rw-r--r--src/avr/gpio.h9
-rw-r--r--src/avr/i2c.c103
4 files changed, 114 insertions, 0 deletions
diff --git a/src/avr/Kconfig b/src/avr/Kconfig
index e0f407c0..b2d6ffe0 100644
--- a/src/avr/Kconfig
+++ b/src/avr/Kconfig
@@ -8,6 +8,7 @@ config AVR_SELECT
select HAVE_GPIO
select HAVE_GPIO_ADC
select HAVE_GPIO_SPI
+ select HAVE_GPIO_I2C
select HAVE_GPIO_HARD_PWM
select HAVE_GPIO_BITBANGING if !MACH_atmega168
select NO_UNSTEP_DELAY
diff --git a/src/avr/Makefile b/src/avr/Makefile
index 5b95d792..775bafc3 100644
--- a/src/avr/Makefile
+++ b/src/avr/Makefile
@@ -12,6 +12,7 @@ src-y += avr/main.c avr/timer.c
src-$(CONFIG_HAVE_GPIO) += avr/gpio.c
src-$(CONFIG_HAVE_GPIO_ADC) += avr/adc.c
src-$(CONFIG_HAVE_GPIO_SPI) += avr/spi.c
+src-$(CONFIG_HAVE_GPIO_I2C) += avr/i2c.c
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += avr/hard_pwm.c
src-$(CONFIG_AVR_WATCHDOG) += avr/watchdog.c
src-$(CONFIG_AVR_USBSERIAL) += avr/usbserial.c generic/usb_cdc.c
diff --git a/src/avr/gpio.h b/src/avr/gpio.h
index 4b10b3d7..9d98ee70 100644
--- a/src/avr/gpio.h
+++ b/src/avr/gpio.h
@@ -45,4 +45,13 @@ void spi_prepare(struct spi_config config);
void spi_transfer(struct spi_config config, uint8_t receive_data
, uint8_t len, uint8_t *data);
+struct i2c_config {
+ uint8_t addr;
+};
+
+struct i2c_config i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr);
+void i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write);
+void i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg
+ , uint8_t read_len, uint8_t *read);
+
#endif // gpio.h
diff --git a/src/avr/i2c.c b/src/avr/i2c.c
new file mode 100644
index 00000000..b4c0ad75
--- /dev/null
+++ b/src/avr/i2c.c
@@ -0,0 +1,103 @@
+// I2C functions on AVR
+//
+// Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include <avr/io.h> // TWCR
+#include "autoconf.h" // CONFIG_CLOCK_FREQ
+#include "board/misc.h" // timer_is_before
+#include "command.h" // shutdown
+#include "gpio.h" // i2c_setup
+#include "internal.h" // GPIO
+#include "sched.h" // sched_shutdown
+
+#if CONFIG_MACH_atmega168 || CONFIG_MACH_atmega328 || CONFIG_MACH_atmega328p
+static const uint8_t SCL = GPIO('C', 5), SDA = GPIO('C', 4);
+#elif CONFIG_MACH_atmega644p || CONFIG_MACH_atmega1284p
+static const uint8_t SCL = GPIO('C', 0), SDA = GPIO('C', 1);
+#elif CONFIG_MACH_at90usb1286 || CONFIG_MACH_at90usb646 || CONFIG_MACH_atmega32u4 || CONFIG_MACH_atmega1280 || CONFIG_MACH_atmega2560
+static const uint8_t SCL = GPIO('D', 0), SDA = GPIO('D', 1);
+#endif
+
+static void
+i2c_init(void)
+{
+ if (TWCR & (1<<TWEN))
+ // Already setup
+ return;
+
+ // Setup output pins and enable pullups
+ gpio_out_setup(SDA, 1);
+ gpio_out_setup(SCL, 1);
+
+ // Set 100Khz frequency
+ TWSR = 0;
+ TWBR = ((CONFIG_CLOCK_FREQ / 100000) - 16) / 2;
+
+ // Enable interface
+ TWCR = (1<<TWEN);
+}
+
+struct i2c_config
+i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr)
+{
+ if (bus)
+ shutdown("Unsupported i2c bus");
+ i2c_init();
+ return (struct i2c_config){ .addr=addr };
+}
+
+static void
+i2c_wait(uint32_t timeout)
+{
+ for (;;) {
+ if (TWCR & (1<<TWINT))
+ break;
+ if (!timer_is_before(timer_read_time(), timeout))
+ shutdown("i2c timeout");
+ }
+}
+
+static void
+i2c_start(uint32_t timeout)
+{
+ TWCR = (1<<TWEN) | (1<<TWINT) | (1<<TWSTA);
+ i2c_wait(timeout);
+ uint32_t status = TWSR;
+ if (status != 0x10 && status != 0x08)
+ shutdown("Failed to send i2c start");
+}
+
+static void
+i2c_send_byte(uint8_t b, uint32_t timeout)
+{
+ TWDR = b;
+ TWCR = (1<<TWEN) | (1<<TWINT);
+ i2c_wait(timeout);
+}
+
+static void
+i2c_stop(uint32_t timeout)
+{
+ TWCR = (1<<TWEN) | (1<<TWINT) | (1<<TWSTO);
+}
+
+void
+i2c_write(struct i2c_config config, uint8_t write_len, uint8_t *write)
+{
+ uint32_t timeout = timer_read_time() + timer_from_us(5000);
+
+ i2c_start(timeout);
+ i2c_send_byte(config.addr, timeout);
+ while (write_len--)
+ i2c_send_byte(*write++, timeout);
+ i2c_stop(timeout);
+}
+
+void
+i2c_read(struct i2c_config config, uint8_t reg_len, uint8_t *reg
+ , uint8_t read_len, uint8_t *read)
+{
+ shutdown("i2c_read not supported on avr");
+}