Skip to main content
Moritz1
Associate III
March 20, 2023
Question

I2C passiv Mode possible?

  • March 20, 2023
  • 7 replies
  • 4078 views

Hi,

I try to sniff a I2C communication between multiple devices with my STM32G473.

The controller should only listen passiv. Is this possible?

For sure, I can always set the NACK-Bit to prevent sending an ACK and disable the Clock-stretching.

And I can use the Adress-Masking feature to listen on all Addresses.

But if I understand correctly, the Controller will always send automatically an ACK if the Address matchs? Is this correct? I can not disable this.

So this ACK after Address match is the only point that prevents me from doing what I want to do.

Is there any possibility to listen 100% passively with the I2C periphery?

I tryed another way before: Generating interrupts on clock edges and reading the level of Data Line, and than getting the data Bit by Bit. This works fine for 250kHz, but unfortunately the Interrupt is too slow to sample an 1MHz I2C signal (Interrupt needs 440ns to be called and one clock pulse is just 500ns long, so I miss the databit). So I was thinking if it is mayby possible to use the I2C peripheral.

This topic has been closed for replies.

7 replies

AScha.3
Super User
March 20, 2023

0693W00000aJKUmQAO.pngso - if slave adress valid -> ack , otherwise no reception.

i would try some "standard" serial , e.g. USART in synchronous slave mode (with 9 bits , for ack), nss soft.

clk can go up to 20MHz ... that should be enough. :)

and at 1Mbit you have about 9us in INT to save the data.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Moritz1
Moritz1Author
Associate III
March 20, 2023

This is a good Idea, but unfortunately this will not work because the I2C slaves have a 10 Bit Address, so I would need 10Bit Addr + R/W Bit + ACK Bit = 12 Bit length and the USART can handle only 7, 8 or 9 Bit wordlength.

AScha.3
Super User
March 20, 2023

so you could try the SPI , in slave mode, 12 bit data. (nss low), max 75Mbit here. :)

0693W00000aJLSIQA4.png

"If you feel a post has answered your question, please click ""Accept as Solution""."
Moritz1
Moritz1Author
Associate III
March 20, 2023

I think the biggest challenge at this point is the start/stop detection to identify when the 10-bit address is being received and when to switch to 9-bit data reception.

AScha.3
Super User
March 20, 2023

maybe difficult when switching data size while receiving...but you should try it.

write in INT to spix_CR2 ...data size.

but when using 2 SPI in parallel , one 12bit mode, in int switch it off and on other SPI which is in 9bit mode -- this could be also possible

"If you feel a post has answered your question, please click ""Accept as Solution""."
Moritz1
Moritz1Author
Associate III
March 24, 2023

I tryed it out with SPI.

For the beginning with 7Bit Address.

Setup: 9 Bit Datasize (7Bit Addr + R/W + ACK / 8 Bit Data + ACK), SPI captures data on rising clock edge.

So I do not need any data size switching for the first try.

Startcondition i detect by interrupt: Falling edge on SDA while SCL is High. This interrupt enables the SPI. Than, SPI is receiving address and some bytes of data, this works fine.

But the Problem is the stop condition at the end of transfer.

As you can see here:

https://cdn.sparkfun.com/assets/learn_tutorials/8/2/I2C_Basic_Address_and_Data_Frames.jpg

For stop condition the SCL line goes high and thereafter the SDA goes high. So I can detect the stop condition with my interrupt on rising SDA edge, while SCL is high.

But the SPI peripheral has already shifted the first bit into shift register.

So at the next Start condition the SPI starts with one bit shifted ofset.

Unfortunately, it is not possible to reset the internal shift register of SPI after i have detected the stop condition (even not by disabling the peripheral).

AScha.3
Super User
March 27, 2023

Oo - the stop condition is a problem .

the spi shift-counter should be reset, when spi is disabled.

try write to SPIxCR1 ->

0693W00000aJlITQA0.pngthere must be some way to reset the counter - otherwise you could never use spi again, if it got one spike on clk line or got one wrong sized word.

for detect stop condition spontaneously i only have this idea :

need hardware : a d-flip-flop , 74HC74 or so.

flipflop is clocked by rising SDA , Data in is SCL ; -> FF output will rise on "stop" .

-> goes to cpu pin with exti int.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Moritz1
Moritz1Author
Associate III
March 27, 2023

In this case the problem will still be the interrupt runtime, as I mentioned in the initial post.

In my application, after the stop condition, there is immediately another start condition to communicate with another device.

So the problem will be: On the stop condition, the SPI shifts 1 bit into the shift register, then the EXTI interrupt is triggered by the stop condition. But until I reach the handler and be able to disable and enable the SPI, I have missed the first clock edge of the new transmission.

Moritz1
Moritz1Author
Associate III
March 31, 2023

Unfortunately, my hardware already exists and it is not possible to add an external flip-flop.

I had another idea to detect the stop condition:

I tried to use a timer. I configured the timer in gated mode. This means that the timer only counts when the SCL line is high. As clock source for the timer I use the SDA on rising edge.

So every time there is a stop condition on the bus, the counter increments by one.

This works fine. But I have a small problem with this: I want to get an interrupt when a stop condition happens. So I set the Auto-Reload ARR register to 1 and enabled the update interrupt.

Unfortunately with this solution the interrupt is only generated every second stop condition.

This is because the update event is generated when the counter value is higher than ARR.

(Setting ARR to 0 is not possible)

Surely I can additionaly set the compare value to 1 and enable the CCIE. This will generate a CCIE on every time the counter changes from 0 to 1. And the Update interrupt is generated if the counter changes from 1 to 2 (rest to 0). So I have an interrupt on every stop condition. But I want to trigger a DMA on this event to solve the interrupt run time problem. But for DMA you can only select TIM_UP or TIM_CH1 as the trigger source. It is not possible to trigger the DMA on both.

So is there a way to generate a timer interrupt every time the counter is incremented?