RP2040 + ICS-43432 issue
giampiero7 opened this issue · 9 comments
Hi @miketeachman,
As promised, here I am with my test results.
I'm using a ICS-43432 I2S MEMS mic. It outputs 24-bit data, little endian, every 64 clock cycles.
Here is its data sheet:
https://invensense.tdk.com/wp-content/uploads/2015/02/ICS-43432_DS.pdf
My test program:
from machine import I2S, Pin
buf = bytearray(10000)
i2s = I2S(0, sck=Pin(6), ws=Pin(7), sd=Pin(8), mode=I2S.RX, bits=32, format=I2S.MONO, rate=44100, ibuf=40000)
while True:
ret = i2s.readinto(buf)
for i in range(0, 20, 4):
print(buf[i])
print(buf[i + 1])
print(buf[i + 2])
print(buf[i + 3])
print("------------")
Running on:
MicroPython v1.18 on 2022-01-17; Raspberry Pi Pico with RP2040
Some output:
0
102
2
0
------------
192
67
0
0
------------
192
71
2
0
------------
0
16
250
255
------------
192
79
250
255
------------
0
84
254
255
------------
192
93
248
255
------------
0
68
254
255
------------
192
57
254
255
------------
192
99
251
255
------------
0
202
0
0
------------
0
208
250
255
------------
0
60
253
255
------------
192
183
4
0
------------
192
21
255
255
------------
192
13
2
0
------------
192
241
0
0
------------
What I noticed is that the first byte is always 0 or 192.
I've tried the same mic with a Raspberry Pi and some C code and that is not the case.
Can you think of anything causing this?
Thanks!
This is the gold standard on how to present an issue !
I have a theory on the issue: The SD line is staying high for a couple of bit times after the line has gone tri-state, after the 24th bit has been output, but only in the case where the 24th bit is high. For example, you see a 192 (0xC0), when bit 24 is high. Look at the bit immediately before a 192 result...it is always 1. When the last bit of the 3rd byte is 0, then the 4th byte is always 0x00.
What's going on? There is always a small capacitance between SD and ground. When the mic tri-states the line (after the 3rd byte), and the line is high, there needs to be some resistance to ground to bring the line back to low, to discharge this capacitance. In your hardware setup with the Pico, the SD line is going low too slowly, hence the two high bits times (0xC0). When the line is already low, there is no capacitance to discharge, hence you see the expected value of the 4th byte (0x00).
How to fix: It can be fixed in hardware.
From the mic datasheet:
The SD trace should have a pulldown resistor to discharge the line during the time that all microphones on the bus have tri-stated their outputs. A 100 kΩ resistor is sufficient for this, as shown in Figure 10. If the SD line needs to be discharged faster than a 100 kΩ resistor can, a smaller resistor, such as 10 kΩ, can be used.
To test the theory -- add a smaller value pull-down resistor, between the SD signal and ground, e.g. 47k ohm, or 10k ohm. See if the 0xC0 value changes to 0x00. It might change to 0x80 which is moving in the right direction, but still indicates that a smaller resistor is needed to discharge the capacitance on the SD line.
With the Raspberry Pi setup there may already be a resistor in place that discharges this capacitance at a faster rate.
I have a theory on the issue: The SD line is staying high for a couple of bit times after the line has gone tri-state, after the 24th bit has been output, but only in the case where the 24th bit is high. For example, you see a 192 (0xC0), when bit 24 is high. Look at the bit immediately before a 192 result...it is always 1.
Maybe I'm misunderstanding you, but I don't see what you are saying:
0 // byte 1 (buf[i])
102 // byte 2 (buf[i + 1])
2 // byte 3 (buf[i + 2])
0 // byte 4 (buf[i + 3])
------------
192
67
0
0
------------
192
71
2
0
Here the 192's come after zeros on the 3rd and 4th bytes.
When the last bit of the 3rd byte is 0, then the 4th byte is always 0x00.
It looks to me that the 4th byte is always the continuation of the sign, i.e. if the 24th bit (last bit of the 3rd byte) is 1, then the 4th byte if 0xff, 0x00 otherwise.
See if the 0xC0 value changes to 0x00
I would expect relevant data on my first byte, not 0x00.
Am I missing something?
Thanks!
I see the confusion -- in my explanation I am using the byte ordering as output on the physical I2S SD line, as shown in figure 11 of the ICS-43432 datasheet. But it doesn't get translated to the MicroPython array in that same order. It's reversed.
Here is a translation from the sample bytes that come from the actual device to the MicroPython buf[] array.
I2S SD line MicroPython buf[]
======= ============
1st byte (Most significant byte, first to be transmitted from the mic) buf[i+3]
2nd byte buf[i+2]
3rd byte (Least significant byte of data transmitted from the mic) buf[i+1]
4th byte (mic is in tri-state for this data, needs the pull-down to read 0) buf[i]
It might help to look at one of your samples,
looking at the bits, as the order they appear on the physical I2S SD line:
0 <-- most significant data byte of data transmitted from the mic
0
67 (0x43) <-- least significant byte of data transmitted from the mic
192 (0xC0) <-- mic is in tri-state
It appears on the SD line in this order, MS bit --> LS bit
0x00 0x00 0x43 0xC0
00000000 00000000 01000011 11000000
^
this bit being high causes the following two bits to be high
(due to the lack of adequate pull-down resistance)
I think this is the best I can explain it.
And this must be the gold standard of explanation!
Well, I think I found the real issue here: it got stuck in my head the idea that the ICS-43432 would transmit bytes with little endian format (i.e. LSB first), I even wrote it in the initial issue description! 🤦
I guess the idea came from the format used by ALSA to return data in my RPi code, and then the idea got reinforced by the fact that I saw non-zero data on the last byte (the famous 192), plus it made sense to me to see a sign continuation (unpulled tri-state) in what I assumed was the last transmitted byte which, in fact, was the first...
@miketeachman sorry for wasting your time and thank you very much for your patience, much appreciated!
Case closed!
Hey, great ! I'm glad this issue didn't turn into a difficult debugging exercise. Understanding the data coming from I2S is not trivial. The byte ordering seems backwards from what is expected. Good luck with your projects.
Hi @miketeachman,
This might have been another reason that lead to my wrong interpretation of the bytes ordering:
https://github.com/micropython/micropython/blob/master/ports/rp2/machine_i2s.c#L65
Maybe it can be clarified?
Thanks!
I'm not sure how to make it better. The appbuf[] has little-endian ordering and the comment indicates little-endian ordering. e.g. appbuf[i+3] = most-significant byte, appbuf[i] = least-significant byte.
Which is the same as shown in the "Endian example" figure in wikipedia.
https://en.wikipedia.org/wiki/Endianness
Well... you are right, it is indeed clear enough. While this issue is resolved, it looks like the one in my head isn't :)
No worries. I can never remember endianness. When I commented the code I had to consult wikipedia to make sure the comment was correct.