803 lines
17 KiB
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;
|
|
} |