嵌入式开发软件I2C代码实现

一、软件I2C过程

I2C_WriteData 函数

  1. 发送起始条件(Start): 调用 I2C_Start() 函数发送起始条件,即先拉低SDA线,然后拉低SCL线,以准备开始通信。

  2. 发送设备地址和写标志位: 通过调用 I2C_WriteByte() 函数发送目标设备的地址和写标志位(写操作时,该标志位为 0),以告知设备将要进行写入操作。

  3. 发送寄存器地址: 再次调用 I2C_WriteByte() 函数,发送要写入数据的寄存器地址,告诉设备接下来要写入的数据将存储在哪个寄存器中。

  4. 写入数据: 通过循环调用 I2C_WriteByte() 函数,依次向设备寄存器中写入数据。每次写入一个字节的数据,直到所有数据写入完毕。

  5. 发送停止条件(Stop): 调用 I2C_Stop() 函数发送停止条件,即先拉高SDA线,然后拉高SCL线,表示数据传输结束。

I2C_ReadData 函数

  1. 发送起始条件(Start): 调用 I2C_Start() 函数发送起始条件,同样是先拉低SDA线,然后拉低SCL线。

  2. 发送设备地址和写标志位,并发送寄存器地址: 与写入数据流程类似,首先发送设备地址和写标志位,然后发送要读取数据的寄存器地址。

  3. 再次发送起始条件(Repeated Start): 调用 I2C_Start() 函数,发送重复的起始条件,即先拉低SDA线,然后拉低SCL线,以准备切换到读取模式。

  4. 发送设备地址和读标志位: 通过调用 I2C_WriteByte() 函数,发送目标设备的地址和读标志位(读操作时,该标志位为 1),以告知设备将要进行读取操作。

  5. 读取数据: 通过循环调用 I2C_ReadByte() 函数,依次从设备中读取数据,并存储到指定的数据缓冲区中。在最后一次读取时,不发送应答信号。

  6. 发送停止条件(Stop): 调用 I2C_Stop() 函数发送停止条件,表示读取数据的操作结束。

二、完整代码

BspI2C.c

/*
    BspI2C.c

    Implementation File for STM32 I2C Module
*/

/*
    Modification History
    --------------------
    01a 27Feb24 Jasper Created
*/

/* Includes */

#include "include.h"

/* Define the start and stop conditions of I2C */
void I2C_Start(void) {
    /*    __________
     *SCL           \________
     *    ______
     *SDA       \_____________
     */
    I2C_SDA_HIGH;
    I2C_SCL_HIGH;
    osDelay(I2C_DELAY);
    I2C_SDA_LOW;
    osDelay(I2C_DELAY);
    I2C_SCL_LOW;
    osDelay(I2C_DELAY);
}

void I2C_Stop(void) {
    /*          ____________
     *SCL _____/
     *               _______
     *SDA __________/
     */
    I2C_SCL_LOW;
    I2C_SDA_LOW;
    osDelay(I2C_DELAY);
    I2C_SCL_HIGH;
    osDelay(I2C_DELAY);
    I2C_SDA_HIGH;
    osDelay(I2C_DELAY);
}

/* Define data write and read for I2C */
uint8_t I2C_WriteByte(uint8_t byte) {
    uint8_t i;
    for (i = 0; i < 8; i++) {
        if (byte & 0x80) {
            I2C_SDA_HIGH;
        } else {
            I2C_SDA_LOW;
        }
        osDelay(I2C_DELAY); 
        I2C_SCL_HIGH;
        osDelay(I2C_DELAY); 
        I2C_SCL_LOW;
        byte <<= 1;
    }
    /* Read the acknowledge bit */
    I2C_SDA_HIGH;
    osDelay(I2C_DELAY); 
    I2C_SCL_HIGH;
    osDelay(I2C_DELAY); 
    uint8_t usAck = I2C_SDA_READ; /* Read the acknowledge bit, 0 indicates successful data reception by the device */
    I2C_SCL_LOW;
    return usAck;
}

uint8_t I2C_ReadByte(uint8_t ack) {
    uint8_t i, byte = 0;
    for (i = 0; i < 8; i++) {
        byte <<= 1;
        I2C_SCL_HIGH;
        osDelay(I2C_DELAY); 
        if (I2C_SDA_READ) {
            byte |= 0x01;
        }
        I2C_SCL_LOW;
        osDelay(I2C_DELAY); 
    }
    /* Send the acknowledge bit */
    if (ack) {
        I2C_SDA_LOW;
    } else {
        I2C_SDA_HIGH;
    }
    osDelay(I2C_DELAY); 
    I2C_SCL_HIGH;
    osDelay(I2C_DELAY); 
    I2C_SCL_LOW;
    return byte;
}

/* Initialize the I2C bus */
void I2C_Init(void) {
    /* Initialize GPIO pins */
    GPIO_InitTypeDef GPIO_InitStruct;
    __HAL_RCC_GPIOD_CLK_ENABLE();
    GPIO_InitStruct.Pin = I2C_SCL_PIN | I2C_SDA_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; /* Open-drain output mode */
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStruct);

    /* Initialize the I2C bus */
    I2C_Stop(); /* Send stop condition */
}

uint8_t I2C_WriteData(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size) {
    I2C_Start(); /* Send start condition */
    
    uint8_t usAck = I2C_WriteByte(address << 1); /* Send device address and write flag */
    if (usAck) {
        I2C_Stop(); /* Send stop condition */
        return usAck; /* Return error */
    }
    
    /* Send register address */
    usAck = I2C_WriteByte(reg);
    if (usAck) {
        I2C_Stop(); /* Send stop condition */
        return usAck; /* Return error */
    }
    
    for (uint16_t i = 0; i < size; i++) { /* Loop to send data */
        usAck = I2C_WriteByte(data[i]); /* Send data */

        if (usAck) {
            I2C_Stop(); /* Send stop condition */
            return usAck; /* Return error */
        }
    }
    
    I2C_Stop(); /* Send stop condition */
    return 0; /* Return no error */
}

uint8_t I2C_ReadData(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size) {
    I2C_Start(); /* Send start condition */
    
    uint8_t usAck = I2C_WriteByte(address << 1); /* Send device address and write flag */
    if (usAck) {
        I2C_Stop(); /* Send stop condition */
        return usAck; /* Return error */
    }
    
    /* Send register address */
    usAck = I2C_WriteByte(reg);
    if (usAck) {
        I2C_Stop(); /* Send stop condition */
        return usAck; /* Return error */
    }
    
    I2C_Start(); /* Send start condition again */
    /* Send device address and read flag again */
    usAck = I2C_WriteByte((address << 1) | 0x01);
    if (usAck) {
        I2C_Stop(); /* Send stop condition */
        return usAck; /* Return error */
    }
    
    for (uint16_t i = 0; i < size; i++) { /* Loop to read data */
        data[i] = I2C_ReadByte(i < size - 1); /* Read data */
        /* Check if it's the last byte, if so, exit the loop */
        if (i == size - 1) {
            break;
        }
    }    
    I2C_Stop(); /* Send stop condition */
    return 0; /* Return no error */
}

BspI2C.h

/*
    BspI2C.h

    Implementation File for STM32 I2C Module
*/

/*
    Modification History
    --------------------
    01a 27Feb24 Jasper Created
*/


#ifndef __BSP_I2C_H__
#define __BSP_I2C_H__

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include <stm32f1xx_hal.h>

#define I2C_DELAY   1

#define I2C_GPIO_PORT    GPIOD
#define I2C_SCL_PIN      GPIO_PIN_12
#define I2C_SDA_PIN      GPIO_PIN_13

// 定义I2C的时钟和数据线控制宏
#define I2C_SCL_HIGH  HAL_GPIO_WritePin(I2C_GPIO_PORT, I2C_SCL_PIN, GPIO_PIN_SET)
#define I2C_SCL_LOW   HAL_GPIO_WritePin(I2C_GPIO_PORT, I2C_SCL_PIN, GPIO_PIN_RESET)

#define I2C_SDA_HIGH  HAL_GPIO_WritePin(I2C_GPIO_PORT, I2C_SDA_PIN, GPIO_PIN_SET)
#define I2C_SDA_LOW   HAL_GPIO_WritePin(I2C_GPIO_PORT, I2C_SDA_PIN, GPIO_PIN_RESET)
#define I2C_SDA_READ  HAL_GPIO_ReadPin(I2C_GPIO_PORT, I2C_SDA_PIN)

#if 0

#define HIGH (uint32_t)1
#define LOW  (uint32_t)0

