AnsweredAssumed Answered

I2C Read Combined Format - Please Review code & Queries

Question asked by new guy on Oct 11, 2015
Hi,

Many I2C devices need the STM32 Master to do the so called combined format i2c which involves...
Start,
Slave Address with a 0 bit appended for a write bit,
[ack]
CommandCode to the device (8 bits)
[ack]
REPEATED Start bit
Slave Address with a 0 bit appended for a read bit,
[ack]
Data, ack, Data, ack .........Stop

ST had a dodgey history with i2c examples, and they don't seem to have a good one for this most common of requirements. I want to stay away from cube and cpal, so decided to try and hack together my own from the std peripheral library examples.

Some Issues I would appreciate advice on...

When doing "Slave Address with a 0 bit appended for a read bit," is this right?
I2C_Send7bitAddress(I2Cx, SlaveAddrWriteBit, I2C_Direction_Transmitter);
Points of contention:
(a) Should it be Direction_Transmitter or Receiver at this point (I use Receiver for "Slave Address with a 0 bit appended for a read bit")?

(b) the value SlaveAddrWriteBit is my slave address with the read/write bit added onto the
end. Is this correct or does Receiver/Transmitter do this for you?

(c) what does receiver/transmitter option actual do? 

(d) at times I am not certain as to whether I should be using i2c_Send7bitAddress again, or i2c SendData function - do ye think I made the right choices at each step?

Obviously this will all eventually be made into a function.  

2. Would be most grateful for all who review my code for sanity/errors - first time doing this
    and could easily get lost! May also be a useful example for other noobs if it can be made to work - as this is a common requirement. 

It is derived from the data exchange DMA example in the Std Peripheral Libs for f2/f4

#include "main.h"


/** @addtogroup STM32F2xx_StdPeriph_Examples
  * @{
  */


/** @addtogroup I2C_DataExchangeDMA
  * @{
  */ 


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/


#define countof(a)   (sizeof(a) / sizeof(*(a)))  




uint8_t SlaveAddrWriteBit =0x52;   //0b1010010; 
uint8_t SlaveAddReadBit   =0x53;   //0b1010011;
uint8_t CommandCode       =0xB2;   //0b10110010; 






/* Private variables ---------------------------------------------------------*/
I2C_InitTypeDef  I2C_InitStructure;
DMA_InitTypeDef  DMA_InitStructure;
uint8_t TxBuffer[] = "I2C DMA Example: Communication between two I2C using DMA";
uint8_t RxBuffer [256];
__IO uint32_t TimeOut = 0x0;
RCC_ClocksTypeDef RCC_Clocks;


/* Private function prototypes -----------------------------------------------*/
static void I2C_Config(void);
static void SysTickConfig(void);
static void TimeOut_UserCallback(void);
static void Fill_Buffer(uint8_t *pBuffer, uint16_t BufferLength);


/* Private functions ---------------------------------------------------------*/




int main(void)
{
    /* I2C configuration ---------------------------------------------------------*/
  I2C_Config();
  
 
  /* SysTick configuration -----------------------------------------------------*/
  SysTickConfig();
  
  /* Clear the RxBuffer */
  Fill_Buffer(RxBuffer, RXBUFFERSIZE);
  
  /*************************************Combined Write and Read*******/


 
  /* I2C Struct Initialize */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_DutyCycle = I2C_DUTYCYCLE;
  I2C_InitStructure.I2C_OwnAddress1 = 0xA0;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_ClockSpeed = I2C_SPEED;
  
#ifndef I2C_10BITS_ADDRESS
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
#else
#endif /* I2C_10BITS_ADDRESS */
  
  /* I2C Initialize */
  I2C_Init(I2Cx, &I2C_InitStructure);
  
  /* Master Transmitter --------------------------------------------------------*/   
  
  /* Generate the Start condition */
  I2C_GenerateSTART(I2Cx, ENABLE);
  


      
  /* Test on I2Cx EV5 and clear it or time out */
  TimeOut = USER_TIMEOUT;
  while ((!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))&&(TimeOut != 0x00))
  {} 
  if(TimeOut == 0)
  {
    TimeOut_UserCallback();
  }
  /* Send I2Cx slave Address for write */
  I2C_Send7bitAddress(I2Cx, SlaveAddrWriteBit, I2C_Direction_Transmitter);
      
  
  /* Test on I2Cx EV6 and clear it or time out */
  TimeOut = USER_TIMEOUT;
  while ((!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))&&(TimeOut != 0x00))
  {} 
  if(TimeOut == 0)
  {
    TimeOut_UserCallback();
  }
     
     //commandcode to be inserted here
     I2C_SendData(I2C1, CommandCode); 
     
     //repeatstart
       /* Generate the Start condition */
   /* Send I2Cx START condition */
  I2C_GenerateSTART(I2Cx, ENABLE);
  
