EFM32 硬件I2C操作

有关的例程,请参考如下:

#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);



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值