How to use I2C from an interrupt

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
LaMe_4502671
Level 1
Level 1

I have function stop(...) which I would like to disable all interrupts, spit out some data on an I2C port, then call CyHalt, regardless of where it's called from. When I try to call it from within an Interrupt handler, it sends one byte (seen on oscope), and hangs. Seemingly waiting for the I2C transaction to complete (I2C_MasterStatus() keeps returning I1C_MSTAT_XFER_INP). It does the same thing if I do it after calling CyGlobalIntDisable or CyEnterCriticalSection().

I think the issue is I don't know how to selectively enable whatever interrupts I2C requires.

0 Likes
9 Replies
LaMe_4502671
Level 1
Level 1

I tried I2C_EnableInt(), but that didn't work when called from an ISR

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

I think that I2C also needs interrupts to work, but inside an ISR it won't take place.

So can you just set "EXIT_FLAG" in that ISR and after returning to the main loop,

test EXIT_FLAG and if it's set, do whatever required to clean up and call CyHalt?

moto

yeah, but that assumes the main loop is operating correctly. One of the situations this is used is in a watchdog that fires when the main loop is _not_ functioning. What I'd like to do is reset whatever state allows the requisite interrupts to be delivered. I know this is impossible to return from, but I don't intend to return.

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

> One of the situations this is used is in a watchdog that fires when the main loop is _not_ functioning.

Oh, I see. If it's the case.

Assuming that using I2C component is not a choice, I would try to

(1) Change I2C pins to GPIO

(2) And flip those pins according to the I2C protocol spec,

      but probably supporting clock stretch etc will be difficult.

Note: I've never done this by myself before, though.

moto

0 Likes

Bit banging I2C seems rather absurd. The question is about how to twiddle registers such that the hardware tranceiver works. I have a hard time believing it's not possible and that such workaround are necessary.

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

> Bit banging I2C seems rather absurd.

> The question is about how to twiddle registers such that the hardware tranceiver works.

> I have a hard time believing it's not possible and that such workaround are necessary.

Oh, I'm sorry for that, probably it's lack of my knowledge.

As I have designed an I2C driver in an ASIC a quarter century ago and reading the NXP spec,

I hoped it was not ridiculous, but maybe I was wrong.

moto

0 Likes

Yeah, I think it's absurd to implement I2C from scratch on a PSoC5LP, which has 2 otherwise perfectly fine I2C implementations already (UDB and FF). I dunno about you, but I'd have sold my soul for such a thing in 1995, and good on you for having approximated it with the tools of the time. However, it's 2020, and I can get one (with a programmer!!) for the price of a KFC Bucket, and I don't want to go reinventing (and remaintaining, and retesting) the wheel or reading specs orthogonal to my problem space.

Seems like if I can return to the main loop and use I2C, I should be able to fake up whatever state changes cause I2C to work. I was hoping there's a real API, worst case a way to overwrite the return address from an ISR would do. That's what I was intending to ask with this question. I'm ideally looking for a proper way to do this, failing that, a simple one, not a complex workaround.

Seems like something every firmware would have to do once, is there a pattern for this?

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

Since you wrote that main is not functioning and you are in an ISR I was assuming that we can not use usual I2C.

But if we can force I2C work without interrupt may be beating it to spit out the last data is feasible.

So considering that I2C can not be used in that situation could be my wrong prejudication.

Meantime, if by some reason "main" is dead (or deadlocked), over writing the return address sounds a promising method,

if Cortex-M allows us to do so. (In '80s it was a popular trick, but I have not tried it with an arm core).

And needless to say, I am not a Cypress Engineer so my opinion is not authentic

for the details of the device please consult the TRM (I2C is in Ch.26)

https://www.cypress.com/file/123561/download

moto

Hi LaMe_4502671​,

The I2C component works on interrupts. This means that when you disable I2C interrupts/ enter into critical section, I2C will not work and it will report only "transfer in progress". I2C ISR is responsible for changing the status of I2C status.

You can do two things:

Option A.

When Stop() is called, send out the required bytes through I2C, make sure transfer is complete and then disable the interrupts and call CyHalt. Disabling global interrupts is also not allowed before sending out i2c data

Option B

Since I2C is a hardware block, we can get rid of interrupt based operation. This requires you to directly write into i2c registers. This will take some time to develop. For example :

1. Send start + Address

wait for start and address to be sent

3. Send data

wait for data to go out of FIFO, check if ACK is received.

..

..

4. Send Stop condition

For specific registers, please refer PSoC 5 register TRM. For how to implement these, you can refer to the I2C APIs and I2C ISR (under generated source look for /I2C/I2C_INT.c).

Regards,

Bragadeesh

Regards,
Bragadeesh