diff --git a/src/sam4e8e/Kconfig b/src/sam4e8e/Kconfig index 814c4b79..72e21857 100644 --- a/src/sam4e8e/Kconfig +++ b/src/sam4e8e/Kconfig @@ -6,7 +6,7 @@ config SAM_SELECT bool default y select HAVE_GPIO -# select HAVE_GPIO_I2C + select HAVE_GPIO_I2C select HAVE_GPIO_ADC select HAVE_GPIO_SPI select HAVE_GPIO_BITBANGING diff --git a/src/sam4e8e/Makefile b/src/sam4e8e/Makefile index 6e8a193c..f9aa3f0c 100644 --- a/src/sam4e8e/Makefile +++ b/src/sam4e8e/Makefile @@ -19,6 +19,7 @@ src-y += ../lib/cmsis-sam4e/gcc/system_sam4e.c \ ../lib/cmsis-sam4e/gcc/gcc/startup_sam4e.c src-$(CONFIG_HAVE_GPIO_SPI) += sam4e8e/spi.c +src-$(CONFIG_HAVE_GPIO_I2C) += sam4e8e/i2c.c src-$(CONFIG_SERIAL) += sam4e8e/serial.c generic/serial_irq.c src-$(CONFIG_HAVE_GPIO) += sam4e8e/gpio.c src-y += generic/crc16_ccitt.c generic/alloc.c diff --git a/src/sam4e8e/gpio.h b/src/sam4e8e/gpio.h index 2ac13008..df7f8c6e 100644 --- a/src/sam4e8e/gpio.h +++ b/src/sam4e8e/gpio.h @@ -46,4 +46,16 @@ struct spi_config spi_setup(uint32_t bus, uint8_t mode, uint32_t rate); void spi_transfer(struct spi_config config, uint8_t receive_data , uint8_t len, uint8_t *data); void spi_prepare(struct spi_config config); + +struct i2c_config { + void *twi; + 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/sam4e8e/i2c.c b/src/sam4e8e/i2c.c new file mode 100644 index 00000000..ccd394cd --- /dev/null +++ b/src/sam4e8e/i2c.c @@ -0,0 +1,166 @@ +// SAM4 I2C Port +// +// Copyright (C) 2018 Florian Heilmann +// Copyright (C) 2018 Kevin O'Connor +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "gpio.h" +#include "internal.h" +#include "command.h" // shutdown +#include "sched.h" // sched_shutdown +#include "autoconf.h" // CONFIG_CLOCK_FREQ +#include "board/misc.h" //timer_from_us + +void +i2c_init(Twi *p_twi, uint32_t rate) +{ + uint32_t twi_id = (p_twi == TWI0) ? ID_TWI0 : ID_TWI1; + if ((PMC->PMC_PCSR0 & (1u << twi_id)) == 0) { + PMC->PMC_PCER0 = 1 << twi_id; + } + if (p_twi == TWI0) { + gpio_set_peripheral(TWI0_SCL_BANK, TWI0_SCL_PIN, TWI0_SCL_PERIPH, 0); + gpio_set_peripheral(TWI0_SDA_BANK, TWI0_SDA_PIN, TWI0_SDA_PERIPH, 0); + } else { + gpio_set_peripheral(TWI1_SCL_BANK, TWI1_SCL_PIN, TWI1_SCL_PERIPH, 0); + gpio_set_peripheral(TWI1_SDA_BANK, TWI1_SDA_PIN, TWI1_SDA_PERIPH, 0); + } + p_twi->TWI_IDR = 0xFFFFFFFF; + (void)p_twi->TWI_SR; + p_twi->TWI_CR = TWI_CR_SWRST; + (void)p_twi->TWI_RHR; + p_twi->TWI_CR = TWI_CR_MSDIS; + p_twi->TWI_CR = TWI_CR_SVDIS; + p_twi->TWI_CR = TWI_CR_MSEN; + + uint32_t cldiv = 0; + uint32_t chdiv = 0; + uint32_t ckdiv = 0; + + cldiv = CONFIG_CLOCK_FREQ / ((rate > 384000 ? 384000 : rate) * 2) - 4; + + while((cldiv > 255) && (ckdiv < 7)) { + ckdiv++; + cldiv /= 2; + } + + if (rate > 348000) { + chdiv = CONFIG_CLOCK_FREQ / ((2 * rate - 384000) * 2) - 4; + while((chdiv > 255) && (ckdiv < 7)) { + ckdiv++; + chdiv /= 2; + } + } else { + chdiv = cldiv; + } + p_twi->TWI_CWGR = TWI_CWGR_CLDIV(cldiv) | \ + TWI_CWGR_CHDIV(chdiv) | \ + TWI_CWGR_CKDIV(ckdiv); +} + +uint32_t +addr_to_u32(uint8_t addr_len, uint8_t *addr) +{ + uint32_t address = addr[0]; + if (addr_len > 1) { + address <<= 8; + address |= addr[1]; + } + if (addr_len > 2) { + address <<= 8; + address |= addr[2]; + } + if (addr_len > 3) { + shutdown("Addresses larger than 3 bytes are not supported"); + } + return address; +} + +struct i2c_config +i2c_setup(uint32_t bus, uint32_t rate, uint8_t addr) +{ + if ((bus > 1) | (rate > 400000)) + shutdown("Invalid i2c_setup parameters!"); + Twi *p_twi = (bus == 0) ? TWI0 : TWI1; + i2c_init(p_twi, rate); + return (struct i2c_config){ .twi=p_twi, .addr=addr}; +} + +void +i2c_write(struct i2c_config config, + uint8_t write_len, uint8_t *write) +{ + Twi *p_twi = config.twi; + uint32_t status; + uint32_t bytes_to_send = write_len; + p_twi->TWI_MMR = TWI_MMR_DADR(config.addr); + for(;;) { + status = p_twi->TWI_SR; + if (status & TWI_SR_NACK) + shutdown("I2C NACK error encountered!"); + if (!(status & TWI_SR_TXRDY)) + continue; + if (!bytes_to_send) + break; + p_twi->TWI_THR = *write++; + bytes_to_send--; + } + p_twi->TWI_CR = TWI_CR_STOP; + while(!(p_twi->TWI_SR& TWI_SR_TXCOMP)) { + } +} + +static void +i2c_wait(Twi* p_twi, uint32_t bit, uint32_t timeout) +{ + for (;;) { + uint32_t flags = p_twi->TWI_SR; + if (flags & bit) + break; + if (!timer_is_before(timer_read_time(), timeout)) + shutdown("I2C timeout occured"); + } +} + +void +i2c_read(struct i2c_config config, + uint8_t reg_len, uint8_t *reg, + uint8_t read_len, uint8_t *read) +{ + Twi *p_twi = config.twi; + uint32_t status; + uint32_t bytes_to_send=read_len; + uint8_t stop = 0; + p_twi->TWI_MMR = 0; + p_twi->TWI_MMR = TWI_MMR_MREAD | TWI_MMR_DADR(config.addr) | + ((reg_len << TWI_MMR_IADRSZ_Pos) & + TWI_MMR_IADRSZ_Msk); + p_twi->TWI_IADR = 0; + p_twi->TWI_IADR = addr_to_u32(reg_len, reg); + if (bytes_to_send == 1) { + p_twi->TWI_CR = TWI_CR_START | TWI_CR_STOP; + stop = 1; + } else { + p_twi->TWI_CR = TWI_CR_START; + stop = 0; + } + while (bytes_to_send > 0) { + status = p_twi->TWI_SR; + if (status & TWI_SR_NACK) { + shutdown("I2C NACK error encountered!"); + } + if (bytes_to_send == 1 && !stop) { + p_twi->TWI_CR = TWI_CR_STOP; + stop = 1; + } + i2c_wait(p_twi, TWI_SR_RXRDY, timer_read_time() + timer_from_us(5000)); + if (!(status & TWI_SR_RXRDY)) { + continue; + } + *read++ = p_twi->TWI_RHR; + bytes_to_send--; + } + while (!(p_twi->TWI_SR & TWI_SR_TXCOMP)) {} + (void)p_twi->TWI_SR; +} diff --git a/src/sam4e8e/internal.h b/src/sam4e8e/internal.h new file mode 100644 index 00000000..025b6c70 --- /dev/null +++ b/src/sam4e8e/internal.h @@ -0,0 +1,18 @@ +#include "sam4e.h" + +// I2C pin definitions +#define TWI0_SCL_BANK 'A' +#define TWI0_SCL_PIN PIO_PA4A_TWCK0 +#define TWI0_SCL_PERIPH 'A' + +#define TWI0_SDA_BANK 'A' +#define TWI0_SDA_PIN PIO_PA3A_TWD0 +#define TWI0_SDA_PERIPH 'A' + +#define TWI1_SCL_BANK 'B' +#define TWI1_SCL_PIN PIO_PB5A_TWCK1 +#define TWI1_SCL_PERIPH 'A' + +#define TWI1_SDA_BANK 'B' +#define TWI1_SDA_PIN PIO_PB4A_TWD1 +#define TWI1_SDA_PERIPH 'A'