diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2018-11-23 23:49:26 -0500 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2018-11-23 23:49:26 -0500 |
commit | 729a2e830677acc460514ccb827d79bcb88ba4d6 (patch) | |
tree | 8236968f71875066d6ffa8aeee7f90eb5252b806 /src/avr | |
parent | e5150fe187b9f980ea9148f5087c1994595d56c3 (diff) | |
download | kutter-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/avr')
-rw-r--r-- | src/avr/Kconfig | 1 | ||||
-rw-r--r-- | src/avr/Makefile | 1 | ||||
-rw-r--r-- | src/avr/gpio.h | 9 | ||||
-rw-r--r-- | src/avr/i2c.c | 103 |
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"); +} |