2016年3月2日 星期三

STM32F103 I2C Master Driver

話不說多, 直接上code.

I2C_Master.h

#include "stm32f10x.h"

void I2C_Master_Init(void);
void I2C_Master_DeInit(void);
int I2C_Master_Read(uint8_t deviceAddr, uint8_t readAddr, uint8_t* pBuffer, uint16_t numByteToRead);
int I2C_Master_Write(uint8_t deviceAddress, uint8_t WriteAddr, uint8_t* pBuffer, uint16_t numByteToWrite);
int I2C_TIMEOUT_UserCallback(void);


I2C_Master.c

#include "I2C_Master.h"

#define I2C_MEMS                I2C1
#define I2C_MEMS_CLK            RCC_APB1Periph_I2C1
#define I2C_MEMS_GPIO           GPIOB
#define I2C_MEMS_GPIO_CLK       RCC_APB2Periph_GPIOB
#define I2C_MEMS_SCL            GPIO_Pin_6
#define I2C_MEMS_SDA            GPIO_Pin_7

#define I2C_Speed               400000
#define I2C_SLAVE_ADDRESS7      0xA0
#define I2C_TIMEOUT             3000

/* I2C STOP mask */
#define CR1_STOP_Set            ((uint16_t)0x0200)
#define CR1_STOP_Reset          ((uint16_t)0xFDFF)

/* I2C ACK mask */
#define CR1_ACK_Set             ((uint16_t)0x0400)
#define CR1_ACK_Reset           ((uint16_t)0xFBFF)

/* I2C POS mask */
#define CR1_POS_Set             ((uint16_t)0x0800)
#define CR1_POS_Reset           ((uint16_t)0xF7FF)

#define NULL ((void *)0)


void I2C_Master_Init(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure; 
  I2C_InitTypeDef  I2C_InitStructure; 
  
  /* I2C Periph clock enable */
  RCC_APB1PeriphClockCmd(I2C_MEMS_CLK, ENABLE);   
  
  /* GPIO Periph clock enable */
  RCC_APB2PeriphClockCmd(I2C_MEMS_GPIO_CLK, ENABLE);    
  
  /* GPIO configuration */
  GPIO_InitStructure.GPIO_Pin =  I2C_MEMS_SCL | I2C_MEMS_SDA; 
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  GPIO_Init(I2C_MEMS_GPIO, &GPIO_InitStructure);  
  
   /* I2C configuration */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C_InitStructure.I2C_OwnAddress1 = I2C_SLAVE_ADDRESS7;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
  
  /* I2C Peripheral Enable */
  I2C_Cmd(I2C_MEMS, ENABLE);
  /* Apply I2C configuration after enabling it */
  I2C_Init(I2C_MEMS, &I2C_InitStructure);
}

void I2C_Master_DeInit(void)
{
  GPIO_InitTypeDef  GPIO_InitStructure;  
    
  /* UnConfigure I2C */
  I2C_DeInit(I2C_MEMS);
  I2C_Cmd(I2C_MEMS, DISABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, DISABLE);
           
  /* UnConfigure I2C_MEMS pins: SCL and SDA */
  GPIO_InitStructure.GPIO_Pin = I2C_MEMS_SCL | I2C_MEMS_SDA; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  GPIO_Init(I2C_MEMS_GPIO, &GPIO_InitStructure);  
  
}


