您的位置:首页 > 其它

STM32F1使用I/0模拟I2C接口

2015-05-31 22:27 281 查看
使用模拟时序的方法,对比于硬件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");
    }
     
}
测试好用,要是发现问题,可以发信联系,共同学习。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: