STM32F1使用I/0模拟I2C接口

使用模拟时序的方法,对比于硬件I2C接口来说,在实时性和传输速度上会带来一些无法避免的下降,但是I2C总线本身也不是一种速度很快的总线(据相关资料可查,最高的速度为400KHZ),同时也不需要具备很高的实时性能。
所以,模拟I2C时序完全能满足绝大部分的场合要求,并且移植性得到了很大的提高。
闲话不多说,贴上代码,大家一起分享下。

首先贴出 i2c_soft.h实现:

/***********************************************************************************
 * 文件名  :i2c_soft.h
 * 描述    :使用I/0模拟I2C接口         
 * 实验平台:神舟III号
 * 库版本  :ST3.0.0
 *
 * 作者    :xiayufeng  xiayufeng90520@163.com 
 * 博客    :http://hi.baidu.com/xiayufeng520
**********************************************************************************/
#ifndef __I2C_SOFT_H
#define __I2C_SOFT_H
    
#include "stm32f10x.h"
#define SCL_PIN        GPIO_Pin_6
#define SDA_PIN        GPIO_Pin_7
#define SCL_PORT       GPIOB
#define SDA_PORT       GPIOB
#define SCL_RCC_CLOCK  RCC_APB2Periph_GPIOB
#define SDA_RCC_CLOCK  RCC_APB2Periph_GPIOB
    
#define SCL_H         GPIOB->BSRR = GPIO_Pin_6
#define SCL_L         GPIOB->BRR  = GPIO_Pin_6 
       
#define SDA_H         GPIOB->BSRR = GPIO_Pin_7
#define SDA_L         GPIOB->BRR  = GPIO_Pin_7
    
#define SCL_read      GPIOB->IDR  & GPIO_Pin_6
#define SDA_read      GPIOB->IDR  & GPIO_Pin_7
    
#define I2C_PageSize  8  //24C02每页8字节
    
void I2C_GPIO_Config(void);
bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress);
bool I2C_BufferWrite(u8* pBuffer, u8 length, u16 WriteAddress, u8 DeviceAddress);
void I2C_PageWrite(u8* pBuffer, u8 length, u16 WriteAddress, u8 DeviceAddress);
bool I2C_ReadByte(u8* pBuffer, u8 length, u16 ReadAddress, u8 DeviceAddress);
void I2C_Test(void);
    
#endif
然后贴出 i2c_soft.c实现:

/***********************************************************************************
 * 文件名  :i2c_soft.c
 * 描述    :使用I/0模拟I2C接口         
 * 实验平台:神舟III号
 * 库版本  :ST3.0.0
 *
 * 作者    :xiayufeng  xiayufeng90520@163.com 
 * 博客    :http://hi.baidu.com/xiayufeng520
**********************************************************************************/
#include "i2c_soft.h"
#include "usart1.h"
     
extern void Delay_ms(__IO u32 nTime);
     
/* 
 * 函数名:void I2C_GPIO_Config(void)
 * 描述  : I2C管脚初始化
 * 输入  :无
 * 输出  : 无
 */
void I2C_GPIO_Config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure; 
         
    RCC_APB2PeriphClockCmd(SCL_RCC_CLOCK | SDA_RCC_CLOCK ,ENABLE);
         
    //初始化SCL管脚
    GPIO_InitStructure.GPIO_Pin =  SCL_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  
    GPIO_Init(SCL_PORT, &GPIO_InitStructure);
         
    //初始化SDA管脚
    GPIO_InitStructure.GPIO_Pin =  SDA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    GPIO_Init(SDA_PORT, &GPIO_InitStructure);
}
     
/* 
 * 函数名: void I2C_delay(void)
 * 描述  : 短暂延时
 * 输入  : 无
 * 输出  : 无
 * 说明  : 内部定义的i可以优化速度,经测试最低到5还能写入
 */
static void I2C_delay(void)
{   
    u8 i=100; 
    while(i) 
    { 
        i--; 
    } 
}
     
/* 
 * 函数名: bool I2C_Start(void)
 * 描述  : 起始信号
 * 输入  : 无
 * 输出  : TRUE : 成功
                     FALSE : 失败
 * 说明  : 
 */
