在和Qinheng开发小尺寸点灯治具中,F340和FPGA采用I2C通信,其中F340作为I2C的主机,I2C端口用自己的GPIO编写,总结遇到的问题及注意事项:

1.  F340端口及上拉电阻设置:

  要配成SCL/SDA的端口必须设置成opendrain结构,本例中设置如下:

sbitSCL = P0^;
sbit SDA = P0^;               

void PORT_Init(void)
{
       P0MDOUT = 0x00;     // P0 is open-drain
       XBR1= 0x40;         // Enable crossbarand weak pull-ups
}

上拉电阻搭配XILINXSPARTAN3E的FPGA,经尝试发现用750ohm阻值的电阻,波形较好。1K及2.2K的均可以通信。

2.   I2C时序控制:

  I2C读写的时序一定要准照协议,否则会出现SCL、SDA线电平被拉、互相干扰等异常。本次开发中遇到问题的有下述几点。

  2.1 写时序时注意事项

  写操作时,结束标志由主机发起,发起的位置要注意,是在读完最后一个ack响应后的一个clk上升沿之后,不是在读完ack后直接产生。从clk的个数上来看,从最后一个byte来数,是在第10个clk上升沿之后。写操作正常时序如下图:

注意结束标志起来的位置,结束标志的产生需特别注意,参考代码。

  写操作的代码如下:

                   /*I2C字节写 */
void i2c_write_byte(unsigned char b)
{
         ;
         ; i>=; i--)
         {
                   set_scl();
                   set_sda(b&(<<i));  //从高位到低位依次准备数据进行发送
                   set_scl();
                   set_scl();

                   set_scl();
                   set_scl();
                   set_scl();
         }
         i2c_read_ack();                  //检查目标设备的ACK信号
}

/*   I2C读取ACK信号(写数据时使用)  */
unsigned char i2c_read_ack()
{
         unsigned char ack;

         set_sda();                // 主机释放SDA线,为读响应位做准备

         set_scl();
         set_scl();
         ack = SDA;                // 接收从机的ACK信号
         set_scl();
         set_scl();                  // 上升沿读取ACK信号
         set_scl();

         set_scl();                  // 读取完毕

     return ack;
}

/* I2C终止条件 */
void i2c_stop()
{
         // 结束标志前SCL、SDA状态清零
         set_scl();
         set_sda();                // 在SCL低电平处将SDA拉低
         set_scl();

         // 产生结束标志
         set_scl();                  // 拉高SCL
         set_scl();
         set_scl();

         set_sda();                // 拉高SDA

         TimerDelay_us();
}

/*
I2C写操作
pSlave_addr:I2C slave设备地址
pChip_addr: 目标设备地址
buf:写缓冲区
len:写入字节的长度
自己应用中,协议里的chipaddr和ifsel等参数可向该函数中添加参数
 */
void i2c_write (U8 pSlave_addr, U8 pChip_addr, U8* buf, intlen)
{
         int i;
        unsigned char t;
        i2c_start();                        //起始条件,开始数据通信

        // 发送slave地址和register地址,写方向
         t = (pSlave_addr<< ) | ;                    //低位为0,表示写数据
         i2c_write_byte(t);

         // 发送chipaddress
         t = pChip_addr;
         i2c_write_byte(t);

        // 写入数据
         ; i<len;i++)
                 i2c_write_byte(buf[i]);

         i2c_stop();                     //终止条件,结束数据通信

}

  另外就是注意主机写数据与读ack的操作,对sda线的控制问题:

  主机8bit数据写完后与读ack的衔接,写数据时是主机控制sda线,读ack时要将sda线的控制权交出,作为opendrain接口,交出控制权的方法就是将该端口置1。所以ack操作时第一步是将sda置1。观察波形可以看到如果和从机正常通信,从机响应ack(sda 置0)交出sda控制权后,sda线仍会被拉高,如下图红框中所示。

  open drain结构的特点就是:默认情况下,端口始终是高电平,但要控制总线时,可对其进行拉低操作。如要交出控制权,只需将自己的端口置1即可

  2.2 读时序注意事项:

  在本例开发中读操作时序犯了三个错误:restart信号的格式;读操作结束时最后一个byte读取完毕后,要发送nack,之前读完每个byte后发送的是ack;每读完一个byte后发送ack后,要交出sda控制权,即要将自己端口的sda置1。

  读操作的正确时序如下图所示:

读操作的代码如下:

