cpu/sam0_common: I2C: add support for SAMD20
This commit is contained in:
parent
81e06f46a4
commit
93d27bb9a0
@ -73,6 +73,27 @@ static inline SercomI2cm *bus(i2c_t dev)
|
|||||||
return i2c_config[dev].dev;
|
return i2c_config[dev].dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _syncbusy(SercomI2cm *dev)
|
||||||
|
{
|
||||||
|
#ifdef SERCOM_I2CM_STATUS_SYNCBUSY
|
||||||
|
while (dev->STATUS.bit.SYNCBUSY) {}
|
||||||
|
#else
|
||||||
|
while (dev->SYNCBUSY.reg) {}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _reset(SercomI2cm *dev)
|
||||||
|
{
|
||||||
|
dev->CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST;
|
||||||
|
while (dev->CTRLA.reg & SERCOM_SPI_CTRLA_SWRST) {}
|
||||||
|
|
||||||
|
#ifdef SERCOM_I2CM_STATUS_SYNCBUSY
|
||||||
|
while (dev->STATUS.bit.SYNCBUSY) {}
|
||||||
|
#else
|
||||||
|
while (dev->SYNCBUSY.bit.SWRST) {}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void i2c_init(i2c_t dev)
|
void i2c_init(i2c_t dev)
|
||||||
{
|
{
|
||||||
uint32_t timeout_counter = 0;
|
uint32_t timeout_counter = 0;
|
||||||
@ -85,12 +106,12 @@ void i2c_init(i2c_t dev)
|
|||||||
|
|
||||||
/* Initialize mutex */
|
/* Initialize mutex */
|
||||||
mutex_init(&locks[dev]);
|
mutex_init(&locks[dev]);
|
||||||
|
|
||||||
/* DISABLE I2C MASTER */
|
/* DISABLE I2C MASTER */
|
||||||
_i2c_poweroff(dev);
|
_i2c_poweroff(dev);
|
||||||
|
|
||||||
/* Reset I2C */
|
/* Reset I2C */
|
||||||
bus(dev)->CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST;
|
_reset(bus(dev));
|
||||||
while (bus(dev)->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK) {}
|
|
||||||
|
|
||||||
/* Turn on power manager for sercom */
|
/* Turn on power manager for sercom */
|
||||||
sercom_clk_en(bus(dev));
|
sercom_clk_en(bus(dev));
|
||||||
@ -114,25 +135,32 @@ void i2c_init(i2c_t dev)
|
|||||||
gpio_init_mux(i2c_config[dev].scl_pin, i2c_config[dev].mux);
|
gpio_init_mux(i2c_config[dev].scl_pin, i2c_config[dev].mux);
|
||||||
|
|
||||||
/* I2C CONFIGURATION */
|
/* I2C CONFIGURATION */
|
||||||
while (bus(dev)->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK) {}
|
_syncbusy(bus(dev));
|
||||||
|
|
||||||
/* Set sercom module to operate in I2C master mode and run in Standby
|
/* Set sercom module to operate in I2C master mode and run in Standby
|
||||||
if user requests it */
|
if user requests it */
|
||||||
bus(dev)->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER |
|
bus(dev)->CTRLA.reg = SERCOM_I2CM_CTRLA_MODE_I2C_MASTER
|
||||||
((i2c_config[dev].flags & I2C_FLAG_RUN_STANDBY) ?
|
| ((i2c_config[dev].flags & I2C_FLAG_RUN_STANDBY) ?
|
||||||
SERCOM_I2CM_CTRLA_RUNSTDBY : 0);
|
SERCOM_I2CM_CTRLA_RUNSTDBY : 0);
|
||||||
|
|
||||||
/* Enable Smart Mode (ACK is sent when DATA.DATA is read) */
|
/* Enable Smart Mode (ACK is sent when DATA.DATA is read) */
|
||||||
bus(dev)->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;
|
bus(dev)->CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN;
|
||||||
|
|
||||||
/* Set SPEED */
|
/* Set SPEED */
|
||||||
|
#ifdef SERCOM_I2CM_CTRLA_SPEED
|
||||||
|
/* > 1000 kHz */
|
||||||
if (fSCL > I2C_SPEED_FAST_PLUS) {
|
if (fSCL > I2C_SPEED_FAST_PLUS) {
|
||||||
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(2);
|
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(2);
|
||||||
|
/* > 400 kHz */
|
||||||
} else if (fSCL > I2C_SPEED_FAST) {
|
} else if (fSCL > I2C_SPEED_FAST) {
|
||||||
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(1);
|
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(1);
|
||||||
|
/* ≤ 400 kHz */
|
||||||
} else {
|
} else {
|
||||||
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(0);
|
bus(dev)->CTRLA.reg |= SERCOM_I2CM_CTRLA_SPEED(0);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
assert(fSCL < I2C_SPEED_FAST_PLUS);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Get the baudrate */
|
/* Get the baudrate */
|
||||||
/* fSCL = fGCLK / (10 + 2 * BAUD) -> BAUD = fGCLK / (2 * fSCL) - 5 */
|
/* fSCL = fGCLK / (10 + 2 * BAUD) -> BAUD = fGCLK / (2 * fSCL) - 5 */
|
||||||
@ -144,9 +172,12 @@ void i2c_init(i2c_t dev)
|
|||||||
/* Ensure baudrate is within limits */
|
/* Ensure baudrate is within limits */
|
||||||
assert(tmp_baud < 255 && tmp_baud > 0);
|
assert(tmp_baud < 255 && tmp_baud > 0);
|
||||||
|
|
||||||
|
#ifdef SERCOM_I2CM_BAUD_HSBAUD
|
||||||
if (fSCL > I2C_SPEED_FAST_PLUS) {
|
if (fSCL > I2C_SPEED_FAST_PLUS) {
|
||||||
bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_HSBAUD(tmp_baud);
|
bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_HSBAUD(tmp_baud);
|
||||||
} else {
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud);
|
bus(dev)->BAUD.reg = SERCOM_I2CM_BAUD_BAUD(tmp_baud);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,7 +307,7 @@ void _i2c_poweron(i2c_t dev)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bus(dev)->CTRLA.bit.ENABLE = 1;
|
bus(dev)->CTRLA.bit.ENABLE = 1;
|
||||||
while (bus(dev)->SYNCBUSY.bit.ENABLE) {}
|
_syncbusy(bus(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
void _i2c_poweroff(i2c_t dev)
|
void _i2c_poweroff(i2c_t dev)
|
||||||
@ -287,21 +318,21 @@ void _i2c_poweroff(i2c_t dev)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bus(dev)->CTRLA.bit.ENABLE = 0;
|
bus(dev)->CTRLA.bit.ENABLE = 0;
|
||||||
while (bus(dev)->SYNCBUSY.bit.ENABLE) {}
|
_syncbusy(bus(dev));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _start(SercomI2cm *dev, uint16_t addr)
|
static int _start(SercomI2cm *dev, uint16_t addr)
|
||||||
{
|
{
|
||||||
/* Wait for hardware module to sync */
|
/* Wait for hardware module to sync */
|
||||||
DEBUG("Wait for device to be ready\n");
|
DEBUG("Wait for device to be ready\n");
|
||||||
while (dev->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK) {}
|
_syncbusy(dev);
|
||||||
|
|
||||||
/* Set action to ACK. */
|
/* Set action to ACK. */
|
||||||
dev->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;
|
dev->CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT;
|
||||||
|
|
||||||
/* Send Start | Address | Write/Read */
|
/* Send Start | Address | Write/Read */
|
||||||
DEBUG("Generate start condition by sending address\n");
|
DEBUG("Generate start condition by sending address\n");
|
||||||
dev->ADDR.reg = (addr) | (0 << SERCOM_I2CM_ADDR_HS_Pos);
|
dev->ADDR.reg = addr;
|
||||||
|
|
||||||
/* Wait for response on bus. */
|
/* Wait for response on bus. */
|
||||||
if (addr & I2C_READ) {
|
if (addr & I2C_READ) {
|
||||||
@ -353,7 +384,7 @@ static inline int _write(SercomI2cm *dev, const uint8_t *data, int length,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for hardware module to sync */
|
/* Wait for hardware module to sync */
|
||||||
while (dev->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK) {}
|
_syncbusy(dev);
|
||||||
|
|
||||||
DEBUG("Written byte #%i to data reg, now waiting for DR"
|
DEBUG("Written byte #%i to data reg, now waiting for DR"
|
||||||
" to be empty again\n", count);
|
" to be empty again\n", count);
|
||||||
@ -395,7 +426,8 @@ static inline int _read(SercomI2cm *dev, uint8_t *data, int length,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Wait for hardware module to sync */
|
/* Wait for hardware module to sync */
|
||||||
while (dev->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK) {}
|
_syncbusy(dev);
|
||||||
|
|
||||||
/* Check if this is the last byte to read */
|
/* Check if this is the last byte to read */
|
||||||
if (length == 0 && stop) {
|
if (length == 0 && stop) {
|
||||||
/* Send NACK before STOP */
|
/* Send NACK before STOP */
|
||||||
@ -420,7 +452,7 @@ static inline int _read(SercomI2cm *dev, uint8_t *data, int length,
|
|||||||
static inline void _stop(SercomI2cm *dev)
|
static inline void _stop(SercomI2cm *dev)
|
||||||
{
|
{
|
||||||
/* Wait for hardware module to sync */
|
/* Wait for hardware module to sync */
|
||||||
while (dev->SYNCBUSY.reg & SERCOM_I2CM_SYNCBUSY_MASK) {}
|
_syncbusy(dev);
|
||||||
/* Stop command */
|
/* Stop command */
|
||||||
dev->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);
|
dev->CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3);
|
||||||
DEBUG("Stop sent\n");
|
DEBUG("Stop sent\n");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user