static bool I2C_Start(void)
{
    SDA_H;
    SCL_H;
    I2C_delay();
    if(!SDA_read)
        return FALSE;   //SDA线为低电平则总线忙,退出
    SDA_L;
    I2C_delay();
    if(SDA_read) 
        return FALSE;   //SDA线为高电平则总线出错,退出
    SDA_L;
    I2C_delay();
    return TRUE;
}
/* 
 * 函数名: static void I2C_Stop(void)
 * 描述  : 终止信号
 * 输入  : 无
 * 输出  : 无
 * 说明  : 
 */
static void I2C_Stop(void)
{
    SCL_L;
    I2C_delay();
    SDA_L;
    I2C_delay();
    SCL_H;
    I2C_delay();
    SDA_H;
    I2C_delay();
}
/* 
 * 函数名: static void I2C_Ack(void)
 * 描述  : 应答信号
 * 输入  : 无
 * 输出  : 无
 * 说明  : 
 */
static void I2C_Ack(void)
{   
    SCL_L;
    I2C_delay();
    SDA_L;
    I2C_delay();
    SCL_H;
    I2C_delay();
    SCL_L;
    I2C_delay();
}
/* 
 * 函数名: void I2C_NoAck(void)
 * 描述  : 无应答信号
 * 输入  : 无
 * 输出  : 无
 * 说明  : 
 */
static void I2C_NoAck(void)
{   
    SCL_L;
    I2C_delay();
    SDA_H;
    I2C_delay();
    SCL_H;
    I2C_delay();
    SCL_L;
    I2C_delay();
}
/* 
 * 函数名: bool I2C_Start(void)
 * 描述  : 等待应答信号
 * 输入  : 无
 * 输出  : TRUE : 有应答
                     FALSE : 无应答
 * 说明  : 
 */
static bool I2C_WaitAck(void)   
{
    SCL_L;
    I2C_delay();
    SDA_H;          
    I2C_delay();
    SCL_H;
    I2C_delay();
    if(SDA_read)
    {
        SCL_L;
        return FALSE;
    }
    SCL_L;
    return TRUE;
}
/* 
 * 函数名: static void I2C_SendByte(u8 SendByte) 
 * 描述  : 发送一个字节
 * 输入  : SendByte : 字节数据
 * 输出  : 无
 * 说明  : 数据从高位到低位
 */
static void I2C_SendByte(u8 SendByte) 
{
    u8 i=8;
    while(i--)
    {
        SCL_L;
        I2C_delay();
        if(SendByte&0x80)
            SDA_H;  
        else
            SDA_L;   
        SendByte<<=1;
        I2C_delay();
        SCL_H;
        I2C_delay();
    }
    SCL_L;
}
/* 
 * 函数名: static u8 I2C_ReceiveByte(void) 
 * 描述  : 读取一个字节
 * 输入  : 无 
 * 输出  : 字节数据
 * 说明  : ReceiveByte : 数据从高位到低位
 */
static u8 I2C_ReceiveByte(void)  
{ 
    u8 i=8;
    u8 ReceiveByte=0;
         
    SDA_H;              
    while(i--)
    {
        ReceiveByte<<=1;      
        SCL_L;
        I2C_delay();
        SCL_H;
        I2C_delay();    
        if(SDA_read)
        {
            ReceiveByte|=0x01;
        }
    }
    SCL_L;
    return ReceiveByte;
}
     
/* 
 * 函数名: bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress)
 * 描述  : 写入1字节数据  
 * 输入  : SendByte : 要写入数据
                     WriteAddress : 写入地址
                     DeviceAddress : 器件地址
 * 输出  : TRUE : 成功
                     FALSE : 失败
 * 说明  : 
 */
bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress)
{       
    if(!I2C_Start())
            return FALSE;
    I2C_SendByte(((WriteAddress & 0x0700) >>7) | DeviceAddress & 0xFFFE);//设置高起始地址+器件地址 
    if(!I2C_WaitAck())
        {
            I2C_Stop(); 
            return FALSE;
        }
    I2C_SendByte((u8)(WriteAddress & 0x00FF));   //设置低起始地址      
    I2C_WaitAck();  
    I2C_SendByte(SendByte);
    I2C_WaitAck();   
    I2C_Stop(); 
         
    Delay_ms(10);//注意:因为这里要等待EEPROM写完,可以采用查询或延时方式(10ms)
         
        return TRUE;
}
/* 
 * 函数名: bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress)
 * 描述  : 写入1串数据 
 * 输入  : pBuffer : 要写入数据缓冲区指针
           length : 待写入长度  
                     WriteAddress : 写入地址
                     DeviceAddress : 器件地址
 * 输出  : TRUE : 成功
                     FALSE : 失败
 * 说明  : 注意不能跨页写
 */
bool I2C_BufferWrite(u8* pBuffer, u8 length, u16 WriteAddress, u8 DeviceAddress)
{
    if(!I2C_Start())
            return FALSE;
    I2C_SendByte(((WriteAddress & 0x0700) >>7) | DeviceAddress & 0xFFFE);//设置高起始地址+器件地址 
    if(!I2C_WaitAck())
        {
            I2C_Stop(); 
            return FALSE;
        }
    I2C_SendByte((u8)(WriteAddress & 0x00FF));   //设置低起始地址      
        I2C_WaitAck();  
        while(length--)
        {
            I2C_SendByte(* pBuffer);
            I2C_WaitAck();
            pBuffer++;
        }
        I2C_Stop();
             
        Delay_ms(10);//注意:因为这里要等待EEPROM写完,可以采用查询或延时方式(10ms)
             
        return TRUE;
}
     
/* 
 * 函数名: bool I2C_WriteByte(u8 SendByte, u16 WriteAddress, u8 DeviceAddress)
 * 描述  : 写入1串数据 
 * 输入  : pBuffer : 要写入数据缓冲区指针
           length : 待写入长度  
                     WriteAddress : 写入地址
                     DeviceAddress : 器件地址
 * 输出  : TRUE : 成功
                     FALSE : 失败
 * 说明  : 跨页写入1串数据
 */
void I2C_PageWrite(  u8* pBuffer, u8 length, u16 WriteAddress, u8 DeviceAddress)
{
    u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
    Addr  = WriteAddress % I2C_PageSize;      //写入地址是开始页的第几位
    count = I2C_PageSize - Addr;                        //在开始页要写入的个数
    NumOfPage   =  length / I2C_PageSize;     //要写入的页数
    NumOfSingle =  length % I2C_PageSize;     //不足一页的个数
         
    if(Addr == 0)         //写入地址是页的开始 
    {
        if(NumOfPage == 0)  //数据小于一页 
        {
            I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress);   //写少于一页的数据
        }
        else                    //数据大于等于一页  
        {
            while(NumOfPage)//要写入的页数
            {
                I2C_BufferWrite(pBuffer,I2C_PageSize,WriteAddress,DeviceAddress);//写一页的数据
                WriteAddress +=  I2C_PageSize;
                pBuffer      +=  I2C_PageSize;
                NumOfPage--;
                Delay_ms(10);
            }
            if(NumOfSingle!=0)//剩余数据小于一页
            {
                I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress); //写少于一页的数据
                Delay_ms(10);
            }
        }
    }
         
    else                  //写入地址不是页的开始 
    {
        if(NumOfPage== 0)   //数据小于一页 
        {
            I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress);   //写少于一页的数据
        }
        else                //数据大于等于一页
        {
            length       -= count;
            NumOfPage     = length / I2C_PageSize;  //重新计算要写入的页数
            NumOfSingle   = length % I2C_PageSize;  //重新计算不足一页的个数   
                 
            if(count != 0)
            {  
                I2C_BufferWrite(pBuffer,count,WriteAddress,DeviceAddress);      //将开始的空间写满一页
                WriteAddress += count;
                pBuffer      += count;
            } 
                 
            while(NumOfPage--)  //要写入的页数
            {
                I2C_BufferWrite(pBuffer,I2C_PageSize,WriteAddress,DeviceAddress);//写一页的数据
                WriteAddress +=  I2C_PageSize;
                pBuffer      +=  I2C_PageSize; 
            }
            if(NumOfSingle != 0)//剩余数据小于一页
            {
                I2C_BufferWrite(pBuffer,NumOfSingle,WriteAddress,DeviceAddress); //写少于一页的数据 
            }
        }
    } 
}
     
