Gieneq Kwiecień 20, 2021 Udostępnij Kwiecień 20, 2021 @ethanak Dziękujemy albo raczej dziękuję za uwagę Będę na to zwracał uwagę. 20 minut temu, ethanak napisał: Jakby ktoś pytał czego się czepiam: WCAG 2.1 kryterium 1.4.3 Przyda się. Z ciekawości wyznaczyłem relatywną luminescencję, współczynnik kontrastu i wyszło dla żółtego 1,28 a dla czerwonego 2,24, czyli daleko do 4,5. Opis tego czerwonego pinu 3,3 V jest dostatecznie czytelny?
ethanak Kwiecień 20, 2021 Udostępnij Kwiecień 20, 2021 4 minuty temu, Gieneq napisał: wyszło dla żółtego 1,28 Jak to liczyłeś? Bo analizator pokazał 1.66 dla żółtego. Poza tym w tym przypadku można przyjąć wartość 3 a nie 4.5 (duży tekst). Czerwony jest czytelny - zresztą analizator pokazuje 3.81, co idealnie zgadza się z moim okiem
Gieneq Kwiecień 20, 2021 Udostępnij Kwiecień 20, 2021 @ethanak Z tego co podesłałeś. Względną luminescencję z tego wzoru, a współczynnik kontrastu z tego. Sprawdziłem dwa razy i obliczenia sa ok, chyba że nie zrozumiałem jak zdefiniowane są wartości R, G, B we zorze na luminescencję, acz żadne RsRGB, GsRGB, BsRGB nie są poniżej progu 0.03928 więc wzór się nie komplikuje. No cóż. 18 minut temu, ethanak napisał: Czerwony jest czytelny - zresztą analizator pokazuje 3.81, co idealnie zgadza się z moim okiem To najważniejsze
ethanak Kwiecień 20, 2021 Udostępnij Kwiecień 20, 2021 6 minut temu, Gieneq napisał: Z tego co podesłałeś Czyli trzeba używać analizatora - podaje przyjemniejsze wyniki 1
Popularny post Gieneq Kwiecień 20, 2021 Popularny post Udostępnij Kwiecień 20, 2021 (edytowany) Zgodnie z pogróżkami zabrałem się za przykłady z kursu - o dziwo tak proste zadanie jak sterowanie linijki LED jest bardzo rozwojowe. Pochwalę się efektami wieczora z STM Dobrym sposobem na uczenie się jest wystawianie sobie większego celu i dążenie do niego poprzez realizację pomniejszych zadań, pisanie działającego kodu i optymalizowanie go, nabudowywanie aby był bardziej elastyczny - to wszystko pewnie jakoś się nazywa w żargonie programistycznym. Dodatek do dodatku Wymyśliłem sobie, że takie klikanie w przycisk i przełączanie LEDa jest dobre, ale chcę mieć jeszcze: lepszą obsługę przycisku, tak by dało się kliknąć i wyzwolić funkcję, brak delayów i pozorne działanie równoległe, różne animacje i jakiś elastyczny sposób na ich dodawanie. Obsługa przycisku V2 Na początek struktura podobna do pin_t: typedef struct { GPIO_TypeDef *port; uint16_t pin; } pin_t; typedef struct { GPIO_PinState lastState; pin_t buttonPin; } button_t; Dodałem zmienną do trzymania informacji o poprzednim stanie lastState - dzięki niej porównując obecny stan i ten z poprzedniego obiegu pętli, możliwa jest detekcja zbocza. Zbocze pozwala jednorazowo wykonać jakiś kod programu. Można to zrealizować wewnątrz głównej pętli programu: static button_t button = {GPIO_PIN_RESET, { BUTTON_BACKWARD_GPIO_Port, BUTTON_BACKWARD_Pin } }; /* ... */ while (1) { GPIO_TypeDef *port = button.buttonPin.port; int pin = button.buttonPin.pin; GPIO_PinState currentState = HAL_GPIO_ReadPin(port, pin); if (button.lastState == GPIO_PIN_RESET && currentState == GPIO_PIN_SET) { //on release event } else if (button.lastState == GPIO_PIN_SET && currentState == GPIO_PIN_RESET) { //on press event } button.lastState = currentState; } Umożliwia to detekcję nawet bardzo krótkich kliknięć, a jeżeli nie wpakuje się delaya, to praktycznie zawsze będzie działać w czasie rzeczywistym Teraz w miejsce komentarzy można dodać kod obsługi przycisku. Tylko jeżeli teraz wymarzę sobie drugi przycisk, to będę musiał skopiować ten kod. A co przy 10 przyciskach? Czas to usprawnić! Twór klasopodobny W C istnieje sposób na stworzenie takiej jakby tworu klasopodobnego. W strukturze można przechować wskaźnik na uprzednio zdefiniowaną funkcję. Potrzebny jest tylko typ, który będzie reprezentował strukturę funkcji. Podobnie jak z innymi własnymi typami dodaję końcówkę _t aby łatwiej odróżnić to od nazw funkcji: typedef void (*button_event_t)(); Brak argumentów, brak wyniku - nie ma potrzeby, grunt żeby coś robiła. Teraz można w strukturze dodać kolejne elementy i je zainicjować. Przykład przełączania LD2 przyciskiem na płytce Nucleo: typedef struct { GPIO_PinState lastState; pin_t buttonPin; button_event_t onRelease; } button_t; void toggleLD2() { //onRelease code HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); } static button_t button = { GPIO_PIN_RESET, { USER_BUTTON_GPIO_Port, USER_BUTTON_Pin }, toggleLD2}; Teraz wystarczy uzupełnić kod obsługi zdarzenia puszczenia przycisku. W przypadku podłączenia z Pull-up zdarzeniem będzie zmiana stanu z niskiego (0) na wysoki (1): while (1) { GPIO_TypeDef *port = button.buttonPin.port; int pin = button.buttonPin.pin; GPIO_PinState currentState = HAL_GPIO_ReadPin(port, pin); if (button.lastState == GPIO_PIN_RESET && currentState == GPIO_PIN_SET) { //on release event button.onRelease(); } else if (button.lastState == GPIO_PIN_SET && currentState == GPIO_PIN_RESET) { //on press event } button.lastState = currentState; } Warto zauważyć, że detekcja zdarzeń nie nic wspólnego z implementacją kodu w funkcjach wykonywanych przy tych zdarzeniach. Dzięki takiemu podejściu można zapomnieć jak działa funkcja obsługi przycisku, tylko skupić się na tym co ma robić. Podobny mechanizm można spotkać w systemie sygnałów i slotów Pythona Z taką wygodną strukturą można pokusić się o więcej przycisków, tworzymy więc tablicę przycisków: static button_t buttons[] = { { GPIO_PIN_RESET, { BUTTON_BACKWARD_GPIO_Port, BUTTON_BACKWARD_Pin }, pressBackward }, { GPIO_PIN_RESET, { BUTTON_FORWARD_GPIO_Port, BUTTON_FORWARD_Pin }, pressForward }, { GPIO_PIN_RESET, { BUTTON_MODE_GPIO_Port, BUTTON_MODE_Pin }, changeTransition } }; Nazwy użytych funkcji tyczą się efektu końcowego, który opiszę później. Pytanie jak obsłużyć wszystkie przyciski? Najłatwiej poprzez iterację. Tylko wymaga to znajomości liczby elementów w tablicy, a w takim układzie nie jest to takie oczywiste, bo trzebaby jawnie zdefiniowac liczbe elementów. Aby tego uniknąć posłużę się takim macrem: #define ARRLEN(x) (sizeof(x)/sizeof(x)[0]) static const int BUTTONS_COUNT = ARRLEN(buttons); Dzieląc rozmiar całej tablicy przez rozmiar pojedynczego elementu uzyskamy liczbę elementów w tablicy. Można teraz rozbudować obsługę przycisków oraz wydzielić to do osobnej funkcji - dzięki temu już długo nie zajrzymy do funkcji main(): void checkAllButtonsEvents() { for (int i = 0; i < BUTTONS_COUNT; ++i) { button_t *button = &buttons[i]; GPIO_TypeDef *port = button->buttonPin.port; int pin = button->buttonPin.pin; GPIO_PinState currentState = HAL_GPIO_ReadPin(port, pin); if (button->lastState == GPIO_PIN_RESET && currentState == GPIO_PIN_SET) { //on release event button->onRelease(); } else if (button->lastState == GPIO_PIN_SET && currentState == GPIO_PIN_RESET) { //on press event } button->lastState = currentState; } Pięknie! Bardzo elastyczna funkcja, gdzieś w kodzie dodajemy funkcje obsługi przycisków, tworzymy struktury reprezentujące przycisk, podpinamy i działa. Wygląda to już tak abstrakcyjnie, że prawie nie zastanawiamy się jak to działa. Debouncing Tylko co z drganiami styków. Miało być bez delayów Szybki dodatek do dodatku - takie STMowe millis(): uint32_t millis = HAL_GetTick(); Do struktury dodaję pole informujące o momencie wciśnięcia przycisku, wystarczy odczekać zadany czas aby uznać, że drgania styków minęły. Odbędzie się to jednak bez zatrzymywania programu! typedef struct { GPIO_PinState lastState; pin_t buttonPin; button_event_t onRelease; uint32_t bounceStartTimeMillis; } button_t; #define BOUNCE_TIME_MILLIS 50 static button_t buttons[] = { { GPIO_PIN_RESET, { BUTTON_BACKWARD_GPIO_Port, BUTTON_BACKWARD_Pin }, pressBackward, 0 }, { GPIO_PIN_RESET, { BUTTON_FORWARD_GPIO_Port, BUTTON_FORWARD_Pin }, pressForward, 0 }, { GPIO_PIN_RESET, { BUTTON_MODE_GPIO_Port, BUTTON_MODE_Pin }, changeTransition, 0 } }; void checkAllButtonsEvents() { uint32_t millis = HAL_GetTick(); for (int i = 0; i < BUTTONS_COUNT; ++i) { button_t *button = &buttons[i]; if (millis - button->bounceStartTimeMillis > BOUNCE_TIME_MILLIS) { GPIO_TypeDef *port = button->buttonPin.port; int pin = button->buttonPin.pin; GPIO_PinState currentState = HAL_GPIO_ReadPin(port, pin); if (button->lastState == GPIO_PIN_RESET && currentState == GPIO_PIN_SET) { //on release event button->bounceStartTimeMillis = millis; button->onRelease(); } else if (button->lastState == GPIO_PIN_SET && currentState == GPIO_PIN_RESET) { //on press event } button->lastState = currentState; } } } Zaczyna wyglądać groźnie, ale logika jest prosta: gdy wciśnięto przycisk, zapisz aktualny czas i wykonaj zadanie (delegat inaczej funkcję, na którą wskaźnik mamy w strukturze), sprawdzaj czy upłynął czas, jeżeli tak to możesz znowu realizować obsługę przycisków. Longpress Jeżeli już umiemy wyznaczać czas, to zaimplementujmy zdarzenie długiego naciśnięcia przycisku (ang. longpress). Dodajemy kolejne pola do struktury: typedef struct { GPIO_PinState lastState; pin_t buttonPin; button_event_t onRelease; button_event_t onLongpress; uint32_t bounceStartTimeMillis; uint32_t longpressTimeMillis; } button_t; static button_t buttons[] = { { GPIO_PIN_RESET, { BUTTON_BACKWARD_GPIO_Port, BUTTON_BACKWARD_Pin }, pressBackward, longpressBackward, 0, 0 }, { GPIO_PIN_RESET, { BUTTON_FORWARD_GPIO_Port, BUTTON_FORWARD_Pin }, pressForward, longpressForward, 0, 0 }, { GPIO_PIN_RESET, { BUTTON_MODE_GPIO_Port, BUTTON_MODE_Pin }, changeTransition, changeMode, 0, 0 } }; Obsługa tego niewiele się różni od eliminacji drgań styków - znowu trzeba sprawdzać czy dla wyznaczonego przycisku upłynął zadany czas: void checkAllButtonsEvents() { uint32_t millis = HAL_GetTick(); for (int i = 0; i < BUTTONS_COUNT; ++i) { button_t *button = &buttons[i]; if (millis - button->bounceStartTimeMillis > BOUNCE_TIME_MILLIS) { GPIO_TypeDef *port = button->buttonPin.port; int pin = button->buttonPin.pin; GPIO_PinState currentState = HAL_GPIO_ReadPin(port, pin); if (button->lastState == GPIO_PIN_RESET && currentState == GPIO_PIN_SET) { //on release event if (button->longpressTimeMillis > 0){ button->bounceStartTimeMillis = millis; button->onRelease(); } } else if (button->lastState == GPIO_PIN_SET && currentState == GPIO_PIN_RESET) { //on press event -> on longpress event button->longpressTimeMillis = millis; } else if (currentState == GPIO_PIN_RESET) { if (button->longpressTimeMillis > 0) { if(millis - button->longpressTimeMillis > LONGPRESS_TIME_MILLIS){ button->longpressTimeMillis = 0; button->onLongpress(); } } } button->lastState = currentState; } } } To już ostateczna postać tej funkcji Dodałem w końcu coś w bloku onPress, tym razem inicjuje to zliczanie czasu. Następnie, gdy stan przycisku jest niski (wciśnięty) to sprawdzamy czy upłynął czas zdefiniowany dla długiego kliknięcia: #define LONGPRESS_TIME_MILLIS 500 Jeżeli tak, to kasujemy zmienną i wykonujemy delegata. Kasacja zmiennej zapobiegnie wykonaniu zdarzenia onRelease. Jeżeli zanim upłynie 500 ms przycisk zostanie puszczony (zmienna nie zostanie skanowana) to zdarzenie onRelease zostanie wykonane. Tyle kodu żeby obsłużyć przycisk, ale warto. Możemy teraz klikać co chcemy, ile chcemy i w dowolnej kolejności Efekty i siła Mamy sterownie, teraz efekty. Pierwsze co bym sobie życzył to możliwość przechodzenia między trybem klikania i takim gdzie wyświetlana jest animacja. Najprościej dodać zmienną odpowiedzialną za to zadanie i sprawdzać w jakim trybie ma działać program. Do przęłączania można wykorzystać funkcję obsługi zdarzenia przycisku. Aby jednak zawęzić możliwości utworzę typ wyliczeniowy: typedef enum { MODE_MANUAL, MODE_ANIMATED } mode_t; Teraz wystarczy zdefiniować interwał i sprawdzać warunki podobne co poprzednio: static uint32_t interval = 128; static uint32_t transitionTimer = 0; void animate(bool force) { if(force || ((mode == MODE_ANIMATED) && (HAL_GetTick() - transitionTimer > interval))) { //execute selected transition animations[transition](); if(!force) transitionTimer += interval; } } A co to za kwiatki? Jakieś force? Czemu nie stały interwał? Znowu tablica!? Trochę popchnę ten post bez rozdrabniania się na 10 kroków pośrednich Argument force jest potrzebny do wymuszania aktualizacji - robi się to przyciskiem. Jako że jest to funkcja, to w pętli głównej będzie używana bez "siły", a w funkcjach zdarzeń przycisków z "siłą". W główne pętli programu, gdy program działa w trybie automatycznym to liczymy czas (z modyfikowalną szybkością) tak jak w przyciskach i wykonujemy... przejścia. Znowu jakieś delegaty. Przejścia Pomyślałem, że jedna animacja na taką ładną płytkę to za mało. Dodaję więc trochę kodu: typedef enum { BACKWARD = -1, FORWARD = 1 } direction_t; typedef void (*transition_handler_t)(); typedef enum { TRANSITION_SIMPLE, TRANSITION_BOUNCE, TRANSITION_TOGGLE_SIMPLE, TRANSITION_TOGGLE_BOUNCE, TRANSITION_TWOCOLORS, TRANSITION_CROSSROADS, TRANSITION_NOISE, TRANSITION_RAINDROPS, TRANSITIONS_COUNT } transition_t; static const transition_handler_t animations[TRANSITIONS_COUNT] = {simpleTransitionAnimation, bounceAnimation, toggleSimpleAnimation, bounceToggleAnimation, twocolorsAnimation, crossroadsAnimation, noiseAnimation, raindropsAnimation}; W przykładzie z kursu można było poruszać się w dwie strony - wiele animacji będzie miało taką możliwość więc przy tym zostańmy - typ direction_t. Kolejna część to znana już tablica z delegatami i typ wyliczeniowy. Kod ten został tak przygotowany, że aby dodać nowy efekt wystarczy dopisać jego nazwę i wstawić wskaźnik na funkcję do tablicy. Nie musimy liczyć elementów tablicy, bo ostatni element typu wyliczeniowego trzyma tę informację za nas (nie wolno tylko wpakować czegoś po tym elemencie). Wybierając jakiś efekt będzie on automatycznie odtwarzany. Zmiana efektu sprowadza się do wpisania czegoś do zmiennej: static transition_t transition = TRANSITION_SIMPLE; Sterowanie To teraz sterowanie. Używam 3 przycisków i 6 zdarzeń. Pierwsze 2 służą do sterowania kierunkiem animacji i szybkością: void pressForward() { if(mode == MODE_MANUAL){ direction = FORWARD; animate(true); } else { if(--intervalLevel < 1) intervalLevel = 1; interval = powint(TRANSITION_INTERVAL_BASE, intervalLevel); } } void pressBackward() { if(mode == MODE_MANUAL){ direction = BACKWARD; animate(true); } else{ ++intervalLevel; interval = powint(TRANSITION_INTERVAL_BASE, intervalLevel); } } Widać tu wymuszenie w trybie manualnym - ustalony zostaje kierunek i wyzwalane jest przejście. A co w trybie automatycznym? Znowu jakieś dziwactwo... Przy testach denerwowało mnie klikanie dziesiątki razy żeby zwiększyć interwał. Więc zmiana czasu następuje wykładniczo #define TRANSITION_INITIAL_LEVEL 8 #define TRANSITION_INTERVAL_BASE 2 static int intervalLevel = TRANSITION_INITIAL_LEVEL; static uint32_t interval = 128; Kolejne 2 zdarzenia związane są z długim przytrzymaniem przycisku i działają tak samo dla obu trybów - zmiana kierunku: void longpressBackward() { direction = BACKWARD; } void longpressForward() { direction = FORWARD; } Teraz zmiana trybu pracy. Zwykłe naciśnięcie zwyczaje przełącza animacje i czyści linijkę LED, zaś długie naciśnięcie zmieni tryb z manualnego na automatyczny i odwrotnie. void changeMode() { if(mode == MODE_MANUAL) { transitionTimer = HAL_GetTick(); mode = MODE_ANIMATED; } else mode = MODE_MANUAL; return; } void changeTransition() { if(mode != MODE_MANUAL){ transitionTimer = HAL_GetTick(); leds_clear(); led = 0; direction = FORWARD; } if(++transition >= TRANSITIONS_COUNT) transition = TRANSITION_SIMPLE; } Widać tu zaletę używania typu wyliczeniowego - elementy są automatycznie ponumerowane. Wincyj animacji Chciałem animacje to mam, wymyśliłem kilka ale każdy może wymyślić swoje. Grunt to znać zasadę tworzenia. W bieżącej iteracji trzeba wyczyścić LEDy, które były ostatnio wyświetlone i ustawić nowe - po tym nastąpi opóźnienie umożliwiające obserwację animacji. Dysponujemy przy tym zmiennymi: kierunek animacji (zmienna direction) i aktualnie wybrany led (zmienna led). Amnimacja z kursu wygląda tak: void simpleTransitionAnimation(){ led_set(led, false); led += direction; if(direction == BACKWARD && led < 0) led += LEDS_COUNT; else if(direction == FORWARD && led >= LEDS_COUNT) led -= LEDS_COUNT; led_set(led, true); } Widać, że użyłem tej samej funkcji led_set. Podobna funkcja zrealizuje animację "odbijania piłeczki": void bounceAnimation(){ led_set(led, false); led += direction; if(direction == BACKWARD && led < 0){ led = -led; direction = FORWARD; } else if(direction == FORWARD && led >= LEDS_COUNT){ led = led - LEDS_COUNT + 1; led = LEDS_COUNT - led - 1; direction = BACKWARD; } led_set(led, true); } Różni się tylko zmianą kierunku. Oczywiście zmiana kierunku przez funkcje przycisku nadal działa - program napisany elastycznie dalej zachowuje swoje właściwości To teraz dodamy stylowego "knight ridera": void led_toggle(int led) { if (led >= 0 && led < LEDS_COUNT) HAL_GPIO_TogglePin(LED[led].port, LED[led].pin); } void toggleSimpleAnimation(){ led_toggle(led); led += direction; if(direction == BACKWARD && led < 0) led += LEDS_COUNT; else if(direction == FORWARD && led >= LEDS_COUNT) led -= LEDS_COUNT; } void bounceToggleAnimation(){ led_toggle(led); led += direction; if(direction == BACKWARD && led < 0){ led = -led-1; direction = FORWARD; } else if(direction == FORWARD && led >= LEDS_COUNT){ led = led - LEDS_COUNT + 1; led = LEDS_COUNT - led ; direction = BACKWARD; } } Jeszcze 2 inne. Tu warto zwrócić uwagę na wykorzystanie operacji XOR (daszek ^): void twocolorsAnimation(){ for (int i = 0; i < LEDS_COUNT/2; ++i) led_set(i*2+led, false); led ^= 1; for (int i = 0; i < LEDS_COUNT/2; ++i) led_set(i*2+led, true); } void crossroadsAnimation(){ led_set(led, false); led_set(LEDS_COUNT - led - 1, false); led += direction; if(direction == BACKWARD && led < 0) led += LEDS_COUNT; else if(direction == FORWARD && led >= LEDS_COUNT) led -= LEDS_COUNT; led_set(led, true); led_set(LEDS_COUNT - led - 1, true); } Dodatek do dodatku - liczby losowe Szybkie wtrącenie o liczbach losowych. Trzeba ustawić coś w konfiguracji i gotowe: W kodzie zostanie dodany uchwyt: RNG_HandleTypeDef hrng; Z którego można teraz skorzystać: int randLED() { HAL_RNG_GenerateRandomNumber(&hrng, &randResult); return randResult % LEDS_COUNT; } Jak podejrzymy implementację funkcji HAL_RNG_GenerateRandomNumber (Ctrl+click) to zobaczymy, że zwraca liczbę typu uint32_t: HAL_StatusTypeDef HAL_RNG_GenerateRandomNumber(RNG_HandleTypeDef *hrng, uint32_t *random32bit) { /* ... */ Nie będę się w nią jednak zagłębiał i przejdę dalej. Tworzę teraz kolejne efekty: static uint32_t randResult = 0; static int rainAnimationStage; void noiseAnimation(){ led_set(led, false); led = randLED(); led_set(led, true); } void raindropsAnimation() { if(rainAnimationStage == 0) { led = randLED(); led_set(led, true); } else if(rainAnimationStage == 1) { led_set(led - 1, true); led_set(led + 1, true); } else if(rainAnimationStage == 2) { led_set(led, false); led_set(led - 2, true); led_set(led + 2, true); } else if(rainAnimationStage == 3) { led_set(led - 1, false); led_set(led + 1, false); } else if(rainAnimationStage == 4) { led_set(led - 2, false); led_set(led + 2, false); } if(++rainAnimationStage > 4) rainAnimationStage = 0; } Pierwsza animacja zwyczajnie losuje LED. Druga to taki jakby efekt deszczu - w losowym miejscu spada kropla i wyświetlany jest "kolisty ślad", potrzeba tylko sporo wyobraźni Podsumowanie Co z tego wszystkiego wyszło? Odpicowana linijka LED, z bardzo elastycznym kodem, swobodą dodawania efektów, brakiem opóźnień w obsłudze przycisków. Można by to jeszcze zapakować w osobny plik i nazwać biblioteką. Oczywiście da się do tego przyczepić, ale myślę, że pozostawi jakąś wartość edukacyjną. Efekt końcowy na filmiku: Edytowano Kwiecień 21, 2021 przez Gieneq 15 2
szumen Kwiecień 22, 2021 Udostępnij Kwiecień 22, 2021 Mega artykuł, z niecierpliwością czekam na następne 1
Kicajski Kwiecień 22, 2021 Udostępnij Kwiecień 22, 2021 Super kurs ale jednego mi brakuje, brakowało mi też tego na studiach elektronicznych - choćby skrócony kurs jak krok po kroku wyszukiwać w dokumentacji to co trzeba wpisywać w kodzie, linijka po linijce. Wytłumaczenie choćby na początku za co odpowiada każdy człon w jakiejś części kodu. Umiejętność czytania dokumentacji i wyszukiwania w nim informacji, których potrzebujemy to moim zdaniem połowa sukcesu.
Treker (Damian Szymański) Kwiecień 22, 2021 Udostępnij Kwiecień 22, 2021 @Kicajski witam na forum Miło słyszeć, że kurs Ci się podoba! Jeśli chodzi o Twoją sugestię to temat ten nie jest zbyt często poruszany w różnych miejscach dlatego, że jest to raczej mało uniwersalne. To, że pokażemy wprost jak z dokumentacji uruchomić konkretną rzecz nie oznacza, że tak samo łatwo pójdzie czytelnikowi z innymi tematami. Czasami proste poradniki, które pokazują jak łatwo coś uruchomić, wynikają jednak głównie z doświadczenia danego autora. To co mogę na ten moment polecić to radziłbym np. wykonywać ćwiczenia z kursu i jednocześnie próbować odszukiwać tych samych rzeczy w dokumentacjach układu - trochę od drugiej strony, ale na początek może być pomocne W dalszych częściach kursu będziemy korzystać np. z modułów czujników, wtedy na pewno pokażemy gdzie i po co zajrzeć do noty katalogowej danego układu. 1
Gieneq Kwiecień 23, 2021 Udostępnij Kwiecień 23, 2021 Dnia 20.04.2021 o 14:20, ethanak napisał: Rysunek zatytułowany "Lokalizacja pinów używanych w ćwiczeniu", bardzo ładne białe cyferki na żółtym tle. Estetyczne, nie powiem, ale fajnie by było położyć większy nacisk na czytelność, a dopiero potem estetykę. @ethanak grafika w kursie zaktualizowana, teraz mamy piękny współczynnik kontrastu 6.67:1
Treker (Damian Szymański) Kwiecień 23, 2021 Udostępnij Kwiecień 23, 2021 Kolejna część kursu STM32L4 jest już dostępna - zachęcam do lektury: Kurs STM32L4 – #4 – komunikacja przez UART, debugger
ethanak Kwiecień 23, 2021 Udostępnij Kwiecień 23, 2021 51 minut temu, Gieneq napisał: grafika w kursie zaktualizowana, teraz mamy piękny współczynnik kontrastu 6.67:1 Super - tak trzymać! 1
szeryf Maj 22, 2021 Udostępnij Maj 22, 2021 (edytowany) Świetny odcinek kursu, w końcu znalazłem czas żeby przysiąść i podziałać. Szczególnie rozwijający od strony programistycznej jak dla mnie, okazał się ten dodatek. Jak do tego jeszcze dołożymy dodatek do dodatku, którym się podzielił Gieneq, to mamy naprawdę super materiał do nauki. Podczas zabawy z przełączaniem portów i pinów zauważyłem, że diody mi nie działają, gdy podłączę je pod PA2 lub PA3. Czy to normalne, czy mogę mieć jakiś problem z płytką? Edytowano Maj 22, 2021 przez szeryf 2
Popularny post Elvis Maj 25, 2021 Popularny post Udostępnij Maj 25, 2021 Piny PA2 i PA3 są na płytce Nucleo połączone z konwerterem UART-USB, więc nie można ich wykorzystywać jako zwykłe GPIO, albo raczej można, ale wymagałoby to usunięcia kilku zworek na płytce. Najlepiej jednak zostawić płytkę bez zmian i użyć innych pinów 2 1
Frantick Maj 29, 2021 Udostępnij Maj 29, 2021 (edytowany) co do zadania domowego nr 1. Czy zakaz zmiany programu ogranicza sie tylko do tych 4 linijek w petli while? czy np. zmiana w enumie GPIO_PinState jest uznawana za zmiane w programie? Edytowano Maj 29, 2021 przez Frantick
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ę »