Author Topic: Tips on implementing an I2C Software stack that doesn't waste time? (dspic)  (Read 1288 times)

0 Members and 1 Guest are viewing this topic.

Online JPorticiTopic starter

  • Super Contributor
  • ***
  • Posts: 3461
  • Country: it
Hi there.
I have a confession: until last couple of days i've basically never used I2C.. mainly because it seemed often buggy (why is it so had to implement an I2C peripheral? i remember reading that older STM32Fx parts actually needed a software implementation!)
and not so much automation seems to be possible.

Infact, in dspics at least, there is no operation queue so the user has to rely on individual actions and interrupts.

So, in next projects i have used up most resources and pins... I2C for memories and others has suddently become very attractive and i started coding.
Now i've implemented a solution i'm liking.. relies on interrupts and uses very little processing time, even with interrupt latency. Compiled with XC16, -O1 (still in free mode) the solution is reasonably tidy and neat.
The only peripheral i've handled for now is a 24xxyyyy I2C eeprom type (e.g. : 24LC512)
My approach was the following:
- there is an event queue
- last event of queue is _I2C_IDLE (actually first, it's a LIFO buffer)
- every interrupt corresponds to the end of a previous event so new event must be handled (check ACK/NACK, move data, ..) and event position counter is updated (e.g.: i still have locations to read: decrement counter and go back to "READ" instead of going forward)
- a new i2c operation can be initiated only with the peripheral free (interrupt at event "_I2C_IDLE")

*** The code IS working as expected. Not claiming this is perfect, infact i'm missing collision handling (don't know what to do in that case)

What i'm asking is, how do you proceed in such cases?
 

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5319
  • Country: gb
I'm assuming it's a master rather than a slave. There are three stacks I use:

1) Simple blocking API, no error or time out handling
2) Event driven FSM, no error or time out handling
3) Event driven FSM, error and time out handling

If you can get away with 1), it saves an awful lot of grief, and for I2C slaves that only need initialisation it makes sense.

For a lot of slaves, if they don't respond in an anticipated manner immediately, then there is often a hardware error anyway. However for things like EEPROMS that typically need a lengthy delay for writing and erasing, it is often necessary to implement an FSM.

Adding in time out and error handling, and clock stretching, multi-master etc can be gilding the lily in a lot of applications, but sometimes it's necessary, at the expense of adding significant complexity.
 

Online JPorticiTopic starter

  • Super Contributor
  • ***
  • Posts: 3461
  • Country: it
Well, i'm not aiming for a general purpose solution (as i said, i have little experience with I2C) but rather some general structure i can adapt with minimum pain

yes, in this case i am a master, single master multiple slaves. god forbid i have to make a multi-mcu board i'd rather use UART or CAN for mcu to mcu communication so shared resources like EEPROM would be read from MCUA then transferred to MCUB

i think i have implemented 2), for example

Code: [Select]
uint16_t i2c_ee_readmap(uint16_t ee_add, uint8_t * memloc, uint16_t memsize) {
 
  if (i2c_status != _I2C_FREE)
    return _I2C_BUSY;
 
  i2c_status = _I2C_BUSY;
 
  i2c_event_queue[10] = _I2C_EVENT_START_BIT;
  i2c_event_queue[9] = _I2C_EVENT_MEMORY_SETADDR;
  i2c_event_queue[8] = _I2C_EVENT_MEMORY_MSB;
  i2c_event_queue[7] = _I2C_EVENT_MEMORY_LSB;
  i2c_event_queue[6] = _I2C_EVENT_START_BIT;
  i2c_event_queue[5] = _I2C_EVENT_MEMORY_SETREAD;
  i2c_event_queue[4] = _I2C_EVENT_MEMORY_READ;
  i2c_event_queue[3] = _I2C_EVENT_SEND_ACK;
  i2c_event_queue[2] = _I2C_EVENT_SEND_NACK;
  i2c_event_queue[1] = _I2C_EVENT_STOP_BIT;
  i2c_event_queue[0] = _I2C_EVENT_IDLE;
 
  i2c_event_pos = 9;
 
  i2c_ee_add = ee_add;
  i2c_memloc = memloc;
  i2c_memcnt = memsize;
 
  /* Set Start Bit */
  I2C1CON1bits.SEN = 1;
  _MI2C1IF = 0;
  _MI2C1IE = 1;
 
  return _I2C_SUCCESS;
}

and the interrupt routine does the rest...
e.g. cycle between event 4 and 3 until the last location was read, then jump from 4 to 2 and finish

for writing i already have a plan, to wait at least the max amount of time for programming. writing already goes down this way anyway:
- Computer issues WRITE command to board (UART)
- Start Writing data
- At the end a countdown inside the timekeeping interrupt starts
- When countdown finish send back WRITE FINISH packet
 

Offline Howardlong

  • Super Contributor
  • ***
  • Posts: 5319
  • Country: gb
You could always use MCC to write the code for you.  :-DD  :palm:
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf