i2c slave block transfer driver library

Dependents:   i2c_slave_block_example i2c_lora_slave

I2C block read/write slave

operates with I2C master using the i2c_smbus_read_i2c_block_data() and i2c_smbus_write_i2c_block_data() calls.

Tested with raspberry pi as master.
STM32Lx nucleo as slave.

signal pinmbed slaveRPi master
GND6
SCLD155
SDAD143
IRQany avail pinany avail pin

IRQ pin is implemented in application, not in this library. (see example)

Up to 32byte length per transfer.
Raspberry pi I2C doesnt support SMBUS block read, so I2C block transfers are supported here.
SMBUS block transfers are variable length. Instead, here in this driver, I2C block length is mapped to command byte by const array cmd_to_length[] (see example)

master write to slave

For i2c_smbus_write_i2c_block_data() from master, this driver lets the main loop on slave take the write request. The main loop on slave calls service_i2c(), which then calls service_i2c_write() upon each write from master. This occurs thru a circular buffer, permitting the slave to take its time doing the request, while the master can issue several back-to-back requests without delays.

clock stretching

For i2c_smbus_read_i2c_block_data(): Since raspberry pi doesnt support clock stretching out the the box, providing transmit data must be done in the interrupt service routine. fill_tx_buf() populates the transmit buffer to be sent to master, which must be done immediately because this occurs inside interrupt handler. If I2C master were to support clock stretching, then transmit buffer work could be done in main loop of slave.
/media/uploads/dudmuck/i2c_rpi_nucleo_sm_.png
An STM32 running at 32MHz is unlikely to operate with 400KHz I2C. Use 100KHz speed with 32MHz CPU, or use faster speed CPU for 400KHz I2C.

Files at this revision

API Documentation at this revision

Comitter:
Wayne Roberts
Date:
Mon Feb 04 13:42:05 2019 -0800
Parent:
4:7ab789db70da
Commit message:
update HAL state for STM32L4

Changed in this revision

TARGET_STM/smbus.c Show annotated file Show diff for this revision Revisions of this file
diff -r 7ab789db70da -r 493e5cc9c052 TARGET_STM/smbus.c
--- a/TARGET_STM/smbus.c	Sun Feb 03 16:55:29 2019 -0800
+++ b/TARGET_STM/smbus.c	Mon Feb 04 13:42:05 2019 -0800
@@ -46,15 +46,17 @@
         SmbusHandle.Instance->CR1 &= ~I2C_CR1_TXDMAEN;
         __HAL_DMA_DISABLE(&hdma_tx);
         __HAL_UNLOCK(&hdma_tx);
+        hdma_tx.State = HAL_DMA_STATE_READY;
 
         SmbusHandle.Instance->CR1 &= ~I2C_CR1_RXDMAEN;
         __HAL_DMA_DISABLE(&hdma_rx);
         __HAL_UNLOCK(&hdma_rx);
+        hdma_rx.State = HAL_DMA_STATE_READY;
 
         put_cbuf(CMD_TIMEOUT);
         put_cbuf(1);
         put_cbuf(hdma_tx.Instance->CNDTR); // req[0]
-        
+
         _icr |= SMBUS_FLAG_TIMEOUT;
     }
 
@@ -92,14 +94,20 @@
         i2cTransferDirection = SMBUS_GET_DIR(&SmbusHandle);
         if (i2cTransferDirection) { // if host read
             // 1: Read transfer, slave enters transmitter mode..  cmd has already been provided
+
             SmbusHandle.Instance->CR1 &= ~I2C_CR1_RXDMAEN;
             __HAL_DMA_DISABLE(&hdma_rx);
             __HAL_UNLOCK(&hdma_rx);
+            hdma_rx.State = HAL_DMA_STATE_READY;
+#ifdef TARGET_STM32L4
+            // clear dma interrupt flags
+            hdma_rx.DmaBaseAddress->IFCR = (DMA_ISR_GIF1 << hdma_rx.ChannelIndex);
+#endif
 
             fill_tx_buf(_rx_buf[0]);
 
 #ifdef DEBUG_SMBUS
-            put_cbuf(CMD_ADDR_HOST_READ);
+            put_cbuf(CMD_ADDR_HOST_READ); // AHR
             put_cbuf(2);
             put_cbuf(cmd_to_length[_rx_buf[0]]);
             put_cbuf(hdma_tx.Instance->CNDTR);
@@ -110,13 +118,14 @@
             icr |= SMBUS_FLAG_ADDR;
         } else if (!stopWaiting) {
             // 0: Write transfer, slave enters receiver mode.
+
             SmbusHandle.Instance->CR1 |= I2C_CR1_RXDMAEN;
             HAL_DMA_Start(&hdma_rx, (uint32_t)&SmbusHandle.Instance->RXDR, (uint32_t)_rx_buf, sizeof(_rx_buf));
 
 #ifdef DEBUG_SMBUS
-            put_cbuf(CMD_ADDR_HOST_WRITE);
+            put_cbuf(CMD_ADDR_HOST_WRITE); // AHW
             put_cbuf(1);
-            put_cbuf(sizeof(_rx_buf));
+            put_cbuf(hdma_rx.Instance->CNDTR);
 #endif /* DEBUG_SMBUS */
 
             icr |= SMBUS_FLAG_ADDR;
@@ -134,6 +143,7 @@
         SmbusHandle.Instance->CR1 &= ~I2C_CR1_TXDMAEN;
         __HAL_DMA_DISABLE(&hdma_tx);
         __HAL_UNLOCK(&hdma_tx);
+        hdma_tx.State = HAL_DMA_STATE_READY;
 
 #ifdef DEBUG_SMBUS
         put_cbuf(CMD_AF);
@@ -158,6 +168,7 @@
             SmbusHandle.Instance->CR1 &= ~I2C_CR1_RXDMAEN;
             __HAL_DMA_DISABLE(&hdma_rx);
             __HAL_UNLOCK(&hdma_rx);
+            hdma_rx.State = HAL_DMA_STATE_READY;
 
             len = sizeof(_rx_buf) - hdma_rx.Instance->CNDTR;
             if (len > 0) {
@@ -311,7 +322,7 @@
     SmbusHandle.Init.SMBusTimeout         = I2C_TIMEOUTR_TIMOUTEN | 0x186;
 
     if (HAL_SMBUS_Init(&SmbusHandle) != HAL_OK)
-    { 
+    {
         return -1;
     }
 
@@ -326,7 +337,7 @@
 
     NVIC_SetVector(event_i2cIRQ, (uint32_t)SMBUSx_EV_IRQHandler);
     NVIC_SetVector(error_i2cIRQ, (uint32_t)SMBUSx_ER_IRQHandler);
-  
+
 #else
     HAL_NVIC_SetPriority(event_i2cIRQ, 0, 0);
     HAL_NVIC_EnableIRQ(event_i2cIRQ);
@@ -358,7 +369,7 @@
     hdma_rx.Init.Priority            = DMA_PRIORITY_HIGH;
 
     HAL_DMA_Init(&hdma_rx);
-    
+
     return 0;
 }