I2C implementation for STM32

This forum is dedicated to feedback, discussions about ongoing or future developments, ideas and suggestions regarding the ChibiOS projects are welcome. This forum is NOT for support.
User avatar
barthess
Posts: 861
Joined: Wed Dec 08, 2010 7:55 pm
Been thanked: 7 times

Re: I2C implementation for STM32

Post by barthess »

Giovanni wrote:try to disable the stream first and THEN do the clearing

The same behavior.
I solved problem by enabling stream only after successful ACK.
matis
Posts: 53
Joined: Fri Jul 01, 2011 1:46 pm

Re: I2C implementation for STM32

Post by matis »

barthess wrote:matis
Fixed. Check out rev. 3561.
If you want to handle situations of communicating with non-exisiting I2C node use error callback, for example:

Code: Select all

static void i2c_tmp75_error_cb(I2CDriver *i2cp, const I2CSlaveConfig *i2cscfg){
  (void)i2cscfg;
  if (i2cp->errors & I2CD_ACK_FAILURE){
    while(TRUE); // wrong address
  }
}


Thanks in advance, I'll check this first thing in the morning. Because we have hot pluggable I2C nodes, it is possible that they don't reply. I've already implemented the error callbacks, but due to the low level crashes, it didn't reach the error trap.

In my particular case, a I2CD_ACK_FAILURE isn't a bad thing, so I can return in that function. Because of the synchronization mechanism in my caller function, will the i2drv->id_state also become I2C_READY?
User avatar
barthess
Posts: 861
Joined: Wed Dec 08, 2010 7:55 pm
Been thanked: 7 times

Re: I2C implementation for STM32

Post by barthess »

barthess wrote:will the i2drv->id_state also become I2C_READY?

Yes, after error handling driver falls back to I2C_READY state.
matis
Posts: 53
Joined: Fri Jul 01, 2011 1:46 pm

Re: I2C implementation for STM32

Post by matis »

I found out why I get the

Code: Select all

#error "invalid DMA stream associated to I2C1 RX"


Due to my STM32F103VET (STM32F10X_HD) the I2C DMA attributes are not set. They are only set for the MD-devices. When copying the

Code: Select all

/* I2C attributes.*/
#define STM32_HAS_I2C1          TRUE
#define STM32_I2C1_RX_DMA_MSK   (STM32_DMA_STREAM_ID_MSK(1, 7))
#define STM32_I2C1_RX_DMA_CHN   0x00000000
#define STM32_I2C1_TX_DMA_MSK   (STM32_DMA_STREAM_ID_MSK(1, 6))
#define STM32_I2C1_TX_DMA_CHN   0x00000000

#define STM32_HAS_I2C2          TRUE
#define STM32_I2C2_RX_DMA_MSK   (STM32_DMA_STREAM_ID_MSK(1, 5))
#define STM32_I2C2_RX_DMA_CHN   0x00000000
#define STM32_I2C2_TX_DMA_MSK   (STM32_DMA_STREAM_ID_MSK(1, 4))
#define STM32_I2C2_TX_DMA_CHN   0x00000000

#define STM32_HAS_I2C3          FALSE
#define STM32_I2C3_RX_DMA_MSK   0
#define STM32_I2C3_RX_DMA_CHN   0x00000000
#define STM32_I2C3_TX_DMA_MSK   0
#define STM32_I2C3_TX_DMA_CHN   0x00000000

to the I2C attributes in the

Code: Select all

#if defined(STM32F10X_HD) || defined(__DOXYGEN__)

os/hal/platforms/STM32F1xx/hal_lld_f103.h on line 416 it gets rid of the error :)
matis
Posts: 53
Joined: Fri Jul 01, 2011 1:46 pm

Re: I2C implementation for STM32

Post by matis »

barthess wrote:matis
Fixed. Check out rev. 3561.
If you want to handle situations of communicating with non-exisiting I2C node use error callback, for example:

Code: Select all

static void i2c_tmp75_error_cb(I2CDriver *i2cp, const I2CSlaveConfig *i2cscfg){
  (void)i2cscfg;
  if (i2cp->errors & I2CD_ACK_FAILURE){
    while(TRUE); // wrong address
  }
}


After I got rid of the compile error, I tried your code (rev 3564). I see that you added the following chDbgCheck(...) in i2c.c void i2cMasterTransmit(...) on line 158:

Code: Select all

  chDbgCheck((i2cp != NULL) && (i2cscfg != NULL) &&\
        (slave_addr != 0) &&\
        (txbytes > 0) &&\
        (txbuf != NULL) &&\
this line =>((rxbytes == 0) || ((rxbytes > 1) && (rxbuf != NULL))),
        "i2cMasterTransmit");

I want to read only 1 byte and my rxbuff is set; Why can't I read only 1 byte?

Code: Select all

i2cp   0x20005a24   
i2cscfg   0x08015410   
slave_addr   0x51   
txbuf   0x20005288   
txbytes   1   
rxbuf   0x20005278   
rxbytes   1   

Shouldn't it be ((rxbytes == 0) || ((rxbytes > 0) && (rxbuf != NULL))) ?

Looking forward to your explantation :)
User avatar
barthess
Posts: 861
Joined: Wed Dec 08, 2010 7:55 pm
Been thanked: 7 times

Re: I2C implementation for STM32

Post by barthess »

That because of rotten I2C cell in stm32 family. There is not possible to read 1 byte via DMA. If your hardware allow than read 2 bytes and drop unneeded one. Otherwise you must to fall back to I2Cv1, but I not recommend to do that because it too knotty.
User avatar
barthess
Posts: 861
Joined: Wed Dec 08, 2010 7:55 pm
Been thanked: 7 times

Re: I2C implementation for STM32

Post by barthess »

Hi, Giovanni
I measure time of polling STOP bit. It take about 2.24uS and very stable (SCL period is 2.40uS). Is it really necessary to use delayed polling via dedicated hardware timer? Without it driver code is beautiful and clean. Also, now we can set I2C IRQ priority to lowest value because of DMA usage.
My I2CConfig:

Code: Select all

static const I2CConfig i2cfg2 = {
    OPMODE_I2C,
    400000,
    FAST_DUTY_CYCLE_16_9,
};

screenshot:
http://www.freeimagehosting.net/cea97
Image
Yellow chart up is polling cycle, magenta chart is SCL.
Attachments
TEK00000.PNG
TEK00000.PNG (12.84 KiB) Viewed 6525 times
User avatar
Giovanni
Site Admin
Posts: 14891
Joined: Wed May 27, 2009 8:48 am
Has thanked: 1202 times
Been thanked: 996 times

Re: I2C implementation for STM32

Post by Giovanni »

Hi,

Such a polling in IRQ context would degrade heavily the response time of the whole system (we are talking of an RTOS here) which, in case of the STM32F4 is well below the microsecond. I also think that that time is dependent on the I2C speed, probably by setting lower speeds that time would increase making degradation worse.

If the driver API was purely synchronous then a polling of 3uS would be perfectly acceptable because it would be performed in thread context.

In a synchronous driver I would, make a transmission API implementation this way:

1) Poll if there is a STOP ongoing from a *previous* transmission.
2) Start the transmission and wait for completion on a semaphore or a Thread *.
3) On the final IRQ wakeup the waiting thread.
4) Return status (RDY_OK=ok, RDY_TIMEOUT=timeout, you may use a VT to establish a maximum execution time for extra safety).

Note that the polling would be done at the beginning, this way the wait would most certainly be masked by the time between a transmission and next one. This is not much different from what you are doing now, it just don't have callbacks. The API name would be i2CTransmitTimeout() instead of i2cStartTransmission() because it would be synchronous, I-class variants would also no more be required.

We need to evaluate tradeoffs but I think we would get a couple of serious advantages vs losing callbacks.

Giovanni
User avatar
barthess
Posts: 861
Joined: Wed Dec 08, 2010 7:55 pm
Been thanked: 7 times

Re: I2C implementation for STM32

Post by barthess »

Hm... looks like drive already have all needed stuff, I just need to "remix" code to make API synchronous. I will try to do that on this evening.
User avatar
barthess
Posts: 861
Joined: Wed Dec 08, 2010 7:55 pm
Been thanked: 7 times

Re: I2C implementation for STM32

Post by barthess »

Driver switched to synchronous model. Callbacks and SlaveConfig struct were deleted.
Also updated testhal for F1x.

Giovanni
What to do with i2cAcquireBus()/i2cReleaseBus() functions? Hide inside the driver or leave as is?

matis
Now not responding nodes catched like there:

Code: Select all

  i2cAcquireBus(&I2CD1);
  errors = i2cMasterReceive(&I2CD1, addr, rx_data, 2);
  i2cReleaseBus(&I2CD1);

  if (errors == I2CD_ACK_FAILURE){
    ;
  }
Post Reply