/* 
 * 函数名: bool I2C_ReadByte(u8* pBuffer,   u8 length,     u16 ReadAddress,  u8 DeviceAddress)
 * 描述  : 读出1串数据
 * 输入  : pBuffer : 要读取数据缓冲区指针
           length : 待读取长度  
                     WriteAddress : 读取地址
                     DeviceAddress : 器件地址
 * 输出  : TRUE : 成功
                     FALSE : 失败
 * 说明  : 跨页写入1串数据
 */
bool I2C_ReadByte(u8* pBuffer,   u8 length,     u16 ReadAddress,  u8 DeviceAddress)
{       
    if(!I2C_Start())return FALSE;
    I2C_SendByte(((ReadAddress & 0x0700) >>7) | DeviceAddress & 0xFFFE);//设置高起始地址+器件地址 
    if(!I2C_WaitAck()){I2C_Stop(); return FALSE;}
    I2C_SendByte((u8)(ReadAddress & 0x00FF));   //设置低起始地址      
    I2C_WaitAck();
    I2C_Start();
    I2C_SendByte(((ReadAddress & 0x0700) >>7) | DeviceAddress | 0x0001);
    I2C_WaitAck();
    while(length)
    {
        *pBuffer = I2C_ReceiveByte();
        if(length == 1)I2C_NoAck();
        else I2C_Ack(); 
        pBuffer++;
        length--;
    }
    I2C_Stop();
    return TRUE;
}
     
/* 
 * 函数名: void I2C_Test(void)
 * 描述  : 测试函数
 * 输入  : 无 
 * 输出  : 无
 * 说明  : 无
 */
void I2C_Test(void)
{
     
    u8 I2cVal_Write = 0xFE;
    u8 I2cVal_Read = 0x00;
         
    printf("Start IIC test\r\n");
    printf("The Simulation_IIC has sended data:%d \r\n", I2cVal_Write);
    I2C_WriteByte(I2cVal_Write, 0X01, 0xa0);
         
     
    I2C_ReadByte(&I2cVal_Read, 1, 0x01 ,0xa0);
    printf("The Simulation_IIC has Received data:%d \r\n", I2cVal_Read);
         
    if(I2cVal_Read == I2cVal_Write)
    {
        printf("The Simulation IIC is successful!\r\n");
    }
    else
    {
        printf("The Simulation IIC is failed!\r\n");
    }
     
}
测试好用,要是发现问题,可以发信联系,共同学习。


  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用内部上拉的模拟 I2C 的代码示例,使用的是STM32的HAL库: ```c++ #include "stm32f1xx_hal.h" #define I2C_ADDRESS 0x10 // 你的设备地址 #define I2C_TIMEOUT 1000 // 超时时间 I2C_HandleTypeDef hi2c1; // I2C句柄 void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_I2C1_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); while (1) { // 等待接收数据 uint8_t rxData[10]; HAL_I2C_Master_Receive(&hi2c1, I2C_ADDRESS, rxData, 10, I2C_TIMEOUT); // 处理接收到的数据 // 发送数据 uint8_t txData[] = "Hello World"; HAL_I2C_Master_Transmit(&hi2c1, I2C_ADDRESS, txData, sizeof(txData), I2C_TIMEOUT); HAL_Delay(1000); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } static void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = I2C_ADDRESS; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } ``` 在以上示例中,我们使用STM32的HAL库来模拟I2C通信。在main函数中,我们初始化了GPIOI2C,并使用HAL_I2C_Master_Receive和HAL_I2C_Master_Transmit函数来接收和发送数据。由于我们没有使用外部上拉电阻,所以默认使用了内部上拉电阻来确保正确的通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值