近期帮同学做一个项目,开发板是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里。

读写函数:

  1.         void SMB_Write(uint8_t Flag)
  2.         {
  3.          while(SMB_BUSY); // Wait for SMBus to be free.
  4.          SMB_BUSY = 1; // Claim SMBus (set to busy)
  5.          SMB_RW = Flag; // Mark this transfer as a WRITE
  6.          SMB0CN0_STA = 1; // Start transfer
  7.          while(SMB_BUSY);
  8.         }
  9.  
  10.         void SMB_Read(void)
  11.         {
  12.          while(SMB_BUSY); // Wait for bus to be free.
  13.          SMB_BUSY = 1; // Claim SMBus (set to busy)
  14.          SMB_RW = 1; // Mark this transfer as a READ
  15.  
  16.          SMB0CN0_STA = 1; // Start transfer
  17.  
  18.          while(SMB_BUSY); // Wait for transfer to complete
  19.         }

中断处理子程序:

  1. switch (SMB0CN0 & 0xF0) // Status vector
  2. {
  3. // Master Transmitter/Receiver: START condition transmitted.
  4. case SMB_MTSTA:
  5. SMB0DAT = TARGET; // Load address of the target slave
  6. SMB0DAT &= 0xFE; // Clear the LSB of the address for the
  7. // R/W bit
  8. SMB0DAT |= RW_FLAG; // Load R/W bit
  9. SMB0CN0_STA = 0; // Manually clear START bit
  10. sent_byte_counter = 1; // Reset the counter
  11. break;
  12.  
  13. // Master Transmitter: Data byte transmitted
  14. case SMB_MTDB:
  15. if (SMB0CN0_ACK) // Slave SMB0CN0_ACK?
  16. {
  17. if (RW_FLAG == WRITE) // If this transfer is a WRITE,
  18. {
  19. if (sent_byte_counter <= NUM_BYTES_WR)
  20. {
  21. // send data byte
  22. SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1];
  23. sent_byte_counter++;
  24. }
  25. else
  26. {
  27. SMB0CN0_STO = 1; // Set SMB0CN0_STO to terminate transfer
  28. SMB_BUSY = 0; // And free SMBus interface
  29. }
  30. }
  31. }
  32. else // If slave NACK,
  33. {
  34. SMB0CN0_STO = 1; // Send STOP condition, followed
  35. SMB0CN0_STA = 1; // By a START
  36. }
  37. break;
  38.  
  39. // Master Receiver: byte received
  40. case SMB_MRDB:
  41.          SMB_DATA_OUT = SMB0DAT; // Store received byte
  42. SMB_BUSY = 0; // Free SMBus interface
  43. SMB0CN0_ACK = 0; // Send NACK to indicate last byte of this transfer
  44.  
  45. SMB0CN0_STO = 1; // Send STOP to terminate transfer
  46. break;
  47.  
  48. default:
  49. FAIL = 1; // Indicate failed transfer
  50. // and handle at end of ISR
  51. break;

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

  1. 理解一下单片机的I2C和SPI通信

    应某位网友要求,今天说一下单片机的I2C SPI通信,可能说不清楚,因为这毕竟要做实验才可完全理解. I2C和SPI是两种不同的通信协议. 听到协议,似乎高不可攀,其实协议就是人们定义的一个标准而已, ...

  2. linux i2c 的通信函数i2c_transfer在什么情况下出现错误

    问题: linux i2c 的通信函数i2c_transfer在什么情况下出现错误描述: linux i2c设备驱动 本人在写i2c设备驱动的时候使用i2c transfer函数进行通信的时候无法进行 ...

  3. Android单片机与蓝牙模块通信实例代码

    Android单片机与蓝牙模块通信实例代码 参考路径:http://www.jb51.net/article/83349.htm 啦啦毕业了,毕业前要写毕业设计,需要写一个简单的蓝牙APP进行交互,通 ...

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

    Ⅰ.写在前面 关注我分享文章的朋友应该知道我在前面讲述过(软件.硬件)I2C主机控制从机EEPROM的例子.在I2C通信主机控制程序是比较常见的一种,可以说在实际项目中,很多应用都会使用到I2C通信. ...

  5. 如何在niosII中添加i2c外设_winday_新浪博客

    如何在niosII中添加i2c外设_winday_新浪博客 如何在niosII中添加i2c外设 winday 摘要:本文说明了如何在niosII添加第三方i2c外设,以供参考. 由于本人使用的Alte ...

  6. usb-host与外设通信(二)

    本文是接着上一篇的的usb-host与外设通信(一)接着写的 3.枚举设备 当你的程序运行时,如果应用程序对当前连接的USB设备都感兴趣的,程序可以枚举所以当前的设备.使用getDeviceList( ...

  7. I2C总线通信

    UART 属于异步通信,比如电脑发送给单片机,电脑只负责把数据通过TXD 发送出来即可,接收数据是单片机自己的事情.而 I2C 属于同步通信, SCL 时钟线负责收发双方的时钟节拍, SDA 数据线负 ...

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

    ———————————————————————————————————————————— 参考地址: http://blog.csdn.net/junyeer/article/details/4648 ...

  9. 张高兴的 .NET Core IoT 入门指南:(三)使用 I2C 进行通信

    什么是 I2C 总线 I2C 总线(Inter-Integrated Circuit Bus)是设备与设备间通信方式的一种.它是一种串行通信总线,由飞利浦公司在1980年代为了让主板.嵌入式系统或手机 ...

随机推荐

  1. [USACO06DEC] Milk Patterns

    题目描述 Farmer John has noticed that the quality of milk given by his cows varies from day to day. On f ...

  2. 将SeqReader打包成可执行的jar包

    SeqReader是我定义的一个读取SequenceFile文件,并将部分(key,value)打印到控制台窗口的类,其完整代码如下: /** * Created with IntelliJ IDEA ...

  3. Scala实战高手****第2课:Scala零基础实战入门的第一堂课及如何成为Scala高手

    val声明的不可变的战略意义:1.函数式编程中要求值不可变,val天然符合这一特性:2.在分布式系统中,一般都要求值不可变,这样才能够要求分布式系统的设计和实现,同时拥有更高的效率,val声明的内容都 ...

  4. [HTML/CSS]盒子模型,块级元素和行内元素

    目录 概述 盒子模型 块级元素 行内元素 可变元素 总结 概述 在div+css中,了解块级元素和行内元素还是非常有必要的,比如:对行内元素使用width属性就会失效.虽然自己不是做前端的,但是,在项 ...

  5. activemq 5.13.2 jdbc 数据库持久化 异常 找不到驱动程序

    原文:https://my.oschina.net/u/2284972/blog/662033 摘要: activemq jdbc 数据库持久化 异常 找不到驱动程序 Caused by: java. ...

  6. linux如何启动/停止/重启MySQL

    如何启动/停止/重启MySQL 一.启动方式 1.使用 service 启动:service mysqld start2.使用 mysqld 脚本启动:/etc/inint.d/mysqld star ...

  7. [Java基础] java多线程关于消费者和生产者

    多线程: 生产与消费 1.生产者Producer生产produce产品,并将产品放到库存inventory里:同时消费者Consumer从库存inventory里消费consume产品. 2.库存in ...

  8. gin框架中间件解决跨域问题

    http://www.niu12.com/article/45// 初始化routerrouter := gin.New() router.Use(gin.Logger()) router.Use(g ...

  9. Linux C 面试题总结

    1.进程和线程的区别,及优劣性比较 进程和线程的主要差别在于它们是不同的操作系统资源管理方式.进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路 ...

  10. HTC相关开发所需SDK等工具都在这里了。 【转】

    OpenVR SDK https://github.com/ValveSoftware/openvr  OpenVR SDK是由原本的SteamWorks SDK更新而来,新增对HTC VIVE开发者 ...