前言
最近项目上用到了EFM32HG222F64G芯片中的硬件通讯,i2c通讯不稳定问题,导致花了很长时间去解决,走了不少弯路,最终还是解决了。在此写一下,做个记录,也方便给后面做产品的攻城狮,少走一些弯路吧!好了 ^_^! 进入正题。
来个直接点,直接上代码:
需要创建两个文件,I2C.c与I2C.h
I2C.h
#ifndef I2C_H
#define I2C_H
#include "em_i2c.h"
void initI2c(void);
I2C_TransferReturn_TypeDef i2c_writeData(uint8_t wrStAddr, uint8_t* txData, uint8_t writeLen);
I2C_TransferReturn_TypeDef i2c_readData(uint8_t srAddr, uint8_t *rxData, uint8_t readLen);
#endif
I2C.c
#include "em_i2c.h"
#include "em_emu.h"
#include "i2c.h"
#include "em_cmu.h"
#include "lee_log.h"
#define I2C_ADDRESS 0xD0
#define TIME_OUTT 100
I2C_TransferReturn_TypeDef I2C_Status;
/*----------------------------------------------------------------------------*/
//@Breif Description:Initialize the i2c module
/*----------------------------------------------------------------------------*/
void initI2c(void)
{
I2C_Init_TypeDef init_I2C =
{
.enable = true,
.clhr = i2cClockHLRStandard,
.freq = I2C_FREQ_STANDARD_MAX,
.master = true,
.refFreq = 0
};
/* Enabling clock to the I2C, GPIO*/
CMU_ClockEnable(cmuClock_HFPER, true);
CMU_ClockEnable(cmuClock_I2C0, true);
CMU_ClockEnable(cmuClock_GPIO, true);
/* Starting LFXO and waiting until it is stable */
CMU_OscillatorEnable(cmuOsc_LFRCO, true, true);
/* Routing the LFXO clock to the RTC */
CMU_ClockSelectSet(cmuClock_LFA, cmuSelect_LFRCO);
//GPIO_DriveModeSet(gpioPortA, gpioDriveModeHigh);
/* Using PA0 (SDA) and PA1 (SCL) */
GPIO_PinModeSet(gpioPortA, 1, gpioModeWiredAndPullUpFilter, 1);
GPIO_PinModeSet(gpioPortA, 0, gpioModeWiredAndPullUpFilter, 1);
/* Configure interrupt pin*/
GPIO_PinModeSet(gpioPortC, 4, gpioModeInput, 0);
//I2C0->ROUTE |= I2C_ROUTE_LOCATION_LOC2 + I2C_ROUTE_SCLPEN + I2C_ROUTE_SDAPEN;
I2C0->ROUTE = I2C_ROUTE_SDAPEN
| I2C_ROUTE_SCLPEN
| (0 << _I2C_ROUTE_LOCATION_SHIFT);
I2C_Init(I2C0, &init_I2C);
I2C0->CTRL |= I2C_CTRL_AUTOSN; //一个字节的读取,这里需要更改
//I2C0->CTRL |= I2C_CTRL_AUTOACK | I2C_CTRL_AUTOSN; //两个字节的读取,这里需要更改
//NVIC_EnableIRQ(I2C0_IRQn);
}
/*----------------------------------------------------------------------------*/
//@Breif Description: write data to BMI160
/*----------------------------------------------------------------------------*/
I2C_TransferReturn_TypeDef i2c_writeData(uint8_t wrStAddr, uint8_t* txData, uint8_t writeLen)
{
uint32_t loop = TIME_OUTT;
I2C_TransferSeq_TypeDef tx_Init =
{
.addr = I2C_ADDRESS,
.buf[0].data = &wrStAddr,
.buf[0].len = 1,
.buf[1].data = txData,
.buf[1].len = writeLen,
.flags = I2C_FLAG_WRITE_WRITE,
};
I2C_Status = I2C_TransferInit(I2C0, &tx_Init);
while ((I2C_Status == i2cTransferInProgress) && loop--)
{
I2C_Status = I2C_Transfer(I2C0);
//EMU_EnterEM1();
}
if (loop == TIME_OUTT) {
LEE_LOG(LOG_INFO,"i2c_write loop fail.\n");
}
return(I2C_Status);
}
/*----------------------------------------------------------------------------*/
//@Breif Description:read data from BMI160
/*----------------------------------------------------------------------------*/
I2C_TransferReturn_TypeDef i2c_readData(uint8_t srAddr, uint8_t *rxData, uint8_t readLen)
{
uint32_t loop = TIME_OUTT;
I2C_TransferSeq_TypeDef rx_Init =
{
.addr = I2C_ADDRESS,
.buf[0].data = &srAddr,
.buf[0].len = 1,
.buf[1].data = rxData,
.buf[1].len = readLen,
.flags = I2C_FLAG_WRITE_READ,
};
/* Do a polled transfer */
I2C_Status = I2C_TransferInit(I2C0, &rx_Init);
while ((I2C_Status == i2cTransferInProgress) && (loop--))
{
/* Enter EM1 while waiting for I2C interrupt */
I2C_Status = I2C_Transfer(I2C0);
//EMU_EnterEM1();
/* Could do a timeout function here. */
}
if (loop == TIME_OUTT) {
LEE_LOG(LOG_INFO,"i2c_read loop fail.\n");
}
return(I2C_Status);
}
/*----------------------------------------------------------------------------*/
//@Breif Description:the interrupt service function
/*----------------------------------------------------------------------------*/
//void I2C0_IRQHandler(void)
//{
// I2C_Status = I2C_Transfer(I2C0);
//}
在这说明一下,我出现过的问题,在I2C初始化的时候,按照实例代码配置
I2C0->CTRL |= I2C_CTRL_AUTOACK | I2C_CTRL_AUTOSN;
我明明写的读取按照单字节来读取的,但是使用逻辑分析仪抓到的波形而双字节的波形(现在项目上是使用BMI160出现这种情况,不知道跟其他产品是否有类似的问题)
逻辑分析仪抓的波形如下:
write:
read:
使用下面的配置的话,是可以读取单字节的数据(项目上是使用BMI160与EFM32通讯)
I2C0->CTRL |= I2C_CTRL_AUTOSN; //一个字节的读取,这里需要更改
write:
read:
在main(),使用
int main(void)
{
uint8_t data_wr[]={0};
uint8_t reg_data = 0;
initI2c();
bmi160_init(0xd0);
while (1) {
data_wr[0] = 0x15;
i2c_writeData(0x40,data_wr,1);
LED_Delay_ms(1000);
i2c_readData(0x40, ®_data,1);
LEE_LOG(LOG_INFO," acc_data40 = %X \r\n", reg_data);
LEE_LOG(LOG_INFO,"**************************** \r\n");
}
只需要调用读、写方法即可
i2c_writeData(0x40,data_wr,1);
i2c_readData(0x40, ®_data,1);
以上的代码已经在项目上使用,测试过没什么问题。谢谢各位看官。
还有一个问题就是在初始化时。下面这两个要配合使用,不能单独配置一个,不然,不起作用。
.freq = I2C_FREQ_FASTPLUS_MAX;
CMU_ClockEnable(cmuClock_HFPER, true);