C语言模拟IIC

为了加深对I2C总线的理解,用C语言模拟IIC总线,边看源代码边读波形:

如下图所示的写操作的时序图:


完整的程序如下:

#include<reg51.h>
#define uchar unsigned char
#define uint unsigned int
#define write_ADD 0xa0
#define read_ADD 0xa1
uchar a;  
sbit SDA=P2^0;
sbit SCL=P2^1;
void SomeNop();     //短延时
void init();    //初始化
void check_ACK(void);
void I2CStart(void);
void I2cStop(void);
void write_byte(uchar dat);//写字节
void delay(uint z);
uchar read_byte();     //读字节
void write(uchar addr,uchar dat);  //指定地址写
uchar read(uchar addr);       //指定地址读
bit flag;  //应答标志位
void main()
{
    init();
    write_add(5,0xaa); //向地址5写入0xaa
    delay(10);      //延时,否则被坑呀!!!
     P1=read_add(5);      //读取地址5的值
     while(1);    
}



//***************************************************************************  
void delay()//简单延时函数  
{ ;; }  
//***************************************************************************  
void start()  //开始信号 SCL在高电平期间,SDA一个下降沿则表示启动信号  
{     
    sda=1; //释放SDA总线  
    delay();  
    scl=1;  
    delay();  
    sda=0;  
    delay();  
}  
//***************************************************************************  
void stop()   //停止 SCL在高电平期间,SDA一个上升沿则表示停止信号  
{  
    sda=0;  
    delay();  
    scl=1;  
    delay();  
    sda=1;  
    delay();  
}
//***************************************************************************  
void respons()  //应答 SCL在高电平期间,SDA被从设备拉为低电平表示应答  
{  
    uchar i;  
    scl=1;  
    delay(); 
    //至多等待250个CPU时钟周期 
    while((sda==1)&&(i<250))i++;  
    scl=0;  
    delay();  
}  
//***************************************************************************  
void init()//总线初始化 将总线都拉高一释放总线  发送启动信号前,要先初始化总线。即总有检测到总线空闲才开始发送启动信号  
{  
    sda=1;  
    delay();  
    scl=1;  
    delay();  
}  
//***************************************************************************  
void write_byte(uchar date) //写一个字节  
{  
    uchar i,temp;  
    temp=date;  
  
  
    for(i=0;i<8;i++)  
    {  
        temp=temp<<1;  
        scl=0;//拉低SCL,因为只有在时钟信号为低电平期间按数据线上的高低电平状态才允许变化;并在此时和上一个循环的scl=1一起形成一个上升沿  
        delay();  
        sda=CY;  
        delay();  
        scl=1;//拉高SCL,此时SDA上的数据稳定  
        delay();  
    }  
    scl=0;//拉低SCL,为下次数据传输做好准备  
    delay();  
    sda=1;//释放SDA总线,接下来由从设备控制,比如从设备接收完数据后,在SCL为高时,拉低SDA作为应答信号  
    delay();  
}  
//***************************************************************************  
uchar read_byte()//读一个字节  
{  
    uchar i,k;  
    scl=0;  
    delay();  
    sda=1;  
    delay();  
    for(i=0;i<8;i++)  
    {  
        scl=1;//上升沿时,IIC设备将数据放在sda线上,并在高电平期间数据已经稳定,可以接收啦  
        delay();      
        k=(k<<1)|sda;  
        scl=0;//拉低SCL,使发送端可以把数据放在SDA上  
        delay();      
    }  
    return k;  
}  
//***************************************************************************  
void write_add(uchar address,uchar date)//任意地址写一个字节  
{  
    start();//启动  
    write_byte(0xa0);//发送从设备地址  
    respons();//等待从设备的响应  
    write_byte(address);//发出芯片内地址  
    respons();//等待从设备的响应  
    write_byte(date);//发送数据  
    respons();//等待从设备的响应  
    stop();//停止  
}  
//***************************************************************************  
uchar read_add(uchar address)//读取一个字节  
{  
    uchar date;  
    start();//启动  
    write_byte(0xa0);//发送发送从设备地址 写操作  
    respons();//等待从设备的响应  
    write_byte(address);//发送芯片内地址  
    respons();//等待从设备的响应  
    start();//启动  
    write_byte(0xa1);//发送发送从设备地址 读操作  
    respons();//等待从设备的响应  
    date=read_byte();//获取数据  
    stop();//停止  
    return date;//返回数据  
}


