/**
  ******************************************************************************
  * @file    i2c_master_poll.c
  * @author  MCD Application Team
  * @version V0.0.3
  * @date    Oct 2010
  * @brief   This file contains optimized drivers for I2C master
  ******************************************************************************
  * @copy
  *
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2009 STMicroelectronics</center></h2>
  */ 

#include "i2c_master_poll.h"

/* Таймаут ожидания события I2C */
extern __IO uint8_t I2C_timeout;

/* flag clearing sequence - uncoment next for peripheral clock under 2MHz */
#define dead_time() { /* _asm("nop"); _asm("nop"); */ }
#define tout()            (I2C_timeout)
#define set_tout_ms(a)    { I2C_timeout = a; }

/******************************************************************************
* Function name : I2C_Init
* Description 	: Initialize I2C peripheral
* Input param 	: None
* Return 		    : None
* See also 		  : None
*******************************************************************************/
void I2C_Init(void) {
  //define SDA, SCL outputs, HiZ, Open drain, Fast
  GPIOB->ODR |= (GPIO_PIN_4 | GPIO_PIN_5);
  GPIOB->DDR |= (GPIO_PIN_4 | GPIO_PIN_5);
  GPIOB->CR2 |= (GPIO_PIN_4 | GPIO_PIN_5);

#ifdef FAST_I2C_MODE
  I2C->FREQR = 16;               // input clock to I2C - 16MHz 
  I2C->CCRL = 15;                // 900/62.5= 15, (SCLhi must be at least 600+300=900ns!)
  I2C->CCRH = 0x80;              // fast mode, duty 2/1 (bus speed 62.5*3*15~356kHz)
  I2C->TRISER = 5;               // 300/62.5 + 1= 5  (maximum 300ns)
#else
  I2C->FREQR = 8;                // input clock to I2C - 8MHz
  I2C->CCRL = 40;                // CCR= 40 - (SCLhi must be at least 4000+1000=5000ns!)
  I2C->CCRH = 0;                 // standard mode, duty 1/1 bus speed 100kHz
  I2C->TRISER = 9;               // 1000ns/(125ns) + 1  (maximum 1000ns)
#endif
  I2C->OARL = 0xA0;              // own address A0;
  I2C->OARH |= 0x40;
  I2C->ITR = 1;                  // enable error interrupts
  I2C->CR2 |= 0x04;              // ACK=1, Ack enable
  I2C->CR1 |= 0x01;              // PE=1
}

