Strona 1 z 1

STM32F103 problem z odpaleniem I2C

: niedziela 03 lip 2016, 00:27
autor: squeez
Męczę się już cały dzień :)
Chciałbym odpalić OLED-a po I2C ale utknąłem na samym początku czyli komunikacji.

Czytam RM wte i nazad ale coś kiepsko mi idzie.

Funkcja do inicjalizacji:

Kod: Zaznacz cały

void I2C_Init(void)
{
   // I2C GPIO CONFIG
   GPIOB->CRL |= GPIO_CRL_CNF6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7;

   // Software reset I2C
   I2C1->CR1 |= I2C_CR1_SWRST;
   I2C1->CR1 &= ~I2C_CR1_SWRST;

   I2C1->CR1 &= ~I2C_CR1_PE;
   I2C1->CR2 = I2C_CR2_FREQ_3; // 8MHz HSI
   I2C1->TRISE |= 9;
   I2C1->CCR = 80;

   // Wlaczenie I2C
   I2C1->CR1 |= I2C_CR1_PE | I2C_CR1_ACK;
   I2C1->OAR1 = (1 << 14);
}


Zegar dla szyny włączam nieco wcześniej (RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;)
Do I2C1->CR2 wpisuję 8 (tak wynika z dokumentacji).
I2C1->TRISE = 9 też z RM tam jest (1000 ns / 125 ns = 8 + 1)
I2C1->CCR z tym mam największy problem ale dla 100kHz powinno być 80 czy 160?

potem mam funkcje:

Kod: Zaznacz cały

void I2C_Start(void)
{
   I2C1->CR1 |= I2C_CR1_START;
   while( !(I2C1->SR1 & I2C_SR1_SB) );
}

void I2C_Stop(void)
{
   I2C1->CR1 |= I2C_CR1_STOP;
}

void I2C_SendAddr(uint8_t address)
{
   I2C1->DR = address & ~I2C_OAR1_ADD0;
   while(  !(I2C1->SR1 & I2C_SR1_ADDR) );
   uint16_t dummy = I2C1->SR2;
}

void I2C_SendByte(uint8_t byte)
{
   while( !( I2C1->SR1 & I2C_SR1_TXE ));
   I2C1->DR = byte;
}


Ale i tak wisi mi na pierwszej pętli po wysłaniu start, czyli tu: while( !(I2C1->SR1 & I2C_SR1_SB) );

Prosił bym o jakieś nakierowanie gdzie robię błąd :)

Re: STM32F103 problem z odpaleniem I2C

: niedziela 03 lip 2016, 01:01
autor: Antystatyczny
A to: I2C1->CR2 = I2C_CR2_FREQ_3; // 8MHz HSI faktycznie wpisuje wartość dziesiętną 8 w bity FREQ [5:0]? U mnie nigdzie nie ma makra I2C_CR2_FREQ_3

Ok, dogrzebałem się do tego makra w CMSIS. Tak czy siak coś mi tu nie gra. Ty masz zegar 8MHz, a w CCR ustawiasz 80 i zastanawiasz się nad 160. W RM zaś napisane jest jak byk:

For instance: in Sm mode, to generate a 100 kHz SCL frequency:
If FREQR = 08, TPCLK1 = 125 ns so CCR must be programmed with 0x28
(0x28 <=> 40d x 125 ns = 5000 ns.)


No i wygląda na to, że musisz wpisać wartość dziesiętną 40

Re: STM32F103 problem z odpaleniem I2C

: niedziela 03 lip 2016, 01:30
autor: Antystatyczny
Zapytasz pewnie dlaczego 40, a nie 80. Otóż wczytałem się nieco uważniej w RM i dotarło do mnie, że wartość wpisana w CCR ustala czas trwania POŁOWY okresu na pinie SCL. A skoro tak, to CCR (40 dziesiętnie) * 125ns = 5us. 5us wysokiego poziomu + 5us niskiego poziomu daje nam 10us okres, czyli 100kHz częstotliwość.

Re: STM32F103 problem z odpaleniem I2C

: niedziela 03 lip 2016, 02:08
autor: Antystatyczny
Aha, zegar dla GPIO podłączanych pod moduł I2C1 również należy włączyć zanim wydasz jakąkolwiek komendę modułowi GPIO. Napisałeś, że włączyłeś zegar dla modułu I2C1, a o GPIO nie wspomniałeś. Dlatego ja o tym wspominam.
Przejrzałem raz jeszcze rejestry i skonfrontowałem z Twoim kodem. Wydaje mi się, że reszta jest ok, ale to się okaże w praniu.

edit:

A jednak coś mi tu nie gra. Zerknij na tę linijkę:

GPIOB->CRL |= GPIO_CRL_CNF6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7;

Gdy spojrzysz do RM zauważysz, że do CNF6, CNF7, MODE6 i MODE7 wpisuje się wartości od 0 do 3, a Ty wpisałeś...No właśnie, co wpisałeś? ;)


Pozdrawiam

Re: STM32F103 problem z odpaleniem I2C

: niedziela 03 lip 2016, 08:42
autor: squeez
Zegar dla lini GPIO tez mam tylko wlaczony szybciej w system_init().
GPIO dla linii I2C powinny byc ustawione jako alternatywne, otwarty dren.
czyli bity CNF powinny być 11 to własnie ustawia makro GPIO_CRL_CNF6 pojedyncze bity można ustawić np. GPIO_CRL_CNF6_0 i GPIO_CRL_CNF6_1 i wyjdzie na to samo.Podobnie jest z MODE ustawione na 11.

Mnie interesuje konfiguracja AF
For bidirectional Alternate Functions, the port bit must be configured in Alternate
Function Output mode (Push-Pull or Open-Drain). In this case the input driver is
configured in input floating mode


W tab 27 dla I2C jest podane
GPIO configuration: Alternate function open drain


A co CCR to miałem tam 40 (0x28) i też nic.

Jeszce budzi moje wątpliwości że w RM podane jest by skasować bit SB w rejestrze SR1 należy go odczytać
Cleared by software by reading the SR1 register followed by writing the DR register, or by hardware when PE=0

Ale pętla while czyta ten rejestr, no chyba ze przed pętlą mam go permanentnie odczyta np. tmp = I2C1->SR1;

Re: STM32F103 problem z odpaleniem I2C

: niedziela 03 lip 2016, 11:29
autor: Antystatyczny
Ok, z bitami w GPIO_CRL się wyjaśniło. O tym kasowaniu bitu SB też czytałem i zasadniczo pętla while czyta ten rejestr, ale można do próby zrobić tak, jak sugerujesz. Zastanawiam się też nad tym sygnałem ACK, który wysyłasz w momencie włączenia modułu I2C1. Kolejna sprawa to część sprzętowa. Masz dołączone rezystory do pinów SDA i SCL?

Re: STM32F103 problem z odpaleniem I2C

: niedziela 03 lip 2016, 11:56
autor: squeez
rezystory podciągające 4k7 mam na SDA i SCL. Bez ACK sprawdzałem (na początku miałem bez) potem dodałem w akcie desperacji :)
Podepnę tez analizator i zobaczę czy start jest generowany albo czy jest przebieg na SCL.

Re: STM32F103 problem z odpaleniem I2C

: niedziela 03 lip 2016, 23:59
autor: squeez
OK udało się odpalić, OLED działa :D

Jakie zrobiłem błędy ... a bo podstawa początkującego, pierwsze nie podłączyłem GPIOB :/ choć pisałem że to zrobiłem bo było w kodzie ale za komentowane a potem pokręciło mi się że to na linie I2C są na porcie A.
Ale to nie jedyne wpadki, gdy na oscyloskopie zobaczyłem przebiegi (jak GPIOB załączyłem) to ustawiłem dokłądnie częstotliwość i dla mojej konfiguracji CCR = 0x24 co daje lekko ponad 100kHz ale OLED cały czas pusty, musiałem przerobić nieco funkcję do "zamykania" połączenia czyli wysyłanie STOP.

