有关的例程,请参考如下:
#include "efm32.h"
#include "em_chip.h"
#include "em_cmu.h"
#include "em_emu.h"
#include "em_gpio.h"
#include "em_i2c.h"
#include "I2C_hw.h"
unsigned long i2c_error = 0;
void I2C_WaitForAck(void);
void I2C_Buslock_Process(void);
void I2C_IO_Initial(void)
{
i2c_error = 0;
/* Enabling clock to the I2C, GPIO*/
CMU_ClockEnable(cmuClock_GPIO, true);
CMU_ClockEnable(cmuClock_I2C0, true);
/* Using PC6 (SDA) and PC7 (SCL) */
GPIO_PinModeSet(gpioPortD, 6, gpioModeWiredAndPullUpFilter, 1);
GPIO_PinModeSet(gpioPortD, 7, gpioModeWiredAndPullUpFilter, 1);
/* Enable pins at location 2 */
I2C0->ROUTE = (I2C_ROUTE_SDAPEN |I2C_ROUTE_SCLPEN |I2C_ROUTE_LOCATION_LOC1);
I2C0->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX | I2C_CMD_ABORT;
I2C0->CTRL &= ~_I2C_CTRL_MASK;
/* Set the CLHR (clock low to high ratio). */
I2C0->CTRL |= i2cClockHLRStandard <<_I2C_CTRL_CLHR_SHIFT;
// Set SCK clock low timeout and bus idle timeout
I2C0->CTRL |= I2C_CTRL_CLTO_1024PPC | I2C_CTRL_GIBITO | I2C_CTRL_BITO_160PCC;
// Set auto STOP when NACK
I2C0->CTRL |= I2C_CTRL_AUTOSN;
/* Frequency is given by fSCL = fHFPERCLK/((Nlow + Nhigh)(DIV + 1) + 4), thus */
/* DIV = ((fHFPERCLK - 4fSCL)/((Nlow + Nhigh)fSCL)) - 1 */
//Assume system clock = 1000000, i2c clock = 30kHz;
//div = (1000000 - (4 * 30000)) / (8 * 30000) = 3.67 = 4;
//CLKDIV = div - 1;
I2C0->CLKDIV = 16;
NVIC_ClearPendingIRQ(I2C0_IRQn);
I2C0->IFC = _I2C_IFC_MASK;
//I2C0->IEN = I2C_IEN_ARBLOST | I2C_IEN_CLTO | I2C_IEN_BITO | I2C_IEN_NACK | I2C_IEN_BUSERR;
//NVIC_EnableIRQ(I2C0_IRQn);
BITBAND_Peripheral(&(I2C0->CTRL),_I2C_CTRL_EN_SHIFT,true);
}
unsigned char I2C_SentData(unsigned char Address, unsigned short Offset, unsigned char Data)
{
//Perpare for a new transmit
I2C0->CMD = I2C_CMD_ABORT;
I2C0->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX;
//Clear all the flags
I2C0->IFC = _I2C_IFC_MASK;
//Begin to transmit
I2C0->CMD = I2C_CMD_START; //Start
I2C0->TXDATA = Address | I2C_WRITE_COMMAND; //I2C hardware address
I2C_WaitForAck();
I2C0->TXDATA = Offset; //I2C internal address
I2C_WaitForAck();
I2C0->TXDATA = Data; //I2C data
I2C_WaitForAck();
I2C0->CMD = I2C_CMD_STOP; //Stop
unsigned char Result = i2c_error;
i2c_error = 0;
return Result;
}
unsigned char I2C_ReadData(unsigned char Address, unsigned short Offset, unsigned char *Rx)
{
//Perpare for a new transmit
I2C0->CMD = I2C_CMD_ABORT;
I2C0->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX;
//Clear all the flags
I2C0->IFC = _I2C_IFC_MASK;
//Begin to transmit
I2C0->CMD = I2C_CMD_START; //S
I2C0->TXDATA = Address | I2C_WRITE_COMMAND; //AD+W hardware address
I2C_WaitForAck();
I2C0->TXDATA = Offset; //internal register address
I2C_WaitForAck();
I2C0->CMD = I2C_CMD_START; //S
I2C0->TXDATA = Address | I2C_READ_COMMAND; //AD+R internal register address
I2C_WaitForAck();
while(!((I2C0->IF) & I2C_IF_RXDATAV));
*Rx = (unsigned char)(I2C0->RXDATA);
I2C0->CMD = I2C_CMD_NACK; //NACK
I2C0->CMD = I2C_CMD_STOP; //P
unsigned char Result = i2c_error;
i2c_error = 0;
return Result;
}
void I2C_WaitForAck(void)
{
while(!((I2C0->IF) & I2C_IF_ACK))
{
//STOP auto send
if(i2c_error)
{
break;
}
}
I2C0->IFC = _I2C_IFC_MASK;
}
void I2C0_IRQHandler(void)
{
static unsigned char Arblost = 0;
unsigned long Flag = I2C0->IF;
if(Flag & I2C_IF_NACK)
{
//auto STOP send
i2c_error = 1;
}
if(Flag & I2C_IF_BUSERR)
{
I2C0->CMD = I2C_CMD_ABORT;
I2C0->CMD = I2C_CMD_CLEARPC | I2C_CMD_CLEARTX;
i2c_error = 1;
}
if(Flag & I2C_IF_ARBLOST)
{
//arbitration losses, maybe SDA is stuck
if(Arblost++ > 5)
{
Arblost = 0;
I2C_Buslock_Process();
}
i2c_error = 1;
}
if((Flag & I2C_IF_CLTO) || (Flag & I2C_IF_BITO))
{
I2C_Buslock_Process();
i2c_error = 1;
}
I2C0->IFC = Flag;
}
void I2C_Buslock_Process(void)
{
unsigned long Delay = 1000;
//Disable I2C module, and clearing the route register
BITBAND_Peripheral(&(I2C0->CTRL),_I2C_CTRL_EN_SHIFT,false);
I2C0->ROUTE = 0;
//Set as pushpull mode
GPIO_PinModeSet(gpioPortD, 6, gpioModePushPull, 1);//SDA
GPIO_PinModeSet(gpioPortD, 7, gpioModePushPull, 1);//SCL
//send 9 clock of SCL
for(unsigned char i = 0; i < 9; i++)
{
Delay = 3000;
GPIO_PinOutClear(gpioPortD, 7);
while(Delay--);
Delay = 3000;
GPIO_PinOutSet(gpioPortD, 7);
while(Delay--);
}
/* Using PD6 (SDA) and PD7 (SCL) */
GPIO_PinModeSet(gpioPortD, 7, gpioModeWiredAndPullUpFilter, 1);
GPIO_PinModeSet(gpioPortD, 6, gpioModeWiredAndPullUpFilter, 1);
/* Enable pins at location 2 */
I2C0->ROUTE = (I2C_ROUTE_SDAPEN |I2C_ROUTE_SCLPEN |I2C_ROUTE_LOCATION_LOC1);
//enable again
BITBAND_Peripheral(&(I2C0->CTRL),_I2C_CTRL_EN_SHIFT,true);
}
在以上代码中,并没有使能硬件I2C的中断,因此也不会调用I2C_Buslock_Process()函数。另外,针对该函数,目前也还没有做过功能验证,我只能通过短接SCL和SDA来测试。
I2C_Buslock_Process()原意是当I2C总线死锁的时候,控制I2C中的SCL时钟线产生9个时钟脉冲(针对8位数据的情况),这样I2C从设备就可以完成被挂起的读操作,从死锁状态中恢复过来。
在主函数中的调用方式如下:I2C_SentData(SLAVE_ADDRESS, PWR_MGMT_1, 0x00);
I2C_ReadData(Address, RegAddress+1, &L);