/******************************************************************************
* Function name : I2C_ReadRegister
* Description 	: Read defined number bytes from slave memory starting with defined offset
* Input param 	: offset in slave memory, number of bytes to read, starting address to store received data
* Return 		    : None
* See also 		  : None
*******************************************************************************/
void I2C_ReadRegister(u8 u8_regAddr, u8 u8_NumByteToRead, u8 *ReadBuffer)
{
  /*--------------- BUSY? -> STOP request ---------------------*/
	while(I2C->SR3 & I2C_SR3_BUSY && tout())	  				// Wait while the bus is busy
  {
		I2C->CR2 |= I2C_CR2_STOP;                   				// Generate stop here (STOP=1)
    while(I2C->CR2 & I2C_CR2_STOP && tout()); 				// Wait until stop is performed
	}
  I2C->CR2 |= I2C_CR2_ACK;                      				// ACK=1, Ack enable
  /*--------------- Start communication -----------------------*/  
  I2C->CR2 |= I2C_CR2_START;                    				// START=1, generate start
  while((I2C->SR1 & I2C_SR1_SB)==0 && tout());				// Wait for start bit detection (SB)
  /*------------------ Address send ---------------------------*/      
  if(tout())
  {
    I2C->DR = (u8)(SLAVE_ADDRESS << 1);   						// Send 7-bit device address & Write (R/W = 0)
  }
  while(!(I2C->SR1 & I2C_SR1_ADDR) &&  tout()); 				// test EV6 - wait for address ack (ADDR)
  dead_time();                                  				// ADDR clearing sequence
  I2C->SR3;
  /*--------------- Register/Command send ----------------------*/
  while(!(I2C->SR1 & I2C_SR1_TXE) &&  tout());  				// Wait for TxE
  if(tout())
  {  
    I2C->DR = u8_regAddr;                         			// Send register address
  }                                            					// Wait for TxE & BTF
  while((I2C->SR1 & (I2C_SR1_TXE | I2C_SR1_BTF)) != (I2C_SR1_TXE | I2C_SR1_BTF) && tout()); 
  dead_time();                                  				// clearing sequence
  /*-------------- Stop/Restart communication -------------------*/  
  #ifdef NO_RESTART																		// if 7bit address and NO_RESTART setted
    I2C->CR2 |= I2C_CR2_STOP;                     		// STOP=1, generate stop
    while(I2C->CR2 & I2C_CR2_STOP && tout());   		// wait until stop is performed
  #endif // NO_RESTART
  /*--------------- Restart communication ---------------------*/  
  I2C->CR2 |= I2C_CR2_START;                     				// START=1, generate re-start
  while((I2C->SR1 & I2C_SR1_SB)==0 && tout()); 				// Wait for start bit detection (SB)
  /*------------------ Address send ---------------------------*/      
  if(tout())
  {
    I2C->DR = (u8)(SLAVE_ADDRESS << 1) | 1;         	// Send 7-bit device address & Write (R/W = 1)
  }
  while(!(I2C->SR1 & I2C_SR1_ADDR) && tout());  			// Wait for address ack (ADDR)
  /*------------------- Data Receive --------------------------*/
  if (u8_NumByteToRead > 2)                 						// *** more than 2 bytes are received? ***
  {
    I2C->SR3;                                     			// ADDR clearing sequence    
    while(u8_NumByteToRead > 3 && tout())       			// not last three bytes?
    {
      while(!(I2C->SR1 & I2C_SR1_BTF) && tout()); 				// Wait for BTF
			*ReadBuffer++ = I2C->DR;                   				// Reading next data byte
      --u8_NumByteToRead;																		// Decrease Numbyte to reade by 1
    }
																												//last three bytes should be read
    while(!(I2C->SR1 & I2C_SR1_BTF) && tout()); 			// Wait for BTF
    I2C->CR2 &=~I2C_CR2_ACK;                      			// Clear ACK
    disableInterrupts();                          			// Errata workaround (Disable interrupt)
    *ReadBuffer++ = I2C->DR;                     		// Read 1st byte
    I2C->CR2 |= I2C_CR2_STOP;                       		// Generate stop here (STOP=1)
    *ReadBuffer++ = I2C->DR;                     		// Read 2nd byte
    enableInterrupts();																	// Errata workaround (Enable interrupt)
    while(!(I2C->SR1 & I2C_SR1_RXNE) && tout());			// Wait for RXNE
    *ReadBuffer++ = I2C->DR;                   			// Read 3rd Data byte
  }
  else
  {
   if(u8_NumByteToRead == 2)                						// *** just two bytes are received? ***
    {
      I2C->CR2 |= I2C_CR2_POS;                      		// Set POS bit (NACK at next received byte)
      disableInterrupts();                          		// Errata workaround (Disable interrupt)
      I2C->SR3;                                       	// Clear ADDR Flag
      I2C->CR2 &=~I2C_CR2_ACK;                        	// Clear ACK 
      enableInterrupts();																// Errata workaround (Enable interrupt)
      while(!(I2C->SR1 & I2C_SR1_BTF) && tout()); 		// Wait for BTF
      disableInterrupts();                          		// Errata workaround (Disable interrupt)
      I2C->CR2 |= I2C_CR2_STOP;                       	// Generate stop here (STOP=1)
      *ReadBuffer++ = I2C->DR;                     	// Read 1st Data byte
      enableInterrupts();																// Errata workaround (Enable interrupt)
			*ReadBuffer = I2C->DR;													// Read 2nd Data byte
    }
    else                                      					// *** only one byte is received ***
    {
      I2C->CR2 &=~I2C_CR2_ACK;;                     		// Clear ACK 
      disableInterrupts();                          		// Errata workaround (Disable interrupt)
      I2C->SR3;                                       	// Clear ADDR Flag   
      I2C->CR2 |= I2C_CR2_STOP;                       	// generate stop here (STOP=1)
      enableInterrupts();																// Errata workaround (Enable interrupt)
      while(!(I2C->SR1 & I2C_SR1_RXNE) && tout()); 		// test EV7, wait for RxNE
      *ReadBuffer = I2C->DR;                     		// Read Data byte
    }
  }
  /*--------------- All Data Received -----------------------*/
  while((I2C->CR2 & I2C_CR2_STOP) && tout());     		// Wait until stop is performed (STOPF = 1)
  I2C->CR2 &=~I2C_CR2_POS;                          		// return POS to default state (POS=0)
}

