I2C設備驅動(二)

本小節內容基於單片機平臺的firmware開發,也可用於非保護模式運行的操作系統內的開發。

1. GPIO模擬

//function: set_scl_high
//description: 
static void set_scl_high ( void )
{
    uint16_t volatile timeout = SCL_TIMEOUT;

    SET_SCL;
    while ( !GET_SCL )
    {
        if ( timeout-- == 0 )
        {
            err_handler();
        }
    }
}
//function: send_stop
//description:
static void send_stop ( void )
{
    CLEAR_SCL;
    CLEAR_SDA;
    set_scl_high();
    SET_SDA;
}
//function: send_byte
//description:
static void send_byte ( uint8_t abyte )
{
    uint8_t i;

    for ( i = 0; i < 8; i++ )
    {
        if ( abyte & 0x80 )
        {
            SET_SDA;            //send each bit, MSB first
        }
        else
        {
            CLEAR_SDA;
        }

        set_scl_high();        //do clock cycle
        CLEAR_SCL;

        abyte <<= 1;            //shift over to get next bit
    }

    SET_SDA;                    //listen for ACK
    set_scl_high();

    if ( GET_SDA )              //error if no ACK
    {
        if ( !is_transaction )
        {
            SendStopBus();
            err_handler();
        }
    }

    CLEAR_SCL;
}
//function: send_addr
//description:
static void send_addr ( uint8_t addr, uint8_t read )
{
    volatile uint8_t x = 0;    //delay variable

    //generate START condition
    set_scl_high();
    x++;            //short delay to keep setup times in spec
    CLEAR_SDA;
    x++;
    CLEAR_SCL;
    x++;

    send_byte( addr | read );  //send address byte with read/write bit
}
// function: get_byte
// description:
static uint8_t get_byte ( uint8_t lastbyte )
{
    uint8_t abyte = 0;
    uint8_t i;

    for ( i = 0; i < 8; i++ )  //get each bit, MSB first
    {
        set_scl_high();

        abyte <<= 1;    //shift result over to make room for next bit

        if ( GET_SDA )
        {
            abyte++;    //same as 'abyte |= 1', only faster
        }
        CLEAR_SCL;
    }

    if ( lastbyte )
    {
        SET_SDA;        //do not ACK last byte read
    }
    else
    {
        CLEAR_SDA;
    }

    set_scl_high();
    CLEAR_SCL;
    SET_SDA;

    return( abyte );
}

上述函數爲基本操作模塊函數,下面的函數爲可對外提供的API接口。

// Function: i2c_read_byte
// Description:
uint8_t i2c_read_byte ( uint8_t device_id, uint8_t addr )
{
    uint8_t abyte = 0;

    send_addr( device_id, WRITE );
    send_byte( addr );
    send_addr( device_id, READ );
    abyte = get_byte( LAST_BYTE );  //get single byte, same as last byte, no ACK
    send_stop();

    return( abyte );
}
// Function: i2c_write_byte
// Description:
void i2c_write_byte ( uint8_t deviceID, uint8_t offset, uint8_t value )
{
    send_addr( deviceID, WRITE );
    send_byte( offset );
    send_byte( value );
    send_stop();
}
// Function: i2c_read_block
// Description:
bool_t i2c_read_block ( uint8_t deviceID, uint8_t addr, uint8_t *p_data, uint16_t nbytes )
{
    uint16_t i;

    if ( nbytes == 0 )
    {
        return( true );
    }

    send_addr( deviceID, WRITE );
    send_byte( addr );
    send_addr( deviceID, READ );

    nbytes--;   //get all except last byte
    for ( i = 0; i < nbytes; i++ )
    {
        p_data[i] = get_byte( NOT_LAST_BYTE );
    }
    p_data[i] = get_byte( LAST_BYTE );  //get last byte, no ACK
    send_stop();

    return true;
}
// Function:    i2c_write_block
// Description:
bool_t i2c_write_block ( uint8_t device_id, uint8_t addr, uint8_t *p_data, uint16_t nbytes )
{
    uint16_t i;

    if ( nbytes == 0 )
    {
        return( true );
    }

    send_addr( device_id, WRITE );
    send_byte( addr );

    for ( i = 0; i < nbytes; i++ )
    {
        send_byte( p_data[i] );
    }

    send_stop();

    return true;
}