Poniżej kod może się komuś przyda, to prymitywna wersja w poolingu ale jak to odpaliłem to teraz będę chciał zrobić komunikację I2C na przerwaniach. tak by wywalić wszystkie while() z funkcji I2C.

Kod: Zaznacz cały

void I2C_Init(void)
{
   // I2C GPIO CONFIG
   GPIOB->CRL |= GPIO_CRL_CNF6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7;

   // Software reset I2C
   I2C1->CR1 |= I2C_CR1_SWRST;
   I2C1->CR1 &= ~I2C_CR1_SWRST;

   I2C1->CR1 &= ~I2C_CR1_PE;
   I2C1->CR2 = I2C_CR2_FREQ_3;
   I2C1->TRISE = 9;
   I2C1->CCR = 0x24;

   // Wlaczenie I2C
   I2C1->CR1 |= I2C_CR1_PE | I2C_CR1_ACK;
   I2C1->OAR1 = (1 << 14);
}


void I2C_Start(void)
{
   I2C1->CR1 |= I2C_CR1_START;
   while( !(I2C1->SR1 & I2C_SR1_SB) );
   uint32_t tmp = I2C1->SR1;
}

void I2C_Stop(void)
{
   while( !(I2C1->SR1 & I2C_SR1_BTF) );
   I2C1->CR1 |= I2C_CR1_STOP;
}

void I2C_SendAddr(uint8_t address)
{
   I2C1->DR = address;
   while( !(I2C1->SR1 & I2C_SR1_ADDR) );
   uint32_t dummy = I2C1->SR1;
   dummy = I2C1->SR2;
}

void I2C_SendByte(uint8_t byte)
{
   while( !(I2C1->SR1 & I2C_SR1_TXE) );
   I2C1->DR = byte;
}

Re: STM32F103 problem z odpaleniem I2C

: wtorek 05 lip 2016, 09:55
autor: squeez
Kurczaczki ... mam problem uruchomić komunikację na przerwaniach, erratę czytałem, zwiększyłem priorytet dla wyjątku I2C jak sugerowali ale nadal d...

Kod: Zaznacz cały

// Inicjalizacja
void I2C_Init(void)
{
   // I2C GPIO CONFIG
   RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;

   // Software reset I2C
   I2C1->CR1 |= I2C_CR1_SWRST;
   I2C1->CR1 &= ~I2C_CR1_SWRST;

   I2C1->CR1 &= ~I2C_CR1_PE;
   I2C1->CR2 = I2C_CR2_FREQ_3;
   I2C1->TRISE = 9;
   I2C1->CCR = 0x24;
   // Innterupt
   //I2C1->CR2 |= I2C_CR2_ITEVTEN;
   NVIC_EnableIRQ( I2C1_EV_IRQn );

   // I2C GPIO CONFIG
   GPIOB->CRL |= GPIO_CRL_CNF6 | GPIO_CRL_CNF7 | GPIO_CRL_MODE6 | GPIO_CRL_MODE7;

   // Wlaczenie I2C
   I2C1->CR1 |= I2C_CR1_PE | I2C_CR1_ACK;
   I2C1->OAR1 = (1 << 14);
}

// struktura i funkcja do wysyłania danych
typedef struct {
   uint8_t addr;
   uint8_t *data;
   uint8_t len;
   uint8_t state;
} T_i2c;

volatile T_i2c I2C_host;

void I2C_transmit(uint8_t addr, uint8_t *data, uint8_t len)
{
   I2C_host.addr = addr;
   I2C_host.data = data;
   I2C_host.len = len;
   I2C_host.state = 1;

   I2C1->CR2 |= I2C_CR2_ITEVTEN;
   I2C1->CR1 |= I2C_CR1_START;
}