/******************************************************************************
* Function name : I2C_WriteRegister
* Description 	: write defined number bytes to slave memory starting with defined offset
* Input param 	: offset in slave memory, number of bytes to write, starting address to send
* Return 		    : None.
* See also 		  : None.
*******************************************************************************/
void I2C_WriteRegister(u8 u8_regAddr, u8 u8_NumByteToWrite, u8 *ReadBuffer)
{
  while((I2C->SR3 & 2) && tout())       									// Wait while the bus is busy
  {
    I2C->CR2 |= 2;                        								// STOP=1, generate stop
    while((I2C->CR2 & 2) && tout());      								// wait until stop is performed
  }
  
  I2C->CR2 |= 1;                        									// START=1, generate start
  while(((I2C->SR1 & 1)==0) && tout()); 									// Wait for start bit detection (SB)
  dead_time();                          									// SB clearing sequence
  if(tout())
  {
    I2C->DR = (u8)(SLAVE_ADDRESS << 1);   							// Send 7-bit device address & Write (R/W = 0)
  }
  while(!(I2C->SR1 & 2) && tout());     									// Wait for address ack (ADDR)
  dead_time();                          									// ADDR clearing sequence
  I2C->SR3;
  while(!(I2C->SR1 & 0x80) && tout());  									// Wait for TxE
  if(tout())
  {
    I2C->DR = u8_regAddr;                 								// send Offset command
  }
  if(u8_NumByteToWrite)
  {
    while(u8_NumByteToWrite--)          									
    {																											// write data loop start
      while(!(I2C->SR1 & 0x80) && tout());  								// test EV8 - wait for TxE
      I2C->DR = *ReadBuffer++;           								// send next data byte
    }																											// write data loop end
  }
  while(((I2C->SR1 & 0x84) != 0x84) && tout()); 					// Wait for TxE & BTF
  dead_time();                          									// clearing sequence
  
  I2C->CR2 |= 2;                        									// generate stop here (STOP=1)
  while((I2C->CR2 & 2) && tout());      									// wait until stop is performed  
}

/**
 * @brief 
 * 
 * @param NumByteToRead 
 * @param ReadBuffer 
 */
