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;
时间: 2024-08-01 05:07:58

EFM8单片机与I2C外设通信的相关文章

STM32F10x_硬件I2C主从通信(轮询发送,中断接收)

Ⅰ.写在前面 关注我分享文章的朋友应该知道我在前面讲述过(软件.硬件)I2C主机控制从机EEPROM的例子.在I2C通信主机控制程序是比较常见的一种,可以说在实际项目中,很多应用都会使用到I2C通信.但在实际项目中作为I2C从机的应用相对要少的多,本文主要讲述关于[STM32F10x_硬件I2C主从通信]中STM32作为从机的例子. 在学习本问内容之前,如果对I2C协议还不太了解的朋友请先去了解一下I2C协议,或看我之前关于I2C通信的文章(我微信公众号和博客都有). 关于STM32硬件I2C作

单片机: EEPROM和串口通信

名称:IIC协议 EEPROM24c02 通过串口通信存数读取数据 内容:此程序用于检測EEPROM性能,測试方法例如以下:写入24c02一个数据,然后在内存中改变这些数据. 掉电后主内存将失去这些信息,然后从24c02中调入这些数据.看是否与写入的同样. 电脑通过串口发送一个十六进制的数据到单片机,存储进24c02,要求断电重新启动后在数码管上显示上一次发送的数据. (本例是1us机器周期,即晶振频率要小于12MHZ) #include <reg52.h> //头文件的包括 #include

51单片机基于I2C总线的秒表模拟应用

-------------------------------------------- 参考地址: http://blog.csdn.net/junyeer/article/details/46480863 http://blog.csdn.net/bob_fly1984/article/details/22690381 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

【单片机实验】串口通信实验

实验三:串口通信实验 内容: (1)利用电脑串口与实验台串口进行连接: (2)根据帧格式定义.波特率定义,编程配置串口,并编写控制程序: (3)实现从电脑上输入控制字符,由单片机通过串口接收,并控制LED显示的功能: (4)对所编程序进行调试及验证. 要求: (1)掌握波特率的概念.学会编程设置波特率和工作方式: (2)掌握串口通信的编程控制方法: (3)理解解MAX232串口通信的硬件电路结构. 注意实验三,需要同学们自己编写程序,实现从电脑串口助手(利用单片机下载软件提供的串口调试助手),发

「51单片机」RS232串口通信代码分析

想来想去不知道要怎么样把232串口通信说清楚,想想还是直接把代码分析一遍吧... 重点是“常用波特率与定时器1的参数关系”这张表格!波特率的设置很重要! 一.串口初始化 void usart_init() { SCON = 0x50; //REN=1允许串行接受状态,串口工作模式1 TMOD = 0x20; //定时器工作方式2 PCON = 0x00; TH1 = 0xFD; //波特率9600.数据位8.停止位1.效验位无 (11.0592M) TL1 = 0xFD; ES = 1; //开

教你如何在51单片机上模拟串口通信!!!

我们可以不使用单片机本身带有的串口,而自己用程序去模拟一个串口并达到和本身的串口具有同样的功能, 首先,我们需要用到CH340串口模块,大家可以上某宝自行购买. 正面: 反面: 然后我们需要了解一下这串口模块上的引脚: 5V  :与VCC短路为5V TLL输出(电源和信号输出都是5V) VCC:可以与3.3V和5V用跳帽连接 3.3V:与VCC短路为3.3V TLL输出(电源和信号输出都是3.3V) TXD:发送数据端口(与单片机上的接收引脚用杜邦线连接) RXD:接收数据端口(与单片机上的发送

I2C通信详解

什么是I2C通信 物理接口:SCL + SDA (1)SCL(serial clock):时钟线,传输CLK信号,一般是I2C主设备向从设备提供时钟的通道 (2)SDA(serial data):数据线,I2C通信的通信数据都通过SDA线来传输 通信特征:串行.同步.非差分.低速率 (1)I2C属于串行通信,所有的数据以位为单位在SDA上串行传输 (2)同步通信就是通信双方工作在同一个时钟下面,一般是通信的A方通过一根CLK线传输A自己的时钟给B,B工作在A传输的时钟下.所以通信的显著特征就是:

Android Things:外设I/O接口-I2C

一.接口简介 内部集成电路(IIC或者I2C)总线使用小数据负载连接简单的外部设备.传感器和执行器是常见的I2C使用案例,例如包含加速度计,温度计,LCD显示器,和电机驱动. I2C总线是一种同步的串行接口:这意味着它依赖于共享的时钟信号来同步设备之间的数据传输.控制时钟信号的设备被称为master,其它所有连接的外设被认为是Slaves,每个设备连接到同一组数据信号以形成总线. I2C设备连接使用3线接口: 共享时间信号(SCL): 共享数据线(SDA): 共同的接地参考(GND): I2C仅

关于单片机串口通信的问题

最近在调板子的时候又遇到了单片机MCU检测不到的问题,之前百度了一堆驱动问题,已经可以正常烧写程序,但是昨天又写不了了,连单片机都检测不到,将驱动器的RxD与TxD短接,使用串口助手自己给自己发送数字,是可以接收到的,证明仿真器没有问题,交换驱动器与单片机的RxD与TxD,从顺接改为交叉连接,还是不行.后来在一哥们的指导下解决了该问题.然后发 现一个小小的串口通信其实门道很多,特别是对于对单片机不熟悉的初级选手是个不小的挑战,所以今天跟小伙伴们聊聊我的感受. 我的问题概而论之就是对于CH340驱