/* * FRToSI2C.cpp * * Created on: 14Apr.,2018 * Author: Ralim */ #include "BSP.h" #include "IRQ.h" #include "Setup.h" #include "bl_mcu_sdk/drivers/bl702_driver/std_drv/inc/bl702_dma.h" extern "C" { #include "bflb_platform.h" #include "bl702_dma.h" #include "bl702_glb.h" #include "bl702_i2c.h" } #include // Semaphore for locking users of I2C SemaphoreHandle_t FRToSI2C::I2CSemaphore = nullptr; StaticSemaphore_t FRToSI2C::xSemaphoreBuffer; #define I2C_TIME_OUT (uint16_t)(12000) #define I2C_TX_FIFO_ADDR (0x4000A300 + 0x88) // Used by the irq handler volatile uint8_t *IRQDataPointer; volatile uint8_t IRQDataSizeLeft; volatile bool IRQFailureMarker; volatile TaskHandle_t IRQTaskWaitingHandle = NULL; /****** IRQ Handlers ******/ void i2c_irq_tx_fifo_low() { // Filling tx fifo // Fifo is 32 bit, LSB sent first // FiFo can store up to 2, 32-bit words // So we fill it until it has no free room (or we run out of data) while (IRQDataSizeLeft > 0 && I2C_GetTXFIFOAvailable() > 0) { // Can put in at least 1 byte // Build a 32-bit word from bytes uint32_t value = 0; int packing = IRQDataSizeLeft >= 4 ? 0 : 4 - IRQDataSizeLeft; for (int i = 0; i < 4 && IRQDataSizeLeft > 0; i++) { value >>= 8; value |= (*IRQDataPointer) << 24; // Shift to the left, adding new data to the higher byte IRQDataPointer++; // Shift to next byte IRQDataSizeLeft--; } // Handle shunting remaining bytes if not a full 4 to send for (int i = 0; i < packing; i++) { value >>= 8; } // Push the new value to the fifo *((volatile uint32_t *)I2C_TX_FIFO_ADDR) = value; } if (IRQDataSizeLeft == 0) { // Disable IRQ, were done I2C_IntMask(I2C0_ID, I2C_TX_FIFO_READY_INT, MASK); } } void i2c_irq_done() { IRQFailureMarker = false; // Mask IRQ's back off FRToSI2C::CpltCallback(); // Causes the lock to be released } void i2c_irq_nack() { IRQFailureMarker = true; // Mask IRQ's back off FRToSI2C::CpltCallback(); // Causes the lock to be released } /****** END IRQ Handlers ******/ void FRToSI2C::CpltCallback() { // This is only triggered from IRQ context I2C_IntMask(I2C0_ID, I2C_TX_FIFO_READY_INT, MASK); I2C_IntMask(I2C0_ID, I2C_TRANS_END_INT, MASK); I2C_IntMask(I2C0_ID, I2C_NACK_RECV_INT, MASK); CPU_Interrupt_Disable(I2C_IRQn); // Disable IRQ's I2C_Disable(I2C0_ID); // Disable I2C to tidy up // Unlock the semaphore && allow task switch if desired by RTOS BaseType_t xHigherPriorityTaskWoken = pdFALSE; xSemaphoreGiveFromISR(I2CSemaphore, &xHigherPriorityTaskWoken); vTaskNotifyGiveFromISR(IRQTaskWaitingHandle, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } bool FRToSI2C::I2C_RegisterWrite(uint8_t address, uint8_t reg, uint8_t data) { return Mem_Write(address, reg, &data, 1); } uint8_t FRToSI2C::I2C_RegisterRead(uint8_t add, uint8_t reg) { uint8_t temp = 0; Mem_Read(add, reg, &temp, 1); return temp; } bool FRToSI2C::Mem_Read(uint16_t DevAddress, uint16_t read_address, uint8_t *p_buffer, uint16_t number_of_byte) { if (!lock()) { return false; } I2C_Transfer_Cfg i2cCfg = {0, DISABLE, 0, 0, 0, 0}; BL_Err_Type err = ERROR; i2cCfg.slaveAddr = DevAddress >> 1; i2cCfg.stopEveryByte = DISABLE; i2cCfg.subAddr = read_address; i2cCfg.dataSize = number_of_byte; i2cCfg.data = p_buffer; i2cCfg.subAddrSize = 1; // one byte address vTaskSuspendAll(); /* --------------- */ err = I2C_MasterReceiveBlocking(I2C0_ID, &i2cCfg); xTaskResumeAll(); bool res = err == SUCCESS; if (!res) { I2C_Unstick(); } unlock(); return res; } bool FRToSI2C::Mem_Write(uint16_t DevAddress, uint16_t MemAddress, uint8_t *p_buffer, uint16_t number_of_byte) { if (!lock()) { return false; } I2C_Transfer_Cfg i2cCfg = {0, DISABLE, 0, 0, 0, 0}; BL_Err_Type err = ERROR; i2cCfg.slaveAddr = DevAddress >> 1; i2cCfg.stopEveryByte = DISABLE; i2cCfg.subAddr = MemAddress; i2cCfg.dataSize = number_of_byte; i2cCfg.data = p_buffer; i2cCfg.subAddrSize = 1; // one byte address // Store handles for IRQ IRQDataPointer = p_buffer; IRQDataSizeLeft = number_of_byte; IRQTaskWaitingHandle = xTaskGetCurrentTaskHandle(); IRQFailureMarker = false; // Setup and run I2C_Init(I2C0_ID, I2C_WRITE, &i2cCfg); // Setup hardware for the I2C init header with the device address I2C_IntMask(I2C0_ID, I2C_TRANS_END_INT, UNMASK); I2C_IntMask(I2C0_ID, I2C_NACK_RECV_INT, UNMASK); I2C_IntMask(I2C0_ID, I2C_TX_FIFO_READY_INT, UNMASK); I2C_Int_Callback_Install(I2C0_ID, I2C_TRANS_END_INT, i2c_irq_done); I2C_Int_Callback_Install(I2C0_ID, I2C_NACK_RECV_INT, i2c_irq_nack); I2C_Int_Callback_Install(I2C0_ID, I2C_TX_FIFO_READY_INT, i2c_irq_tx_fifo_low); CPU_Interrupt_Enable(I2C_IRQn); i2c_irq_tx_fifo_low(); // Start I2C_Enable(I2C0_ID); // Wait for transfer in background ulTaskNotifyTake(pdTRUE, TICKS_100MS); MSG((char *)"-"); return !IRQFailureMarker; } bool FRToSI2C::Transmit(uint16_t DevAddress, uint8_t *pData, uint16_t Size) { return Mem_Write(DevAddress, pData[0], pData + 1, Size - 1); } bool FRToSI2C::probe(uint16_t DevAddress) { uint8_t temp[1]; return Mem_Read(DevAddress, 0x00, temp, sizeof(temp)); } void FRToSI2C::I2C_Unstick() { unstick_I2C(); } bool FRToSI2C::lock() { if (I2CSemaphore == nullptr) { return false; } return xSemaphoreTake(I2CSemaphore, TICKS_SECOND) == pdTRUE; } void FRToSI2C::unlock() { xSemaphoreGive(I2CSemaphore); } bool FRToSI2C::writeRegistersBulk(const uint8_t address, const I2C_REG *registers, const uint8_t registersLength) { for (int index = 0; index < registersLength; index++) { if (!I2C_RegisterWrite(address, registers[index].reg, registers[index].val)) { return false; } if (registers[index].pause_ms) { delay_ms(registers[index].pause_ms); } } return true; } bool FRToSI2C::wakePart(uint16_t DevAddress) { // wakepart is a special case where only the device address is sent if (!lock()) { return false; } uint8_t temp[1] = {0}; I2C_Transfer_Cfg i2cCfg = {0, DISABLE, 0, 0, 0, 0}; BL_Err_Type err = ERROR; i2cCfg.slaveAddr = DevAddress >> 1; i2cCfg.stopEveryByte = DISABLE; i2cCfg.subAddr = 0; i2cCfg.dataSize = 1; i2cCfg.data = temp; i2cCfg.subAddrSize = 0; err = I2C_MasterReceiveBlocking(I2C0_ID, &i2cCfg); bool res = err == SUCCESS; if (!res) { I2C_Unstick(); } unlock(); return res; }