void I2C_ReadBytes(const u8 Addr, const u8 NumByteToRead, u8 *ReadBuffer) {
  u8 adr = Addr << 1;
  u8 n = NumByteToRead;
  set_tout_ms(10);

  /*--------------- BUSY? -> STOP request ---------------------*/
	while(I2C->SR3 & I2C_SR3_BUSY && tout())	  			// Wait while the bus is busy
  {
		I2C->CR2 |= I2C_CR2_STOP;                   		// Generate stop here (STOP=1)
    while(I2C->CR2 & I2C_CR2_STOP && tout()); 			// Wait until stop is performed
	}
  I2C->CR2 |= I2C_CR2_ACK;                      		// ACK=1, Ack enable
  /*--------------- Start communication -----------------------*/  
  I2C->CR2 |= I2C_CR2_START;                    		// START=1, generate start
  while((I2C->SR1 & I2C_SR1_SB)==0 && tout());			// Wait for start bit detection (SB)
  /*------------------ Address send ---------------------------*/      
  if(tout())
  {
    I2C->DR = (adr | 1);         	                  // Send 7-bit device address & Write (R/W = 1)
  }
  while(!(I2C->SR1 & I2C_SR1_ADDR) && tout());  		// Wait for address ack (ADDR)
  /*------------------- Data Receive --------------------------*/
  if (n > 2)                 						            // *** more than 2 bytes are received? ***
  {
    I2C->SR3;                                     	// ADDR clearing sequence    
    while(n > 3 && tout())       			              // not last three bytes?
    {
      while(!(I2C->SR1 & I2C_SR1_BTF) && tout()); 	// Wait for BTF
			*ReadBuffer++ = I2C->DR;                   		// Reading next data byte
      --n;																		      // Decrease Numbyte to reade by 1
    }
																										//last three bytes should be read
    while(!(I2C->SR1 & I2C_SR1_BTF) && tout()); 		// Wait for BTF
    I2C->CR2 &=~I2C_CR2_ACK;                      	// Clear ACK
    disableInterrupts();                          	// Errata workaround (Disable interrupt)
    *ReadBuffer++ = I2C->DR;                     		// Read 1st byte
    I2C->CR2 |= I2C_CR2_STOP;                       // Generate stop here (STOP=1)
    *ReadBuffer++ = I2C->DR;                     		// Read 2nd byte
    enableInterrupts();															// Errata workaround (Enable interrupt)
    while(!(I2C->SR1 & I2C_SR1_RXNE) && tout());		// Wait for RXNE
    *ReadBuffer++ = I2C->DR;                   			// Read 3rd Data byte
  }
  else
  {
   if(n == 2)                						            // *** just two bytes are received? ***
    {
      I2C->CR2 |= I2C_CR2_POS;                      // Set POS bit (NACK at next received byte)
      disableInterrupts();                          // Errata workaround (Disable interrupt)
      I2C->SR3;                                     // Clear ADDR Flag
      I2C->CR2 &=~I2C_CR2_ACK;                      // Clear ACK 
      enableInterrupts();														// Errata workaround (Enable interrupt)
      while(!(I2C->SR1 & I2C_SR1_BTF) && tout()); 	// Wait for BTF
      disableInterrupts();                          // Errata workaround (Disable interrupt)
      I2C->CR2 |= I2C_CR2_STOP;                     // Generate stop here (STOP=1)
      *ReadBuffer++ = I2C->DR;                     	// Read 1st Data byte
      enableInterrupts();														// Errata workaround (Enable interrupt)
			*ReadBuffer = I2C->DR;												// Read 2nd Data byte
    }
    else                                      			// *** only one byte is received ***
    {
      I2C->CR2 &=~I2C_CR2_ACK;;                     // Clear ACK 
      disableInterrupts();                          // Errata workaround (Disable interrupt)
      I2C->SR3;                                     // Clear ADDR Flag   
      I2C->CR2 |= I2C_CR2_STOP;                     // generate stop here (STOP=1)
      enableInterrupts();														// Errata workaround (Enable interrupt)
      while(!(I2C->SR1 & I2C_SR1_RXNE) && tout());  // test EV7, wait for RxNE
      *ReadBuffer = I2C->DR;                     		// Read Data byte
    }
  }
  /*--------------- All Data Received -----------------------*/
  while((I2C->CR2 & I2C_CR2_STOP) && tout());     	// Wait until stop is performed (STOPF = 1)
  I2C->CR2 &= ~I2C_CR2_POS;                         // return POS to default state (POS=0)
}
/**
 * @brief write defined number bytes to slave device
 * 
 * @param Addr 
 * @param NumByteToWrite 
 * @param DataBuffer 
 */
void I2C_WriteBytes(const u8 Addr, const u8 NumByteToWrite, u8 *DataBuffer) {
  u8 adr = Addr << 1;
  u8 n = NumByteToWrite;
  set_tout_ms(10);

  while((I2C->SR3 & 2) && tout())       				// Wait while the bus is busy
  {
    I2C->CR2 |= 2;                        			// STOP=1, generate stop
    while((I2C->CR2 & 2) && tout());      			// wait until stop is performed
  }
  
  I2C->CR2 |= 1;                        				// START=1, generate start
  while(((I2C->SR1 & 1)==0) && tout()); 				// Wait for start bit detection (SB)
  dead_time();                          				// SB clearing sequence
  if(tout())
  {
    I2C->DR = adr;   							              // Send 7-bit device address & Write (R/W = 0)
  }
  while(!(I2C->SR1 & 2) && tout());     				// Wait for address ack (ADDR)
  dead_time();                          				// ADDR clearing sequence
  I2C->SR3;
  if(n)
  {
    while(n--)          									
    {																						// write data loop start
      while(!(I2C->SR1 & 0x80) && tout());  		// test EV8 - wait for TxE
      I2C->DR = *DataBuffer++;           				// send next data byte
    }																						// write data loop end
  }
  while(((I2C->SR1 & 0x84) != 0x84) && tout()); // Wait for TxE & BTF
  dead_time();                          				// clearing sequence
  
  I2C->CR2 |= 2;                        				// generate stop here (STOP=1)
  while((I2C->CR2 & 2) && tout());      				// wait until stop is performed  
}