C语言中,GPIO (General Purpose Input Output) 模拟 IIC 可能是指通过 GPIO 端口来实现 I2C 总线通信的一种做法。通常,微控制器硬件上自带了专门用于 I2C 的模块,但在某些情况下,开发者可能会选择只利用 GPIO 来实现基本的 I2C 功能,这需要自定义驱动和协议处理。 ### C语言实现IIC的基本步骤 1. **初始化IIC**: - 配置GPIO作为I2C总线的SCL和SDA信号线。 - 设置定时器或中断机制来处理数据的发送和接收时间。 2. **生成起始和停止条件**: - 发送起始条件(0x00)来开始传输。 - 发送设备地址加上读写标志(读写命令),并等待应答。 3. **发送和接收数据**: - 使用循环结构来轮流发送和接收数据字节。 - 对于每个数据字节,先发送或接收开始位(0),然后是8位数据位。 4. **终止交易**: - 发送结束条件(0xFF)来结束交易。 ### 示例代码结构 假设我们有一个简单的函数 `send_i2c_message` 来实现这个过程: ```c #include <avr/io.h> // 引入AVR寄存器操作库 void send_i2c_message(unsigned char device_address, unsigned char read_write_bit, unsigned char data[], int length) { unsigned char i; // 初始化SCL和SDA GPIO端口 DDRD |= (1 << PD6); // SCL为输出 PORTD &= ~(1 << PD6); DDRE |= (1 << PE2); // SDA为输出 PORTE &= ~(1 << PE2); // 生成起始条件 SCL = HIGH; // 将SCL设置高电平 delayMicroseconds(100); // 给器件一些初始化时间 SDA = LOW; // 将SDA拉低,发送起始条件 SCL = LOW; delayMicroseconds(1); // 停留片刻以便接收方准备好 SCL = HIGH; // 写入设备地址和读写标志 for(i=7; i>=0; i--) { if((device_address >> i) & 0x01) { // 判断地址的每一位是否为1 SDA ^= 1; // 如果为1,则将SDA拉高表示1;如果为0,则保持SDA的状态 delayMicroseconds(1); // 等待 } SCL = LOW; delayMicroseconds(1); SCL = HIGH; delayMicroseconds(1); } // 添加读写标志 if(read_write_bit == READ) { SDA ^= 1; // 读操作标志 } else { SDA = 0; // 写操作标志 } SCL = LOW; delayMicroseconds(1); SCL = HIGH; // 发送数据 for(i=0; i<length; i++) { while(!bit_is_set(SDA)); // 等待SDA为低电平 SCL = LOW; // 开始位 for(int j=7; j>=0; j--) { SDA ^= 1; // 根据data[i]的第j位值改变SDA状态 SCL = HIGH; delayMicroseconds(1); SCL = LOW; delayMicroseconds(1); } SCL = HIGH; // 数据发送完成,进入下一个位 while(bit_is_set(SDA)); // 等待SDA回到低电平 } // 释放资源 PORTD |= (1 << PD6); // SCL变为输入 PORTE |= (1 << PE2); // SDA变为输入 // 结束条件 SCL = HIGH; delayMicroseconds(100); SDA = HIGH; delayMicroseconds(500); } // 定义一个辅助函数来判断GPIO引脚状态 int bit_is_set(unsigned char pin) { return (PIND & (1 << pin)) != 0; // 返回非零值,说明引脚为高电平 } ``` ### 注意事项和潜在改进点 - 这是一个非常基础的示例,实际应用中可能需要添加更多的错误检查、时序调整以及更复杂的控制逻辑。 - 对于不同的微控制器平台,GPIO配置和操作细节会有所不同。 - 实际编码时需考虑时钟同步、数据冲突等问题,并可能需要用到定时器中断或其他高级功能来优化性能。 ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

minyuanxiani

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值