• EFM8单片机与I2C外设通信


            近期帮同学做一个项目,开发板是EFM8单片机,支持SPI和I2C协议(SMBus)。非常久没搞过单片机了,并且条件限制,为了使单片机和外设成功通信。花了一个星期时间。刚開始使用SPI。发现代码逻辑都没问题,就是结果不正确(后来知道是由于带中断的程序单步调试导致的。说多了都是泪),调了几天发现SPI确实调不通。就换了I2C。半天时间搞定,哈哈。本文重点解释I2C,废话少说了。


    1、简单介绍

            I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。是微电子通信控制领域广泛採用的一种总线标准。它是同步通信的一种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等长处。这些长处不是吹的,仅仅须要两个IO口即可了,比起并行传输节省了不知道多少成本。


    2、连接图

            2条双向串行线,一条数据线SDA,一条时钟线SCL。SDA数据传输是大端传输,每次传输8bit,即一字节。支持多主控(multimastering)。不论什么时间点仅仅能有一个主控。总线上每一个设备都有自己的一个addr,共7个bit。广播地址全0。

            本文用的是ADXL345,CS引脚拉高至VDD。ADXL345处于I2C模式,须要简单2线式连接。ALT ADDRESS(SDO)引脚处于高电平,器件的7位I2C地址是0x1D。随后为R/W位。这转化为0x3A写入,0x3B读取。通过ALT ADDRESS引脚(引脚12)接地,能够选择备用I2C地址0x53(随后为R/W位)。这里特别说明,外设和MCU不须要共GND,也不须要共VDD。我刚開始纠结了好久,查了非常多资料,硬是没查到。这转化为0xA6写入。0xA7读取。

    连线方式例如以下图:

    3、读写流程

            I2C的时序这些就不多介绍了,网上一搜一大堆,想用IO口模拟I2C能够。大多数MCU都内置I2C模块,仅仅要连线正确,配置和操作寄存器就能正常通信了。

    只是,I2C读写数据的流程是必须了解的。

    3.1、写流程

            写寄存器的标准流程为:

    1.    Master发起START

    2.    Master发送I2C addr(7bit)和w操作0(1bit),等待ACK

    3.    Slave发送ACK

    4.    Master发送reg addr(8bit),等待ACK

    5.    Slave发送ACK

    6.    Master发送data(8bit),即要写入寄存器中的数据,等待ACK

    7.    Slave发送ACK

    8.    第6步和第7步能够反复多次,即顺序写多个寄存器

    9.    Master发起STOT

    3.2、读流程

            读流程比写略微麻烦一点,在读之前要先把寄存器地址写入,然后再開始读:

    1.    Master发起START

    2.    Master发送I2C addr(7bit)和w操作1(1bit)。等待ACK

    3.    Slave发送ACK

    4.    Master发送reg addr(8bit),等待ACK

    5.    Slave发送ACK

    6.    Master发起START

    7.    Master发送I2C addr(7bit)和r操作1(1bit)。等待ACK

    8.    Slave发送ACK

    9.    Slave发送data(8bit)。即寄存器里的值

    10.    Master发送ACK

    11.    第8步和第9步能够反复多次,即顺序读多个寄存器

    4、程序原理

            程序是依据配置和操作寄存器实现I2C通信。将I2C设为忙状态,START标志開始,兴许全部收发数据在中断子程序中处理。中断子程序中,依据SMB0CN0寄存器推断是什么状态,然后做出响应的处理。

            特别说明,寄存器地址和读写的数据复用放在数组SMB_DATA_OUT里。

            读写函数:

            void SMB_Write(uint8_t Flag)
            {
               while(SMB_BUSY);                    // Wait for SMBus to be free.
               SMB_BUSY = 1;                       // Claim SMBus (set to busy)
               SMB_RW = Flag;                         // Mark this transfer as a WRITE
               SMB0CN0_STA = 1;                    // Start transfer
               while(SMB_BUSY);
            }
    
            void SMB_Read(void)
            {
               while(SMB_BUSY);                    // Wait for bus to be free.
               SMB_BUSY = 1;                       // Claim SMBus (set to busy)
               SMB_RW = 1;                         // Mark this transfer as a READ
    
               SMB0CN0_STA = 1;                    // Start transfer
    
               while(SMB_BUSY);                    // Wait for transfer to complete
            }


            中断处理子程序:

          switch (SMB0CN0 & 0xF0)          // Status vector
          {
             // Master Transmitter/Receiver: START condition transmitted.
             case SMB_MTSTA:
                SMB0DAT = TARGET;          // Load address of the target slave
                SMB0DAT &= 0xFE;           // Clear the LSB of the address for the
                                           // R/W bit
                SMB0DAT |= RW_FLAG;        // Load R/W bit
                SMB0CN0_STA = 0;           // Manually clear START bit
                sent_byte_counter = 1;     // Reset the counter
                break;
    
             // Master Transmitter: Data byte transmitted
             case SMB_MTDB:
                if (SMB0CN0_ACK)            // Slave SMB0CN0_ACK?
                {
                   if (RW_FLAG == WRITE)    // If this transfer is a WRITE,
                   {
                      if (sent_byte_counter <= NUM_BYTES_WR)
                      {
                         // send data byte
                         SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1];
                         sent_byte_counter++;
                      }
                      else
                      {
                         SMB0CN0_STO = 1;  // Set SMB0CN0_STO to terminate transfer
                         SMB_BUSY = 0;     // And free SMBus interface
                      }
                   }
                }
                else                       // If slave NACK,
                {
                   SMB0CN0_STO = 1;        // Send STOP condition, followed
                   SMB0CN0_STA = 1;        // By a START
                }
                break;
    
             // Master Receiver: byte received
             case SMB_MRDB:
                SMB_DATA_OUT = SMB0DAT; // Store received byte
                SMB_BUSY = 0;           // Free SMBus interface
                SMB0CN0_ACK = 0;        // Send NACK to indicate last byte of this transfer
    
                SMB0CN0_STO = 1;        // Send STOP to terminate transfer
                break;
    
             default:
                FAIL = 1;                  // Indicate failed transfer
                                           // and handle at end of ISR
                break;

  • 相关阅读:
    Distinct Values
    树状数组求逆序数
    Color the ball
    Cube
    树状数组
    手动编写JQUERY插件
    JQuery和原生JS跨域加载JSON数据或HTML。
    使用CEF(CEFGLUE)作为您的客户端UI(一)
    给IIS添加网站配置权限
    SQL SERVER 报:由于数据移动,未能继续以 NOLOCK 方式扫描错误的解决办法。
  • 原文地址:https://www.cnblogs.com/yangykaifa/p/6773663.html
Copyright © 2020-2023  润新知