Popularny post virtualny Napisano Sierpień 29, 2023 Popularny post Udostępnij Napisano Sierpień 29, 2023 Wielu miłośników STM32 zapewne zna ten zestaw i podobne do niego jego klony: W szczególności interesował mnie wyświetlacz z równoległym wyprowadzeniem magistrali i8080, którą może emulować interfejs FSMC z STM32F407VET6-ZET6-ZGT6... Postanowiłem podłączyć takie cacko do zwykłego "blupila". Analizując Schemat kitu STM32F407ZGT6 zaprojektowałem coś takiego starając się możliwie optymalnie ustawić bity dla magistrali danych 16 bit. Dodatkowo jest możliwość zamontowania BLACKPILL'a zamiast niebieskiej pigułki. Po schemacie przyszła pora na routing: I w końcu powstało i przyjechało coś takiego: Po zlutowaniu i "oporządzeniu wyszło takie cacko: Cacko można oprogramować na przykład w PlatformIO: By w końcu odpalić swój benchamark: Projekt na github: https://github.com/wegi1/BLUEPILL_AND_I8080 youtube: W tym miejscu podziękowania dla mojego przyjaciela PROTEUS'a, który wstępnie oprogramował i8080 dla zapisu. Stworzenie czegoś sensownego wymagało wielu optymalizacji zaczynając od algorytmu programu po broń ostateczną (assembler). Z tym interfejsem ruszyły płynnie 512 vestordots ball, ale naprawdę wymagało to wielkiego wysiłku - poniżej zoptymalizowana procedura stawiania serii plotów(w tym wypadku 512): .cpu cortex-m3 .arch armv7-m .fpu softvfp .thumb .thumb_func .syntax unified .global Set_Many_Plots .type Set_Many_Plots , %function .text .align 2 //========================================================= //============================================================== //Set_Many_Plots(uint16_t * plots_X, uint16_t * plots_y, uint32_t ManyPlots, uint16_t color ); // R0 = PLOT X POINTER // R1 = PLOT Y POINTER // R2 = MANY PLOTS TO PASTE // R3 = UINT16_T COLOR // R4 OFFSET TO BUFFERS // R5 = GPIO ODR OFFSET // R6 DATA TO SEND (REGISTER NUMBER OR X-Y COORDINATES) // R7 GPIO_ODR VALUE // R8-R9 DATA FOR CLICK NWE FOR WRITEREG OPERATION // R10 BACKUP OF R6 REGISTER // R11 = 0x00100020 => [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] // R12 GPIOB->ODR VALUE (HIGH 8 BYTES OF PERIPH ARE 8 HIGHEST BYTES ON THE DATABUS) Set_Many_Plots: PUSH {R3-R12} MOV R4, #0 // OFFSET TO TABLES LDR R5, = 0x40010800 // GPIOA ADDRESS POINTER MOV R8, #0x30 // [GPIO_B->BRR] NWE = 0 RS = 0 MOV R9, #0x10 // [GPIO_B->BSRR] NWE = 1 LDR R11, = 0x00100020 // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] LDR R7, [R5, #0x0C] // read GPIO_A ODR LDR R12, [R5, #0x040C] // read GPIO_B ODR (addres pointer added to sign GPIO_B ODR #0x040C) SET_PLOT_LOOP: //--- SEND REGISTER NUMBR 0x2A MOV R6, #0x2A // R6 = DATA TO SEND AND R7, 0xFF00 // mask 8 high bits ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS LDRH R10, [R1, R4] // READ Y COORD STR R8, [R5, #0x0414] // [GPIO_B->BRR] NWE = 0, RS = 0 [GPIO B] MOV R6, R10, LSR #8 // HIGH 8 BITS FIRST // COPY Y COORD STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 //--- FIRST Y COORD SEND --- // DON'T NEED BEFOR WE READ PORT A //LDR R7, [R5, #0x0C] // read GPIO_A ODR AND R7, 0xFF00 // mask 8 high bits ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, R10 STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] AND R6, 0xFF STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 //--- AND R7, 0xFF00 // mask 8 high bits ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, R10, LSR #8 // HIGH 8 BITS FIRST STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] AND R7, 0xFF00 // mask 8 high bits STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 //--- //- 2nd TIME PUT Y COORD --- ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, R10 STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] AND R6, 0xFF STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 AND R7, 0xFF00 // mask 8 high bits ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, #0x2B // R6 = DATA TO SEND STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] AND R7, 0xFF00 // mask 8 high bits STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 //--- //--- SEND REGISTER NUMBR 0x2B ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS LDRH R10, [R0, R4] // READ X COORD STR R8, [R5, #0x0414] // [GPIO_B->BRR] NWE = 0, RS = 0 [GPIO B] MOV R6, R10, LSR #8 // COPY X COORD STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 //************************* //--- NOW X COORD SEND --- AND R7, 0xFF00 // mask 8 high bits ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, R10 STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] AND R6, 0xFF STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 AND R7, 0xFF00 // mask 8 high bits ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, R10, LSR #8 STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] AND R7, 0xFF00 // mask 8 high bits STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 //--- //- 2nd TIME PUT X COORD --- ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, R10 STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] AND R6, 0xFF STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 AND R7, 0xFF00 // mask 8 high bits ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, #0x2C // R6 = DATA TO SEND STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] AND R7, 0xFF00 // mask 8 high bits STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 //--- //--- SEND REGISTER NUMBR 0x2C ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, R3 // GAVE COLOR AND PUT 16 BITS OF COLOR TO DATABUS STR R8, [R5, #0x0414] // [GPIO_B->BRR] NWE = 0, RS = 0 [GPIO B] AND R6, 0xFF STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 AND R7, 0xFF00 // mask 8 high bits ORR R7, R6 // join ODR GPIO_A prepared value with a nev lowest 8 bits STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, R3 // RESTORE COLOR FOR PUT 8 HIGH BITS TO DATABUS AND R6, 0xFF00 AND R12, 0x00FF // prepare for high 8 bits from R0 ORR R12, R6 // join data to send STR R12, [R5, #0x040C] // and finally store high byte our data on the DATABUS //FINALLY LATCH THE COLOR ADD R4, R4, #0x02 // increase offset STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] SUBS R2, R2, #1 // manyDots count STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 BNE SET_PLOT_LOOP //**************************** POP {R3-R12} BX LR //============================================================== Z efektu końcowego jestem bardziej niż dumny, można by założyć kolejny wątek historii optymalizacji, kiedy czyszczenie ekranu początkowo zajmowało kilka sekund, gdy na końcu szybkość wyniosła ponad 80Hz. Tak samo aż niewiarygodnie 512 vectordotów działa lepiej, jak na nieoptymalizowanych CM4 168MHz. Tak naprawdę najwięcej czasu program spędza na oczekiwaniu na jedną z bliskich końca ekranu linii by rozpocząć błyskawiczne wymazywanie poprzednio postawionych dotów i stawianie kolejnych nowych - to zajmuje około 50 linii w ustawieniu pionowym LCD (portrait). Samo liczenie 512 dotów o ile pamiętam zajmuje o wiele mniej, niż ich stawianie. Jest także kwestia hmm... dziwnego działania czy wymagań sterownika ILI9341, który wiele rozkazów jak np. WriteReg używają tylko najmłodszych 8 bitów magistrali danych. Bardzo nieszczęsna jest także procedura stawiania plota, według której najpierw trzeba "otworzyć okno" o wielkości 1*1 pixel - kołomyja po prostu... Wystarczyło by podać współrzędne pixela i kolor (na szczęście kolor jest przyjmowany przez całą magistralę 16 bitów). A tak trzeba podawać 2 razy współrzędne plota, które są wielkości 16 bitów przez najmłodsze 8 bitów magistrali - po prostu strata czasu... Jeszcze nie zdążyłem przetestować, czy ruszy to z black pillem. Wyświetlacz i bluepil zagospodarowany 🙂 4 Link do komentarza Share on other sites More sharing options...
matsobdev Sierpień 31, 2023 Udostępnij Sierpień 31, 2023 (edytowany) Dnia 29.08.2023 o 16:40, virtualny napisał: A tak trzeba podawać 2 razy współrzędne plota, które są wielkości 16 bitów przez najmłodsze 8 bitów magistrali - po prostu strata czasu... Cała klatka jest rysowana (generowana) od początku za każdym razem, czy tylko wybrane piksele są gaszone i zapalane? Edytowano Sierpień 31, 2023 przez matsobdev Link do komentarza Share on other sites More sharing options...
virtualny Sierpień 31, 2023 Autor tematu Udostępnij Sierpień 31, 2023 Co klatkę liczonych jest 512 pixeli, następnie są sprawdzane i oznaczane czy znajdują się w zakresie widocznym na ekranie. Od linii (rastra) 310 z (320) następuje wymazanie pixeli postawionych w poprzedniej ramce i natychmiast po tym stawiane są nowe doty w ilości do 512 - czyli wybrane pixele są wygaszane i zapalane. Niestety mamy tutaj opóźnienie wynikające z wymagań sterownika: 1. Po pierwsze nie mogę zastosować rozkazu (bo taki nie istnieje) typu "Put_Pixel(x,y, color)", aby raz podać współrzędne X i Y oraz kolor pixela, tylko muszę podać okno (start x, start y, end x, end y) o wielkości 1 pixela w x i y, czyli: LCD_WriteReg(ILI_CASET); LCD_WriteData(y0); LCD_WriteData(y0); LCD_WriteReg(ILI_PASET); LCD_WriteData(x0); LCD_WriteData(x0); LCD_WriteReg(ILI_RAMWR); LCD_WriteData(Color); 2. Po drugie w wypadku tego rozkazu, pomimo że magistrala jest 16 bitowa przesyłanie danych X i Y jest wyłącznie przez najniższe 8 bitów magistrali, pomimo że X i Y są wielkościami 16 bitowymi - czyli tak naprawdę wykonywane jest to tak: LCD_WriteReg(ILI_CASET); LCD_WriteData(y0 >> 8); LCD_WriteData(y0); LCD_WriteData(y0 >> 8); LCD_WriteData(y0); LCD_WriteReg(ILI_PASET); LCD_WriteData(x0 >> 8); LCD_WriteData(x0); LCD_WriteData(x0 >> 8); LCD_WriteData(x0); LCD_WriteReg(ILI_RAMWR); LCD_WriteData(Color); A wszystko tylko dla 1 pixela czyli 3 zapisy do rejestrów i 9 razy wystawianie danej na magistrali (za każdym razem po wystawieniu danej trzeba "kliknąć 2 razy linią NWE! 0/1 ) -zamiast : LCD_WriteReg(PIXEL); // mój wyimaginowany rejestr i rozkaz optymalizujący stawianie pixela LCD_WriteData(y0); LCD_WriteData(x0); LCD_WriteData(Color); W poprzednim moim poście, zamieszczona procedura stawia w pętli do 512 dotów, gdzie przez referencje podany jest w R0 wskaźnik do tablicy współrzędnych X, R1 współrzędnych Y, R2 przez wartość ilość dotów do postawienia i R3 przez wartość kolor. Z C tą procedurę wywołuje się: //Set_Many_Plots(uint16_t * plots_X, uint16_t * plots_y, uint32_t ManyPlots, uint16_t color ); Set_Many_Plots(& plots_X, & plots_y, ManyPlots, color ); Czyli nawet stawianie samych plotów zamiast pełnej ramki jest upierdliwe. Dodatkowo dochodzimy do pewnej granicy, ponieważ flash może być "access'owany" do bodajże 24MHz i postawienie tego na maszynie 160MHz zwiększy tylko ilość waitstate'ów, a kod się szybciej nie wykona jeżeli nie mamy jakiegoś cashe na podorędziu. 1 Link do komentarza Share on other sites More sharing options...
matsobdev Sierpień 31, 2023 Udostępnij Sierpień 31, 2023 (edytowany) To nie zmniejszy ilości przesyłanych danych do wyświetlacza, ani tym bardziej możliwego odświeżania tak aby zmieścić się czasie rysowania klatki przez wyświetlacz, co najwyżej "uprości". A mianowicie siłowe wysyłanie całej klatki na bieżąco (czy częściowego) bufora, bez selektywnego gaszenia tego co było wcześniej na ekranie. Nie trzeba wtedy sterować wyświetlaczem (wystarczy raz wydać polecenie zapisu do pamięci), tylko wprost zarzucić strumień - 240 * 320 * RGB565 = 150 KB. Jak się zapełni pamięć wyświetlacza, to następne dane zapisują się od początku pamięci. Edytowano Sierpień 31, 2023 przez matsobdev Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. 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
virtualny Sierpień 31, 2023 Autor tematu Udostępnij Sierpień 31, 2023 (edytowany) 512 x 12 zapisów z czego 11 ignoruje 8 najstarszych bitów magistrali to tak policzyłbym "plus-minus" 8 "pełnych" zapisów na 1 dot, co daje 4KB dla 512 dotów versus 150KB "brutal force" na ramkę, gdzie oprócz przesyłania danych trzeba jeszcze doty poobracać w 3 osiach... No i mała ilośc RAM w bluepillu... Coś takiego to na czymś bardziej wypasionym z własnym i DMA transferującym go do LCD framebufforem. edit: UPS... plus drugie 4KB na zapalenie nowych vectordotów - razem 8KB Edytowano Wrzesień 1, 2023 przez virtualny Link do komentarza Share on other sites More sharing options...
Popularny post virtualny Wrzesień 1, 2023 Autor tematu Popularny post Udostępnij Wrzesień 1, 2023 Mogę odstąpić PCB jeżeli ktoś chce za cenę wysyłki paczkomatem. Zdjęcia z pozostałych części benchmarku: 3 Link do komentarza Share on other sites More sharing options...
H1M4W4R1 Wrzesień 3, 2023 Udostępnij Wrzesień 3, 2023 (edytowany) To ja będę tym narzekającym na design PCB 😄 Widzę bardzo niemiłe przecięcia ścieżek (aka. nie są pod kątem prostym). To bardzo szybka droga do dużej ilości problemów z zakłóceniami. Energia nie płynie ścieżkami, tylko polem między nimi. Masa i ścieżki sygnałowe powinny być jak najbliżej siebie. Czy jest jakiś powód, dla którego nie zastosowałeś pola masowego na całej płytce? Osobiście unikam przelotek jak ognia, ale czasem jednak je trzeba stosować. Tutaj obowiązuje zasada parzystości - dwie przelotki. Jedna dla sygnału, druga zaraz obok dla masy. Zwykle nie jest to aż tak istotne, ale jak pojawią się wyższe częstotliwości to zacznie to być całkiem problematyczne. NIGDY NIE PRZECINAJ ŚCIEŻKI SYGNAŁOWEJ I MOCY (chyba, że naprawdę nie ma już żadnej innej możliwości, ale to wtedy tylko i wyłącznie przecięcie pod kątem 90 stopni). To byłoby chyba na tyle. W każdym razie pomysł dość interesujący, chociaż osobiście ostatnio wolę styl retro i wszystko w THT. Ref: Edytowano Wrzesień 3, 2023 przez H1M4W4R1 2 Link do komentarza Share on other sites More sharing options...
virtualny Wrzesień 3, 2023 Autor tematu Udostępnij Wrzesień 3, 2023 12 godzin temu, H1M4W4R1 napisał: Widzę bardzo niemiłe przecięcia ścieżek (aka. nie są pod kątem prostym). To bardzo szybka droga do dużej ilości problemów z zakłóceniami. Nie jestem pewien o co chodzi, prawdopodobnie o routing linii 5V albo 3V gdzie ścieżki mają po 60 milsów... 12 godzin temu, H1M4W4R1 napisał: Czy jest jakiś powód, dla którego nie zastosowałeś pola masowego na całej płytce? Wylewka masowa jest po obu stronach, tylko że tego nie pokazałem. Pokazałem routing bez przelotek na liniach sygnałowych. 13 godzin temu, H1M4W4R1 napisał: Osobiście unikam przelotek jak ognia, ale czasem jednak je trzeba stosować. Tutaj obowiązuje zasada parzystości - dwie przelotki. Jedna dla sygnału, druga zaraz obok dla masy. Zwykle nie jest to aż tak istotne, ale jak pojawią się wyższe częstotliwości to zacznie to być całkiem problematyczne. Linie sygnałowe są bez przelotek (UDAŁO SIĘ), w miejsce przelotek zastosowałem na liniach 3 i 5V pady dwuwarstwowe (jak się uprzeć, to też przelotka tylko w powiększeniu) plus miejscowe wylewki 3 i 5V (ponieważ było dużo miejsca) żeby mieć dobre przejście linii 3 i 5V pomiędzy warstwami. Z tych samych powodów zastosowałem pady łączące wylewki GND zamiast przelotek 12mils. Częstotliwości maksymalne jakie się tu pojawiają, to strob zapisu na linii NWE, który mógłby działać z prędkością 18MHz, i który celowo został obniżony do 9 MHZ, pomiędzy strob intencjonalnie "wplecono" kolejne zadania - widać w zamieszczonej procedurze Set_Many_Plots(uint16_t * plots_X, uint16_t * plots_y, uint32_t ManyPlots, uint16_t color ) : //=========================================== STR R7, [R5, #0x0C] // store lowest 8 bits into GPIO_A as our low byte DATABUS MOV R6, R10, LSR #8 // intentionally delay for databus stabilise STR R11,[R5, #0x0410] // [GPIO_B->BSRR] NWE = 0, RS = 1 [GPIO B] AND R7, 0xFF00 //INTENTIONALLY DELAY FOR SLOW DOWN WRITE STROBE // mask 8 high bits STR R9, [R5, #0x0410] // [GPIO_B->BSRR] NWE = 1 //============================================ (W innym wątku robiłem doświadczenia z jaką maksymalną prędkością da się "machać" programowo pinem, tudzież ile cykli rdzenia zajmuje wykonanie wybranego rozkazu.) 13 godzin temu, H1M4W4R1 napisał: NIGDY NIE PRZECINAJ ŚCIEŻKI SYGNAŁOWEJ I MOCY (chyba, że naprawdę nie ma już żadnej innej możliwości, ale to wtedy tylko i wyłącznie przecięcie pod kątem 90 stopni). Dzięki za wartościową radę. I dzięki za dobry filmik. Link do komentarza Share on other sites More sharing options...
H1M4W4R1 Wrzesień 3, 2023 Udostępnij Wrzesień 3, 2023 2 godziny temu, virtualny napisał: Częstotliwości maksymalne jakie się tu pojawiają, to strob zapisu na linii NWE, który mógłby działać z prędkością 18MHz Czyli na oko może on powodować zakłócenia harmoniczne aż do ~500MHz, czyli bez problemu zahacza o fale radiowe 😉 (Nawet przy obniżeniu o połowę). 2 godziny temu, virtualny napisał: Nie jestem pewien o co chodzi, prawdopodobnie o routing linii 5V albo 3V gdzie ścieżki mają po 60 milsów... Chodzi o kąt pomiędzy ścieżką na TOP i BOT. Powinny zawsze mieć 90 stopni, by fale EM nie wchodziły ze sobą w interferencje. W pierwszym poście jest tego dość sporo, a najbardziej w oczy rzuca się 3V3 przecinające się z liniami sygnałowymi po lewej stronie. Bazowałem na routingu pokazanym w pierwszym poście, stąd sporo elementów było w formie pytań 😛 2 godziny temu, virtualny napisał: Jak podsyłasz routing to lepszy jest zrzut ekranu z edytora zamiast podglądu PCB 😉 (Ten z czerwonymi i niebieskimi ścieżkami, bo łatwiej znaleźć potencjalne błędy). To taka rada na przyszłość 😉 Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Bądź aktywny - zaloguj się lub utwórz konto!
Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony
Utwórz konto w ~20 sekund!
Zarejestruj nowe konto, to proste!
Zarejestruj się »Zaloguj się
Posiadasz własne konto? Użyj go!
Zaloguj się »