上述API已經包含了常用的字節讀寫和block讀寫操作,滿足日常i2c的應用。

2. 硬件實現

i2c功能早已經很成熟,各芯片廠商都有很成熟的IP解決方案,一般只需要配置幾個狀態位即可實現數據傳輸功能,本節提到的是AT89C51上的twi方案,可能相對複雜,但是可讓軟件開發人員看到更多狀態,對i2c協議也能有更深的理解。
twi_at89c51
上圖是AT89C51 twi模塊的master trasmitter模式下的狀態轉移圖,以下代碼是通過配置寄存器實現狀態轉換的,大家也可以去下載官方twi的demo code進行學習。具體寄存器的說明請參見該芯片說明文檔。

void twi_init( void )
{
    SSCON = TWI_RATIO_960;  //clock config 
    SSCON |= TWI_SSIE;
}
void twi_start( void )
{
    uint32_t volatile Timeout = I2C_BUS_TIMEOUT;
    SSCON = (SSCON & ~(TWI_STO|TWI_SI)) | TWI_STA;
    while(( SSCON & TWI_SI ) == 0)
    {
        if (Timeout-- == 0)
        {
            return;
        }
    }
}
void twi_stop( void )
{
    unint32_t volatile Timeout = I2C_BUS_TIMEOUT;
    SSCON = (SSCON & ~(TWI_STA|TWI_SI)) | TWI_STO;
    while( SSCON & TWI_STO )
    {
        if (Timeout-- == 0)
        {
            return;
        }
    }
}
uint8_t twi_send_byte( uint8_t val, uint8_t expected )
{
    uint32_t volatile Timeout = I2C_BUS_TIMEOUT;
    SSDAT = val;
    SSCON &= ~(TWI_STA|TWI_SI|TWI_STO);
    while(( SSCON & TWI_SI ) == 0)
    {
        if (Timeout-- == 0)
        {
            return 1;
        }
    }
    expected ^= SSCS;
    return expected;
}
#define twi_send_MTslave( val ) twi_send_byte( val, 0x18 )
#define twi_send_data( val )    twi_send_byte( val, 0x28 )

uint8_t twi_write( uint8_t slave_addr, uint8_t offset, uint16_t count, uint8_t *devdata )
{
    uint16_t i;
    slave_addr &= 0xfe;
    twi_start();
    if( twi_send_MTslave( slave_addr) ){
        twi_stop();
        return I2C_NOACK; /* NoACK */
    }
    if ( twi_send_data( offset )){
        twi_stop();
        return I2C_NOACK; /* NoACK */
    }
    for( i=0; i<count; i++ ){
        if (twi_send_data( devdata[i] )){
            twi_stop();
            return I2C_NOACK;   /* NoACK */
        }
    }
    twi_stop();
    return I2C_NOERROR;
}
uint8_t twi_write_byte( uint8_t slave_addr, uint8_t reg_addr, uint8_t byte )
{
    uint8_t count = 0;
    while(twi_write(slave_addr, reg_addr, 1, &byte) != 0 )
    {
        if (++count == 3)
        {
            return -1;
        }
    }
    return 0;
}
uint8_t twi_write_block( uint8_t slave_addr, uint8_t reg_addr, uint8_t *p_data, uint16_t nbytes )
{
    uint8_t count = 0;
    while(twi_write(slave_addr, reg_addr, nbytes, p_data) != 0 )
    {
        if (++count == 3)
        {
            return -1;
        }
    }
    return 0;
}

上述函數實現了基於AT89C51的twi模塊實現的i2c寫功能,讀功能類似,在此處不再贅述。

發佈了29 篇原創文章 · 獲贊 27 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章