/*
I2C读操作
pSlave_addr:I2C slave设备地址
pChip_addr: 目标设备地址
pReg_addr_msb: 要读取寄存器地址高字节
pReg_addr_lsb:要读取寄存器地址低字节
buf:读缓冲区
len:读入字节的长度
 */
void i2c_read(U8 pSlave_addr, U8 pChip_addr, U8 pReg_addr_msb, U8pReg_addr_lsb, U8* buf, int len)
 {
        int i;
         unsigned char t;
         i2c_start();                        //起始条件,开始数据通信

        //发送slave地址和register地址,写方向
         t = (pSlave_addr<< ) | ;                    //低位为0,表示读数据
         i2c_write_byte(t);

         // 发送chipaddress
         i2c_write_byte(pChip_addr);

         // 发送registeraddress
         i2c_write_byte(pReg_addr_msb);
         i2c_write_byte(pReg_addr_lsb);

         TimerDelay_us(I2C_READ_WAIT_FPGA);                        // 等待FPGA读取

         // set restart
         i2c_restart();

         //发送slave地址,读方向
         t = (pSlave_addr<< ) | ;                    //低位为1,表示读数据
         i2c_write_byte(t);

          //读入数据
         ; i<len;i++)
         {
                   ))
                           buf[i] =i2c_read_byte();                    // 读取一个byte后发送ack
                   else
                            buf[i] = i2c_read_lastbyte();           // 读取完最后一个byte发送nack
         }
        i2c_stop();                    //终止条件,结束数据通信
 }

/* I2C字节读 */
unsigned char i2c_read_byte()
{
         ;
         unsigned ;
         ; i<; i++)
         {
                   set_scl();
                   set_scl();

                   rbyte =(rbyte<<)|SDA;            //从高位到低位依次准备数据进行读取
                   set_scl();
                   set_scl();

                   set_scl();
         }
         i2c_send_ack();                   //向目标设备发送ACK信号

         return rbyte;
}

/* I2C 读最后一字节 */
unsigned char i2c_read_lastbyte()
{
         ;
         unsigned ;
         ; i<; i++)
         {
                   set_scl();
                   set_scl();

                   rbyte =(rbyte<<)|SDA;            //从高位到低位依次准备数据进行读取
                   set_scl();
                   set_scl();

                   set_scl();
         }
         i2c_send_nack();                 //向目标设备发送NACK信号

         return rbyte;
}

/* I2C发出ACK信号(读数据时使用) */
void i2c_send_ack()
{
         set_sda();                // 主机发送ack,并控制sda线

         set_scl();
         set_scl();                  // 给从机提供上升沿读取ack
         set_scl();
         set_scl();

         set_sda();                //主机释放sda线,为读下一byte数据做准备
}

/* I2C发出NACK信号(读数据时使用) */
void i2c_send_nack()
{
         set_sda();                // 主机发送nack,并控制sda线

         set_scl();
         set_scl();                  // 给从机提供上升沿读取ack
         set_scl();
         set_scl();
}

/* I2C restart */
void i2c_restart(void)
{
         set_scl();
         set_scl();

         set_sda();
}

注意点:

Restart信号和之前I2C写操作的衔接。在上一步写操作完成后,会读取从机的ack响应,读取完毕后,sda线会被拉高,此时按程序所示产生restart信号。

I2C读操作过程中,多个字节读取时,前面的字节读取完毕发送ack信号,最后一个字节读取完毕一定要发送nack信号。

多字节读取过程中,每个字节读取完毕发送ack信号(sda由主机置低)之后,务必要有交出sda线控制权的操作,即将主机的sda端口置高,如i2c_send_ack()函数中的set_sda(1)。不然会产生竞争,从机发出的数据高电平会出现被拉低的现象,如下图所示。

不加set_sda(1):

加set_sda(1):

