Skocz do zawartości

[STM32][SPI]Wyświetlacz OLED


napiad

Pomocna odpowiedź

Cześć!

Korzystam z STM32F303RE i chciałem podłączyć do niego wyświetlacz oled na sterowniku SSD1306 (https://botland.com.pl/wyswietlacze-oled/8245-wyswietlacz-oled-niebieski-graficzny-096-128x64px-spii2c.html?search_query=oled&results=25) korzystając ze SPI. Mój kod na obecną chwilę wygląda tak:

#include "stm32f30x.h"

#define OLED_SCL_Pin GPIO_Pin_5
#define OLED_SDA_Pin GPIO_Pin_7
#define OLED_RST_Pin GPIO_Pin_9
#define OLED_DC_Pin GPIO_Pin_8


/* do funkcji delay */
volatile uint32_t timer_ms = 0;

void SysTick_Handler()
{
   if (timer_ms) {
       timer_ms--;
   }
}
void delay_ms(int time)
{
   timer_ms = time;
   while(timer_ms > 0){};
}
/* wysylanie bajta danych */
void SPI_SendByte(uint8_t data)
{
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //czeka az bufor bedzie wolny 
SPI_SendData8(SPI1, data); //wysyla
}

/* wysylanie komendy do wyswietlacza */
void OLED_WriteCmd(uint8_t cmd)
{
GPIO_ResetBits(GPIOA, OLED_DC_Pin); //DC 0 
SPI_SendByte(cmd);
}
/* wysylanie danych do wyswietlacza */
void OLED_WriteData(uint8_t data)
{
GPIO_SetBits(GPIOA, OLED_DC_Pin); //DC 1
SPI_SendByte(data);
}

void OLED_Reset()
{
GPIO_ResetBits(GPIOA, OLED_RST_Pin); //RST 0
delay_ms(50); //odczekanie
GPIO_SetBits(GPIOA, OLED_RST_Pin); //RST 1
}

void OLED_Init()
{	
OLED_Reset(); //reset

/* sugerowane komendy z user guide'a */
OLED_WriteCmd(0xAE); //turn off oled panel
OLED_WriteCmd(0x00); //set low column address
OLED_WriteCmd(0x10); //set high column address
OLED_WriteCmd(0x40); //set start line address
OLED_WriteCmd(0x81); //set contrast control register
OLED_WriteCmd(0xCF); 
OLED_WriteCmd(0xA1); //set segment re-map 95 to 0
OLED_WriteCmd(0xA6); //set normal display
OLED_WriteCmd(0xA8); //set multiplex ratio(1 to 64)
OLED_WriteCmd(0x3F); //1/64 duty
OLED_WriteCmd(0xD3); //set display offset
OLED_WriteCmd(0x00); //no offset
OLED_WriteCmd(0xD5); //set display clock divide ratio/oscillator freq
OLED_WriteCmd(0x80); //set divide radio
OLED_WriteCmd(0xD9); //set pre-charge period
OLED_WriteCmd(0xF1); 
OLED_WriteCmd(0xDA); //set com pins hardware configuration
OLED_WriteCmd(0x12);
OLED_WriteCmd(0xDB); //set vcomh
OLED_WriteCmd(0x40);
OLED_WriteCmd(0x8D); //set CHarge Pump enable
OLED_WriteCmd(0x14);
OLED_WriteCmd(0xAF); //turn on oled panel
}

int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

SysTick_Config(SystemCoreClock / 1000);

GPIO_InitTypeDef gpio;
	gpio.GPIO_Pin = OLED_SCL_Pin | OLED_SDA_Pin;
	gpio.GPIO_Mode = GPIO_Mode_AF;
	gpio.GPIO_OType = GPIO_OType_PP;
	gpio.GPIO_Speed = GPIO_Speed_Level_3;
	gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &gpio);

GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_5);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_5);

	gpio.GPIO_Pin = OLED_RST_Pin | OLED_DC_Pin;
	gpio.GPIO_Speed = GPIO_Speed_Level_3;
	gpio.GPIO_Mode = GPIO_Mode_OUT;
	gpio.GPIO_OType = GPIO_OType_PP;
	gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &gpio);

SPI_InitTypeDef spi;
	SPI_StructInit(&spi);
	spi.SPI_Direction = SPI_Direction_1Line_Tx;
	spi.SPI_CPHA = SPI_CPHA_2Edge;
	spi.SPI_CPOL = SPI_CPOL_High;
	spi.SPI_DataSize = SPI_DataSize_8b;
	spi.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
	spi.SPI_Mode = SPI_Mode_Master;
	spi.SPI_NSS = SPI_NSS_Soft;
	spi.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_Init(SPI1, &spi);

SPI_Cmd(SPI1, ENABLE);

OLED_Init();
delay_ms(50);

OLED_WriteData(0xFF);

while(1)
{
}
}

Jest dosyć podstawowy i bazuje po części na kursie z forbota i znalezionych rzeczach w internecia, no i na datasheecie, przynajmniej to, co mam nadzieję dobrze zrozumiałem (komendy inicjalizacyjne są również z user guide).

A jednak wyświetlacz nie reaguje i pozostaje czarny, i nie wiem, w którym miejscu popełniam błąd, a kod nie jest w sumie aż tak skomplikowany. Może ktoś ma jakiś pomysł lub doświadczenie w takim przypadku?

Link do komentarza
Share on other sites

Być może rozmawiasz z nim za szybko -- one mają ograniczenie prędkości. Być może fazę i/lub polaryzację masz źle.

Ja bym na początek znalazł w datasheecie jakiś rejestr, którego wartość znasz i spróbował go odczytać -- to może ci dać potwierdzenie, że na poziomie protokołu komunikacja działa.

Link do komentarza
Share on other sites

Polaryzację i fazę sprawdzałem i wydaje mi się, że jest dobrze (załączam fragment datacheetu). A co do szybkości to próbowałem też wyższe 'baud rate prescalery', więc to chyba nie to.

Mógłbyś bardziej wytłumaczyć jak to z tym rejestrem? (nie jestem jeszcze w to zbyt biegły)

Chodzi o rejestr u slave'a? (a odczytać chyba nie mogę, bo 'chyba' obsługuje tylko jednostronną komunikację (bez MISO), chociaż w sumie nie wiem, bo ma tylko piny SCL, SDA, RST, DC).

Czy taki rejestr mam sprawdzać w stmie jakoś po prostu? I który to mógłby być?

(Jutro też spróbuję zbadać oscyloskopem, może to coś wykaże)

CaptureSPI.thumb.JPG.a9dcb7e1c5622389d8b667f69a736050.JPG

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Wyświetlaczy OLED na tym sterowniku jest mnóstwo rodzajów. Jeśli masz dokładnie taki, jaki pokazany jest na podlinkowanej stronie Botlandu, to na spodniej stronie płytki jest legenda. Zamontowane oporniki R3 i R4 wskazują na interfejs typu "4SPI". W takim wypadku znaczenie linii/pinów modułu jest następujące:

RES → Chip Select, aktywny niskim, musisz robić tu 0 przed transferem SPI i wracać do 1 po zakończonym przesyłaniu ramki.

D/C → Data/Command, przed wysłaniem czegokolwiek robisz tu 0 gdy przesyłasz komendę, 1 gdy dane.

SDA → MOSI, tędy przepychasz dane do wyświetlacza.

SCL → SCK, tu generujesz zegar transmisji SPI.

Tak więc nie masz żadnego resetu a dane możesz tylko wysyłać. Jeśli chcesz zmienić interfejs na inny, musisz pozmieniać obsadę oporników na PCB.

Link do komentarza
Share on other sites

Właściwie to mam ten: https://sklep.avt.pl/wyswietlacz-oled-0-96-128-64-niebieski-spi.html (na początku myślałem, że są takie same, ale jednak nie do końca)

tylko teraz który tryb tu jest zdefiniowany opornikami? stawiałbym też na 4spi

ale ten RST to chyba nie jest chip select, tylko pin do restartu przed inicjalizacją wyświetlacza(?) w tym moim z tyłu jest narysowana ramka z napisanym CS, które jest zwarte do czegoś

Link do komentarza
Share on other sites

Mam identyczny. Podłącz go tak jak napisałem. U mnie działa od pierwszego razu, z biblioteką u8glib inicjowaną w ten sposób:

U8GLIB_SH1106_128X64_2X u8g(9, 8); // HW SPI, CS = 9, D/C = 8

czyli właśnie w trybie SPI4, z sygnalami CS i D/C.

Link do komentarza
Share on other sites

