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

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

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
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.