#ifdef I2C_10BITS_ADDRESS  
   #else /* I2C_7BITS_ADDRESS */
      
  /* Test on I2Cx EV5 and clear it or time out*/
  TimeOut = USER_TIMEOUT;
  while ((!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))&&(TimeOut != 0x00))
  {}
  if(TimeOut == 0)
  {
    TimeOut_UserCallback();
  }
  /* Send I2Cx slave Address for write */
  I2C_Send7bitAddress(I2Cx, SlaveAddrWriteBit, I2C_Direction_Receiver);
      
#endif /* I2C_10BITS_ADDRESS */
  
  /* Test on I2Cx EV6 and clear it or time out */
  TimeOut = USER_TIMEOUT;
  while ((!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))&&(TimeOut != 0x00))
  {}
  if(TimeOut == 0)
  {
    TimeOut_UserCallback();
  }
  /* I2Cx DMA Enable */
  I2C_DMACmd(I2Cx, ENABLE);
  
  /* Enable DMA RX Channel */
  DMA_Cmd(I2Cx_DMA_STREAM_RX, ENABLE);
  
  /* Wait until I2Cx_DMA_STREAM_RX enabled or time out */
  TimeOut = USER_TIMEOUT;
  while ((DMA_GetCmdStatus(I2Cx_DMA_STREAM_RX)!= ENABLE)&&(TimeOut != 0x00))
  {}  
  if(TimeOut == 0)
  {
    TimeOut_UserCallback();
  }
  
  /* Transfer complete or time out */
  TimeOut = USER_TIMEOUT;
  while ((DMA_GetFlagStatus(I2Cx_DMA_STREAM_RX,I2Cx_RX_DMA_TCFLAG)==RESET)&&(TimeOut != 0x00))
  {}
  if(TimeOut == 0)
  {
    TimeOut_UserCallback();
  }
  /* Send I2Cx STOP Condition */
  I2C_GenerateSTOP(I2Cx, ENABLE);


  /* Disable DMA RX Channel */
  DMA_Cmd(I2Cx_DMA_STREAM_RX, DISABLE);
  
  /* Wait until I2Cx_DMA_STREAM_RX disabled or time out */
  TimeOut = USER_TIMEOUT;
  while ((DMA_GetCmdStatus(I2Cx_DMA_STREAM_RX)!= DISABLE)&&(TimeOut != 0x00))
  {}  
  if(TimeOut == 0)
  {
    TimeOut_UserCallback();
  }
  
  /* Disable I2C DMA request */  
  I2C_DMACmd(I2Cx,DISABLE);
  
  /* Clear any pending flag on Rx Stream  */
  DMA_ClearFlag(I2Cx_DMA_STREAM_RX, I2Cx_RX_DMA_TCFLAG | I2Cx_RX_DMA_FEIFLAG | I2Cx_RX_DMA_DMEIFLAG | \
                                       I2Cx_RX_DMA_TEIFLAG | I2Cx_RX_DMA_HTIFLAG);
  
  
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
 
  while(1)
  {}
}


/**
  * @brief  Enables the I2C Clock and configures the different GPIO ports.
  * @param  None
  * @retval None
  */
