【问题标题】:Using Python smbus on a Raspberry Pi - confused with syntax在 Raspberry Pi 上使用 Python smbus - 与语法混淆
【发布时间】:2013-06-26 10:13:41
【问题描述】:

我正在尝试在 Raspberry Pi 上使用 python-smbus 与使用 I2C 的 MMA7660 加速度计芯片进行通信。

在下面的代码中,我正在读取芯片的寄存器 0x​​00、0x01、0x02 和 0x03,并且我得到的所有值完全相同。查看数值,倾斜芯片,我可以看到它们都对应于寄存器 0x​​00,即 X 值寄存器。

输出:

...
1 1 1 2
3 3 3 3
1 1 1 1
59 60 60 60
51 51 51 51
58 58 58 58
3 3 3 3
62 62 62 62
58 58 58 58
62 62 62 62
...

代码:

  import smbus
  import time

  bus = smbus.SMBus(1)
  # I2C address for MMA7660                                                     
  addr = 0x4C
  try:
    bus.write_byte_data(addr, 0x07, 0x00)
    bus.write_byte_data(addr, 0x06, 0x10)
    bus.write_byte_data(addr, 0x08, 0x00)
    bus.write_byte_data(addr, 0x07, 0x01)
  except IOError, err:
    print err

  while True:
    try:
      x = bus.read_byte_data(addr,0x00)
      y = bus.read_byte_data(addr,0x01)
      z = bus.read_byte_data(addr,0x02)
      tr = bus.read_byte_data(addr,0x03)
      print x, y, z, tr
      time.sleep(0.25)
    except:
      print 'exiting...'
      break

我的 smbus 语法有问题吗?我确实看过文档here

我已验证该芯片工作正常 - 我可以使用 Arduino 与它正常通信,并按与上述相同的顺序设置寄存器。

更新 #1(2013 年 6 月 28 日)

根据 Sylvain 的评论,我正在为以下代码附加 SDA/SCL 线路的示波器输出:

bus.write_byte(addr, 0x01)
print bus.read_byte(addr)

更新 #2:

我猜 Raspberry Pi 上的 I2C 存在一个已知问题 - 没有“重复启动”。

https://raspberrypi.stackexchange.com/questions/7138/mma8452-i2c-module

根据 Linux SMBus 规范:

SMBus Read Byte:  i2c_smbus_read_byte_data()
============================================

This reads a single byte from a device, from a designated register.
The register is specified through the Comm byte.

S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P

但是当我尝试时,示波器在重复启动(S)之前清楚地显示了一个停止(P)。

所以我想我在 Pi 上使用 I2C 硬件与 MMA7760 通信并不走运。

【问题讨论】:

    标签: python raspberry-pi i2c


    【解决方案1】:

    如果你一次读取所有需要的寄存器,它工作正常:

    import smbus
    bus = smbus.SMBus(1) 
    
    Register = bus.read_i2c_block_data(0x4c, 0x99,4)
    acc_x = Register[0]*1.0
    acc_y = Register[1]*1.0
    acc_z = Register[2]*1.0
    acc_tilt     = Register[3] 
    

    【讨论】:

    • 用 MMA8452 试过这个,但没有帮助。我猜你使用的任何芯片都可以容忍不正确的 START/STOP/REPEATED-START
    【解决方案2】:

    绝对不确定这是不是问题,但根据规范 p22:

    MMA7660FC 使用它的内部存储寄存器地址作为地址指针读取,与存储寄存器地址相同 用作写入的地址指针。指针通常在使用相同规则读取每个数据字节后自动递增 至于写(表5)。因此,首先通过执行写入配置设备的寄存器地址来启动读取(图 11) 随后是重复的开始。主机现在可以从中读取“n”个连续字节,第一个数据字节从 寄存器由初始化的寄存器地址寻址。

    据我了解,要从寄存器“读取”,您必须先写入寄存器地址,然后盲目读取一个字节。我不知道SMBus.read_byte_data 是否会为您解决这个问题,但您可以手动尝试

      bus.write_byte(addr,0x00)
      x = bus.read_byte(addr)
    
      bus.write_byte(addr,0x01)
      y = bus.read_byte_data(addr)
    
      bus.write_byte(addr,0x02)
      z = bus.read_byte(addr)
    
      bus.write_byte(addr,0x03)
      tr = bus.read_byte(addr)
    

    也许甚至会起作用:

      bus.write_byte(addr,0x00)
    
      x = bus.read_byte(addr)
      y = bus.read_byte_data(addr)
      z = bus.read_byte(addr)
      tr = bus.read_byte(addr)
    

    【讨论】:

    • 不幸的是,两者都没有奏效,但我认为你走在正确的轨道上。这是一个弄清楚如何使用 smbus 做到这一点的问题。我希望他们对 I2C 启动/停止/重启等有较低级别的调用。我可以在 C 和 ATmega168 中做同样的事情,而且一切正常。
    • @M-V 你有示波器或逻辑分析仪来检查总线吗?
    • 我有一台示波器 - 但不是很强大。老实说,我还没有弄清楚如何使用它来分析数字波形 - 数据会瞬间消失。
    • @M-V 你可能需要DSO 来调查这种信号。
    • 谢谢@Sylvain - 我花了一些时间来研究如何使用我的示波器来捕捉这些信号。我已经发布了信号 - 请分享任何见解。
    【解决方案3】:

    查看您的示例以及为 MMA7455 编写的课程后,我能够编写以下内容:

    import smbus
    import time
    import os
    import math
    
    # Define a class for the accelerometer readings
    class MMA7660():
        bus = smbus.SMBus(1)
        def __init__(self):
            self.bus.write_byte_data(0x4c, 0x07, 0x00) # Setup the Mode
            self.bus.write_byte_data(0x4c, 0x06, 0x10) # Calibrate
            self.bus.write_byte_data(0x4c, 0x08, 0x00) # Calibrate
            self.bus.write_byte_data(0x4c, 0x07, 0x01) # Calibrate
        def getValueX(self):
            return self.bus.read_byte_data(0x4c, 0x00)
        def getValueY(self):
            return self.bus.read_byte_data(0x4c, 0x01)
        def getValueZ(self):
            return self.bus.read_byte_data(0x4c, 0x02)
    
    mma = MMA7660()
    
    for a in range(1000):
        x = mma.getValueX()
        y = mma.getValueY()
        z = mma.getValueZ()
        print("X=", x)
       print("Y=", y)
       print("Z=", z)
        time.sleep(0.2)
        os.system("clear")
    

    这应该可以解决问题。

    【讨论】:

    • 当它与问题中的代码基本相同时,为什么要“解决问题”?
    • @DanielK。 - 抱歉,已经 5 年了,所以我失去了这个答案的上下文。我相信这可能与 - os.system("clear") 有关
    【解决方案4】:

    Raspberry PI I2C 内核驱动程序不支持在特定时间重复启动。但是,I2C 内核驱动程序已更新,现在支持重复启动,但必须明确激活此功能。

    将合并传输设置为“开启”

    sudo sh -c '/bin/echo Y > /sys/module/i2c_bcm2708/parameters/combined'

    将合并传输设置为“关闭”

    sudo sh -c '/bin/echo N > /sys/module/i2c_bcm2708/parameters/combined'

    在这里找到的信息:http://raspberrypi.znix.com/hipidocs/topic_i2c_rs_and_cs.htm

    【讨论】:

    • 一旦完成,是什么控制了发出多少次重复启动命令?如果我尝试使用 smbus 的 read_i2c_block_data 方法,是什么控制了它在发出停止之前发出重复启动的次数?
    • @Aerovistae 在主设备将其数据传输到从设备(接收模式下的从设备)后发出一次重复启动,以便从从设备(发送模式下的从设备)读回数据。重复启动用于避免总线上的另一个主机过早获得对总线的控制权。 Master 请求数据,只要它没有发出 Stop Condition 或 Slave 在上一个传输字节后没有发出 Not Acknowledge 信号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多