// Obsługa wyjątku
void I2C1_EV_IRQHandler(void)
{
   uint32_t tsr;

   if( (I2C1->SR1 & I2C_SR1_TXE) && I2C_host.state > 2 )
   {
      I2C1->DR = I2C_host.data[I2C_host.state-2];
      I2C_host.state++;

      if( (I2C1->SR1 & I2C_SR1_BTF) || (I2C_host.state > I2C_host.len+1) )
      {
         I2C1->CR1 |= I2C_CR1_STOP;
         I2C1->CR2 &= ~I2C_CR2_ITEVTEN;
         I2C_host.state = 0;
      }
   }

   if( (I2C1->SR1 & I2C_SR1_SB) && I2C_host.state == 1 )
   {
         tsr = I2C1->SR1;
         I2C1->DR = I2C_host.addr;
         I2C_host.state = 2;
   }

   if( (I2C1->SR1 & I2C_SR1_ADDR) && I2C_host.state == 2 )
   {
      tsr = I2C1->SR1;
      tsr = I2C1->SR2;
      I2C1->DR = I2C_host.data[0];
      I2C_host.state = 3;
   }

}

// Wysyłanie danych
uint8_t buff[] = {0x00, 0x40, 0xFF};
I2C_transmit(SLAVE_I2C_ADDRESS, buff, (sizeof buff / sizeof *buff) );


Jeśli miałby ktoś jakieś sugestie, pomysły, działające przykłady ... byłbym wdzięczny.

Re: STM32F103 problem z odpaleniem I2C

: wtorek 05 lip 2016, 10:41
autor: Antystatyczny
W dokumentacji HAL jest napisane, że przerwania I2C mają mieć najwyższy możliwy priorytet, by NIC nie zakłóciło pracy I2C.

Re: STM32F103 problem z odpaleniem I2C

: wtorek 05 lip 2016, 11:11
autor: squeez
Teraz nie mam kodu przy sobie (został w domu) ale ustawiłem priorytety na 4G i 4S.
Czyli coś w stylu:

Kod: Zaznacz cały

#define PRIGROUP_4G_4S ((const uint32_t) 0x05)

NVIC_SetPriorityGrouping( PRIGROUP_4G_4S );
uint32_t prio;

prio = NVIC_EncodePriority(PRIGROUP_4G_4S, 1, 0);
NVIC_SetPriority(I2C1_EV_IRQn, prio);

prio = NVIC_EncodePriority(PRIGROUP_4G_4S, 2, 0);
NVIC_SetPriority(SysTick_IRQn, prio);


Mam jeszcze przerwanie od TIM1 i EXTI ale TIM1 generuje przerwanie co 2s to raczej nie ma szans by akurat wchodził w drogę, podobnie jak EXTI z przycisku (nie wciskam go).
Jak rozumiem im mniejsza wartość priorytetu, tym jest on ważniejszy, czyli nadanie 1 dla I2C1_EV_IRQn powinno go uczynić najważniejszym (poza tymi o stałych priorytetach jak reset itp.).

@rezasurmar ale to jest transmisja na przerwaniach i działająca na F103?
PS. rejestry są fajne :)

Re: STM32F103 problem z odpaleniem I2C

: wtorek 05 lip 2016, 12:37
autor: squeez
OLED też mi działa ale w poolingu (teoretycznie to nie problem) ale w ramach zabawy i poznawania STM32 chciałem odpalić I2C na przerwaniach/wyjątkach a potem dodać jeszcze do tego DMA.
A biblioteki jakoś mi nie podchodzą (masa poznawania funkcji, dokumentacja do nich itp.) już wolę przeczytać RM i ustawiać wszystko na rejestrach, chociaż wiem co dokładnie robię :) ale fakt na początku bardziej upierdliwe jest niż skorzystanie z gotowca.

Re: STM32F103 problem z odpaleniem I2C

: środa 06 lip 2016, 02:40
autor: squeez
Debuger to nieocenione narzędzie :)

Ile to ja się namęczyłem nad tym a problem bardzo prozaiczny, funkcja inicjująca OLED-a przygotowywała tablicę komend i wysyłała ją (wskaźnik na tą tablicę), tyle że gdy funkcja kończyła swoją pracę (tablica była lokalna) to wiadomo co się z nią działo ... obszar pamięci był nadpisywany i w przerwaniu wysyłane były śmieci, choć tak nie do końca bo kilka wartości początkowych się zgadzało co mnie mocno zmyliło :)

