使用 python 在 rpi0 上设置多个 mcp3008 ADC 芯片时遇到问题。任何关于错误的想法都会被采纳
我目前正在进行一个项目,要求我读取 16 个不同传感器的模拟电压并将其转换为角度读数。为了实现这一目标,我决定使用 MCP3008 ADC 芯片以及 rpi 和 python,因为这是我之前为需要类似过程的其他项目所做的事情。然而,与我之前的项目不同的是,我必须使用 2 个 MCP3008 芯片,因为每个芯片只能读取 8 个通道。我知道根据 spi 通信的工作方式和 MCP3008 芯片的工作方式,这应该是可能的,但我在让两个芯片同时工作时遇到了一些问题。作为前言,我使用 adafruits mcp3xxx 库来处理芯片。我尝试的第一件事是使用不同的芯片选择将两个芯片放在同一个 spi 总线上。我使用的代码看起来像这样,
import busio
import digitalio
import board
import time
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn
# this function is used to calculate angle of displacement
def Angle(Vmin,Vmax,R,Vt):
return ((Vt-Vmin)/(Vmax-Vmin))*R
# create the spi bus
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
#create the chip select(s)
cs0 = digitalio.DigitalInOut(board.CE0)
cs1 = digitalio.DigitalInOut(board.CE1)
#create the mcp object(s)
mcp0 = MCP.MCP3008(spi,cs0)
mcp1 = MCP.MCP3008(spi,cs1)
#create the analog channels
chan0_0 = AnalogIn(mcp0, MCP.P0)
chan1_0 = AnalogIn(mcp1, MCP.P0)
R_elbow = 180;
from CalibratedVoltages import*
while True:
# A_elbow = Angle(VminElbow,VmaxElbow,R_elbow,chan0_0.voltage)
# time.sleep(1)
# A_knee = Angle(VminKnee, VmaxKnee, R_knee,chan1_0.voltage)
# print('elbow angle:', A_elbow,'degrees')
print('0',chan0_0.voltage)
print('1',chan1_0.voltage)
time.sleep(2)
print('update')
但是通过此设置,由于某种原因,在 mcp1 对象上获取的任何读数都依赖于在 mcp0 对象上获取的读数。例如,如果您要在 mcp1 的通道 0 上读取读数,而不是从 0 到 3.3 伏,因为它应该在 0 到 mcp0 对象的通道 0 上读取的值之间进行读取。这对于 mcp1 对象上的所有通道都是相同的。
由于无法弄清楚为什么会发生这种情况,我决定尝试简单地将每个 MCP3008 芯片放在单独的 spi 总线上,以防止共享总线造成的干扰,我认为这是导致错误的原因。我做了一个 dtoverlay 来启用 spi1 总线,然后将我的代码更改为更像这样的代码。
import busio
import digitalio
import board
import time
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn
# this function is used to calculate angle of displacement
def Angle(Vmin,Vmax,R,Vt):
return ((Vt-Vmin)/(Vmax-Vmin))*R
# create the spi bus
spi0 = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
spi1 = busio.SPI(clock=21, MISO=19, MOSI=20)
#create the chip select(s)
cs0 = digitalio.DigitalInOut(board.D5)
cs1 = digitalio.DigitalInOut(board.D9)
#create the mcp object(s)
mcp0 = MCP.MCP3008(spi0,cs0)
mcp1 = MCP.MCP3008(spi1,cs1)
#create the analog channels
chan1_0 = AnalogIn(mcp1, MCP.P0)
chan0_0 = AnalogIn(mcp0, MCP.P0)
#chan0_1 = AnalogIn(mcp0, MCP.P1)
#chan0_2 = AnalogIn(mcp0, MCP.P2)
#chan0_3 = AnalogIn(mcp0, MCP.P3)
#chan0_4 = AnalogIn(mcp0, MCP.P4)
#chan0_5 = AnalogIn(mcp0, MCP.P5)
#chan0_6 = AnalogIn(mcp0, MCP.P6)
R_elbow = 180;
from CalibratedVoltages import*
while True:
# A_elbow = Angle(VminElbow,VmaxElbow,R_elbow,chan0_0.voltage)
# time.sleep(1)
# A_knee = Angle(VminKnee, VmaxKnee, R_knee,chan1_0.voltage)
# print('elbow angle:', A_elbow,'degrees')
print('0',chan0_0.voltage)
print('1',chan1_0.voltage)
# print('2',chan0_2.voltage)
# print('3',chan0_3.voltage)
# print('4',chan0_4.voltage)
# print('5',chan0_5.voltage)
# print('6',chan0_6.voltage)
time.sleep(2)
print('update')
通过此设置,只有 spi1 总线上的 MCP 芯片工作,而另一个芯片在所有通道上返回 0.0,无论其连接的电压是什么。即使您将代码更改回仅使用 spi0 总线,spi0 总线上的芯片也会继续返回所有 0.0 读数,直到并且除非我重新启动 rpi 并仅运行 spi0 总线读数的代码,而无需运行任何以下行:为 spi1 总线设置任何内容。在这一点上,我相当迷失,只能假设它与我正在使用的库之一有关,我选择了 MCP3008 芯片,因为我知道 adafruit 提供了一个易于使用的库,我想避免必须专门为项目的这一部分投入大量精力。如果有人遇到过同样的问题并且知道如何解决它,或者只是猜测一下,我们将非常感激。解决所描述的任何一个问题都将使我能够完成该项目。
I am currently working on a project that requires me to read analog voltages for 16 different sensors and convert them to angle readings. To accomplish this I decided to utilize the MCP3008 ADC chip along with a rpi and python as that was something that I had done before for other projects that required a similar process. unlike my previous project however, I have to use 2 MCP3008 chips as they can only read 8 channels each. I know that this should be possible based on the way that spi communication works and the way that the MCP3008 chips work but I am running in to some problems getting both chips to work simultaneously. To preface, I am using adafruits mcp3xxx library to work with the chips. The first thing that I tried was putting both chips on the same spi bus using different chip select. the code that I used for that looked something like this
import busio
import digitalio
import board
import time
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn
# this function is used to calculate angle of displacement
def Angle(Vmin,Vmax,R,Vt):
return ((Vt-Vmin)/(Vmax-Vmin))*R
# create the spi bus
spi = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
#create the chip select(s)
cs0 = digitalio.DigitalInOut(board.CE0)
cs1 = digitalio.DigitalInOut(board.CE1)
#create the mcp object(s)
mcp0 = MCP.MCP3008(spi,cs0)
mcp1 = MCP.MCP3008(spi,cs1)
#create the analog channels
chan0_0 = AnalogIn(mcp0, MCP.P0)
chan1_0 = AnalogIn(mcp1, MCP.P0)
R_elbow = 180;
from CalibratedVoltages import*
while True:
# A_elbow = Angle(VminElbow,VmaxElbow,R_elbow,chan0_0.voltage)
# time.sleep(1)
# A_knee = Angle(VminKnee, VmaxKnee, R_knee,chan1_0.voltage)
# print('elbow angle:', A_elbow,'degrees')
print('0',chan0_0.voltage)
print('1',chan1_0.voltage)
time.sleep(2)
print('update')
With this setup however, any readings taken on the mcp1 object were dependent on the readings taken on the mcp0 object for some reason. For example, if you were to take a reading on channel 0 of mcp1 instead of ranging from 0 to 3.3 volts as it should have it ranged from 0 to whatever was being read on channel 0 of the mcp0 object. This was the same for all of the channels on the mcp1 object.
Unable to figure out why this was happening, I decided to try to simply put each MCP3008 chip on a separate spi bus to prevent interference due to a shared bus which is what I assumed was the causing the error. I did a dtoverlay to enable the spi1 bus and then changed my code to something more like this.
import busio
import digitalio
import board
import time
import adafruit_mcp3xxx.mcp3008 as MCP
from adafruit_mcp3xxx.analog_in import AnalogIn
# this function is used to calculate angle of displacement
def Angle(Vmin,Vmax,R,Vt):
return ((Vt-Vmin)/(Vmax-Vmin))*R
# create the spi bus
spi0 = busio.SPI(clock=board.SCK, MISO=board.MISO, MOSI=board.MOSI)
spi1 = busio.SPI(clock=21, MISO=19, MOSI=20)
#create the chip select(s)
cs0 = digitalio.DigitalInOut(board.D5)
cs1 = digitalio.DigitalInOut(board.D9)
#create the mcp object(s)
mcp0 = MCP.MCP3008(spi0,cs0)
mcp1 = MCP.MCP3008(spi1,cs1)
#create the analog channels
chan1_0 = AnalogIn(mcp1, MCP.P0)
chan0_0 = AnalogIn(mcp0, MCP.P0)
#chan0_1 = AnalogIn(mcp0, MCP.P1)
#chan0_2 = AnalogIn(mcp0, MCP.P2)
#chan0_3 = AnalogIn(mcp0, MCP.P3)
#chan0_4 = AnalogIn(mcp0, MCP.P4)
#chan0_5 = AnalogIn(mcp0, MCP.P5)
#chan0_6 = AnalogIn(mcp0, MCP.P6)
R_elbow = 180;
from CalibratedVoltages import*
while True:
# A_elbow = Angle(VminElbow,VmaxElbow,R_elbow,chan0_0.voltage)
# time.sleep(1)
# A_knee = Angle(VminKnee, VmaxKnee, R_knee,chan1_0.voltage)
# print('elbow angle:', A_elbow,'degrees')
print('0',chan0_0.voltage)
print('1',chan1_0.voltage)
# print('2',chan0_2.voltage)
# print('3',chan0_3.voltage)
# print('4',chan0_4.voltage)
# print('5',chan0_5.voltage)
# print('6',chan0_6.voltage)
time.sleep(2)
print('update')
With this set up, only the MCP chip on the spi1 bus works and the other chip returns 0.0 on all channels regardless of what voltage is attached to it. Even if you change the code back to only utilizing the spi0 bus, the chip on the spi0 bus continues to return all 0.0 readings until and unless I reboot the rpi and run the code for just spi0 bus readings without ever running any of the lines that set up anything for the spi1 bus. At this point I am fairly lost and can only assume that it has something to do with one of the library's that I am using and I chose the MCP3008 chips because I knew there was an easy to use library available from adafruit and I wanted to avoid having to put a lot of effort in to this part of the project specifically. If anyone has had this same kind of issue and knows how to solve it or even just take a guess it would be very appreciated. A solution to either of the problems that were described would allow me to finish the project.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我做了同样的事情,得到了同样的结果。
调试此问题的最佳工具称为“piscope”,可以在此处找到:
http://abyz.me.uk/rpi/pigpio/piscope.html
答案是 - 不要使用 CE0。
解释:
由于某种原因,在使用 adafruit 库时,每当发生 spi 调用时,CE0 就会变低 - 无论配置哪个设备/芯片选择。
这可以在 piscope 中通过观察 GPIO 引脚来看到。正确的 cs 引脚变低 - 但稍后,CE0 也变低,使两个 spi 设备都处于活动状态并产生冲突情况。
在布局 PCB 时,我遵循典型逻辑,并使我的两个设备使用 cs CE0 和 CE1,正如逻辑所说,因为这些是默认设置。
我不知道是 adafruit 库还是实际的 pi 内核造成了冲突情况,但为了解决这个问题,我从芯片中取出了引脚 D8,并连接到 D20(一个未使用的引脚)。
然后我按预期在 python 中设置 cs 配置:
cs1 = digitalio.DigitalInOut(board.D7)
cs2 = digitalio.DigitalInOut(board.D20)
一切正常 - 16 个通道的独立电压读数。
在 piscope 中观察显示了我的预期:对于每个 spi 调用,CE0 都会变低。
解决方案:在多个spi设备设计中不要使用CE0。
我还使用 2 个额外的仅配置软件的设备进行了测试,总共 4 个独立的 spi 调用,带有 4 个 gpio 引脚,没有 D8。每个调用都会导致 CE0 被拉低,但当未在硬件中连接 CE0 时,spi 调用表现正常,表明(暂时)可以使用 adafruit 库将任意数量的设备连接到 spi 总线。
I did the same thing, and got the same results.
The best tool for debugging this is called 'piscope' and can be found here:
http://abyz.me.uk/rpi/pigpio/piscope.html
Here is the answer - DO NOT USE CE0.
Explanation:
For some reason when using the adafruit libraries, whenever an spi call happens CE0 goes low - no matter which device/chip select is configured.
This can be seen in piscope by watching the gpio pins. The correct cs pin goes low - but then a little bit later, CE0 also goes low, making both spi devices active and creating a collision situation.
I followed the typical logic when laying out my pcb, and made my two devices use cs CE0 and CE1, as logic would say to, because these are the defaults.
I don't know if it is the adafruit library or the actual pi kernel that creates the collision situation, but to fix this, I lifted pin D8 from my chip, and connected to D20 - an unused pin.
Then I set the cs configurations in python as expected:
cs1 = digitalio.DigitalInOut(board.D7)
cs2 = digitalio.DigitalInOut(board.D20)
and everything worked perfectly - 16 channels of independent voltage readings.
Watching in piscope showed what I expected: for every spi call, CE0 goes low.
Solution: Do not use CE0 in multiple spi device designs.
I also tested using 2 additional software configured only devices, for a total of 4 separate spi calls with 4 gpio pins, without D8. Each call caused CE0 to be pulled low, but when not connecting CE0 in hardware, the spi calls acted normal, showing (tentatively) that any number of devices can be attached to the spi bus with the adafruit libraries.
手动设置 GPIO 将不起作用。
我尝试手动操作 GPIO,但 CE0 甚至总是覆盖手动 GPIO 条件。 CE0 覆盖位于 adafruit 库中的某个位置,或者可能位于 spi 驱动程序的 pi 内核中。
有多种方法可以对引脚进行寻址,但我从不使用粒子方法。我在某处读到粒子标识符适用于非常高级的处理器类型的人,但我不是。我使用 adafruit 示例级代码
要使用 GPIO 名称,只需将 GPIOxx 的 GPIO 部分更改为 Dxx 即可。例如,D20 是引脚 38,或 D15/A6 作为粒子。 D7 是 GPIO7,引脚 26,D8(未使用)将是 GPIO8 或引脚 24。
这是我的热敏电阻测试的完整代码:
我发现 mcp3008 相对嘈杂,即使输入信号干净,所以我使用 100样本来平均最终结果。速度会慢一些,但结果看起来更好。
Setting the gpio manually will not work.
I tried manual manipulation of the GPIO, but CE0 always overrides even manual gpio conditions. The CE0 override is somewhere in the adafruit library or possibly the pi kernel for spi driver.
There are several ways to address pins, and I never use the particle method. I read somewhere the particle identifiers are for very high level processor type people, which I am not. I use the adafruit example level code
To use the GPIO designations, just change the GPIO part of GPIOxx to Dxx. For example, D20 is pin 38, or D15/A6 as a particle. D7 is GPIO7, pin 26, and D8 (not used) would be GPIO8 or pin 24.
Here is the full code for my thermistor test:
I found the mcp3008 to relatively noisy, even when the input signal was clean, so I use 100 samples to average the final result. Slows things down a bit, but the results look nicer.