在和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. 给自己取了个英文名-Jamy Cai,哈哈~~

    给自己取了个英文名:Jamy Cai, 同时开始启用新邮箱:Jamycai@outlook.com ~~

  2. JavaScript的DOM操作(一)

    DOM:文档对象模型 --树模型文档:标签文档,对象:文档中每个元素对象,模型:抽象化的东西 一:window: 属性(值或者子对象):opener:打开当前窗口的源窗口,如果当前窗口是首次启动浏览器 ...

  3. 第六篇:web之python框架之django

    python框架之django   python框架之django 本节内容 web框架 mvc和mtv模式 django流程和命令 django URL django views django te ...

  4. Oracle REGEXP_INSTR 用法

    原文出处 ORACLE中的支持正则表达式的函数主要有下面四个:    1,REGEXP_LIKE :与LIKE的功能相似    2,REGEXP_INSTR :与INSTR的功能相似    3,REG ...

  5. 滚动效果,marquee便签

    语法: <marquee></marquee> 例子: <marquee><font size=+3 color=red>Hello, World< ...

  6. 字符编码笔记:ASCII、Unicode、UTF-8、UTF-16、UCS、BOM、Endian

    转载:http://witmax.cn/character-encoding-notes.html 今天中午,我突然想搞清楚Unicode和UTF-8之间的关系,于是就开始在网上查资料. 结果,这个问 ...

  7. How to hanganalyze and systemstate dumps

    Oracle support request hang analysis and system state dumps when rasing SR. One 10.1 or higher versi ...

  8. WCF 无法生成 client

    在MVC中调用WCF 总是没有client 后来在网上查找原因,去掉Reuse type in referrenced assenbiles ,就可以生成代理代码.

  9. C# 之【获取网页】

    C#获取指定网页HTML原代码可使用 WebClient WebRequest HttpWebRequest 三种方式来实现. 当然也可使用webBrowse!在此就不研究webBrowse如何获取了 ...

  10. java_反射_及其简单应用(2016-11-16)

    话不多说直接上代码 接口: package bean; /** * user接口 */ public interface User { public String getName(); public ...