Kolejna spraw to dzięki debugerowi poprawnie napisałem obsługę wyjątku dla I2C.

Jak by kogoś interesowało (tak w skrócie):

Kod: Zaznacz cały

typedef struct {
   uint8_t addr;
   uint8_t *data;
   uint8_t len;
   uint8_t state;
} T_i2c;

volatile T_i2c I2C_host;

void I2C_transmit(uint8_t addr, uint8_t *data, uint8_t len)
{
   I2C_host.addr = addr;
   I2C_host.data = data;
   I2C_host.len = len;
   I2C_host.state = 1;

   I2C1->CR2 |= I2C_CR2_ITEVTEN;
   I2C1->CR1 |= I2C_CR1_START;
}

void ssd1306_init(void)
{
   static const uint8_t buff[] = {0x00, 0xFE, 0x40}; // itp.   
   I2C_transmit(SSD1306_I2C_ADDRESS, buff, (sizeof buff / sizeof *buff) );
}

void I2C1_EV_IRQHandler(void)
{
   uint16_t tsr;

   if( (I2C1->SR1 & I2C_SR1_SB) )   //EV5
   {
      tsr = I2C1->SR1;
      I2C1->DR = I2C_host.addr;
      I2C_host.state = 2;
      return;
   }

   if( (I2C1->SR1 & I2C_SR1_ADDR) )   //EV6
   {
      tsr = I2C1->SR1;
      tsr = I2C1->SR2;
      I2C1->DR = I2C_host.data[0];   //EV8_1
      I2C_host.state = 3;
      return;
   }

   if( (I2C1->SR1 & I2C_SR1_TXE) )
   {
      if( (I2C_host.len+2)-I2C_host.state != 0 )
      {
         I2C1->DR = *(I2C_host.data + (I2C_host.state - 2));
         I2C_host.state++;
      } else {
            tsr = I2C1->SR1;
            I2C1->CR1 |= I2C_CR1_STOP;
            I2C1->CR2 &= ~I2C_CR2_ITEVTEN;
            I2C_host.state = 0;
      }
   }
}


P.S. jutro pod palec idzie DMA dla I2C :)

Re: STM32F103 problem z odpaleniem I2C

: czwartek 07 lip 2016, 12:36
autor: squeez
Z kronikarskiego "obowiązku" dodam że transmisja (nadawanie) I2C w przerwaniu z DMA zrobiona w sumie z DMA łatwiej niż bez bo nie trzeba obsługiwać w zdarzenia TXE, robi to za nas DMA.

Teraz poszukam jakiegoś zegarka i dopiszę odbieranie dla mastera :)
Jak naskrobię coś co będzie bardziej użytkowe to się podzielę.

Re: STM32F103 problem z odpaleniem I2C

: czwartek 25 sie 2016, 21:28
autor: Marcin
Również mam problem z I2C w STM32, moja platforma testowa to chińska płytka z mikrokontrolerem STM32F103RCT6 na pokładzie.
Sam zapis wydaje się że mam opanowany, problem mam natomiast z odczytem. Funkcja która powinna w założeniu odczytać N-bajtów w rzeczywistości odczytuje z układu slave N+1 bajtów. Na oscyloskopie to wygląda tak

Obrazek

Konfigurację I2C jak i portów GPIO z nim związanych użyłem taką jak mi HAL wygenerowało.

I2C programowo zrealizowałem tak:

Kod: Zaznacz cały

#include "I2CLibrary.h"
#include "stm32f1xx_hal.h"

//#include <stdio.h>
//#include <stdlib.h>

#define I2C I2C1

#define SR2         (I2C->SR2)
#define ACK         (I2C->CR1 |= I2C_CR1_ACK)
#define NOACK          (I2C->CR1 &= ~I2C_CR1_ACK)
#define START          (I2C->CR1 |= I2C_CR1_START)
#define STOP          (I2C->CR1 |= I2C_CR1_STOP)
#define POS         (I2C->CR1 |= I2C_CR1_POS)

