单片机IO口模拟I2C软件驱动

//延时函数
static void I2c_Delay(uint8_t count)
{
    uint8_t i;
    
    for(i = 0;i < count;i++);
}  
//读取SDA脚位状态
static uint8_t I2c_SDAStatus(void) 
{
    uint8_t SDA_Status;
    
    SDA_Status = PB06_GETVALUE();
    
    return SDA_Status?1:0;
}
//设置SDA脚为输出
void SDA_OutMode(void)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    
    GPIO_InitStructure.Pins = GPIO_PIN_6;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;  
    GPIO_InitStructure.IT   = GPIO_IT_NONE;
    GPIO_Init(CW_GPIOB,&GPIO_InitStructure);
}
//设置SDA脚为输入
void SDA_inputMode(uint8_t InputMode)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    
    GPIO_InitStructure.Pins = GPIO_PIN_6;
    if(InputMode == 0)
    {
        GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;  //无下拉,只能输出低电平作ACK(2004特殊I2C协议,主机即作写数据又作ACK,最后一字节非ACK)
    }
    else 
    {
        GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP;  
    }
    GPIO_InitStructure.IT   = GPIO_IT_NONE;
    GPIO_Init(CW_GPIOB,&GPIO_InitStructure);
}
//I2C开始信号
void I2c_Start(void)
{    
    SDA_OutLow;
    I2c_Delay(30);
    SCL_OutLow;
    I2c_Delay(30);
}
//I2C停止信号
void I2c_Stop(void)
{
    SCL_OutLow;
    I2c_Delay(30);
    
    SDA_OutLow;
    I2c_Delay(30);
    
    SCL_OutHigh; 
    I2c_Delay(30);
    
    SDA_OutHigh;
}
//等待(从机)应答
uint8_t I2c_WaitAck(uint8_t AckFlag) 
{
    uint8_t AckNack = 1;
    
    if(AckFlag)  //2004特殊I2C协议,主机即作写数据又作ACK,最后一字节非ACK
    { 
        SDA_inputMode(0);  //输入下拉,模拟假应答;主机写主机应答/非应答,从机2004识别
        SDA_OutLow;
    }
    else
    {
        SDA_inputMode(0);  //输入下拉,模拟假应答;主机写主机应答/非应答,从机2004识别
        SDA_OutHigh;
    }

    I2c_Delay(12);

    SCL_OutHigh;
    I2c_Delay(30);
    SCL_OutLow;
    I2c_Delay(10);
    
    SDA_OutMode();
    I2c_Delay(5);
    
    return AckNack;
}
//发送ACK
void I2c_SendAck(void)
{
    SDA_OutLow;
    I2c_Delay(30);
    SCL_OutHigh;
    I2c_Delay(30);
    SCL_OutLow;
    I2c_Delay(30);
}
//发送NACK
void I2c_SendNack(void)
{
    SDA_OutHigh;
    I2c_Delay(30);
    SCL_OutHigh;
    I2c_Delay(30);
    SCL_OutLow;
    I2c_Delay(30);
}
//写一个字节
void I2c_WriteByte(uint8_t Byte)
{
    uint8_t i;
    
    for(i = 0;i < 8;i++)
    {
        I2c_Delay(26);
        I2c_Delay(6);
        
        if(Byte&0x80)
        {
            SDA_OutHigh;
        }
        else
        {
            SDA_OutLow;
        }
        I2c_Delay(30);
        SCL_OutHigh;
        I2c_Delay(30);    
        SCL_OutLow;

        Byte <<= 1;
    }
    SCL_OutLow;
    I2c_Delay(5);
}
//读一个字节
uint8_t I2c_ReadByte(void)
{
    uint8_t i,byte;
    
    SDA_inputMode(0);
    I2c_Delay(5);
    
    for(i = 0;i < 8;i++)
    {
        byte <<= 1;
        I2c_Delay(26);

        if(I2c_SDAStatus())
        {
           byte |= 0x01;
        }
        I2c_Delay(26);
        SCL_OutHigh;
        I2c_Delay(26);
        SCL_OutLow;
    }
    
    SDA_OutMode();
    
    return byte;
}
//连续写多个字节
void I2c_WriteNbyte(uint8_t I2C_Addr,uint8_t* data,uint8_t len)
{
    uint8_t j = 0;
    
    I2c_Start();
    I2c_WriteByte(I2C_Addr);
    I2c_WaitAck(1);

    for(j = 0;j < len;j++)
    {
        I2c_WriteByte(data[j]);  
        if(j < (len-1))
        {
            I2c_WaitAck(1); //2004特殊I2C协议,主机写,还得主机应答

        }
        else
        {
            I2c_WaitAck(0); //最后一个字节NACK
        }
    }
    
    I2c_Stop();
}
//读取多个字节
void I2c_ReadNbyte(uint8_t I2C_Addr,uint8_t* buf,uint8_t len)
{
    uint8_t j;
    
//    I2C_Addr <<= 1;
    
    I2c_Start();
    I2c_WriteByte(I2C_Addr|0x01);
    
    if(!I2c_WaitAck(1)) 
    {
        I2c_Stop();
        return;
    }
    for(j = 0;j < len;j++)
    {
        buf[j] = I2c_ReadByte();
        if(j != (len-1))
        {
            I2c_SendAck();
        }
        else
        {
            I2c_SendNack();
        }
    }
    I2c_Delay(26);
    I2c_Stop();
}
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/weixin_50707044/article/details/141259267

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值