diff --git a/bootloaders/zero/board_driver_i2c.c b/bootloaders/zero/board_driver_i2c.c index a83fccf03fbd59653c6e94259f84370e016a8926..c7d73deb671796dd3958c84de014dc6d4495a0bb 100644 --- a/bootloaders/zero/board_driver_i2c.c +++ b/bootloaders/zero/board_driver_i2c.c @@ -18,31 +18,55 @@ */ #include "board_driver_i2c.h" +#include "board_driver_led.h" #ifdef CONFIGURE_PMIC /*- Definitions -------------------------------------------------------------*/ -#define I2C_SERCOM SERCOM2 -#define I2C_SERCOM_PMUX PORT_PMUX_PMUXE_D_Val -#define I2C_SERCOM_GCLK_ID SERCOM0_GCLK_ID_CORE +#define I2C_SERCOM SERCOM0 +#define I2C_SERCOM_GCLK_ID GCLK_CLKCTRL_ID_SERCOM0_CORE_Val #define I2C_SERCOM_CLK_GEN 0 #define I2C_SERCOM_APBCMASK PM_APBCMASK_SERCOM0 -#define T_RISE 215e-9 // Depends on the board, actually +static uint8_t txBuffer[2]; +static uint8_t rxBuffer[1]; +static uint8_t txBufferLen = 0; +static uint8_t rxBufferLen = 0; +static uint8_t txAddress; -enum +typedef enum { - I2C_TRANSFER_WRITE = 0, - I2C_TRANSFER_READ = 1, -}; + WIRE_UNKNOWN_STATE = 0x0ul, + WIRE_IDLE_STATE, + WIRE_OWNER_STATE, + WIRE_BUSY_STATE +} SercomWireBusState; -enum +typedef enum { - I2C_PINS_SDA = (1 << 0), - I2C_PINS_SCL = (1 << 1), -}; + WIRE_WRITE_FLAG = 0x0ul, + WIRE_READ_FLAG +} SercomWireReadWriteFlag; -#define F_CPU 48000000 +typedef enum +{ + WIRE_MASTER_ACT_NO_ACTION = 0, + WIRE_MASTER_ACT_REPEAT_START, + WIRE_MASTER_ACT_READ, + WIRE_MASTER_ACT_STOP +} SercomMasterCommandWire; + +typedef enum +{ + WIRE_MASTER_ACK_ACTION = 0, + WIRE_MASTER_NACK_ACTION +} SercomMasterAckActionWire; + +typedef enum +{ + I2C_SLAVE_OPERATION = 0x4u, + I2C_MASTER_OPERATION = 0x5u +} SercomI2CMode; static inline void pin_set_peripheral_function(uint32_t pinmux) { @@ -68,136 +92,356 @@ static inline void pin_set_peripheral_function(uint32_t pinmux) PORT->Group[port].PMUX[pin/2].reg |= ( (uint8_t)( (pinmux&0xFFFF) <<(4*(pin&1)) ) ); } -//----------------------------------------------------------------------------- -void i2c_init(int freq) +static inline void initClockNVIC( void ) { + //Setting clock + GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID( I2C_SERCOM_GCLK_ID ) | // Generic Clock 0 (SERCOMx) + GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source + GCLK_CLKCTRL_CLKEN ; - PM->APBCMASK.reg |= I2C_SERCOM_APBCMASK; + while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY ) + { + /* Wait for synchronization */ + } + PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0; +} - GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(I2C_SERCOM_GCLK_ID) | - GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(I2C_SERCOM_CLK_GEN); +static inline bool isBusIdleWIRE( void ) +{ + return I2C_SERCOM->I2CM.STATUS.bit.BUSSTATE == WIRE_IDLE_STATE; +} - I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_SWRST; - while (I2C_SERCOM->I2CM.CTRLA.reg & SERCOM_I2CM_CTRLA_SWRST); +static inline bool isBusOwnerWIRE( void ) +{ + return I2C_SERCOM->I2CM.STATUS.bit.BUSSTATE == WIRE_OWNER_STATE; +} - I2C_SERCOM->I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN; - while (I2C_SERCOM->I2CM.SYNCBUSY.reg); +static inline bool isDataReadyWIRE( void ) +{ + return I2C_SERCOM->I2CS.INTFLAG.bit.DRDY; +} - I2C_SERCOM->I2CM.BAUD.reg = SERCOM_I2CM_BAUD_BAUD(48); - while (I2C_SERCOM->I2CM.SYNCBUSY.reg); +static inline bool isStopDetectedWIRE( void ) +{ + return I2C_SERCOM->I2CS.INTFLAG.bit.PREC; +} - I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_ENABLE | - SERCOM_I2CM_CTRLA_MODE_I2C_MASTER | - SERCOM_I2CM_CTRLA_SDAHOLD(3); - while (I2C_SERCOM->I2CM.SYNCBUSY.reg); +static inline bool isRestartDetectedWIRE( void ) +{ + return I2C_SERCOM->I2CS.STATUS.bit.SR; +} - I2C_SERCOM->I2CM.STATUS.reg |= SERCOM_I2CM_STATUS_BUSSTATE(1); +static inline bool isAddressMatch( void ) +{ + return I2C_SERCOM->I2CS.INTFLAG.bit.AMATCH; +} - pin_set_peripheral_function(PINMUX_PA08C_SERCOM0_PAD0); - pin_set_peripheral_function(PINMUX_PA09C_SERCOM0_PAD1); +static inline bool isMasterReadOperationWIRE( void ) +{ + return I2C_SERCOM->I2CS.STATUS.bit.DIR; } -//----------------------------------------------------------------------------- -bool i2c_start(int addr) +static inline bool isRXNackReceivedWIRE( void ) { - I2C_SERCOM->I2CM.ADDR.reg = addr; + return I2C_SERCOM->I2CM.STATUS.bit.RXNACK; +} - while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) && - 0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)); +static inline int availableWIRE( void ) +{ + return I2C_SERCOM->I2CM.INTFLAG.bit.SB; +} - if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) +static inline uint8_t readDataWIRE( void ) +{ + while( I2C_SERCOM->I2CM.INTFLAG.bit.SB == 0 ) { - I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); - return false; + // Waiting complete receive } - return true; + return I2C_SERCOM->I2CM.DATA.bit.DATA ; } -//----------------------------------------------------------------------------- -bool i2c_stop(void) +/* ========================= + * ===== Sercom WIRE + * ========================= + */ +static inline void resetWIRE() { - if ((I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB) || - (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)) + //I2CM OR I2CS, no matter SWRST is the same bit. + + //Setting the Software bit to 1 + I2C_SERCOM->I2CM.CTRLA.bit.SWRST = 1; + + //Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0 + while(I2C_SERCOM->I2CM.CTRLA.bit.SWRST || I2C_SERCOM->I2CM.SYNCBUSY.bit.SWRST); +} + +static inline void enableWIRE() +{ + // I2C Master and Slave modes share the ENABLE bit function. + + // Enable the I²C master mode + I2C_SERCOM->I2CM.CTRLA.bit.ENABLE = 1 ; + + while ( I2C_SERCOM->I2CM.SYNCBUSY.bit.ENABLE != 0 ) { - I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + // Waiting the enable bit from SYNCBUSY is equal to 0; } - return true; + // Setting bus idle mode + I2C_SERCOM->I2CM.STATUS.bit.BUSSTATE = 1 ; + + while ( I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP != 0 ) + { + // Wait the SYSOP bit from SYNCBUSY coming back to 0 + } } -//----------------------------------------------------------------------------- -bool i2c_read_byte(uint8_t *byte, bool last) +static inline void disableWIRE() { - while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_SB)); + // I2C Master and Slave modes share the ENABLE bit function. - if (last) - I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_ACKACT | SERCOM_I2CM_CTRLB_CMD(3); - else - I2C_SERCOM->I2CM.CTRLB.reg &= ~SERCOM_I2CM_CTRLB_ACKACT; + // Enable the I²C master mode + I2C_SERCOM->I2CM.CTRLA.bit.ENABLE = 0 ; + + while ( I2C_SERCOM->I2CM.SYNCBUSY.bit.ENABLE != 0 ) + { + // Waiting the enable bit from SYNCBUSY is equal to 0; + } +} - *byte = I2C_SERCOM->I2CM.DATA.reg; +static inline void initMasterWIRE( uint32_t baudrate ) +{ + // Initialize the peripheral clock and interruption + initClockNVIC() ; + + //NVIC_EnableIRQ(SERCOM0_IRQn); + //NVIC_SetPriority (SERCOM0_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority */ + + resetWIRE() ; + + // Set master mode and enable SCL Clock Stretch mode (stretch after ACK bit) + I2C_SERCOM->I2CM.CTRLA.reg = SERCOM_I2CM_CTRLA_MODE( I2C_MASTER_OPERATION )/* | + SERCOM_I2CM_CTRLA_SCLSM*/ ; + + // Enable Smart mode and Quick Command + //I2C_SERCOM->I2CM.I2CM.CTRLB.reg = SERCOM_I2CM_CTRLB_SMEN /*| SERCOM_I2CM_CTRLB_QCEN*/ ; + + + // Enable all interrupts + //I2C_SERCOM->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR ; - return true; + // Synchronous arithmetic baudrate + I2C_SERCOM->I2CM.BAUD.bit.BAUD = 48000000 / ( 2 * baudrate) - 1 ; } -//----------------------------------------------------------------------------- -bool i2c_write_byte(uint8_t byte) +static inline void prepareNackBitWIRE( void ) { - I2C_SERCOM->I2CM.DATA.reg = byte; + // Send a NACK + I2C_SERCOM->I2CM.CTRLB.bit.ACKACT = 1; +} - while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB)); +static inline void prepareAckBitWIRE( void ) +{ + // Send an ACK + I2C_SERCOM->I2CM.CTRLB.bit.ACKACT = 0; +} - if (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK) +static inline void prepareCommandBitsWire(uint8_t cmd) +{ + I2C_SERCOM->I2CM.CTRLB.bit.CMD = cmd; + + while(I2C_SERCOM->I2CM.SYNCBUSY.bit.SYSOP) + { + // Waiting for synchronization + } +} + +static inline bool startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag) +{ + // 7-bits address + 1-bits R/W + address = (address << 0x1ul) | flag; + + + // Wait idle or owner bus mode + while ( !isBusIdleWIRE() && !isBusOwnerWIRE() ); + + // Send start and address + I2C_SERCOM->I2CM.ADDR.bit.ADDR = address; + + // Address Transmitted + if ( flag == WIRE_WRITE_FLAG ) // Write mode + { + while( !I2C_SERCOM->I2CM.INTFLAG.bit.MB ) + { + // Wait transmission complete + } + } + else // Read mode + { + while( !I2C_SERCOM->I2CM.INTFLAG.bit.SB ) + { + // If the slave NACKS the address, the MB bit will be set. + // In that case, send a stop condition and return false. + if (I2C_SERCOM->I2CM.INTFLAG.bit.MB) { + I2C_SERCOM->I2CM.CTRLB.bit.CMD = 3; // Stop condition + return false; + } + // Wait transmission complete + } + + // Clean the 'Slave on Bus' flag, for further usage. + //I2C_SERCOM->I2CM.I2CM.INTFLAG.bit.SB = 0x1ul; + } + + + //ACK received (0: ACK, 1: NACK) + if(I2C_SERCOM->I2CM.STATUS.bit.RXNACK) { - I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); return false; } + else + { + return true; + } +} + +static inline bool sendDataMasterWIRE(uint8_t data) +{ + //Send data + I2C_SERCOM->I2CM.DATA.bit.DATA = data; + + //Wait transmission successful + while(!I2C_SERCOM->I2CM.INTFLAG.bit.MB) { + + // If a bus error occurs, the MB bit may never be set. + // Check the bus error bit and bail if it's set. + if (I2C_SERCOM->I2CM.STATUS.bit.BUSERR) { + return false; + } + } + + //Problems on line? nack received? + if(I2C_SERCOM->I2CM.STATUS.bit.RXNACK) + return false; + else + return true; +} + +static inline void i2c_init(uint32_t baud) { + //Master Mode + initMasterWIRE(baud); + enableWIRE(); + + pin_set_peripheral_function(PINMUX_PA08C_SERCOM0_PAD0); + pin_set_peripheral_function(PINMUX_PA09C_SERCOM0_PAD1); +} - return true; +static inline void i2c_end() { + disableWIRE(); } -//----------------------------------------------------------------------------- -bool i2c_busy(int addr) +uint8_t i2c_requestFrom(uint8_t address, uint8_t quantity, bool stopBit) { - bool busy; + if(quantity == 0) + { + return 0; + } - I2C_SERCOM->I2CM.ADDR.reg = addr | I2C_TRANSFER_WRITE; + uint8_t byteRead = 0; - while (0 == (I2C_SERCOM->I2CM.INTFLAG.reg & SERCOM_I2CM_INTFLAG_MB)); + rxBufferLen = 0;; - busy = (0 != (I2C_SERCOM->I2CM.STATUS.reg & SERCOM_I2CM_STATUS_RXNACK)); + if(startTransmissionWIRE(address, WIRE_READ_FLAG)) + { + // Read first data + rxBuffer[rxBufferLen++] = readDataWIRE(); + + // Connected to slave + for (byteRead = 1; byteRead < quantity; ++byteRead) + { + prepareAckBitWIRE(); // Prepare Acknowledge + prepareCommandBitsWire(WIRE_MASTER_ACT_READ); // Prepare the ACK command for the slave + rxBuffer[rxBufferLen++] = readDataWIRE(); // Read data and send the ACK + } + prepareNackBitWIRE(); // Prepare NACK to stop slave transmission + //I2C_SERCOM->I2CM.readDataWIRE(); // Clear data register to send NACK + + if (stopBit) + { + prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); // Send Stop + } + } - I2C_SERCOM->I2CM.CTRLB.reg |= SERCOM_I2CM_CTRLB_CMD(3); + return byteRead; +} - return busy; +void i2c_beginTransmission(uint8_t address) { + // save address of target and clear buffer + txAddress = address; + txBufferLen = 0; } -uint8_t readRegister(uint8_t startAddress) { +// Errors: +// 0 : Success +// 1 : Data too long +// 2 : NACK on transmit of address +// 3 : NACK on transmit of data +// 4 : Other error +uint8_t i2c_endTransmission(bool stopBit) +{ + + // Start I2C transmission + if ( !startTransmissionWIRE( txAddress, WIRE_WRITE_FLAG ) ) + { + prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + return 2 ; // Address error + } + + // Send all buffer + int tempLen = txBufferLen; + while( txBufferLen > 0 ) + { + // Trying to send data + if ( !sendDataMasterWIRE( txBuffer[tempLen-txBufferLen] ) ) + { + prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + return 3 ; // Nack or error + } else { + txBufferLen--; + } + } + + if (stopBit) + { + prepareCommandBitsWire(WIRE_MASTER_ACT_STOP); + } - uint8_t DATA = 0; - i2c_start(PMIC_ADDRESS); - i2c_write_byte(startAddress); - i2c_stop(); + return 0; +} - i2c_start(PMIC_ADDRESS); - i2c_read_byte(&DATA, true); - return DATA; +uint8_t i2c_write(uint8_t ucData) +{ + txBuffer[txBufferLen++] = ucData ; + return 1 ; } +uint8_t readRegister(uint8_t reg) { + i2c_beginTransmission(PMIC_ADDRESS); + i2c_write(reg); + i2c_endTransmission(true); + + i2c_requestFrom(PMIC_ADDRESS, 1, true); + return rxBuffer[0]; +} -/******************************************************************************* - * Function Name : - * Description : - * Input : - * Return : - *******************************************************************************/ -void writeRegister(uint8_t address, uint8_t DATA) { +uint8_t writeRegister(uint8_t reg, uint8_t data) { + i2c_beginTransmission(PMIC_ADDRESS); + i2c_write(reg); + i2c_write(data); + i2c_endTransmission(true); - i2c_start(PMIC_ADDRESS); - i2c_write_byte(address); - i2c_write_byte(DATA); - i2c_stop(); + return 2; } bool disableWatchdog(void) {