stm32_freertos_template/hw/i2c.c

803 lines
17 KiB
C

/*! \file i2c.c
\author Tomasz Adamczyk
\date 2011.12.02
\version 1.0
\brief Obsługa magistrali I2C
*/
#include "i2c.h"
#include "../error.h"
#include "../spdif.h"
#include "../dac.h"
#include "../eeprom.h"
#include "gpio.h"
#include "stm32f10x.h"
#ifdef USE_RTOS
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#endif
#include <stdint.h>
//--------------------------Zmienne zewnętrzne------------------------//
#ifdef USE_RTOS
#define TICK xTaskGetTickCount()
#else
extern uint32_t tick; // z systick.c
#define TICK tick
#endif
//--------------------------Zmienne globalne------------------------//
#ifdef USE_RTOS
xSemaphoreHandle I2C_Semaphore;
static uint8_t semaphore_created;
#endif
uint32_t num_i2c_errors;
uint32_t num_i2c_spdif_errors;
//-----------------------------Deklaracje funkcji prywatnych------------------------------//
void RecoverFromBusError(I2C_TypeDef *port, uint8_t addr);
uint8_t CheckBusState();
//-----------------------------Funkcje------------------------------//
/*! \brief Inicjalizacja magistrali I2C
Funkcja inicjalizuję magistralę I2C. Zegar 50kHz, adresowanie 7bit.
\param port którego portu I2C uzywamy
\return kod błędu
*/
int8_t I2C_Initialize(I2C_TypeDef *port)
{
GPIO_InitTypeDef GPIO_Conf;
I2C_InitTypeDef I2C_Conf;
//Zresetuj I2C, może jest zawieszone
I2C_Cmd(port, DISABLE);
I2C_SoftwareResetCmd(port, ENABLE);
I2C_SoftwareResetCmd(port, DISABLE);
//konifiguracja zegarów i linii I/O
switch((uint32_t)port)
{
case (uint32_t)I2C1:
#ifdef USE_I2C1
#ifdef USE_RTOS
if(!semaphore_created)
{
vSemaphoreCreateBinary(I2C_Semaphore);
semaphore_created=1;
}
#endif
/* Zegar GPIOB,I2C1 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
//SDA,SCL - wyjście per
GPIO_Conf.GPIO_Pin=SDA|SCL;
GPIO_Conf.GPIO_Mode=GPIO_Mode_AF_OD;
GPIO_Conf.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_Conf);
#else
return -NO_PORT;
#endif
break;
case (uint32_t)I2C2:
#ifdef USE_I2C2
#ifdef USE_RTOS
if(!semaphore_created)
{
vSemaphoreCreateBinary(I2C_Semaphore);
semaphore_created=1;
}
#endif
/* Zegar GPIOB,I2C1 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
//SDA,SCL - wyjście per
GPIO_Conf.GPIO_Pin=SDA2|SCL2;
GPIO_Conf.GPIO_Mode=GPIO_Mode_AF_OD;
GPIO_Conf.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_Conf);
#else
return -NO_PORT;
#endif
break;
}
//konfiguracja kontrolera I2C
I2C_Conf.I2C_Ack=I2C_Ack_Enable;
I2C_Conf.I2C_AcknowledgedAddress=I2C_AcknowledgedAddress_7bit;
I2C_Conf.I2C_ClockSpeed=100000;
I2C_Conf.I2C_DutyCycle=I2C_DutyCycle_2;
I2C_Conf.I2C_Mode=I2C_Mode_I2C;
I2C_Conf.I2C_OwnAddress1=0;
I2C_Init(port, &I2C_Conf);
//Włącz I2C
I2C_Cmd(port, ENABLE);
}
/*!
\brief Odczyt z urządzenia I2C
Funkcja odczytuje dane z urządzenia I2C i kopiuje do bufora użytkownika
\param port którego portu I2C uzywamy
\param addr adres urządzenia z którego czytamy
\param data wskaznik do bufora użytkownika
\param size ilość bajtów do odczytania
\return kod błędu
*/
int16_t I2C_Read(I2C_TypeDef *port, uint8_t addr, uint8_t *data, uint8_t size)
{
uint32_t timestamp,k;
uint8_t i,j;
#ifdef USE_RTOS
xSemaphoreTake(I2C_Semaphore, portMAX_DELAY);
#endif
//Wysyłamy ACK po odebraniu bajtu (z wyjątkiem ostatniego)
port->CR1|=I2C_CR1_ACK;
//START
port->CR1|=I2C_CR1_START;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_SB) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//adres + read
port->DR=(addr<<1)|0x01;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_ADDR) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
//Nack po wysłaniu adresu. Brak urządzenia?
if(port->SR1&I2C_SR1_AF)
{
//STOP
port->CR1 |= I2C_CR1_STOP;
//wyczyść NACK (trzeba ręcznie)
port->SR1 &= ~I2C_SR1_AF;
//zwróć błąd
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return -I2C_SLAVE_NACK;
}
}
//sprawdź czy jestesmy w trybie odbiornika (głównie po to żeby odczytać SR2, co podobno jest konieczne)
if( (port->SR2&I2C_SR2_TRA) )
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -I2C_BUS_ERROR;
}
//odbierz dane bajt po bajcie
for(i=0;i<size;i++)
{
// jezeli to ostatni bajt, to NACK+STOP
if( i==(size-1) )
{
port->CR1 &= ~I2C_CR1_ACK;
port->CR1 |= I2C_CR1_STOP;
}
//czekaj na bajt danych
timestamp=TICK;
while( !(port->SR1&I2C_SR1_RXNE) )
{
if(TICK-timestamp>2)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_SET);
for(j=0;j<10;j++);
GPIO_WriteBit(GPIOA,GPIO_Pin_1,Bit_RESET);
for(j=0;j<10;j++);
}
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//odbierz dane
*data++=port->DR;
}
//transmisja udana, zwroc ilość wysłanych bajtów
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return size;
}
/*!
\brief Odczyt z urządzenia I2C z wewnętrznym adresowaniem
Funkcja odczytuje dane z urządzenia I2C i kopiuje do bufora użytkownika
\param port którego portu I2C uzywamy
\param addr adres urządzenia z którego czytamy
\param iadr adres wewnętrzny w urzadzeniu
\param data wskaznik do bufora użytkownika
\param size ilość bajtów do odczytania
\return kod błędu
*/
int16_t I2C_ReadIadr(I2C_TypeDef *port, uint8_t addr, uint8_t iadr, uint8_t *data, uint8_t size)
{
uint32_t timestamp,k;
uint8_t i;
#ifdef USE_RTOS
xSemaphoreTake(I2C_Semaphore, portMAX_DELAY);
#endif
//Wysyłamy ACK po odebraniu bajtu (z wyjątkiem ostatniego)
port->CR1|=I2C_CR1_ACK;
//START
port->CR1|=I2C_CR1_START;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_SB) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//adres + write
port->DR=addr<<1;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_ADDR) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
//Nack po wysłaniu adresu. Brak urządzenia?
if(port->SR1&I2C_SR1_AF)
{
//STOP
port->CR1 |= I2C_CR1_STOP;
//wyczyść NACK (trzeba ręcznie)
port->SR1 &= ~I2C_SR1_AF;
//zwróć błąd
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return -I2C_SLAVE_NACK;
}
}
//sprawdź czy jestesmy w trybie nadajnika (głównie po to żeby odczytać SR2, co podobno jest konieczne)
if( !(port->SR2&I2C_SR2_TRA) )
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -I2C_BUS_ERROR;
}
//czy rejestr nadawczy jest pusty?
timestamp=TICK;
while( !(port->SR1&I2C_SR1_TXE) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//adres wewnętrzny
port->DR=iadr;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_TXE) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
//Nack po wysłaniu adresu wewnętrznego. Brak urządzenia?
if(port->SR1&I2C_SR1_AF)
{
//STOP
port->CR1 |= I2C_CR1_STOP;
//wyczyść NACK (trzeba ręcznie)
port->SR1 &= ~I2C_SR1_AF;
//zwróć błąd
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return -I2C_SLAVE_NACK;
}
}
//REPEATED START
port->CR1|=I2C_CR1_START;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_SB) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//adres + read
port->DR=(addr<<1)|0x01;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_ADDR) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
//Nack po wysłaniu adresu. Brak urządzenia?
if(port->SR1&I2C_SR1_AF)
{
//STOP
port->CR1 |= I2C_CR1_STOP;
//wyczyść NACK (trzeba ręcznie)
port->SR1 &= ~I2C_SR1_AF;
//zwróć błąd
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return -I2C_SLAVE_NACK;
}
}
//sprawdź czy jestesmy w trybie odbiornika (głównie po to żeby odczytać SR2, co podobno jest konieczne)
if( (port->SR2&I2C_SR2_TRA) )
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -I2C_BUS_ERROR;
}
//odbierz dane bajt po bajcie
for(i=0;i<size;i++)
{
// jezeli to ostatni bajt, to NACK+STOP
if( i==(size-1) )
{
port->CR1 &= ~I2C_CR1_ACK;
port->CR1 |= I2C_CR1_STOP;
}
//czekaj na bajt danych
timestamp=TICK;
while( !(port->SR1&I2C_SR1_RXNE) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//odbierz dane
*data++=port->DR;
}
//transmisja udana, zwroc ilość wysłanych bajtów
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return size;
}
/*!
\brief Zapis do urządzenia I2C
Funkcja wysyła dane z bufora do urządzenia I2C
\param port którego portu I2C uzywamy
\param addr adres urządzenia do którego nadajemy
\param data wskaznik do bufora użytkownika
\param size ilość bajtów do wysłania
\return kod błędu
*/
int16_t I2C_Write(I2C_TypeDef *port, uint8_t addr, uint8_t *data, uint8_t size)
{
uint32_t timestamp;
uint8_t i;
#ifdef USE_RTOS
xSemaphoreTake(I2C_Semaphore, portMAX_DELAY);
#endif
//START
port->CR1|=I2C_CR1_START;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_SB) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//adres + write
port->DR=(addr<<1);
timestamp=TICK;
while( !(port->SR1&I2C_SR1_ADDR) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
//Nack po wysłaniu adresu. Brak urządzenia?
if(port->SR1&I2C_SR1_AF)
{
//STOP
port->CR1 |= I2C_CR1_STOP;
//wyczyść NACK (trzeba ręcznie)
port->SR1 &= ~I2C_SR1_AF;
//zwróć błąd
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return -I2C_SLAVE_NACK;
}
}
//sprawdź czy jestesmy w trybie nadajnika (głównie po to żeby odczytać SR2, co podobno jest konieczne)
if( !(port->SR2&I2C_SR2_TRA) )
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -I2C_BUS_ERROR;
}
//czy rejestr nadawczy jest pusty?
timestamp=TICK;
while( !(port->SR1&I2C_SR1_TXE) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//nadaj dane bajt po bajcie
for(i=0;i<size;i++)
{
//wyslij dane
port->DR=*data++;
timestamp=TICK;
//czekaj na opróżnienie bufora nadawczego
while( !(port->SR1&I2C_SR1_TXE) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
}
//czekaj na koniec transmisji
timestamp=TICK;
while( !(port->SR1&I2C_SR1_BTF) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//STOP
port->CR1|=I2C_CR1_STOP;
//czy urządzenie odebralo dane?
if(port->SR1&I2C_SR1_AF)
return -I2C_SLAVE_NACK;
//transmisja udana, zwroc ilość wysłanych bajtów
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return size;
}
/*!
\brief Zapis do urządzenia I2C z wewnętrznym adresowaniem
Funkcja wysyła dane z bufora do urządzenia I2C
\param port którego portu I2C uzywamy
\param addr adres urządzenia do którego nadajemy
\param iadr adres wewnętrzny w urządzeniu
\param data wskaznik do bufora użytkownika
\param size ilość bajtów do wysłania
\return kod błędu
*/
int16_t I2C_WriteIadr(I2C_TypeDef *port, uint8_t addr, uint8_t iadr, uint8_t *data, uint8_t size)
{
uint32_t timestamp;
uint8_t i;
#ifdef USE_RTOS
xSemaphoreTake(I2C_Semaphore, portMAX_DELAY);
#endif
//START
port->CR1|=I2C_CR1_START;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_SB) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//adres + write
port->DR=(addr<<1);
timestamp=TICK;
while( !(port->SR1&I2C_SR1_ADDR) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
//Nack po wysłaniu adresu. Brak urządzenia?
if(port->SR1&I2C_SR1_AF)
{
//STOP
port->CR1 |= I2C_CR1_STOP;
//wyczyść NACK (trzeba ręcznie)
port->SR1 &= ~I2C_SR1_AF;
//zwróć błąd
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return -I2C_SLAVE_NACK;
}
}
//sprawdź czy jestesmy w trybie nadajnika (głównie po to żeby odczytać SR2, co podobno jest konieczne)
if( !(port->SR2&I2C_SR2_TRA) )
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -I2C_BUS_ERROR;
}
//czy rejestr nadawczy jest pusty?
timestamp=TICK;
while( !(port->SR1&I2C_SR1_TXE) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//adres wewnętrzny
port->DR=iadr;
timestamp=TICK;
while( !(port->SR1&I2C_SR1_TXE) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
//Nack po wysłaniu adresu wewnętrznego. Brak urządzenia?
if(port->SR1&I2C_SR1_AF)
{
//STOP
port->CR1 |= I2C_CR1_STOP;
//wyczyść NACK (trzeba ręcznie)
port->SR1 &= ~I2C_SR1_AF;
//zwróć błąd
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return -I2C_SLAVE_NACK;
}
}
//nadaj dane bajt po bajcie
for(i=0;i<size;i++)
{
//wyslij dane
port->DR=*data++;
timestamp=TICK;
//czekaj na opróżnienie bufora nadawczego
while( !(port->SR1&I2C_SR1_TXE) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
}
//czekaj na koniec transmisji
timestamp=TICK;
while( !(port->SR1&I2C_SR1_BTF) )
{
if(TICK-timestamp>I2C_WAIT_TIME)
{
//resetuj I2C
RecoverFromBusError(port,addr);
#ifdef USE_RTOS
taskYIELD();
#endif
return -TIMEOUT;
}
}
//STOP
port->CR1|=I2C_CR1_STOP;
//czy urządzenie odebralo dane?
if(port->SR1&I2C_SR1_AF)
return -I2C_SLAVE_NACK;
//transmisja udana, zwroc ilość wysłanych bajtów
#ifdef USE_RTOS
xSemaphoreGive(I2C_Semaphore);
taskYIELD();
#endif
return size;
}
uint8_t CheckBusState()
{
if ( (GPIO_Read(GPIOB)&SDA) && (GPIO_Read(GPIOB)&SCL) )
return 1;
else
return 0;
}