关于I2C时序的学习

在调通STM32F0硬件I2C之前用的是IO模拟I2C,就顺便理解一下I2C时序!

首先是宏定义,定义了I2C SCL和SDA的引脚,以及SDA输入输出、SCL输入,还有SDA、SCL的拉高拉低,最后就是延时。

#define I2C_SDA_PIN        22    //PB11 data
#define I2C_SCL_PIN        21    //PB10 clock

#define SDA_OUT            sys_pin_mode(I2C_SDA_PIN, PIN_MODE_OUTPUT)
#define SDA_IN             sys_pin_mode(I2C_SDA_PIN, PIN_MODE_INPUT_PULLUP)
#define SCL_OUT            sys_pin_mode(I2C_SCL_PIN, PIN_MODE_OUTPUT)

#define SDA_L              sys_pin_write(I2C_SDA_PIN, PIN_LOW)
#define SDA_H              sys_pin_write(I2C_SDA_PIN, PIN_HIGH)
#define SCL_L              sys_pin_write(I2C_SCL_PIN, PIN_LOW)
#define SCL_H              sys_pin_write(I2C_SCL_PIN, PIN_HIGH)

#define sda_read()         i2c_sda_read()

#define i2c_delay()        i2c_delay_us(25)

使用I2C之前,需要配置I2C引脚的时钟、输出类型等,还需要将SDA、SCL都拉高。

/*
***************************************************************************************************
* Function   : i2_init
* Arguments  :
* Description: iic初始化
* Returns    :
***************************************************************************************************
*/
static int i2c_init(void)
{
    SCL_OUT; //时钟线配置为输出
    SDA_OUT; //数据线配置为输出

    SCL_H;   //时钟线拉高
    SDA_H;   //数据线拉高
    
    return 0;
}

接下来学习I2C的四个重要信号,分别是起始信号、结束信号、应答信号、非应答信号。

1.起始信号:SCL在高电平期间,SDA出现下降沿。

/*
***************************************************************************************************
* Function   : i2c_start
* Arguments  :
* Description: 时钟线高电平,数据线下降沿 为起始信号
* Returns    :
***************************************************************************************************
*/
void i2c_start(void)
{
    SCL_H;       //时钟线拉高
    i2c_delay();
    SDA_H;       //数据线拉高
    i2c_delay();
    SDA_L;       //数据线拉低(产生下降沿)
    i2c_delay();
    SCL_L;       //时钟线拉低(为传输数据做准备)
    i2c_delay();
}

2.结束信号:SCL在高电平期间,SDA出现上升沿。

/*
***************************************************************************************************
* Function   : i2c_stop
* Arguments  :
* Description: 时钟线高电平,数据线上升沿 为停止信号
* Returns    :
***************************************************************************************************
*/
void i2c_stop(void)
{
    //uint16_t i;
    
    SDA_L;       //数据线拉低
    i2c_delay();
    SCL_H;       //时钟线拉高
    i2c_delay();
    SDA_H;       //数据线拉高(产生上升沿)
    //for (i = 0; i < 0x01ff; i++) { //停止后延长一段时间 可去掉
        i2c_delay();
    //}
}

3.应答信号:SCL在高电平期间,SDA的电平 若为低电平,则为应答信号。

/*
***************************************************************************************************
* Function   : i2c_ack
* Arguments  :
* Description: 主机向从机发送ACK
* Returns    :
***************************************************************************************************
*/
void i2c_ack(void)
{
    SDA_L;       //数据线拉低
    i2c_delay();
    SCL_H;       //时钟线拉高
    i2c_delay();
    SCL_L;       //时钟线拉低
    i2c_delay();
}
/*
***************************************************************************************************
* Function   : i2c_get_ack
* Arguments  :
* Description: 主机从从机获取ACK
* Returns    :
***************************************************************************************************
*/
static int i2c_get_ack(void)
{
    int ack;

    SDA_IN; //数据线配置为输入模式,为接收ACK做准备
    
    SCL_H;  //时钟线拉高
    i2c_delay();
    
    if (sda_read()) { //读数据线引脚
        ack = 1; //读到高电平
    } else {
        ack = 0; //读到低电平
    }

    SCL_L; //时钟线拉低
    i2c_delay();

    SDA_OUT; //数据配置回输出模式

    return ack;
}

4.非应答信号: SCL在高电平期间,SDA的电平 若为高电平,则为非应答信号。

/*
***************************************************************************************************
* Function   : i2c_nak
* Arguments  :
* Description: 主机向从机发送NACK
* Returns    :
***************************************************************************************************
*/
void i2c_nak(void)
{
    SDA_H;       //数据线拉高
    i2c_delay();
    SCL_H;       //时钟线拉高
    i2c_delay();
    SCL_L;       //时钟线拉低
    i2c_delay();
}

接下来介绍I2C读写数据位的有效性规定:
(1) IIC时钟线为高电平期间,数据线必须稳定;
(2) 只有在时钟线为低电平期间,数据线上的高电平或低电平才允许变化。
具体代码如下:
1.主机向从机写一个字节,该字节可以是从机地址、寄存器地址或者寄存器的值。

/*
***************************************************************************************************
* Function   : i2c_write_byte
* Arguments  :
* Description: IIC写一个字节
* Returns    :
***************************************************************************************************
*/
int i2c_write_byte(unsigned char byte)
{
    int i;
    
    for (i = 0; i < 8; i++) {
        if (byte & 0x80) { //取最高位
            SDA_H; //若为1,则数据线拉高
        } else {
            SDA_L; //若为0,则数据线拉低
        }
        i2c_delay();
        SCL_H;     //时钟线拉高,此时需要保证数据稳定
        i2c_delay();
        SCL_L;     //时钟线拉低,为传下一个bit做准备 若为最后一个bit,则为允许从机改变数据线电平(ACK)
        i2c_delay();
        byte <<= 1;
    }
    i2c_delay();

    return i2c_get_ack(); //等待应答
}

2.主机读取从机发送的数据。

/*
***************************************************************************************************
* Function   : i2c_read_byte
* Arguments  :
* Description: IIC读取一个字节
* Returns    :
***************************************************************************************************
*/
int i2c_read_byte(unsigned char *ch)
{
    int i;
    unsigned char byte = 0;

    SDA_IN; //数据线配置为输入模式,为接收数据做准备
    
    for (i = 0; i < 8; i++) {
        SCL_H;       //时钟线拉高
        i2c_delay();
        byte <<= 1;  //从最高位接收,所以接收一位左移一位
        if (sda_read()) { //读数据线引脚
           byte |= 0x01; //若为高电平,置1
        } else {
           byte &= 0xFE; //若为低电平,置0
        }
        SCL_L;       //时钟线拉低
        i2c_delay();
    }
    *ch = byte;

    SDA_OUT; //数据线配置回输出模式

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值