MindSDK_MM32F5270/device/drivers/hal_i2c.c
Yilin Sun 3977144e90
Initial MM32F527x commit.
Signed-off-by: Yilin Sun <imi415@imi.moe>
2023-03-27 21:54:40 +08:00

426 lines
13 KiB
C

/*
* Copyright 2021 MindMotion Microelectronics Co., Ltd.
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "hal_i2c.h"
/* Configure I2C speed to ordinary speed. */
static bool I2C_CalcBandrate(I2C_Type * I2Cx, uint32_t clk, uint32_t bandrate)
{
/*
* SCLH = (xSHR + 12) * I2C_CLK + tSYNC1;
* SCLL = (xSLR + 1) * I2C_CLK + tSYNC2;
* tSYNC1 & tSYNC2 equal 0 ~ 1 clk.
*/
if ( (clk / 24u) < bandrate ) /* the system clock cannot meet the baud rate requirement. */
{
return false;
}
else
{
uint32_t tmp = clk / bandrate;
I2Cx->SSHR = tmp / 2u - 12u; /* Configure high level count in normal speed. */
I2Cx->SSLR = tmp / 2u - 1u; /* Configure low level count in normal speed. */
I2Cx->FSHR = tmp / 2u - 14u; /* Configure high level count in fast speed. */
I2Cx->FSLR = tmp / 2u - 3u; /* Configure low level count in fast speed. */
return true;
}
}
/* Initialize I2C master mode, Initialization failure return false, Initialization success return true. */
bool I2C_InitMaster(I2C_Type * I2Cx, I2C_Master_Init_Type * init)
{
I2Cx->ENR &= ~I2C_ENR_ENABLE_MASK; /* Disable I2C. */
if ( !I2C_CalcBandrate(I2Cx, init->ClockFreqHz, init->BaudRate) ) /* The system clock cannot meet the baud rate requirement. */
{
return false;
}
I2Cx->CR = I2C_CR_SPEED(1u);
/* Setup I2C. */
I2Cx->CR &= ~I2C_CR_MASTER10_MASK; /* Address format. */
I2Cx->CR |= I2C_CR_RESTART_MASK /* Generate restart signal. */
| I2C_CR_DISSLAVE_MASK /* Disable slave module. */
| I2C_CR_REPEN_MASK /* Enable sending restart condition. */
| I2C_CR_EMPINT_MASK /* Control tx_empty interrupt generation. */
| I2C_CR_MASTER_MASK; /* Enable master module. */
I2Cx->IMR = 0u; /* Close all interrupts. */
I2Cx->RXTLR = 0u; /* Configure the sending receive value. */
I2Cx->TXTLR = 0u; /* Configure the sending threshold value. */
return true; /* Initialize I2C succeeded, return true. */
}
/* Initialize I2C slave mode. */
void I2C_InitSlave(I2C_Type * I2Cx, I2C_Slave_Init_Type * init)
{
I2Cx->ENR &= ~I2C_ENR_ENABLE_MASK; /* Disable I2C. */
I2Cx->CR &= ~(I2C_CR_DISSLAVE_MASK | I2C_CR_MASTER_MASK | I2C_CR_SLAVE10_MASK);
I2Cx->SAR = I2C_SAR_ADDR((uint8_t)init->SlaveAddr);
I2Cx->CR |= I2C_CR_REPEN_MASK;
I2Cx->IMR = 0u;
}
/* Enable I2C. */
void I2C_Enable(I2C_Type * I2Cx, bool enable)
{
if (enable)
{
I2Cx->ENR |= I2C_ENR_ENABLE_MASK;
}
else
{
I2Cx->ENR &= ~I2C_ENR_ENABLE_MASK;
}
}
/* Configuration the target device address. */
void I2C_SetTargetAddr(I2C_Type * I2Cx, uint8_t addr)
{
I2Cx->TAR = I2C_TAR_ADDR(addr);
}
/* Get I2C target device address. */
uint16_t I2C_GetTargetAddr(I2C_Type * I2Cx)
{
return (I2Cx->TAR & I2C_TAR_ADDR_MASK);
}
/* Put data to target device. */
void I2C_PutData(I2C_Type * I2Cx, uint8_t val)
{
I2Cx->DR = I2C_DR_DAT(val);
}
/* Control read-write bit to prepare to read data. */
void I2C_PrepareToGetData(I2C_Type * I2Cx)
{
I2Cx->DR = I2C_DR_CMD_MASK;
}
/* Get the data received by target device. */
uint8_t I2C_GetData(I2C_Type * I2Cx)
{
return ( (uint8_t)I2Cx->DR );
}
/* Get the current status flags of the I2C module. */
uint32_t I2C_GetStatus(I2C_Type * I2Cx)
{
return I2Cx->SR;
}
/* Prepare for the stop, when transfer finish. */
void I2C_Stop(I2C_Type * I2Cx)
{
I2Cx->ENR |= I2C_ENR_ABORT_MASK; /* Prepare for the stop. */
I2Cx->TXABRT; /* Read register to release tx fifo. */
}
/* Enable I2C interrupt. */
void I2C_EnableInterrupts(I2C_Type * I2Cx, uint32_t interrupts, bool enable)
{
if (enable)
{
I2Cx->IMR |= interrupts;
}
else
{
I2Cx->IMR &= ~interrupts;
}
}
/* Get the current enabled interrupts the I2C module. */
uint32_t I2C_GetEnabledInterrupts(I2C_Type * I2Cx)
{
return I2Cx->IMR;
}
/* Get the I2C interrupt status flags of the I2C module. */
uint32_t I2C_GetInterruptStatus(I2C_Type * I2Cx)
{
return (I2Cx->RAWISR & I2Cx->IMR); /* To ensure that the acquired interrupt is an enabled interrupt. */
}
/* Clear I2C interrupt status. */
void I2C_ClearInterruptStatus(I2C_Type * I2Cx, uint32_t interrupts)
{
if ( (I2C_INT_RX_UNDER & interrupts) != 0u ) /* Clear receive buffer under status. */
{
I2Cx->RXUNDER;
}
if ( (I2C_INT_TX_ABORT & interrupts) != 0u ) /* Clear I2C transmit abort status. */
{
I2Cx->TXABRT;
}
if ( (I2C_INT_ACTIVE & interrupts) != 0u ) /* Clear I2C interface activation status. */
{
I2Cx->ACTIV;
}
if ( (I2C_INT_STOP & interrupts) != 0u ) /* Clear I2C stop condition detection status. */
{
I2Cx->STOP;
}
if ( (I2C_INT_START & interrupts) != 0u ) /* Clear I2C start condition detection status. */
{
I2Cx->START;
}
if ( (I2C_INT_READREQ & interrupts) != 0u)
{
I2Cx->RDREQ;
}
if ( (I2C_INT_RX_DONE & interrupts) != 0u)
{
I2Cx->RXDONE;
}
}
/* Performs polling tx. */
bool I2C_MasterWriteBlocking(I2C_Type * I2Cx, I2C_MasterXfer_Type * xfer)
{
/* Put register address. */
I2C_PutData(I2Cx, xfer->TxBuf[0u]);
uint32_t waittime1 = xfer->WaitTimes;
/* Wait to tx fifo empty. */
while ( (0u == (I2C_GetStatus(I2Cx) & I2C_STATUS_TX_EMPTY) ) && (0u != waittime1) )
{
waittime1--;
}
if (0u == waittime1) /* I2C write register address timeout. */
{
return false;
}
uint32_t waittime2 = xfer->WaitTimes;
/* Write data to target device. */
for (uint32_t i = 1u; i < xfer->TxLen; i++)
{
I2C_PutData(I2Cx, xfer->TxBuf[i]);
while ( ( 0u == (I2C_GetStatus(I2Cx) & I2C_STATUS_TX_EMPTY) ) && (0u != waittime2) ) /* Wait to tx fifo empty. */
{
waittime2--;
}
if (0u == waittime2) /* I2C write timeout. */
{
return false;
}
}
I2C_Stop(I2Cx); /* Prepare to stop send data. */
uint32_t waittime3 = xfer->WaitTimes;
/* Wait to I2C not active, which means stop is taking effect. */
while ( (I2C_GetStatus(I2Cx) & I2C_STATUS_ACTIVE) && (0u != waittime3) )
{
waittime3--;
}
if (0u == waittime3) /* The wait operation is timeout. */
{
return false;
}
/* Clear fifo and flags. */
I2Cx->ICR;
I2Cx->TXABRT;
return true;
}
/* Performs polling rx. */
bool I2C_MasterReadBlocking(I2C_Type * I2Cx, I2C_MasterXfer_Type * xfer)
{
I2C_PutData(I2Cx, xfer->TxBuf[0u]); /* Put device register address. */
uint32_t waittime1 = xfer->WaitTimes;
while ( ( 0u == (I2C_GetStatus(I2Cx) & I2C_STATUS_TX_EMPTY) ) && (0u != waittime1) ) /* Wait to tx fifo empty. */
{
waittime1--;
}
if (0u == waittime1)
{
return false;
}
/* read data from target device. */
for (uint32_t i = 0u; i < xfer->RxLen; i++)
{
I2C_PrepareToGetData(I2Cx); /* Swich read-write bit, prepare to get data. */
while ( 0u == (I2C_GetStatus(I2Cx) & I2C_STATUS_RX_NOTEMPTY) ) /* Wait to rx fifo not empty. */
{
if ( 0u == (I2C_GetStatus(I2Cx) & I2C_STATUS_ACTIVE) ) /* Receive is active. */
{
return false;
}
}
xfer->RxBuf[i] = I2C_GetData(I2Cx);
}
I2C_Stop(I2Cx); /* Prepare to stop I2C. */
uint32_t waittime2 = xfer->WaitTimes;
while ( (I2C_GetStatus(I2Cx) & I2C_STATUS_ACTIVE) && (0u != waittime2) ) /* Wait I2C not active, which means stop being effective. */
{
waittime2--;
}
if (0u == waittime2)
{
return false;
}
/* Clear fifo and flags. */
I2Cx->ICR;
I2Cx->TXABRT;
return true;
}
/* I2C master interrupt transfer of the I2C module. */
void I2C_MasterXfer(I2C_Type * I2Cx, I2C_MasterXfer_Type * xfer)
{
I2C_PutData(I2Cx, (uint8_t)xfer->TxBuf[0u]); /* Put target register address. */
I2C_EnableInterrupts(I2Cx, I2C_INT_TX_EMPTY | I2C_INT_TX_ABORT | I2C_INT_STOP, true); /* Enable tx required interrupt. */
xfer->TxIdx = 1u; /* One data has been sent. */
xfer->RxIdx = 0u;
xfer->TxLen--;
}
/* I2C master handler. */
void I2C_MasterXferHandler(I2C_Type * I2Cx, I2C_MasterXfer_Type * xfer, uint32_t interrupts)
{
if ( 0u != (interrupts & I2C_INT_TX_ABORT) ) /* Early termination of program. */
{
I2C_EnableInterrupts(I2Cx, I2C_INT_TX_EMPTY | I2C_INT_TX_ABORT | I2C_INT_STOP, false); /* Clear the interrupt used for tx. */
if (NULL != xfer->AbortCallback)
{
xfer->AbortCallback(xfer); /* Use abort call back. */
}
}
else if ( 0u != (interrupts & I2C_INT_TX_EMPTY) ) /* Tx fifo is empty, can send data. */
{
if (I2C_Direction_Rx == xfer->Direction) /* The current operation is receive, the register address has been sent. */
{
I2C_EnableInterrupts(I2Cx, I2C_INT_TX_EMPTY | I2C_INT_TX_ABORT, false); /* Clear tx interrupt. */
I2C_EnableInterrupts(I2Cx, I2C_INT_RX_NOTEMPTY, true); /* Enable receive required interrupt. */
if (0u != xfer->RxLen) /* The data to be received is not 0. */
{
I2C_PrepareToGetData(I2Cx); /* Prepare to get data. */
xfer->RxLen--;
}
else
{
I2C_Stop(I2Cx); /* No more transmition, prepare to stop. */
}
}
else
{
if (0u == xfer->TxLen) /* Tx finish. */
{
I2C_EnableInterrupts(I2Cx, I2C_INT_TX_EMPTY | I2C_INT_TX_ABORT, false); /* Clear Tx interrupt. */
I2C_Stop(I2Cx); /* Prepare to stop. */
}
else
{
xfer->TxLen--;
I2C_PutData(I2Cx, xfer->TxBuf[xfer->TxIdx++]); /* Tx is not over, continue to put data. */
}
}
}
else if ( 0u != (interrupts & I2C_INT_RX_NOTEMPTY) ) /* Receive interrupt. */
{
if (0u == xfer->RxLen) /* Receive finish. */
{
xfer->RxBuf[xfer->RxIdx++] = I2C_GetData(I2Cx); /* Get last data from I2C bus. */
I2C_EnableInterrupts(I2Cx, I2C_INT_RX_NOTEMPTY, false); /* Clear receive interrupt. */
I2C_Stop(I2Cx); /* Prepare to stop. */
}
else
{
xfer->RxLen--; /* Current count length count -1. */
xfer->RxBuf[xfer->RxIdx++] = I2C_GetData(I2Cx); /* Receive is not over, continue to get data. */
I2C_PrepareToGetData(I2Cx); /* Prepare to get data. */
}
}
else if ( 0u != (interrupts & I2C_INT_STOP) ) /* Xfer stop. */
{
I2C_EnableInterrupts(I2Cx, I2C_INT_STOP, false); /* Clear stop interrupt. */
if ( (0u != xfer->TxLen) || (0u != xfer->RxLen) ) /* The transmission was not completed but terminated. */
{
if (NULL != xfer->AbortCallback)
{
xfer->AbortCallback(xfer); /* Early termination of program, abort callback. */
I2Cx->ICR;
I2Cx->TXABRT; /* Clear FIFO. */
}
}
else
{
if (NULL != xfer->DoneCallback)
{
xfer->DoneCallback(xfer); /* Transmission finish and stop, xfer done callback. */
I2Cx->ICR;
I2Cx->TXABRT; /* Clear FIFO. */
}
}
}
}
/* I2C slave module put data. */
void I2C_SlaveWriteData(I2C_Type * I2Cx, I2C_SlaveXfer_Type * xfer)
{
if ( 0u != (I2Cx->RAWISR & I2C_INT_READREQ) )
{
I2C_ClearInterruptStatus(I2Cx, I2C_INT_TX_ABORT | I2C_INT_READREQ);
I2C_PutData(I2Cx, xfer->XferBuf[xfer->TxIdx++]);
}
if (xfer->TxIdx == xfer->BufLen)
{
xfer->TxIdx = 0u;
}
}
/* I2C slave module get data. */
void I2C_SlaveReadData(I2C_Type * I2Cx, I2C_SlaveXfer_Type * xfer)
{
if ( 0u != (I2C_GetStatus(I2Cx) & I2C_STATUS_RX_NOTEMPTY) )
{
xfer->XferBuf[xfer->RxIdx++] = I2C_GetData(I2Cx);
}
if (xfer->RxIdx == xfer->BufLen)
{
xfer->RxIdx = 0u;
}
}
/* I2C slave handler. */
void I2C_SlaveXferHandler(I2C_Type * I2Cx, I2C_SlaveXfer_Type * xfer, uint32_t interrupts)
{
if ( 0u != (interrupts & I2C_INT_RX_NOTEMPTY) )
{
xfer->XferBuf[xfer->RxIdx++] = I2C_GetData(I2Cx);
}
if ( 0u != (interrupts & I2C_INT_READREQ) )
{
I2C_ClearInterruptStatus(I2Cx, I2C_INT_TX_ABORT | I2C_INT_READREQ);
I2C_PutData(I2Cx, xfer->XferBuf[xfer->TxIdx++]);
}
if (xfer->TxIdx == xfer->BufLen)
{
xfer->TxIdx = 0u;
}
if (xfer->RxIdx == xfer->BufLen)
{
xfer->RxIdx = 0u;
}
}
/* EOF. */