#define SCL_OUTPUT(x)  if(x == 0) HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SCL_PIN,GPIO_PIN_RESET); else HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SCL_PIN,GPIO_PIN_SET);
#define SDA_OUTPUT(x)  if(x == 0) HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SDA_PIN,GPIO_PIN_RESET); else HAL_GPIO_WritePin(I2C_GPIO_PORT,I2C_SDA_PIN,GPIO_PIN_SET);
#define SDA_READ()     HAL_GPIO_ReadPin(I2C_GPIO_PORT,I2C_SDA_PIN)

#endif


void I2C_Init(void);
// 写入数据到设备
// 写入数据到设备
uint8_t I2C_WriteData(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size);
// 从设备读取数据
uint8_t I2C_ReadData(uint8_t address, uint8_t reg, uint8_t* data, uint16_t size);



#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* __BSP_SPI_H__ */

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言是一种广泛应用于嵌入式系统开发的编程语言,而STM32F103C8T6是一款常用的基于ARM Cortex-M3内核的微控制器。在STM32F103C8T6上实现软件I2C通信可以通过GPIO口模拟I2C总线的时钟和数据线来实现。 下面是一个简单的示例代码,演示了如何在STM32F103C8T6上使用C语言实现软件I2C通信: ```c #include "stm32f10x.h" #define I2C_SCL_PIN GPIO_Pin_6 #define I2C_SDA_PIN GPIO_Pin_7 #define I2C_GPIO_PORT GPIOB void I2C_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure); GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN | I2C_SDA_PIN); } void I2C_Start(void) { GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN | I2C_SDA_PIN); GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN); GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN); } void I2C_Stop(void) { GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN); GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN); GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN | I2C_SDA_PIN); } void I2C_SendByte(uint8_t byte) { uint8_t i; for (i = 0; i < 8; i++) { GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN); if (byte & 0x80) GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN); else GPIO_ResetBits(I2C_GPIO_PORT, I2C_SDA_PIN); GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN); byte <<= 1; } GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN); } uint8_t I2C_ReceiveByte(void) { uint8_t i, byte = 0; GPIO_SetBits(I2C_GPIO_PORT, I2C_SDA_PIN); for (i = 0; i < 8; i++) { byte <<= 1; GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN); if (GPIO_ReadInputDataBit(I2C_GPIO_PORT, I2C_SDA_PIN)) byte |= 0x01; GPIO_SetBits(I2C_GPIO_PORT, I2C_SCL_PIN); } GPIO_ResetBits(I2C_GPIO_PORT, I2C_SCL_PIN); return byte; } void I2C_WriteByte(uint8_t deviceAddr, uint8_t regAddr, uint8_t data) { I2C_Start(); I2C_SendByte(deviceAddr); I2C_SendByte(regAddr); I2C_SendByte(data); I2C_Stop(); } uint8_t I2C_ReadByte(uint8_t deviceAddr, uint8_t regAddr) { uint8_t data; I2C_Start(); I2C_SendByte(deviceAddr); I2C_SendByte(regAddr); I2C_Start(); I2C_SendByte(deviceAddr | 0x01); data = I2C_ReceiveByte(); I2C_Stop(); return data; } int main(void) { I2C_Init(); // 使用示例:向设备地址为0x50的I2C设备的寄存器0x00写入数据0xAA I2C_WriteByte(0x50, 0x00, 0xAA); // 使用示例:从设备地址为0x50的I2C设备的寄存器0x00读取数据 uint8_t data = I2C_ReadByte(0x50, 0x00); while (1) { // 主循环 } } ``` 上述代码中,我们首先定义了软件I2C通信所需的引脚和端口,然后实现了初始化函数`I2C_Init()`、起始信号函数`I2C_Start()`、停止信号函数`I2C_Stop()`、发送字节函数`I2C_SendByte()`、接收字节函数`I2C_ReceiveByte()`、写入字节函数`I2C_WriteByte()`和读取字节函数`I2C_ReadByte()`。 在`main()`函数中,我们通过调用`I2C_WriteByte()`函数向设备地址为0x50的I2C设备的寄存器0x00写入数据0xAA,并通过调用`I2C_ReadByte()`函数从设备地址为0x50的I2C设备的寄存器0x00读取数据。 请注意,以上代码仅为示例,具体的软件I2C实现可能会因硬件平台和需求而有所不同。在实际应用中,您可能需要根据具体情况进行适当的修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值