在 Raspberry Pi 上使用 Python smbus - 与语法混淆

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17317317/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 07:52:19  来源:igfitidea点击:

Using Python smbus on a Raspberry Pi - confused with syntax

pythonraspberry-pii2c

提问by M-V

I am trying to use python-smbus on a Raspberry Pi to communicate with an MMA7660 accelerometer chip using I2C.

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

In the code below, I am reading registers 0x00, 0x01, 0x02 and 0x03 of the chip, and I am getting the exact same values for all. Looking at the values, and tilting the chip, I can see that they all correspond to register 0x00, the X value register.

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

Output:

输出:

...
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
...

Code:

代码:

  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

Am I doing something wrong with the smbus syntax? I did look at the documentation here.

我在 smbus 语法上做错了吗?我确实看过这里的文档。

I have verified that the chip works - I can communicate fine with it using an Arduino and setting the registers in the same order as above.

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

Update #1 (28 Jun 2013):

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

As per Sylvain's comment, I am attaching oscilloscope output for SDA/SCL lines for the following code:

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

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

enter image description here

在此处输入图片说明

Update #2:

更新#2:

I guess there is a known problem with I2C on Raspberry Pi - there is no "Repeated Start".

我猜树莓派上的 I2C 存在一个已知问题——没有“重复启动”。

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

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

According to the Linux SMBus spec:

根据 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

But when I tried it, the osciiloscope clearly shows a STOP (P) before the Repeated Start (S).

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

So I guess I am out of luck for using I2C hardware on the Pi to talk to the MMA7760.

所以我想我不走运在 Pi 上使用 I2C 硬件与 MMA7760 对话。

回答by Sylvain Leroux

I'm absolutlynot sure this is the problem, but according to the specs p22:

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

MMA7660FC is read using it's internally storedregister address as address pointer, the same way the stored register address is used as address pointer for a write. The pointer generally auto-increments after each data byte is read using the same rules as for a write (Table 5). Thus, a read is initiated by first configuring the device's register address by performing a write(Figure 11) followed by a repeated start. The master can now read 'n' consecutive bytes from it, with the first data byte being read from the register addressed by the initialized register address.

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

As far as I understand, to "read" from a register, you have to start by writingthe register address, and then blindly reada byte. I don't know if SMBus.read_byte_datatake care of that for you, but you could try it manually:

据我了解,要从寄存器“读取”,您必须从写入寄存器地址开始,然后盲目读取一个字节。我不知道是否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)

Maybethat would even work:

也许这甚至会奏效:

  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)

回答by ThomGob

if you read all the needed registers at once, it works fine:

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

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] 

回答by MangoBoy

After viewing your example, as well as class written for the MMA7455, I was able to write the following:

查看您的示例以及为 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")

That should do the trick.

这应该够了吧。

回答by a.Dippel

The Raspberry PI I2C Kernel Driver did not support repeated starts for a specific time. However, the I2C Kernel Driver has been updated and now supports the repeated start, though this functionality must be activated explicitly.

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

To set combined transfers 'on'

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

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

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

To set combined transfers 'off'

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

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

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

Information found here: http://raspberrypi.znix.com/hipidocs/topic_i2c_rs_and_cs.htm

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