软件I2C

文章目录

软I2C

硬 I2C的缺点

  1. 引脚灵活性受限

硬 I2C 接口通常与微控制器上特定的 GPIO 引脚绑定,无法像软 I2C 那样灵活地选择任意可用引脚作为 SDA 和 SCL 信号线。这一限制在复杂的嵌入式系统中可能导致布线困难,特别是当需要连接多个 I2C 设备或在 PCB 布局上有特殊要求时,固定的引脚分配可能会增加设计复杂度

  1. 硬件资源占用问题

使用硬 I2C 需要占用微控制器上专用的 I2C 硬件外设资源。在资源受限的微控制器中,这可能会限制系统同时支持其他硬件功能的能力。此外,当需要多个独立的 I2C 总线时,可用的硬件 I2C 控制器数量将成为系统扩展的瓶颈

  1. 配置和使用复杂度

硬 I2C 接口通常需要配置多个寄存器来设置通信参数、中断和 DMA 传输等功能。相比之下,软 I2C 的实现通常更直观,只需简单的 GPIO 操作和延时函数。不同微控制器厂商的 I2C 硬件实现也可能存在差异,这增加了跨平台开发的难度

因此本节我们使用软件来模拟实现I2C的通信

IO引脚初始化

在使用硬件I2C时,我们将SDA引脚与SCL引脚都设置为复用开漏输出,而在软件实现I2C时,我们要把引脚模式设置为通用开漏输出,初始化完成后,把SDA和SCL都写一,保持高电平,我们选择PB8作为SCL,PB9作为SDA

cpp 复制代码
void My_SI2C_Init(){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOB,&GPIO_InitStructure);

    GPIO_WriteBit(GPIOB,GPIO_Pin_8,Bit_SET);
    GPIO_WriteBit(GPIOB,GPIO_Pin_9,Bit_SET);
}

IO读写和延时函数

接下来我们实现三个函数:SCL_Write()SDA_Write()SDA_Read(),这三个函数配合着Delay_us()延时函数来模拟硬件I2C的时序

cpp 复制代码
void SCL_Write(uint8_t level){
    if(level)
        GPIO_WriteBit(GPIOB,GPIO_Pin_8,Bit_SET);
    else
        GPIO_WriteBit(GPIOB,GPIO_Pin_8,Bit_RESET); 
}

void SDA_Write(uint8_t level){
    if(level)
        GPIO_WriteBit(GPIOB,GPIO_Pin_9,Bit_SET);
    else
        GPIO_WriteBit(GPIOB,GPIO_Pin_9,Bit_RESET); 
}

int SDA_Read(){
    if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9) == Bit_SET)
        return 1;
    else    
        return 0;
}

void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus;				//设置定时器重装值
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}

发送起始位和停止位

cpp 复制代码
void SendStart(){
    SDA_Write(0);
    Delay_us(1);
}

void SendStop(){
    SCL_Write(0);
    SDA_Write(0);
    Delay_us(1);
    SCL_Write(1);
    Delay_us(1);
    SDA_Write(1);
    Delay_us(1);
}

注意在发送停止位时,要先将SCL拉低,否则在SCL保持高电平时拉低SDA就变成了发送起始位

发送一个字节

在发送一个字节之前,我们必须先发送一个bit位:

接下来我们只需要不断重复就可以发送一个字节,注意在最后别忘了处理从机给我们的相应ACK

cpp 复制代码
uint8_t SendByte(uint8_t byte){
    for(int8_t i=7;i>=0;i--)
    {
        SCL_Write(0);
        SDA_Write(byte&(0x01<<i));
        Delay_us(1);
        SCL_Write(1);
        Delay_us(1);
    }
    //处理ack
    SCL_Write(0);
    SDA_Write(1);
    Delay_us(1);
    SCL_Write(1);
    Delay_us(1);
    return SDA_Read();
}

接收一个字节

接收一个字节时也需要先接收一个bit位,然后重复,最后向从机响应

cpp 复制代码
uint8_t ReceiveByte(uint8_t Ack){
    uint8_t byte = 0;
    for(int8_t i=7;i>=0;i--)
    {
        SCL_Write(0);
        SDA_Write(1);
        Delay_us(1);
        SCL_Write(1);
        Delay_us(1);
        byte |= (SDA_Read()<<i);
    }
    SCL_Write(0);
    SDA_Write(!Ack);
    Delay_us(1); 
    SCL_Write(1);
    Delay_us(1);
    return byte;
}

函数封装

最后我们把发送字节和接收字节封装成可以一次性处理多个字节的函数

cpp 复制代码
int8_t My_SI2C_SendBytes(uint8_t Addr,uint8_t* pData,uint16_t size){
    SendStart();
    if(SendByte(Addr&0XFE))
    {
        SendStop();
        return -1;
    }
    for(uint16_t i=0;i<size;i++)
    {
        if(SendByte(pData[i]))
        {
            SendStop();
            return -2;
        }
    }
    SendStop();
    return 0;
}

int8_t My_SI2C_ReceiveBytes(uint8_t Addr,uint8_t* pBuffer,uint16_t size){
    SendStart();
    if(SendByte(Addr|0X01))
    {
        SendStop();
        return -1;
    }
    for(uint16_t i=0;i<size-1;i++)
    {
        pBuffer[i] = ReceiveByte(1);
    }
    pBuffer[size-1] = ReceiveByte(0);
    SendStop();
    return 0;
}
相关推荐
silno5 小时前
图解 STM32 USB CDC虚拟串口 的实现
stm32·单片机·stm32f103c8t6·cdc虚拟串口
Silicore_Emma6 小时前
芯谷科技—D8227 双通道音频功率放大集成电路产品简介与应用推广
单片机·音视频·功率放大器·芯谷科技·便携式音频设备·双通道音频·车载音频系统
Darken037 小时前
单片机的库函数和HAL库有什么区别?还有那些库函数?
单片机·hal库·ai学习
皓月盈江7 小时前
STC12、STC15、STM32系列单片机控制16*64LED点阵屏显示,修改显示内容
单片机·嵌入式硬件·keil·stm32f103c8t6·stc12c5a60s2·stc15w4k32s4·led点阵屏程序源码
qq_448011168 小时前
USB概述
嵌入式硬件
沐欣工作室_lvyiyi8 小时前
智能家居安全报警系统设计(论文+源码)
单片机·毕业设计·智能家居·家居安全报警
一枝小雨9 小时前
7 App代码转AES加密文件生成步骤
stm32·单片机·嵌入式·aes·ota·bootloader·加密升级
li星野9 小时前
打工人日报#20251202
单片机·嵌入式硬件
mylinke9 小时前
永磁同步电机双闭环控制模型故障诊断与仿真研究:基于MATLAB Simulink的仿真代码实现
单片机
云山工作室10 小时前
基于ZigBee的温室智能控制系统设计(论文+源码)
stm32·单片机·嵌入式硬件·物联网·课程设计