历史上的今天
今天是:2025年05月20日(星期二)
2018年05月20日 | GPIO口模拟I2C
2018-05-20 来源:eefocus
根据I2C通信规范(具体可以参考“浅谈I2C总线”),通过普通IO端口模拟可以实现单片机(主设备)与从设备的I2C通信,其中SCL通过IO口延时高低电平变化实现,SDA根据SCL状态变化产生开始信号,结束信号,以及实现发送接收数据等,以下是相关代码
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose: I2C Communication driver(By IO)
* @Author: Purple
* @Version: 1.0
* @Date: Create By Purple 2014.08.09
*
*
* Copyright (C) BlueWhale Tech.
* All rights reserved.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef IICDRV_H
#define IICDRV_H
/* Include Files */
/* Macros */
#define I2C_SDA PTT_PTT0 //举例以Freescale PT0端口为SDA线,PT1端口为SCL线
#define I2C_SDA_IO DDRT_DDRT0
#define I2C_SCL PTT_PTT1
#define I2C_SCL_IO DDRT_DDRT1
#define IO_OUT_MODE 1 //Freescale:1为输出模式,0为输入模式;NEC:0为输出模式,1为输入模式
#define IO_IN_MODE 0
/* Function Prototypes */
void I2CStart(void);
void I2CStop(void);
void I2CFree(void);
void I2CSendACK(void);
void I2CSendNoACK(void);
bool I2CCheckACK(void);
void I2CNoAck(void);
void I2CSendByte(unsigned char sendData);
unsigned char I2CReceiveByte(void);
#endif
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose: I2C Communication driver(By IO)
* @Author: Purple
* @Version: 1.0
* @Date: Create By Purple 2014.08.09
*
*
* Copyright (C) BlueWhale Tech.
* All rights reserved.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Include Files */
#include "IICDrv.h"
/* Function Prototypes */
static void I2CDelay(void);
/* Function Definitions */
/*
* FunctionName: I2CDelay
* Purpose: I2C时序模拟SCL时间间隔(周期),需要根据Slave性能及单片机工作频率调整
* Parameters: 无
*/
static void I2CDelay(void)
{
_asm("nop");
_asm("nop");
_asm("nop");
_asm("nop");
_asm("nop");
}
/*
* FunctionName: I2CStart
* Purpose: 模拟I2C开始信号
* Parameters: 无
*/
void I2CStart(void)
{
I2C_SDA_IO=IO_OUT_MODE; //设置SDA端口为输出端口
I2C_SCL_IO=IO_OUT_MODE; //设置SCL端口为输出端口
I2C_SCL=1;
I2CDelay();
I2C_SDA=1;
I2CDelay();
I2C_SDA=0;
I2CDelay();
I2C_SCL=0;
I2CDelay();
}
/*
* FunctionName: I2CStop
* Purpose: 模拟I2C结束信号
* Parameters: 无
*/
void I2CStop(void)
{
I2C_SDA_IO=IO_OUT_MODE; //设置SDA端口为输出端口
I2C_SCL_IO=IO_OUT_MODE; //设置SCL端口为输出端口
I2C_SCL = 0;
I2CDelay();
I2C_SDA = 0;
I2CDelay();
I2C_SDA = 1;
I2CDelay();
I2C_SCL = 1;
I2CDelay();
}
/*
* FunctionName: I2CFree
* Purpose: 模拟I2C空闲状态信号
* Parameters: 无
*/
void I2CFree(void)
{
I2C_SDA_IO=IO_OUT_MODE; //设置SDA端口为输出端口
I2C_SCL_IO=IO_OUT_MODE; //设置SCL端口为输出端口
I2C_SDA = 1;
I2CDelay();
I2C_SCL = 1;
I2CDelay();
}
/*
* FunctionName: I2CSendACK
* Purpose: 模拟I2C发送ACK响应
* Parameters: 无
*/
void I2CSendACK(void)
{
I2C_SDA_IO=IO_OUT_MODE; //设置SDA端口为输出端口
I2C_SCL_IO=IO_OUT_MODE; //设置SCL端口为输出端口
I2C_SCL=0;
I2CDelay();
I2C_SDA=0;
I2CDelay();
I2C_SCL=1;
I2CDelay();
I2C_SCL=0;
I2CDelay();
}
/*
* FunctionName: I2CSendNoACK
* Purpose: 模拟I2C无ACK响应
* Parameters: 无
*/
void I2CSendNoACK(void)
{
I2C_SDA_IO=IO_OUT_MODE; //设置SDA端口为输出端口
I2C_SCL_IO=IO_OUT_MODE; //设置SCL端口为输出端口
I2C_SCL=0;
I2CDelay();
I2C_SDA=1;
I2CDelay();
I2C_SCL=1;
I2CDelay();
I2C_SCL=0;
I2CDelay();
}
/*
* FunctionName: I2CCheckACK
* Purpose: 检查I2C是否有ACK响应
* Parameters: 无
*/
bool I2CCheckACK(void)
{
bool tempACK;
I2C_SDA_IO=IO_OUT_MODE; //设置SDA端口为输出端口
I2C_SCL_IO=IO_OUT_MODE; //设置SCL端口为输出端口
I2C_SDA=1;
I2CDelay();
I2C_SCL = 1;
I2C_SDA_IO=IO_IN_MODE; //设置SDA端口为输入端口,检查Slave是否有响应
I2CDelay();
if(I2C_SDA)
tempACK=FALSE;
else
tempACK=TRUE;
I2C_SCL=0;
I2CDelay();
return tempACK ;
}
/*
* FunctionName: I2CSendByte
* Purpose: 模拟I2C发送一个字节数据
* Parameters: sendData-发送的一个字节数据
*/
void I2CSendByte(unsigned char sendData)
{
unsigned char serialNum = 0;
I2C_SDA_IO=IO_OUT_MODE; //设置SDA端口为输出端口
I2C_SCL_IO=IO_OUT_MODE; //设置SCL端口为输出端口
for(serialNum=8;serialNum>=1;serialNum--) //以MSB方式按位发送一个字节数据
{
I2C_SDA = (sendData>>(serialNum-1))&0x01;
I2CDelay();
I2C_SCL = 1;
I2CDelay();
I2C_SCL = 0;
I2CDelay();
}
}
/*
* FunctionName: I2CReceiveByte
* Purpose: 模拟I2C接收一个字节数据
* Parameters: 无
*/
unsigned char I2CReceiveByte(void)
{
unsigned char serialNum = 0;
unsigned char dataValue=0;
I2C_SDA_IO=IO_IN_MODE; //设置SDA端口为输入端口
I2C_SCL_IO=IO_OUT_MODE; //设置SCL端口为输出端口
for(serialNum=0;serialNum<=7;serialNum++)//以MSB方式按位接收一个字节数据
{
I2C_SCL=1;
I2CDelay();
if(I2C_SDA) dataValue|=(0b10000000>>serialNum);
I2C_SCL=0;
I2CDelay();
}
return dataValue;
}
需要注意模拟SCL采用的延时需要根据从设备的特性来调整,延时时间不能小于从设备的最小SCL间隔时间
既然已经通过IO端口实现了I2C通信,那么,我们就可以用以上代码实现单片机与相应从设备I2C的通信了,以EEPROM 24C04为例,以下是读取和写入EEPROM数据相关函数的代码
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose: Communication with EEPROM 24C04
* @Author: Purple
* @Version: 1.0
* @Date: Create By Purple 2014.08.09
*
*
* Copyright (C) BlueWhale Tech.
* All rights reserved.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Include Files */
#include "EEApp.h"
/* Function Definitions */
/*
* FunctionName: Slave24C04Write
* Purpose: 向EEPROM24C04中写入一个字节数据
* Parameters: tarAddress-写入数据的起始地址
* wrNumber-待写入数据的长度(字节单位)
* wrPointer-待写入数据的首字节地址
*/
void Slave24C04Write(unsigned char tarAddress,unsigned char wrNumber,unsigned char* wrPointer)
{
bool rxdACK;
I2CStart();
I2CSendByte(SLAVE_ADDRESS); //发送24C04的器件地址,地址LSB最后一位为0代表写入,1代表读取
rxdACK=I2CCheckACK();
I2CSendByte(tarAddress); //发送写入数据的起始地址
rxdACK=I2CCheckACK();
for(;wrNumber!=0;wrNumber--,wrPointer++)
{
I2CSendByte(*wrPointer); //按字节写入数据
rxdACK=I2CCheckACK();
}
I2CStop();
}
/*
* FunctionName: Slave24C04Read
* Purpose: 从EEPROM24C04中读取一个字节数据
* Parameters: tarAddress-读取数据的起始地址
* wrNumber-读取数据的长度(字节单位)
* wrPointer-读取数据的首字节存放地址
*/
void Slave24C04Read(unsigned char tarAddress,unsigned char rdNumber,unsigned char* rdPointer)
{
bool rxdACK;
I2CStart();
I2CSendByte(SLAVE_ADDRESS); //发送24C04的器件地址
rxdACK=I2CCheckACK();
I2CSendByte(tarAddress); //发送读取数据的起始地址
rxdACK=I2CCheckACK();
I2CStart();
I2CSendByte(SLAVE_ADDRESS+1); //发送24C04的器件地址,地址LSB最后一位为1代表读取
rxdACK=I2CCheckACK();
for(;rdNumber!=0;rdNumber--,rdPointer++)
{
*rdPointer=I2CReceiveByte(); //按字节读取数据
if(rdNumber!=1)
I2CSendACK();
else
I2CSendNoACK();
}
I2CStop();
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* @Purpose: Communication with EEPROM 24C04
* @Author: Purple
* @Version: 1.0
* @Date: Create By Purple 2014.08.09
*
*
* Copyright (C) BlueWhale Tech.
* All rights reserved.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef EEAPP_H
#define EEAPP_H
/* Include Files */
#include "IICDrv.h"
/* Macros */
#define SLAVE_ADDRESS (0xA0)
/* Function Prototypes */
void Slave24C04Write(unsigned char tarAddress,unsigned char wrNumber,unsigned char* wrPointer);
void Slave24C04Read(unsigned char tarAddress,unsigned char rdNumber,unsigned char* rdPointer);
#endif
需要注意不同的从设备要根据应用电路调整相应的从设备地址。
采用IO口来模拟I2C通信,一般仅用于单片机没有I2C功能的情况下,如果单片机本身具有I2C功能,还是应该通过配置单片机相应的寄存器,通过中断来实现I2C通信,因为模拟SCL采用的延时就是一个空等待,对于整个操作系统来说,这是一个资源浪费,同时还有可能会影响到其他任务的运行。
史海拾趣
|
随着性能不断提高,成本持续降低,FPGA在各个领域都得到了广泛应用。 红色飓风载选了一些国内核心期刊上发表的FPGA设计方面的论文,供大家参考! 希望对正在从事FPGA设计的工程技术人员或者高校里面做毕业设计的学生有一点点帮助。 ====== ...… 查看全部问答> |
|
3.运行ltibcd到ltib安装到的目录,这个是在上面install过程中让你选择的。然后:./ltib即可运行,第一次运行,实际上就是启动build image的工作,最后会调用编译器等工具连构建出最终内核image和文件系统image。这个过程可能出现错误(但我没碰到) ...… 查看全部问答> |
|
在用alsa的应用程序测试开发板上的音频放音的时候,下面的函数出错了: /* Write the parameters to the driver */ rc= snd_pcm_hw_params(handle,params); &nbs ...… 查看全部问答> |
|
最多只能给100,先这样吧,如果问题解决了,小可另外送分哈! 最近在研究NDIS中间层,有些问题不明白,请教: ProtocolReceivePacket可以处理批量数据包,是怎么处理的?参数只是一个包啊,难道这个批量只是相对于ProcotolReceive偶尔一个包需要 ...… 查看全部问答> |
|
1,我的开发环境为evc4+ppc2003+中文镜像 下载了个freepyCE,编译通过,看freepyCE自带的安装说明,替换了注册表中的键值: HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Control\\Layouts\\e0010804修改,把Ime File键值从msimepy.dll改为fr ...… 查看全部问答> |
|
论坛各位大佬,谁用过sht20这块温湿度传感器,有调通的程序分享下吧,51单片机的代码最好,或者FPGA的。 这里可以变为一个关于sht1X、sht2X和sht7X 瑞士Sensirion的温湿度传感器的交流区 我之前也发过一个帖子,是关于sht10的调试通 ...… 查看全部问答> |