U mnie jest akurat atmega328, no ale przecież nie o to chodzi - pytałeś o interfejs. Masz 4 druty i znasz już ich przeznaczenie więc nie musisz się już obawiać, że coś nie tak jest z połączeniami. Teraz Twoja kolej: podłącz OLED i pisz własny kod lub zajrzyj jak robi to np. u8glib dla kontrolera sh1106. Przecież coś już napisałeś. Wywal z tego linię RESET bo wyświetlacz go nie ma, koniecznie zmień funkcję wysyłania przez SPI żeby używała sygnału CS do ramkowania transferów i próbuj. Konkretne wartości inicjalizacyjne rejestrów kontrolera możesz ściągnąć z biblioteki.

u8glib działa w pewien charakterystyczny sposób: rysuje wszystko w RAMie procesora a na końcu wysyła to do wyświetlacza. Ponieważ małe procki mają mało RAMu, istnieje możliwość zrobienia tego na raty. Choć to nie Twój przypadek (STM spokojnie łyknie cały ekran 128x64=1Kbajt), poczytaj manual, bo idea pozostaje ta sama.

Link do komentarza
Share on other sites

Okej, ale w takim razie czemu pin jest podpisany RST, a nie CS. A na odwrocie jest coś podpisane CS, tylko nie jako pin, więc zakładałem, że CS nie jest w tym wypadku do sterowania programowo

Na arduino użyłem tej biblioteki (załącznik) i działało, a tam np. nie używają CS

IMG_20170410_175211_01.thumb.jpg.7f115c4b4f25a2a454df2564b2bc3355.jpg

OLED.zip

Link do komentarza
Share on other sites

A dlaczego pin zegara jest opisany SCL a danych SDA, co wskazuje na zupełnie inny interfejs?

Nie wiem dlaczego jest tam RST, ale jeśli faktycznie u Ciebie działało z wejściem RST traktowanym jak RESET, to jeszcze raz sprawdzę w domu jak to działało.

Wg dokumentacji tego kontrolera różne piny pełnią różne funkcje w zależności od trzech pinów konfiguracyjnych, ale akurat RST się nie zmienia. Ja założyłem, że moduł bez RST sobie poradzi, ale bez CS będzie to (choć możliwe) bardzo delikatne i zawodne. Wystarczy wtedy jedno dodatkowe zbocze zegara złapane lub zgubione przez sterownik i cała komunikacja z OLED się rozsypuje a procesor nic o tym nie wie i nic nie jest w stanie naprawić.

Dobra, dam znać wieczorem. Ale, zaraz, skoroi już opanowałeś komunikację z tym wyświetlaczem, to gdzie jest problem? Zrób to tak samo jak w Arduino.

Link do komentarza
Share on other sites

Próbowałem, kopiując w sumie cały kod, tylko przerabiając np. digitalWrite na setBits, tylko też nie działało, dlatego nie wiem w czym rzecz, czy to przy inicjalizacji spi czy gdzie...ale może jeszcze raz spróbuję

Ok, dzięki, czekam

Link do komentarza
Share on other sites

U siebie w kodzie (na HALu) mam coś takiego:

 hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
 hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;

co wygląda na dokładnie odwrotne ustawienie niż u Ciebie.

Jak to nie pomoże spróbuj uruchomić w pętli

OLED_WriteData(0xFF);

To powinno zapełnić ekran.

Link do komentarza
Share on other sites

Trochę to trwało, ale znalazłem chwilę czasu i odkopałem wyświetlacz, jakąś dokumentację projektu i kod. Mimo użycia konstruktora u8glib sugerującego używanie pinów CS i D/C mój wyświetlacz miał podpięty tylko D/C i RST. Na wejście RST wyświetlacza podawałem samodzielnie sygnał zerowania zaraz po włączeniu i tyle, więc tak naprawdę korzystałem tylko z D/C i sprzętowego SPI (SCK+MOSI). Robiłem to dawno - jak widać pamięć jest zawodna (niestety coraz bardziej..).

W tej sytuacji dziwnie narysowany CS na odwrocie płytki może sugerować jego zwarcie do masy istniejącym tam opornikiem i szansę wykorzystania tego wejścia po wylutowaniu zworki.

Na pewno używam podstawowego trybu pracy SPI: zegar między transferami jest "zaparkowany" w stanie 0.

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.