int I2C_Master_Read(uint8_t deviceAddr, uint8_t readAddr, uint8_t* pBuffer, uint16_t numByteToRead) {
  
  __IO uint32_t temp = 0;
  volatile int I2C_TimeOut = 0;
  
  // /* While the bus is busy * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(I2C_GetFlagStatus(I2C_MEMS, I2C_FLAG_BUSY))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }
  
  
  // * Send START condition * /
  I2C_GenerateSTART(I2C_MEMS, ENABLE);

  // / * Test on EV5 and clear it * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_MODE_SELECT))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }
  
  // / * Send EEPROM address for write  * /
  I2C_Send7bitAddress(I2C_MEMS, deviceAddr, I2C_Direction_Transmitter);

  // / * Test on EV6 and clear it * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }
  
  // / * Send the EEPROM's internal address to read from: Only one byte address  * /
  I2C_SendData(I2C_MEMS, readAddr);  

  /// * Test on EV8 and clear it * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }  
    
  /// * Send STRAT condition a second time * /  
  I2C_GenerateSTART(I2C_MEMS, ENABLE);

  /// * Test on EV5 and clear it * /
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_MODE_SELECT))
  {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
  }  
  
    // * Send EEPROM address for read * /
  I2C_Send7bitAddress(I2C_MEMS, deviceAddr, I2C_Direction_Receiver);  

  if (numByteToRead == 1)  {
    /* Wait until ADDR is set */
    I2C_TimeOut = I2C_TIMEOUT;
    while ((I2C_MEMS->SR1&0x0002) != 0x0002)
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }    
   /* Clear ACK bit */
    I2C_MEMS->CR1 &= CR1_ACK_Reset;
    /* Disable all active IRQs around ADDR clearing and STOP programming because the EV6_3
    software sequence must complete before the current byte end of transfer */
    __disable_irq();
    /* Clear ADDR flag */
    temp = I2C_MEMS->SR2;
    /* Program the STOP */
    I2C_GenerateSTOP(I2C_MEMS, ENABLE);
    /* Re-enable IRQs */
    __enable_irq();
    /* Wait until a data is received in DR register (RXNE = 1) EV7 */
    I2C_TimeOut = I2C_TIMEOUT;
    while ((I2C_MEMS->SR1 & 0x00040) != 0x000040)
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }    
    /* Read the data */
      *pBuffer = I2C_MEMS->DR;
  
  }
  else if (numByteToRead == 2) {
                  
    /* Set POS bit */
    I2C_MEMS->CR1 |= CR1_POS_Set;
    /* Wait until ADDR is set: EV6 */
    I2C_TimeOut = I2C_TIMEOUT;
    while ((I2C_MEMS->SR1&0x0002) != 0x0002)
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }      
    /* EV6_1: The acknowledge disable should be done just after EV6,
    that is after ADDR is cleared, so disable all active IRQs around ADDR clearing and 
    ACK clearing */
    __disable_irq();
    /* Clear ADDR by reading SR2 register  */
    temp = I2C_MEMS->SR2;
    /* Clear ACK */
    I2C_MEMS->CR1 &= CR1_ACK_Reset;
    /*Re-enable IRQs */
    __enable_irq();
    /* Wait until BTF is set */
    I2C_TimeOut = I2C_TIMEOUT;
    while ((I2C_MEMS->SR1 & 0x00004) != 0x000004)
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }        
    /* Disable IRQs around STOP programming and data reading */
    __disable_irq();
    /* Program the STOP */
    I2C_GenerateSTOP(I2C_MEMS, ENABLE);
    /* Read first data */
    *pBuffer = I2C_MEMS->DR;
    /* Re-enable IRQs */
    __enable_irq();
    /**/
    pBuffer++;
    /* Read second data */
    *pBuffer = I2C_MEMS->DR;
    /* Clear POS bit */
    I2C_MEMS->CR1  &= CR1_POS_Reset;
  }
  

  else { //numByteToRead > 2 
    // * Test on EV6 and clear it * /
    I2C_TimeOut = I2C_TIMEOUT;
    while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
 if (I2C_TimeOut-- <= 0){
  return(I2C_TIMEOUT_UserCallback());
 }
    }         
    // * While there is data to be read * /
    while(numByteToRead)   {
      /* Receive bytes from first byte until byte N-3 */
      if (numByteToRead != 3) {
        /* Poll on BTF to receive data because in polling mode we can not guarantee the
        EV7 software sequence is managed before the current byte transfer completes */
        I2C_TimeOut = I2C_TIMEOUT;
        while ((I2C_MEMS->SR1 & 0x00004) != 0x000004)
        {
            if (I2C_TimeOut-- <= 0){
                    return(I2C_TIMEOUT_UserCallback());
            }
        }           
        /* Read data */
        *pBuffer = I2C_MEMS->DR;
         pBuffer++;
        /* Decrement the read bytes counter */
        numByteToRead--;
      }
      
      /* it remains to read three data: data N-2, data N-1, Data N */
      if (numByteToRead == 3) {
        /* Wait until BTF is set: Data N-2 in DR and data N -1 in shift register */
        I2C_TimeOut = I2C_TIMEOUT;
        while ((I2C_MEMS->SR1 & 0x00004) != 0x000004)
        {
            if (I2C_TimeOut-- <= 0){
                    return(I2C_TIMEOUT_UserCallback());
            }
        }            
        /* Clear ACK */
        I2C_MEMS->CR1 &= CR1_ACK_Reset;
    
        /* Disable IRQs around data reading and STOP programming */
        __disable_irq();
        /* Read Data N-2 */
        *pBuffer = I2C_MEMS->DR;
        /* Increment */
        pBuffer++;
        /* Program the STOP */
        I2C_MEMS->CR1 |= CR1_STOP_Set;
        /* Read DataN-1 */
        *pBuffer = I2C_MEMS->DR;
        /* Re-enable IRQs */
        __enable_irq();
        /* Increment */
        pBuffer++;
        /* Wait until RXNE is set (DR contains the last data) */
        I2C_TimeOut = I2C_TIMEOUT;
        while ((I2C_MEMS->SR1 & 0x00040) != 0x000040)
        {
            if (I2C_TimeOut-- <= 0){
                    return(I2C_TIMEOUT_UserCallback());
            }
        }           
        /* Read DataN */
        *pBuffer = I2C_MEMS->DR;
        /* Reset the number of bytes to be read by master */
        numByteToRead = 0;
      }
    }
  }
 
  /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
  I2C_TimeOut = I2C_TIMEOUT;
  while ((I2C_MEMS->CR1&0x200) == 0x200)
  {
            if (I2C_TimeOut-- <= 0){
                    return(I2C_TIMEOUT_UserCallback());
            }
  }             
  
  // * Enable Acknowledgement to be ready for another reception * /
  I2C_AcknowledgeConfig(I2C_MEMS, ENABLE);
  
  return 0;
    
}