#define EV5       (!(I2C->SR1 & I2C_SR1_SB))
#define EV6       (!(I2C->SR1 & I2C_SR1_ADDR))
#define EV7       (!(I2C->SR1 & I2C_SR1_RXNE))
#define EV8       (!(I2C->SR1 & I2C_SR1_TXE))
#define EV8_2      (!(I2C->SR1 & (I2C_SR1_TXE | I2C_SR1_BTF)))


void I2C_SendData(uint8_t addr, uint8_t* data, uint8_t len)
{
   //send slave address
   
   START;
   while(EV5);   
   I2C->DR = addr & ~(1<<0);
   
   //send data
   
   while(EV6);
   volatile uint32_t sr2 = SR2;      
   while(EV8);
   
   
   while(len)
   {
      if(len == 1)
      I2C->DR = *data++;
      len--;
   }
   
   while(EV8_2);
   STOP;
}

void I2C_ReadData(uint8_t addr, uint8_t command, uint8_t* data, uint8_t len)
{
   
   volatile uint32_t sr2;
      
   //-----send command------
   START;
   while(EV5);   
   I2C->DR = addr & ~(1<<0);
   
   while(EV6);
   sr2 = SR2;      
   while(EV8);

   I2C->DR = command;
   
   while(EV8_2);
   STOP;      
   
   //-----read data---------
   
   START;
   while(EV5);   
   I2C->DR = addr | (1<<0);
   
   while(EV6);
   sr2 = SR2;
   
   ACK;
   
   while(len)
   {   
      if(len==1)
      NOACK;
      while(EV7);
      *data++ = I2C->DR;
      
      len--;
   }
   
   STOP;

}


Przed pierwszym użyciem a po inicjalizacji włączenie I2C

Kod: Zaznacz cały

I2C1->CR1 |= I2C_CR1_PE;


Użycie w programie

Kod: Zaznacz cały

   uint8_t PromMemory[2];
   I2C_ReadData(0xEE, 0xA2, PromMemory, 2);


I nie rozumiem dlaczego po odczycie ostatniego bitu a przed wysłaniem stopu master jeszcze coś próbuje odczytać.

Może ktoś rzucić okiem na mój problem ?

Re: STM32F103 problem z odpaleniem I2C

: czwartek 25 sie 2016, 22:16
autor: Marcin
Na szynie nic więcej nie ma poza GY-86.

Re: STM32F103 problem z odpaleniem I2C

: czwartek 25 sie 2016, 22:23
autor: Antystatyczny
Kluczem do rozwiązania zagadki jest:

1. Świadomość obecności bufora (a więc konieczności odczytu dwóch bajtów zamiast jednego, jak w AVR).
2. Prawidłowe posługiwanie się bitami POS i ACK w rejestrze I2Cx->CR1. Zwłaszcza bit POS jest istotny, gdyż to on decyduje, kiedy nastąpi wygenerowanie ACK lub NACK. Jeśli bit jest wyzerowany, NACK/ACK dotyczy aktualnie odbieranego bajtu (wsuwanego do rejestru). Jeśli POS jest ustawiony, wygenerowanie NACK/ACK będzie dotyczyło dopiero następnego bajtu. Bit POS, zgodnie z treścią reference manual, ma być używany wyłącznie podczas odbierania dwóch bajtów. W przypadku jednego lub trzech i więcej bajtów bit ten ma być wyzerowany.

A wracając do bufora - To normalne, że za każdym razem masz o jeden bajt za dużo do odczytu, ponieważ jeden odebrany bajt siedzi w I2Cx->DR, a kolejny w rejestrze przesuwnym. Gdy odczytasz DR, dane z rejestru przesuwnego przenoszone są automatycznie do przed chwilą opróżnionego DR. Stąd ten nadmiarowy bajt. Spróbuj obniżyć ilość odbieranych bajtów o jeden, tzn.: Chcesz odczytać 10 bajtów. Do funkcji odczytującej dane przekazujesz "len" = 10. Wewnątrz funkcji dekrementujesz "len" i tak naprawdę starasz się odczytać 9 bajtów. Efektem będzie odczyt 10 bajtów.