static void I2C_Config(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;
    
  /* RCC Configuration */
  /*I2C Peripheral clock enable */
  RCC_APB1PeriphClockCmd(I2Cx_CLK, ENABLE);
  
  /*SDA GPIO clock enable */
  RCC_AHB1PeriphClockCmd(I2Cx_SDA_GPIO_CLK, ENABLE);
  
  /*SCL GPIO clock enable */
  RCC_AHB1PeriphClockCmd(I2Cx_SCL_GPIO_CLK, ENABLE);
  
  /* Reset I2Cx IP */
  RCC_APB1PeriphResetCmd(I2Cx_CLK, ENABLE);
  
  /* Release reset signal of I2Cx IP */
  RCC_APB1PeriphResetCmd(I2Cx_CLK, DISABLE);
  
  /* Enable the DMA clock */
  RCC_AHB1PeriphClockCmd(DMAx_CLK, ENABLE);
  
  /* GPIO Configuration */
  /*Configure I2C SCL pin */
  GPIO_InitStructure.GPIO_Pin = I2Cx_SCL_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
  GPIO_Init(I2Cx_SCL_GPIO_PORT, &GPIO_InitStructure);
  
  /*Configure I2C SDA pin */
  GPIO_InitStructure.GPIO_Pin = I2Cx_SDA_PIN;
  GPIO_Init(I2Cx_SDA_GPIO_PORT, &GPIO_InitStructure);
    
  /* Connect PXx to I2C_SCL */
  GPIO_PinAFConfig(I2Cx_SCL_GPIO_PORT, I2Cx_SCL_SOURCE, I2Cx_SCL_AF);
  
  /* Connect PXx to I2C_SDA */
  GPIO_PinAFConfig(I2Cx_SDA_GPIO_PORT, I2Cx_SDA_SOURCE, I2Cx_SDA_AF);


  /* DMA Configuration */
  /* Clear any pending flag on Tx Stream  */
  DMA_ClearFlag(I2Cx_DMA_STREAM_TX, I2Cx_TX_DMA_TCFLAG | I2Cx_TX_DMA_FEIFLAG | I2Cx_TX_DMA_DMEIFLAG | \
                                       I2Cx_TX_DMA_TEIFLAG | I2Cx_TX_DMA_HTIFLAG);
                                       
  /* Clear any pending flag on Rx Stream  */
  DMA_ClearFlag(I2Cx_DMA_STREAM_RX, I2Cx_RX_DMA_TCFLAG | I2Cx_RX_DMA_FEIFLAG | I2Cx_RX_DMA_DMEIFLAG | \
                                       I2Cx_RX_DMA_TEIFLAG | I2Cx_RX_DMA_HTIFLAG);
  
  /* Disable the I2C Tx DMA stream */
  DMA_Cmd(I2Cx_DMA_STREAM_TX, DISABLE);
  /* Configure the DMA stream for the I2C peripheral TX direction */
  DMA_DeInit(I2Cx_DMA_STREAM_TX);
  
  /* Disable the I2C Rx DMA stream */
  DMA_Cmd(I2Cx_DMA_STREAM_RX, DISABLE);
  /* Configure the DMA stream for the I2C peripheral RX direction */
  DMA_DeInit(I2Cx_DMA_STREAM_RX);
  
  /* Initialize the DMA_Channel member */
  DMA_InitStructure.DMA_Channel = I2Cx_DMA_CHANNEL;
  
  /* Initialize the DMA_PeripheralBaseAddr member */
  DMA_InitStructure.DMA_PeripheralBaseAddr = I2Cx_DR_ADDRESS;
  
  /* Initialize the DMA_PeripheralInc member */         
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  
  /* Initialize the DMA_MemoryInc member */
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  
  /* Initialize the DMA_PeripheralDataSize member */
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  
  /* Initialize the DMA_MemoryDataSize member */
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  
  /* Initialize the DMA_Mode member */
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  
  /* Initialize the DMA_Priority member */
  DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  
  /* Initialize the DMA_FIFOMode member */
  DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
  
  /* Initialize the DMA_FIFOThreshold member */
  DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
  
  /* Initialize the DMA_MemoryBurst member */
  DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
  
  /* Initialize the DMA_PeripheralBurst member */
  DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
  
  /* Init DMA for Reception */
   /* Initialize the DMA_DIR member */
   DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
   /* Initialize the DMA_Memory0BaseAddr member */
   DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)RxBuffer;
   /* Initialize the DMA_BufferSize member */
   DMA_InitStructure.DMA_BufferSize = RXBUFFERSIZE;
   DMA_DeInit(I2Cx_DMA_STREAM_RX);
   DMA_Init(I2Cx_DMA_STREAM_RX, &DMA_InitStructure);
  
  /* Init DMA for Transmission */
   /* Initialize the DMA_DIR member */
   DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
   /* Initialize the DMA_Memory0BaseAddr member */
   DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)TxBuffer;
   /* Initialize the DMA_BufferSize member */
   DMA_InitStructure.DMA_BufferSize = TXBUFFERSIZE;
   DMA_DeInit(I2Cx_DMA_STREAM_TX);
   DMA_Init(I2Cx_DMA_STREAM_TX, &DMA_InitStructure);
    
  /* I2C ENABLE */
  I2C_Cmd(I2Cx, ENABLE);
}


/**
  * @brief  Configure a SysTick Base time to 10 ms.
  * @param  None
  * @retval None
  */
static void SysTickConfig(void)
{
  /* SysTick end of count event each 10ms */
  RCC_GetClocksFreq(&RCC_Clocks);
  SysTick_Config(RCC_Clocks.HCLK_Frequency / 100);
  
  /* Configure the SysTick handler priority */
  NVIC_SetPriority(SysTick_IRQn, 0x0);
}


/**
  * @brief  Compares two buffers.
  * @param  pBuffer1, pBuffer2: buffers to be compared.
  * @param  BufferLength: buffer's length
  * @retval PASSED: pBuffer1 identical to pBuffer2
  *         FAILED: pBuffer1 differs from pBuffer2
  */




/**
  * @brief  Fills buffer.
  * @param  pBuffer: pointer on the Buffer to fill
  * @param  BufferLength: size of the buffer to fill
  * @retval None
  */
static void Fill_Buffer(uint8_t *pBuffer, uint16_t BufferLength)
{
  uint16_t index = 0;
  
  /* Put in global buffer same values */
  for (index = 0; index < BufferLength; index++ )
  {
    pBuffer[index] = 0x00;
  }
}


/**
  * @brief  Basic management of the timeout situation.
  * @param  None.
  * @retval None.
  */
static void TimeOut_UserCallback(void)
{
  /* User can add his own implementation to manage TimeOut Communication failure */
  /* Block communication and all processes */
  while (1)
  {   
  }
}


#ifdef  USE_FULL_ASSERT


/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* User can add his own implementation to report the file name and line number,
  ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  
  /* Infinite loop */
  while (1)
  {}
}
#endif


/**
  * @}
  */ 


/**
  * @}
  */ 



Outcomes