int I2C_Master_Write(uint8_t deviceAddress, uint8_t WriteAddr, uint8_t* pBuffer, uint16_t numByteToWrite) {    
  
  volatile int I2C_TimeOut = 0;  
  
  /* Send STRAT condition */
  I2C_GenerateSTART(I2C_MEMS, ENABLE);

  /* Test on EV5 and clear it */
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if (I2C_TimeOut-- <= 0){
            return(I2C_TIMEOUT_UserCallback());
    }
  }       

  /* Send EEPROM address for write */
  I2C_Send7bitAddress(I2C_MEMS, deviceAddress, I2C_Direction_Transmitter);
  
  /* Test on EV6 and clear it */
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    if (I2C_TimeOut-- <= 0){
            return(I2C_TIMEOUT_UserCallback());
    }
  }        
 
  /* Send the EEPROM's internal address to write to : only one byte Address */
  I2C_SendData(I2C_MEMS, WriteAddr); 
  
  /* Test on EV8 and clear it */
  I2C_TimeOut = I2C_TIMEOUT;
  while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
  {
    if (I2C_TimeOut-- <= 0){
            return(I2C_TIMEOUT_UserCallback());
    }
  }        
 
  while(numByteToWrite > 0) {
    /* Send the byte to be written */
    I2C_SendData(I2C_MEMS, *pBuffer); 
   
    /* Test on EV8 and clear it */
    I2C_TimeOut = I2C_TIMEOUT;
    while(!I2C_CheckEvent(I2C_MEMS, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
      if (I2C_TimeOut-- <= 0){
            return(I2C_TIMEOUT_UserCallback());
      }
    }     
  
    pBuffer++;
    numByteToWrite--;
  }
  
  /* Send STOP condition */
  I2C_GenerateSTOP(I2C_MEMS, ENABLE);
  
  return 0;
}

int I2C_TIMEOUT_UserCallback(void)
{
  /* User can add his own implementation to manage TimeOut Communication failure */
  /* Block communication and all processes */
  I2C_Master_DeInit();
  for(int i=0; i<3000; i++){__asm("nop");}
  I2C_Master_Init();
  for(int i=0; i<3000; i++){__asm("nop");}
  return -1;
}


沒有留言:

張貼留言