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写功能,读功能类似,在此处不再赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值