模拟IIC软件代码和实际波形的分析&&真实IIC波形

1.起始信号

时钟线SCL为高时,数据线SDA由高到低

最后一句操作:时钟线拉低,钳住IIC总线,准备发送数据

注意:SDA和SCL同时为高时,为IIC总线的空闲状态,也就是说当没有数据传输时这两根线应该都是高电平

【当没有数据传输时,SDA(数据线)的电平通常由设备的实现方式决定。在大多数情况下,SDA 会被拉高到逻辑高电平(通常为电源电压),以示空闲状态。但是也有一些设备在空闲时可能会将 SDA 拉低到逻辑低电平,具体取决于设备的设计和实现。】

2.停止信号

时钟线SCL为高时,数据线SDA由低到高

第一句确保时钟线为低时,数据线才能变化为0,否则这就可能成起始信号了!

注意:当 SDA 和 SCL 同时保持低电平时,通常表示总线被占用或者处于忙碌状态

3.ACK/NACK信号

从机在第9个时钟信号进行拉低回应,表示收到了主机发来的数据,拉高则表示不应答

4.等待ACK

主机做完操作之后需要等待从机的回应

5.发送一个字节

//==================================
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答 
//==================================
void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
    SDA_OUT();                 //SDA发送模式
    IIC_SCL=0;                 //拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7; //SDA高低电平表示数据1和0
        txd<<=1;      
        delay_us(2);           
        IIC_SCL=1;             //SCL先上升
        delay_us(2); 
        IIC_SCL=0;             //SCL再下降,形成一个脉冲,发送一位数据生效
        delay_us(2);
    }    
}

首先将SCL拉低,设置SDA为输出,然后根据输入的字节数据通过位移操作将该数按二进制解析出来,每解析出一位就拉高拉低SCL产生脉冲时钟传输数据

6.读取一个字节

读取一个字节,也是分8次循环,产生8个时钟信号,并读取SDA的高低电平信号,最后,根据要不要继续读下一个字节,发送第9位的Ack或nACK

/==================================
//读1个字节
//ack=1时,发送ACK,ack=0,发送nACK  
//==================================
u8 IIC_Read_Byte(unsigned char ack)
{
    unsigned char i,receive=0;
    SDA_IN();                  //SDA输入模式
    for(i=0;i<8;i++ )
    {
        IIC_SCL=0;             //SCL先下降,通过循环,形成时钟脉冲
        delay_us(2);
        IIC_SCL=1;             //SCL上升
        receive<<=1;
        if(READ_SDA)
            receive++;         //读取并组合记录数据,++表示读到1了,最低位置1
        delay_us(1); 
    }   
    //读取8位后,主机需要变为发送模式,在第9位进行应答或不应答
    //此时CLK还是高电平状态,不过下面的应答会先将CLK拉低的 
    if (!ack)
    {
        //读1个字节,或读多个字节读到最后一个字节时,使用nACK
        //然后配合使用IIC停止信号
        IIC_NAck();//发送nACK
    }
    else
    {
        //读多个字节还没读完时,使用ACK,表示现在读的ok,还要继续读
        IIC_Ack(); //发送ACK 
    }       
    return receive;
}

7.真实IIC波形

通过上面的介绍再看真实的波形,理解会更加透彻

【图片为借用】

从IIC实测波形入手,搞懂IIC通信 - 知乎 (zhihu.com)icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/161710767

7.实际使用

比如我想需要读取一个芯片上的数据回来

//调用该函数
blRet = HAL_Sxxxx_READ_ARRAY(ADDR, u8ReadBuf, 2);


//上述函数再调用该函数
enuRet = GPIO_I2C_READ(GPIO_I2C_0, ADDRESS, u16RegAddr, 0, pu8ReadData, u8RdLen);

GPIO_I2C_READ(uint32_t I2Cx, uint8_t addr, uint16_t start_Addr, uint8_t Addr_Length, 
uint8_t *buf, uint8_t Data_Length)


/上述参数含义:
I2Cx---【使用哪路IIC】
addr---【从机地址】
start_Addr---【读取数据的起始地址】
Addr_Length---【地址长度】
*buf---【返回读取数据的指针】
Data_Length---【数据长度】

最后数据就会返回到指针所指的地址之中

8.引脚配置

引脚配置比较简单:

	GPIO_BOP(GPIO_SCL) = GPIO_Pin_SCL;
	GPIO_BOP(GPIO_SDA) = GPIO_Pin_SDA;

	gpio_init(GPIO_SCL, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SCL);
	gpio_init(GPIO_SDA, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SDA);

配置解释:

  1. GPIO_BOP(GPIO_SCL) = GPIO_Pin_SCL; 和 GPIO_BOP(GPIO_SDA) = GPIO_Pin_SDA;

    这两行代码使用了一个宏 GPIO_BOP,它的作用是将指定的 GPIO 引脚设为高电平输出。
  2. gpio_init(GPIO_SCL, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, GPIO_Pin_SCL);

    这行代码使用了一个函数 gpio_init,它用于初始化指定的 GPIO 引脚。GPIO_MODE_OUT_OD 表示设置为开漏输出模式,GPIO_OSPEED_50MHZ 表示设置输出速度为 50MHz,GPIO_Pin_SCL 则表示要初始化的具体引脚号。

9.为什么要将模拟IIC引脚配置成开漏输出

  1. 双向通信:

    I2C 总线是一个双向通信总线,其中 SDA(数据线)既可以作为输入线,也可以作为输出线。在这种情况下,开漏输出模式允许引脚在高电平和低电平之间切换,同时也可以让其他设备驱动总线。
  2. 总线共享:

    I2C 总线是一种共享总线,可能有多个从设备连接在同一条总线上。开漏输出允许多个设备连接在一起,不会造成冲突。因为只有在低电平时设备才会驱动总线,而高电平是由上拉电阻提供的。
  3. 防止冲突:

    在总线上,如果多个设备同时尝试驱动总线为高电平或低电平,就会造成电气冲突。开漏输出模式仅能将引脚拉低,而无法主动拉高电平,这样可以避免冲突。
  4. 上拉电阻:

    在开漏输出模式下,SDA 和 SCL 引脚需要通过外部上拉电阻连接到电源电压。这确保了在没有设备主动驱动的情况下,总线处于高电平状态。

总之,使用开漏输出模式是为了确保 I2C 总线的安全、可靠的双向通信以及避免电气冲突。

  • 12
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在1.3寸IIC通信OLED上绘制波形,可以按照以下步骤进行: 1. 初始化OLED屏幕,设置其分辨率和IIC通信地址。 2. 创建一个缓冲区,在其中绘制波形数据。可以选择使用C语言中的数组或指针来存储波形数据。 3. 将缓冲区中的波形数据转换为屏幕像素坐标,并在屏幕上绘制波形。可以使用C语言中的for循环来遍历波形数据,并使用OLED屏幕的API函数来绘制像素。 4. 在需要更新波形时,清空缓冲区并重新绘制波形数据。 以下是一个示例代码,演示了如何在1.3寸IIC通信OLED上绘制正弦波: ```c #include <stdio.h> #include <math.h> #include "oled.h" #define PI 3.1415926 // 初始化OLED屏幕 void oled_init() { // 设置OLED屏幕的分辨率和IIC通信地址 // ... } // 绘制正弦波 void draw_sine_wave() { int n, x, y; float angle, sin_value; int buffer[128]; // 清空缓冲区 for (n = 0; n < 128; n++) { buffer[n] = 0; } // 生成正弦波数据 for (n = 0; n < 128; n++) { angle = (float)n / 128.0 * 2 * PI; sin_value = sin(angle); buffer[n] = (int)(sin_value * 20 + 20); // 将正弦波数据转换为像素坐标 } // 绘制正弦波 for (n = 0; n < 127; n++) { x = n; y = buffer[n]; oled_draw_pixel(x, y, 1); x = n + 1; y = buffer[n + 1]; oled_draw_line(n, buffer[n], x, y, 1); } } int main() { oled_init(); while (1) { draw_sine_wave(); // 等待一段时间后清空屏幕并重新绘制波形 // ... } return 0; } ``` 需要注意的是,以上代码仅为示例,实际应用中还需要根据具体需求进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值