/*! \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 //--------------------------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;iCR1 &= ~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;iCR1 &= ~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;iDR=*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;iDR=*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; }