用F340 GPIO做I2C的更多相关文章

  1. GPIO实现I2C协议模拟(1)

    最近需要用GPIO模拟I2C协议,如果是在Linux下面比较简单,但在Windows下面,是否有没Linux那么简单了. 索性自己对I2C协议还有一些了解,翻了SPEC并结合示波器量出的实际信号分析, ...

  2. gpio模拟i2c驱动

    前段时间做项目,需要gpio模拟i2c通信,最后参考了一些资料,然后编写了一个程序.现在发出来,以免以后忘记,也为一些需要的朋友提供参考.不喜勿喷哈. /* 说明:该程序是基于atmel公司的sama ...

  3. S5PV210之GPIO模拟I2c时序之pcf8591与at24xx linux3.0.8驱动

    目录:一. 说明 二. 驱动程序说明及问题 三. 案例一       四. 案例二 一. 说明 mini210开发板上带了at24c08, 看了linux内核自带的at24.c的驱动程序,编译下载到看 ...

  4. STM32F207 两路ADC连续转换及GPIO模拟I2C给MT9V024初始化参数

    1.为了更好的方便调试,串口必须要有的,主要打印一些信息,当前时钟.转换后的电压值和I2C读出的数据. 2.通过GPIO 模拟I2C对镁光的MT9V024进行参数初始化.之前用我以前公司SP0A19芯 ...

  5. 【转载】GPIO模拟i2c通信

    I2C总线的通信过程(见图4-8)主要包含三个主要阶段:起始阶段.数据传输阶段和终止阶段. 1. 起始阶段 在I2C总线不工作的情况下,SDA(数据线)和SCL(时钟线)上的信号均为高电平.如果此时主 ...

  6. GPIO实现I2C协议模拟(2)

    接着上一节继续补充 结合上一节的描述 写Slave的过程如下(BYTE) 读Slave的过程如下(BYTE) 分为两段 第一段 ,写OFFSET,第二段读数据 WORD的方式与BYTE大同异 读行为 ...

  7. gpio模拟I2C,驱动pcf8574T

    一.pcf8574T介绍 查看pcf8574T的数据手册, A表示读或写,当A为1的时候表示读,当A为0的时候表示写.现把地址控制线,即A2.A1.A0全部接地,可以得到读控制指令为0x41,写控制指 ...

  8. I2C总线以及GPIO模拟I2C

    ·I2C总线的一些特征: 1. 只要求两条总线,一条串行数据线(SDA),一条串行时钟线(SCL) 2. 两个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机系统软件设定的地址:主机可 ...

  9. 硬件GPIO,UART,I2C,SPI电路图

随机推荐

  1. Windows 7系统下局域网文件共享设置方法

    今天给家里增添了一台组装机,小试了一下win7局域网文件共享功能,很爽的说. 记录一下实现方法: 1.关闭防火墙 2.启用共享. 控制面板 – 网络和共享中心 – 更改高级共享设置,将图中的几个选项选 ...

  2. HDU2088JAVA2

    Box of Bricks Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Tot ...

  3. 设计模式之Facade模式

    Facade(外观)模式为子系统中的各类(或结构与方法)提供一个简明一致的界面,隐藏子系统的复杂性,使子系统更加容易使用.他是为子系统中的一组接口所提供的一个一致的界面. 在遇到以下情况使用Facad ...

  4. 对RecycleView的多种item布局的封装

    本文是借鉴bingoogolapple写得BGAAdapter-Android而产生的,对此表示感谢. 效果 1.Adapter的使用 1.继承BaseAdapter 这里是我的adapter pub ...

  5. C#中的两种debug方法

    这篇文章主要介绍了C#中的两种debug方法介绍,本文讲解了代码用 #if DEBUG 包裹.利用宏定义两种方法,需要的朋友可以参考下   第一种:需要把调试方法改成debug代码用 #if DEBU ...

  6. Android - 广播机制和Service

    花了几天,想实现定位功能,使用的是百度的服务,有时真的很无奈,就是一小小个问题,就能折腾好几天. 首先,我是在主线程(Fragment)中单独进行定位,发现不起作用. 然后我想使用Service和广播 ...

  7. Java中I/O的分析

    Java中I/O的原理: 在java程序中,对于数据的输入/输出操作以”流“的方式进行的. 流是内存中一组有序数据序列 Java将数据从源读入到内存当中,形成了流,然后这些流可以写到目的地. Java ...

  8. ERROR ITMS-90167: "No .app bundles found in the package"

    http://stackoverflow.com/questions/37838487/error-itms-90167-no-app-bundles-found-in-the-package 简单说 ...

  9. CoreAnimation实现一个折线表

    将折现表封装到一个view里,暴露给使用者的只有一个传入数据的方法. // // ChartLine.h // BoxingChampion //功能:根据传入的数组,绘制折线图 注意 其frame的 ...

  10. Input的readonly 属性与disabled属性

    readonly 不可编辑,可以获得焦点,背景颜色默认灰色,值的字体颜色默认为灰色,值可以在请求中传递 disabled 不可编辑,不可以获得焦点,背景颜色默认灰色,值的字体颜色默认为灰色,值不可以在 ...