Skocz do zawartości

Przeszukaj forum

Pokazywanie wyników dla tagów 'STM32'.

  • Szukaj wg tagów

    Wpisz tagi, oddzielając przecinkami.
  • Szukaj wg autora

Typ zawartości


Kategorie forum

  • Elektronika i programowanie
    • Elektronika
    • Arduino i ESP
    • Mikrokontrolery
    • Raspberry Pi
    • Inne komputery jednopłytkowe
    • Układy programowalne
    • Programowanie
    • Zasilanie
  • Artykuły, projekty, DIY
    • Artykuły redakcji (blog)
    • Artykuły użytkowników
    • Projekty - roboty
    • Projekty - DIY
    • Projekty - DIY (początkujący)
    • Projekty - w budowie (worklogi)
    • Wiadomości
  • Pozostałe
    • Oprogramowanie CAD
    • Druk 3D
    • Napędy
    • Mechanika
    • Zawody/Konkursy/Wydarzenia
    • Sprzedam/Kupię/Zamienię/Praca
    • Inne
  • Ogólne
    • Ogłoszenia organizacyjne
    • Dyskusje o FORBOT.pl
    • Na luzie
    • Kosz

Szukaj wyników w...

Znajdź wyniki, które zawierają...


Data utworzenia

  • Rozpocznij

    Koniec


Ostatnia aktualizacja

  • Rozpocznij

    Koniec


Filtruj po ilości...

Data dołączenia

  • Rozpocznij

    Koniec


Grupa


Znaleziono 79 wyników

  1. Witam, jak osiągnąć maksymalny sampling rate na ADC + DMA? SysClock ustawiony na 72Mhz. APB2CLKDivider i APB1CLKDivider ustawione na 1. ADC leci w ContinuousConvMode = ENABLE i ExternalTrigConv = ADC_SOFTWARE_START; SamplingTime = ADC_SAMPLETIME_13CYCLES_5; DMA zapisuje do bufora[128] z jednego kanału ADC i wysyłam sobie to na ekran co jakiś czas. Oczekuję zapisu badania sygnału do 20kHz, jak na razie nie łapie mi poprawnie nawet sygnału 9kHz, Co tu zrobić, dać zewnętrzny trigger z jakiegoś timera ustawionego na przerwanie co 44.1kHz? Jest to szybsze?
  2. Witam! To mój pierwszy wpis w dziale DIY. Projektem jest (a właściwie dopiero będzie) mini konsolka do gier na STM32F1. Robię ten projekt dla mojego 3-letniego synka. Docelowo będę mu wgrywać na nią różne napisane przeze mnie gry, ale na początek zacznę od klasyki, czyli gra w węża Robiłem już podobny projekt przy okazji kursu SMT32 na Forbocie, więc szybko powinienem napisać niezbędny kod. Tym razem ma to być gotowe urządzenie które bez problemu będzie mógł obsłużyć 3-latek. Założenia projektu: 1. Mikrokontroler STM32F1 2. Ekran TFT kolorowy 240x320 3. Zasilanie bateryjne LiPo z ładowaniem przez gniazdo USB (więcej szczegółów w moim wątku w dziale Zasilanie - szczególnie ostatni mój post) 4. Obudowa w całości drukowana w 3D 5. Żadnych gotowych modułów. Własny projekt PCB i własny montaż. 6. Klawiatura: Krzyżyk, przycisk A, przycisk B oraz przycisk POWER, jak w starych Game - Boy'ach 7. Dzwięk: Nad tym jeszcze nie myślałem, brak I2S oraz DACa na mojej płytce Nucleo trochę utrudnia sprawę. Może będzie coś na Timerach i PWM. Zobaczymy. Teraz po kolei. Pierwsze co musiałem zrobić to ogarnąć wyświetlanie na TFT. Zakupiłem trochę w ciemno kilka wyświetlaczy 2,8" na ILI9341. Mój brak doświadczenia z TFT natychmiast się zemścił. Po odbiorze przesyłki okazało się że obsługa SPI wcale nie jest oczywista i jestem skazany na 16-bitową magistralę. Nie znalazłem nigdzie driverów na STM32 do sterowania tym kontrolerem przy takim połączeniu, ale znalazłem kilka projektów na GitHub'ie gdzie było sterowanie przez magistralę 8-bitową. Postanowiłem więc na podstawie tych projektów napisać własne drivery i procedury wyświetlania podstawowych kształtów. W trakcie prac nad optymalizacją wyświetlania okazało się że jednak 16-bitowa magistrala to doskonały pomysł i jest 4-krotnie szybsza od 8-bitowej. Dlaczego? Już tłumaczę: Gdy używa się 8-bitowej szyny danych to wysłanie piksela wygląda następująco (już po ustawieniu adresu): - Ustawiamy pierwsze 8 bitów koloru - WR strobe - czyli szybka zmiana linii WR na 0 i powrót 1 - Ustawiamy kolejne 8 bitów koloru - WR strobe - czyli szybka zmiana linii WR na 0 i powrót 1 I powtarzamy do czasu wypełnienia zaadresowanego okienka. Czyli na każdy piksel przypadają 4 kroki. Gdy używamy 16-bitowej magistrali - wygląda to następująco: - Ustawiamy 16 bitów koloru - WR strobe - czyli szybka zmiana linii WR na 0 i powrót 1 I powtarzamy do czasu wypełnienia zaadresowanego okienka. Czyli na każdy piksel przypadają 2 kroki. Ale przecież pisałem że jest 4 razy szybciej, a tu wychodzi że 2 razy No więc łatwo można zauważyć, że przy 16 bitach raz ustawiony kolor na linii danych możemy zostawić do czasu aż będziemy musieli użyć innego koloru. Więc jeżeli mamy kilkaset pikseli do wypełnienia jednym kolorem to raz go ustawiamy, a później już tylko WR strobe tak długo jak potrzebujemy Czyli realnie wykonujemy tylko jeden krok. Zaimplementowałem to w moich driverach i rezultaty były doskonałe. Na tyle dobre że odpadła mi konieczność posiadania pamięci pod bufor klatki. Wszystko wyświetla się błyskawicznie: Aktualnie mogę wyświetlać do 8Mpix/s, co teoretycznie pozwala odświeżyć ekran 240x320 ponad 100 razy na sekundę na STM32F1 taktowanym 64MHz. Czyli mogę jeszcze podnieść tą częstotliwość do 72 MHz i jeszcze zwiększyć transfery. Niestety chwilowo mam odcięty programator od płytki Nucleo i muszę kożystać z wewnetrznego oscylatora który pozwala rozkręcić mikroprocesor do 64MHz. Kolejnym problemem z którym musiałem się zmierzyć to mała ilość flash na grafiki do gier. Jak łatwo policzyć jedna pełnoekranowa grafika to 153600 bajty, a mam tylko 128k do dyspozycji. A jeszcze musi się zmieścić program. Rozwiązaniem problemu jest kompresja. Tu znów musiałem od zera napisać program do kompresji grafiki który spełniałby moje wymagania. Kompresor został napisany w Pythonie. A szybki dekoder zaimplementowany w C w driverach na STM32. Pisałem program praktycznie od zera wg mojego pomysłu. Otóż dzieli on grafikę na bloki o jednolitych kolorach, a ich metadane zapisuje w binarnym pliku wyjściowym, albo w pliku C. Bloki są następujące: pozioma linia, pionowa linia, prostokąt. Pojedynczy piksel to pozioma linia o długości 1. Kompresja odbywa się przez rozkład grafiki na takie bloki, a następnie zapisaniu ich parametrów do pliku wyjściowego. Procedurę dekompresji zaimplementowałem w driverach do wyświetlacza, a każdy blok łatwo można wysłać bezpośrednio do pamięci ekranu, gdzie obraz zostaje odtworzony. Kolejną funkcją którą zaimplementowałem w kompresorze jest możliwość kodowania różnicy pomiędzy dwoma grafikami. Tzn jeżeli robię animację to program kompresuje tylko piksele które różnią dwie klatki. Poniżej proces odtwarzania obrazka na wyświetlaczu w zwolnionym tempie: Skoro miałem już ograne wyświetlanie grafiki, przyszedł czas na prototypowanie konsoli i pisanie samego kodu gry Aktualnie moje stanowisko pracy wygląda następująco: Sama gra jest na etapie tworzenia grafiki. Na razie mam animacje na intro: To tyle na dzisiaj. Wraz z postępami będę aktualizował ten wątek. Pozdrawiam, Marek
  3. Na podstawie kursu STM32 chcę uruchomić płytkę STM32F103C8T6, zdjęcie płytki poniżej. Oczywiście nie mogę podczas tworzenia projektu wybrać NUCLEO-F103RB więc poniżej przesyłam zdjęcia z konfiguracji, czy jest ona prawidłowa ? Czy może powinienem wejść w zakładkę MCU a nie Board i tam wybrać procesor ? Przechodząc do biblioteki, zgodnie z opisaną aktualizacją: https://forbot.pl/forum/topic/8387-kurs-stm32-3-plytka-nucleo-konfiguracja-srodowiska/page/10/#comments Dla NUCLEO-F103RB: https://www.st.com/en/embedded-software/stsw-stm32143.html#overview Dla STM32F103C8T6: https://www.st.com/content/st_com/en/products/microcontrollers-microprocessors/stm32-32-bit-arm-cortex-mcus/stm32-mainstream-mcus/stm32f1-series/stm32f103/stm32f103c8.html#tools-software Następnie wybrałem zakładkę Tools & Software, następnie MCU & MPU Embedded Software, następnie najbardziej podobny plik do "based on Standard Peripheral Library" to (proszę kliknąć ctrl+f i wkleić STM32-CLASSB-SPL). Czy to jest odpowiedni plik ?
  4. Cześć. Mam taki problem, mam program do obsługi radia i chcę za pomocą enkodera wybierać odpowiednie stacje. Mam zmienną do której zapisuje pozycje enkodera (wzorowałem się na kursie Forbota z stm32F4) potem za pomocą instrukcji switch sprawdzam tę pozycję i na tej podstawie wykonuję się odpowiedni case który ustawia stację, to nie jest optymalne bo muszę to zapętlać żeby co chwilę sprawdzać pozycję enkodera. Nie wiem czy słusznie, ale wydaje mi się że powinienem tutaj użyć przerwań i jakiejś funkcji callback. Moje pytanie brzmi jak skonfigurować Timer, a następnie co zrobić żeby program wykrywał zmianę pozycji enkodera? Dodam jeszcze że przeglądałem dokumentacje biblioteki HAL i dalej nie jestem w stanie tego zrobić. Mam nadzieję że w miarę jasno to przedstawiłem dołączyłem jeszcze screen aktualnego programu (jest bardzo prymitywny ale sprawdzałem czy wszystko inne działa :D)
  5. W przypływie wolnego czasu chciałem przećwiczyć sobie obsługę USB w STM32 i przy okazji zrobić coś praktycznego do podłączenia do komputera. Ponieważ mam klawiaturę mechaniczną z przyciskami multimedialnymi ukrytymi pod mało praktyczną kombinacją z klawiszem FN (klawisz po prawej stronie - totalnie niepraktyczne w tym zastosowaniu) postanowiłem zrobić sobie dedykowany pilot do multimediów. Platforma STM32F103 bo mam mały zapas płytek BluePill. Jako sterowanie wybrałem joystick z enkoderem na wzór tych z systemów infotainment w samochodzie. Żeby doprecyzować joysticki ALPS RKJXT1F42001 zamówione jakiś czas temu na aliexpress i czekające na projekt jak ten. Kod na STM32 powstawał na bazie generowanego przez CubeIDE przy middleware USB_DEVICE z wybranym Human Interface Device Class (HID). W moim odczuciu generowany kod jest chyba bardziej demonstracją, ponieważ kod z definicjami deskryptorów jest stworzony dla myszy, i nie ma nigdzie znaczników USER_CODE_BEGIN/END. Próbowałem coś zadziałać z klasą Custom Human Interface Device ale mogłem znaleźć deskryptorów urządzenia, konfiguracji i interfejsu w kodzie, a deskryptor raportu HID był do napisania od zera, co jest raczej trudne przy pierwszym projekcie z USB. Zostałem przy przykładzie z myszką od ST, który sukcesywnie modyfikowałem, tworząc kopie zapasowe na wypadek potrzeby generowania kodu z Cube (przez brak znaczników USER_CODE wszystkie zmiany - głównie zmodyfikowany deskryptor raportu - byłyby nadpisane przez wygenerowany kod). Nie będę opisać tworzenia mojego deskryptora bo robiłem to raczej na czuja metodą prób i błędów. W skrócie chodzi o to, że deskryptor raportu HID informuje host(komputer) o możliwościach urządzenia oraz przypisuje funkcjonalności bajtom w raporcie HID - w moim przypadku każdy bit w drugim bajcie raportu niesie informacje o wciśnięciu przycisku z grupy consumer page (0x0C) (więcej o nich w dokumentacji USB HID - HID Usage Tables 1.12). Utworzenie deskryptora od zera jest raczej trudne dla początkującego ale w internecie jest sporo informacji - wielką pomocą był dla mnie raport z przykładowej implementacji (http://www2.ece.rochester.edu/~parihar/pres/Report_MultiMedia-KB.pdf) w którym o wiele lepiej wytłumaczono działanie USB, klas i deskryptorów, niż ja to jestem w stanie zrobić. Oprócz tego mój deskryptor raportu jest lekko zmodyfikowaną wersją tego spod linku. Deskryptory urządzenia, konfiguracji i interfejsu wykorzystałem w wersji wygenerowanej przez cube dla myszy - zmieniłem jedynie typ urządzenia na klawiaturę (0x01) w deskryptorze konfiguracji (Middlewares/ST/STM32_USB_Device_Library/Class/HID/Src/usbd_hid.c). Deskryptor raportu powstał na podstawie tego podlinkowanego. Mapowanie przycisków do bitów w raporcie jest tam zobrazowane na stronie 11. Dobry tutorial o deskryptorach raportów HID tutaj. Gotowy deskryptor raportu przedstawia się tak: 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) 0x09, 0x01, // USAGE (Consumer Control) 0xa1, 0x01, // COLLECTION (Application) 0x85, 0x01, // REPORT_ID (1) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x10, // REPORT_COUNT (16) 0x09, 0xe2, // USAGE (Mute) 0x01 0x09, 0xe9, // USAGE (Volume Up) 0x02 0x09, 0xea, // USAGE (Volume Down) 0x03 0x09, 0xcd, // USAGE (Play/Pause) 0x04 0x09, 0xb7, // USAGE (Stop) 0x05 0x09, 0xb6, // USAGE (Scan Previous Track) 0x06 0x09, 0xb5, // USAGE (Scan Next Track) 0x07 0x0a, 0x8a, 0x01, // USAGE (Mail) 0x08 0x0a, 0x92, 0x01, // USAGE (Calculator) 0x09 0x0a, 0x21, 0x02, // USAGE (www search) 0x0a 0x0a, 0x23, 0x02, // USAGE (www home) 0x0b 0x0a, 0x2a, 0x02, // USAGE (www favorites) 0x0c 0x0a, 0x27, 0x02, // USAGE (www refresh) 0x0d 0x0a, 0x26, 0x02, // USAGE (www stop) 0x0e 0x0a, 0x25, 0x02, // USAGE (www forward) 0x0f 0x0a, 0x24, 0x02, // USAGE (www back) 0x10 0x81, 0x62, // INPUT (Data,Var,Abs,NPrf,Null) 0xc0 I definicje bitów dla przycisków oraz zmiennej przechowującej raport HID do wysłania #define HID_REPORT_SIZE 3 #define MEDIA_MUTE 0 #define MEDIA_VOLUP 1 #define MEDIA_VOLDOWN 2 #define MEDIA_PLAY_PAUSE 3 #define MEDIA_STOP 4 #define MEDIA_PREV 5 #define MEDIA_NEXT 6 /* USER CODE END PD */ /* Private variables ---------------------------------------------------------*/ TIM_HandleTypeDef htim1; /* USER CODE BEGIN PV */ extern USBD_HandleTypeDef hUsbDeviceFS; uint8_t HID_report[HID_REPORT_SIZE] = {0x01,0,0}; //empty hid report W celu zakomunikowania hostowi wciśnięcia przycisku wysyłamy raport HID z ustawionym bitem odpowiadającym wciśniętemu przyciskowi. Przykładowo dla przycisku VOL_UP ustawić należy drugi bit w drugim bajcie raportu. Ponieważ należy zasymulować wciśniecie i zwolnienie przycisku należy wysłać po chwili drugi raport z samymi zerami. Ważne jest też zachowanie co najmniej 10 ms odstępu między wysyłaniem raportów, by dać czas na przetworzenie informacji bibliotece USB na STMie oraz hostowi (komputerowi). Poniżej kod w funkcji main() obsługujący wysyłanie raportów o wciśnięciu przycisków HID_report[1]=0; //0 means none of the buttons pressed ////THIS CODE CAN SUPPORT ONLY 1 KEY AT A TIME, AND VERY SLOW REPEAT WITH LONG PRESS//// CheckButtonsState(); //check if any button is pressed and set appropriate bits in HID_report[1] if(HID_report[1]!=0){ USBD_HID_SendReport(&hUsbDeviceFS, HID_report, HID_REPORT_SIZE); //send key-press and after 15 ms key-release reports HAL_Delay(400); HID_report[1]=0; USBD_HID_SendReport(&hUsbDeviceFS, HID_report, HID_REPORT_SIZE); HAL_Delay(15); } Funkcja CheckButtonsState() ustawia na podstawie stanów logicznych na wejściach uC, bity w 2 bajcie tablicy uint8_t przechowującej raport HID. void CheckButtonsState(){ if(!HAL_GPIO_ReadPin(PUSH_GPIO_Port, PUSH_Pin)) { //push input is low when whichever button is pressed uint8_t isPushPressed=1; if(!HAL_GPIO_ReadPin(RIGHT_GPIO_Port, RIGHT_Pin)) {SetKeyPress(MEDIA_NEXT); isPushPressed=0; } if(!HAL_GPIO_ReadPin(LEFT_GPIO_Port, LEFT_Pin)) {SetKeyPress(MEDIA_PREV); isPushPressed=0; } if(!HAL_GPIO_ReadPin(UP_GPIO_Port, UP_Pin)) {SetKeyPress(MEDIA_MUTE); isPushPressed=0; } if(!HAL_GPIO_ReadPin(DOWN_GPIO_Port, DOWN_Pin)) {SetKeyPress(MEDIA_STOP); isPushPressed=0; } if(isPushPressed==1){SetKeyPress(MEDIA_PLAY_PAUSE);} } } void SetKeyPress(uint8_t key){ HID_report[1] |= 1<<key; } Zastosowany joystick ALPS jest tak skonstruowany, że wciśnięcie jakiegokolwiek przycisku zwiera także wyprowadzenie PUSH odpowiadające wciśnięciu joysticka. Z tego powodu funkcja jest napisana tak, że jeżeli na wejściu PUSH jest 1 to na każdym innym musi być 1 (styki otwarte). Jeżeli wciśnięty jest PUSH (logiczne 0) ale też 0 jest na pinie od innego przycisku, oznacza to że wciśnięto inny przycisk, a stan na wejściu PUSH jest ignorowany. Jeżeli 0 jest tylko na wejściu PUSH, oznacza to że joystick rzeczywiście został wciśnięty. Takie rozwiązanie prosi się aż o generowanie przerwania z wejścia PUSH i wykonywania kodu wtedy. Ponieważ jestem leniwy i miałem już gotowy kod do wysyłania raportów HID kiedy to odkryłem, zostałem przy pollingu stanu na wejściach. Delay po wysłaniu raportu rozwiązuje mi także problem z debouncingiem przycisków (obserwowałem wejścia na oscyloskopie i stwierdzam że styki w tym joysticku drżą jak złe ). Regulację głośności obsługuję przez timer 1 w trybie enkodera (przydał się kurs zaawansowanych timerów na STM32 z waszego bloga ). Przy zmianie wartości licznika timera wartość jest porównywana z zapisaną w poprzednim przejściu głównej pętli while(1). Jeżeli się zmieniła na mniejszą lub większą wysyłany jest raport HID z ustawionym bitem odpowiadającym klawiszowi VOLUP/VOLDOWN a zmienna lastEncoderCount jest in/dekrementowana. Dopóki obie wartości nie są równe raporty są wysyłane co przejście głównej pętli while. Wartość licznika jest dzielona przez 2 gdyż przy poprawnej pracy enkodera o tyle zmienia się po przeskoczeniu "o jeden ząbek". Czasem miałem sytuację że wartość licznika była nieparzysta (może błąd połączeń/drżenie styków) i algorytm w kółko podnosił/obniżał głośność o 1 bo wartości encoderCount i lastEncoderCount nigdy nie były równe. Dzielenie przez 2 gwarantuje parzystość wartości zmiennej ///TE ZMIENNE SA GLOBALNE/// uint16_t encoderCount=32766; //half of timers resolution (16bit) uint16_t lastEncoderCount=16383; //////////////////////////// //TEN KOD WYKONUJE SIE W PETLI WHILE FUNKCJI MAIN()// encoderCount=__HAL_TIM_GET_COUNTER(&htim1)/2; //get encoder absolute position if(encoderCount!=lastEncoderCount){ if(encoderCount<lastEncoderCount){SetKeyPress(MEDIA_VOLDOWN);lastEncoderCount-=1;} //set key press depending on encoder movement direction else{SetKeyPress(MEDIA_VOLUP);lastEncoderCount+=1;} USBD_HID_SendReport(&hUsbDeviceFS, HID_report, HID_REPORT_SIZE); HAL_Delay(15); HID_report[1]=0; USBD_HID_SendReport(&hUsbDeviceFS, HID_report, HID_REPORT_SIZE); HAL_Delay(15); } Kompletne oprogramowanie STMa dostępne na github.com/wiciu15/STM32-USBHID-MultimediaPilot. Po zweryfikowaniu działania prototypu zaprojektowałem i wytrawiłem prostą płytkę w KiCADzie żeby się pozbyć luźnych kabelków. Urządzenie zasilane jest z USB na BluePillu, programuje je z wyprowadzeń SWD po drugiej stronie płytki za pomocą ST-Linka V2. Żeby urządzenie jakoś wyglądało zaprojektowałem i wydrukowałem obudowę oraz gałkę do joysticka. Od razu ostrzegam że na drukarce FDM ciężko jest osiągnąć taką precyzję żeby gniazdo w gałce ciasno siedziało na wałku joysticka - ja zaprojektowałem 0,1mm ciaśniej i przy montażu rozgrzałem plastik tak by się uplastycznił i dopasował do kształtu na końcu drążka joysticka. Nie jest to ciasne pasowanie ale wystarcza żeby sterować bez urywania gałki co drugie dotknięcie . I wydrukowane: Wszystkie pliki z kodem, płytka i obudowa dostępne na github.com/wiciu15/STM32-USBHID-MultimediaPilot. Z racji że chciałbym dalej to rozwijać to zakupiłem moduł NRF51822 i będę próbował zrobić coś podobnego, ale na Bluetooth HID, i z zastosowaniem w aucie do sterowania tanim tabletem z emulatorem headunitu AndroidAuto (używam apki HeadUnit Reloaded która wspiera obsługę z klawiatury do sterowania API Androida Auto np. sterowanie jasny/ciemny tryb, multimedia, poruszanie się po interfejsie za pomocą enkodera - AA jest tak zaprojektowany bo piloty do systemów infotainment z reguły mają też enkodery). Jedyna moja obawa jest taka że pilocik będzie musiał działać cały czas ze stałej instalacji 12V, bo tablet z niewiadomych powodów po rozłączeniu z sparowanym urządzeniem BT wymaga ręcznego ponownego połączenia, więc po wyłączeniu i włączeniu pilocika czeka mnie wycieczka do ustawień tabletu by go z powrotem połączyć. Nie mam doświadczenia z programowaniem nRF więc zobaczymy jak mi pójdzie na raz z BT HID, przerwaniami, enkoderem i jeszcze minimalizacją poboru energii Łatwiej by było dorobić drugie USB w tablecie i podłączyć taki pilot jak opisałem powyżej. Tu jest problem tej natury, że tablet który mam już zmodyfikowany i założony na bardzo mocną taśmę dwustronną () nie chce działać na OTG z więcej niż jednym urządzeniem. Podłączałem do niego kilka HUBów USB, kupiłem nawet specjalną przejściówkę do OTG z paroma portami USB ale nic nie chce ruszyć. Póki co moduł z NRF81 dopiero przyszedł, będę teraz się oswajał z SDK do niego, może jakiś hello world . Nie jestem jeszcze pewny czy będę w stanie go programować i debugować bo mam tylko ST-Linka na stanie w tej chwili Podobno korzystając z OpenOCD można ST-Linka z tym połączyć ale to musiałbym przy wolnej chwili zrobić próby. Jak nie ruszy to będę szukać jakiegoś JTAGa ale ceny mnie trochę odstraszają. Wszelkie sugestie, pochwały, krytyka i komentarze mile widziane
  6. Cześć, nie wiem czy dokładnie temat opisze mój problem ale: Musiałem zrobić na PC reinstalke Windowsa i pobrałem Keila. Wszystko ładnie pobrało mi - sterowniki do STlinka, biblioteki etc. zautomatu. I pojawił się taki problem, że za każdym razem jak zaprogramuje procka muszę wciskać reset by program ruszył. Wcześniej nie musiałem mam opcję zaznaczoną w ustawieniach STlinka ale po wgraniu i podstawowego programu (mruganie dioda) int main(void) { init_all(); while(1) { GPIOA->ODR^=1<<5; delay(20); } } zawsze musze zresetować plytke (nucelo f103) czy ktoś z Was miał podobny problem? Przed reinstalka systemu ten sam program nie wymagał czegoś takiego Pozdrawiam!
  7. Cześć, po ponad półrocznej przerwie kontynuuje zmagania z z kamerką OV7675 z użyciem płytki Nucleo z stm32f446RE. DCMI ani DMA jeszcze nie udało mi się uruchomić ale na swój AVR'owy sposób jestem w stanie UARTem (baud 2M) przesłać w grayscale wartości pixeli z stm32 do apki w Python na moim PC. Po wygenerowaniu obraz jest akceptowalny, ale daleko mu do tego co widziałem na youtube, gdzie obraz nie dość że był czysty to jeszcze przsyłany na żywo( u mnie wygenerowanie zdjęcia przy 5 sekundach to jest dość szybko ^^). Problem wygląda tak: przesyłane dane a dokładniej linijki ( pixele przesyłam rzędami: [dane][index linijki] ) są często zlepkiem dwóch linijek, przez to rozmiar linijki często jest dwukrotnie większy niż to jest dla prawidłowego pakietu. Wydaje mi się że problem jest po stronie Pythona/Windowsa, który nie radzi sobie z taką ilością danych i gdy się gubi to skleja mi pakiety, chociaż dokumentacja PySerial mówi o obsłudze jeszcze większych baudrate. Podczas przeszukiwania internetu natknąłem się też na informacje że czasem taktowanie kamerki zegarem z MCU może powodować problemy i potrzebne jest ustawianie rejestrów w kamerce w celu przeskalowania zegara co powoduje jej poprawne działanie. Może miał ktoś podobny problem? Co myślicie?
  8. Witam, muszę zaimplementować odczyt temperatury z czujnika DHT11 poprzez STM32F103RB i wysyłać to poprzez USART(9600b/s). Korzystam z DWT_delay aby mieć opóźnienie w us do inicjalizacji DHT. Problem polega na tym, że mikroprocesor pobiera dane tylko raz i zawiesza się. USART obsługuje przerwania i jest zaimplementowany na buforze kołowym(piny domyślnie PA2-PA3). Dodatkowo mam wyświetlacz 7-segmentowy i docelowo chce na nim wyświetlać dane ( podpięcia PA0-PA1,PA4-PA8) z czujnika(PA9). Po resecie z watchdoga/czarnego buttona na nowo pobrane są dobre dane. W załączniku podsyłam program main z CubeIDE. ProjectMain.zip
  9. Po udanym projekcie robota "Copernicus" - w szeregowym łańcuchu kinematycznym 4DOF przyszedł czas na robota klasy delta, nazwanego "Astra", ponieważ ramiona układają się właśnie w gwiazdę Prototyp prezentowałem na targach Hobby 2019 na Międzynarodowych Targach Poznańskich, generalnie nie ma nic rewolucyjnego - zasilacz 36V, 3 silniki NEMA17 z przekładniami planetarnymi, przeguby kulkowe firmy Igus® (serdecznie dziękuję), zerowanie - krańcówki, eżektor, ssawka, w pierwszej wersji - Arduino Mega, teraz to już Discovery F429 z wyświetlaczem LCD (wyświetlam na nim koordynaty, chciałbym też obsługiwać dotyk, ale dodanie biblioteki do obsługi dotyku kłóci się ze sterowaniem silnikami krokowymi ). Aktualnie, robot potrafi wykonać proste zadanie pick&place Projekt cały czas ewoluuje (określiłbym postęp na 60%), mam zamontowaną RPi 3B+ z kamerką, chciałbym dodać podajnik obrotowy, rozpoznawać kolor krążków i układać do określonych pojemników, a na koniec pudełeczko z posortowanymi elementami pneumatycznie wysunąć - chyba, że komuś przychodzi na myśl coś lepszego? (ale, żeby z wykorzystaniem pneumatyki, mam do dyspozycji dużo różnych siłowników i kilka chwytaków ), z góry mówię, że znam aplikację taśmociągu i systemu wizyjnego Podziękowania składam również dla Kolegi @Eukaryota za pomoc i inspirację Pozdrawiam
  10. Poniższe pytanie zostało wydzielone z kursu: https://forbot.pl/forum/topic/10506-kurs-stm32-f1-hal-8-bezposredni-dostep-do-pamieci/ Witam W zadaniu 8.1 wyniki z copy_cpu() i copy_dma() mam jednakowe i są następujące: Bufor zrodlowy: 536873560 Bufor docelowy: 536873592 Jak widać, wartości są różne. W ogóle wyświetlam wartości src_buffer i dst_buffer za pomocą printf. Jakiego formatu używać do wyświetlania liczb typu uint8_t albo uint16_t, bo %d zwraca ostrzeżenia.
  11. Witam, Jestem początkującym w programowaniu STM32F103. Do tej pory programowałem Microchip PIC 8 bitowe w asm i C. Zakupiłem zestaw STM32F103 Nucleo i uczę się programowania z kursu, jest bardzo ciekawy i pomocny. Zgodnie z kursem uruchomiłem UART, przetwornik ADC na DMA, przerwanie na TIM2 z częstotliwością 4kHz, PWM 12kHz. W przerwaniu przepisuję wartość z przetwornika ADC (po przeskalowaniu od 0 - 1000) i zmieniam wypełnienie PWM. Pojawia się problem z przebiegiem PWM. Proszę o pomoc w rozwiązaniu problemu. Pozdrawiam. Jurek /** ****************************************************************************** * @file main.c * @author Ac6 * @version V1.0 * @date 01-December-2013 * @brief Default main function. ****************************************************************************** */ #include <stdio.h> #include <stdint.h> #include "stm32f10x.h" #include <math.h> #define ADC_CHANNELS 2 TIM_OCInitTypeDef channel; // PWM volatile uint32_t timer_ms = 0; uint16_t adc_value[ADC_CHANNELS]; volatile int32_t PID; volatile uint16_t PID_PWM; volatile uint32_t old_adc_0, old_adc_1; void SysTick_Handler() { if (timer_ms) timer_ms--; } void TIM2_IRQHandler() { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); GPIO_SetBits(GPIOA, GPIO_Pin_5); PID = (adc_value[0] / 4); PID_PWM = PID; channel.TIM_Pulse = PID_PWM; TIM_OC1Init(TIM4, &channel); GPIO_ResetBits(GPIOA, GPIO_Pin_5); //if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5)) //GPIO_ResetBits(GPIOA, GPIO_Pin_5); //else //GPIO_SetBits(GPIOA, GPIO_Pin_5); } } void delay_ms(int time) { timer_ms = time; while (timer_ms); } void send_char(char c) { while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); USART_SendData(USART2, c); } int __io_putchar(int c) { if (c=='\n') send_char('\r'); send_char(c); return c; } int main(void) { //int8_t i; GPIO_InitTypeDef gpio; USART_InitTypeDef uart; DMA_InitTypeDef dma; ADC_InitTypeDef adc; TIM_TimeBaseInitTypeDef tim; // Timer 2 NVIC_InitTypeDef nvic; // Timer 2 //TIM_OCInitTypeDef channel; // PWM RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); RCC_ADCCLKConfig(RCC_PCLK2_Div6); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); GPIO_StructInit(&gpio); gpio.GPIO_Pin = GPIO_Pin_5; gpio.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &gpio); //*** konfiguracja pinow UART gpio.GPIO_Pin = GPIO_Pin_2; gpio.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &gpio); gpio.GPIO_Pin = GPIO_Pin_3; gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &gpio); //*** //*** PWM wyjscia gpio.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9; gpio.GPIO_Speed = GPIO_Speed_50MHz; gpio.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOB, &gpio); //*** USART_StructInit(&uart); uart.USART_BaudRate = 115200; USART_Init(USART2, &uart); USART_Cmd(USART2, ENABLE); //*** ustawienie wejsc przetwornika A/D gpio.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1; gpio.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &gpio); //*** //*** ustawienie DMA do odczytu wejsc przetwornika A/D DMA_StructInit(&dma); dma.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable; dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; dma.DMA_MemoryBaseAddr = (uint32_t)adc_value; dma.DMA_MemoryInc = DMA_MemoryInc_Enable; dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; dma.DMA_DIR = DMA_DIR_PeripheralSRC; dma.DMA_BufferSize = ADC_CHANNELS; dma.DMA_Mode = DMA_Mode_Circular; DMA_Init(DMA1_Channel1, &dma); DMA_Cmd(DMA1_Channel1, ENABLE); //*** ADC_StructInit(&adc); adc.ADC_ScanConvMode = ENABLE; adc.ADC_ContinuousConvMode = ENABLE; adc.ADC_NbrOfChannel = ADC_CHANNELS; adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_Init(ADC1, &adc); ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 2, ADC_SampleTime_239Cycles5); ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5); ADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while (ADC_GetResetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); ADC_SoftwareStartConvCmd(ADC1, ENABLE); SysTick_Config(SystemCoreClock / 1000); // *** Timer4 jako PWM //RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); TIM_TimeBaseStructInit(&tim); tim.TIM_ClockDivision = 0; tim.TIM_RepetitionCounter = 0; tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Prescaler = 5 - 1; tim.TIM_Period = 1000 - 1; TIM_TimeBaseInit(TIM4, &tim); //*** //*** ustawienia kanalow PWM TIM_OCStructInit(&channel); channel.TIM_OCMode = TIM_OCMode_PWM1; channel.TIM_OutputState = TIM_OutputState_Enable; channel.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); //channel.TIM_Pulse = PID_PWM; //TIM_OC1Init(TIM4, &channel); channel.TIM_Pulse = 200; TIM_OC2Init(TIM4, &channel); channel.TIM_Pulse = 500; TIM_OC3Init(TIM4, &channel); channel.TIM_Pulse = 900; TIM_OC4Init(TIM4, &channel); TIM_Cmd(TIM4, ENABLE); //*** //*** Timer 2 TIM_TimeBaseStructInit(&tim); tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Prescaler = 640 - 1; tim.TIM_Period = 25 - 1; TIM_TimeBaseInit(TIM2, &tim); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); nvic.NVIC_IRQChannel = TIM2_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 0; nvic.NVIC_IRQChannelSubPriority = 0; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); //*** while (1) { //GPIO_SetBits(GPIOA, GPIO_Pin_5); //printf("ADC%d = %d\n", 0, adc_value[0]); //printf("A = %d (%.3fV)\n", PID); //channel.TIM_Pulse = PID_PWM; //TIM_OC1Init(TIM4, &channel); printf("%d\n",PID); //GPIO_ResetBits(GPIOA, GPIO_Pin_5); delay_ms(1); } }
  12. Dzień dobry, Pojawił się u mnie niedawno problem, uruchomiłem dwa wejścia ADC z pomocą DMA - podłączyłem do nich zwykłe potencjometry, działały one poprawnie lecz przy grzebaniu w hardware mogłem podpiąć napięcie wyższe niż 3,3 V i nagle przetworniki pokazują jedynie maksymalną wartość, a gdy zamienię kable zasilające potencjometr to pokazują wartość minimalną. Czy jest to możliwe, że załatwiłem tym sposobem przetwornik ADC? napięcie mogło wynosić około 3,5 V, potencjometry działają poprawnie. Korzystam z płytki rozwojowej STM32F103C8T6, załączam dodatkowo rysunek z połączeniem potencjometrów oraz projekt CubeIDE asdas.rar
  13. Cześć! Jako że już po sezonie to postanowiłem opisać tu moją i mateuszm konstrukcję. Robot powstał w ramach rekrutacji do koła naukowego robotyków KoNaR i debiutował na Robotic Arenie 2015. Był to nasz debiut w konstruowaniu robotów, ponieważ wcześniej żaden z nas tego nie robił. Mechanika Głównym założeniem mechaniki robota było wykorzystanie w pełni maksymalnej dopuszczalnej masy oraz skupienie jej w podstawie. W osiągnięciu tego celu pomógł nam projekt Autodesk Inventor, w którym to został wykonany szczegółowy projekt robota uwzględniający wszystkie drobne elementy mechaniczne. Podstawa robota została wykonana z trzech elementów: - ostrze z węglika spiekanego - płytka stalowa 3mm - płytka górna, 2mm, która łączyła dwa elementy wymienione wyżej W płytkach zostały wycięte otwory, w których następnie umieszczone były małe, okrągłe płytki z czujnikami białej linii. Stal była cięta laserowo oraz później frezowana. Napęd robota stanowiły znane i lubiane silniczki Pololu HP z przekładnią 50:1 przykręcone bezpośrednio do podstawy za pomocą dedykowanych mocowań. Podpięte do elektroniki były przy pomocy złączy ARK co umożliwiło ich łatwy demontaż w celach serwisowych. Obudowa robota została wykonana z laminatu 1mm. Na obudowie znajduje się nazwa robota oraz nasze imiona i nazwiska. Obudowa mocowana jest do podstawy za pomocą śrub, a jej elementy są do siebie lutowane. Dach robota wykonaliśmy z 2 mm przeźroczystego szkła akrylowego. Finalnie robot mierzy w najwyzszym miejscu 33,7 milimetra od podstawy do dachu, a od podłoza do dachu 37,2. Jego podstawa ma wymiary 98,9 mm na 99,6 mm. Masa waha się od 490 do 499g w zależności jak bardzo nakarmimy robota przed zawodami Elektronika Priorytetem projektu robota była jego dobra konstrukcja mechaniczna, w wyniku czego wymiary płytki z elektroniką były z góry narzucone. Elektronika została zaprojektowana w programie KiCad po zaimportowaniu wymiarów przeznaczonych na nią z programu Inventor. Elektronika robota dzieli się na dwie płytki: główną, która znajduje się tuż nad podstawą robota oraz dodatkowej, na której umieściliśmy włącznik zasilania, interfejs komunikacyjny oraz złącze na moduł startowy. Płytki połączone zostały ze sobą taśmą FFC. Mała płytka widoczna na zdjęciu nad płytką główną służy nam jako podstawka pod akumulator LiPo 7.4V, którym zasilamy robota. Sercem naszego robota został mikroprocesor STM32F1. Wybór tej rodziny wynikał z łatwości ich programowania przez początkujących. Czujniki odległości wykorzystane w robocie to znane i lubiane cyfrowe Sharpy 40cm. Umieściliśmy je 4, dwa z przodu oraz po jednym na bokach. Na czujniki białej linii użyliśmy dwa KTIRy 0711S. Jak wcześniej wspomniałem zostały one umieszczone na małych płytkach, które umieściliśmy w wycięciach w podstawie. Na sterowniki silników wybraliśmy dwa, podwójne mostki H TB6612. Zostały one dobrane ze względu na ich dobrą wydajność prądową, co umożliwiło nam skuteczne wysterowanie silników Pololu bez obawy o to, że przy pełnym zwarciu mostki ulegną uszkodzeniu. Płytki zostały wykonane metodą termotransferu oraz wyfrezowane za pomocą Dremela i pilników ręcznych. Software Kod robota został napisany w środowisku System Workbench for STM32 (SW4STM32) z użyciem generatora kodu konfiguracyjnego STM32CubeMX. Program podzielony był na dwie sekwencje: startową i walki. W pierwszej ustawiana była konfiguracja algorytmu robota oraz dostępne były funkcje testu czujników (widoczny na zdjęciu poniżej - cztery czerwone diody, które odpowiadały za pokazanie aktualnego stanu czujników) oraz czyszczenia kół (drobna funkcja pomocnicza ustawiająca małą prędkość na kołach). Sekwencja walki załączała się po otrzymaniu przez robota sygnału startowego z modułu. Większa część algorytmu walki opierała się na prostych if-ach z odpowiednio dobranymi nastawami silników w każdym przypadku, co okazało się wystarczające w większości starć. Następnie zostały dodane pewne udoskonalenia, o których już rozpisywać się nie będę Osiągnięcia - II miejsce Robomaticon 2016 - I miejsce Robotic Tournament 2016 - III miejsce Festiwal Robotyki Cyberbot 2016 - II miejsce [Minisumo] oraz I miejsce [Minisumo Deathmatch] Trójmiejski Turniej Robotów 2016 - II miejsce Opolski Festiwal Robotów 2016 - I miejsce Robotic Day 2016 [Praga] A poniżej kilka filmów z udziałem robota: 2Weak4U vs Hellfire 2Weak4U w Wiedniu 2Weak4U vs Dzik 2Weak4U vs Shevron (chyba) 2Weak4U w Deatmatchu [Rzeszów] 2Weak4U vs Szwagier Pozdrawiam i do zobaczenia w następnym sezonie, Aleksander
  14. Witajcie Realizuję komunikację pomiędzy mikroprocesorem STM32F411RE (NUCLEO) a modułem SIM800L. Wykorzystuję do tego interfejs UART. Inicjalizuję go poprzez bibliotekę HAL'a. Samą wymianę danych postanowiłem zrobić ręcznie, odwołując się bezpośrednio do rejestrów UART. Poniżej funkcja inicjalizacyjna: void uart_init(uint32_t baud) { uart_gpio.Pin = GPIO_PIN_9 | GPIO_PIN_10; //TX RX uart_gpio.Mode = GPIO_MODE_AF_PP; uart_gpio.Alternate = GPIO_AF7_USART1; uart_gpio.Speed = GPIO_SPEED_HIGH; uart_gpio.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA,&uart_gpio); usart.Instance = USART1; usart.Init.BaudRate = baud; usart.Init.Parity = USART_PARITY_NONE; usart.Init.StopBits = USART_STOPBITS_1; usart.Init.WordLength = USART_WORDLENGTH_8B; usart.Init.Mode = USART_MODE_TX_RX; HAL_USART_Init(&usart); } Sama komunikacja odbywa się w funkcji "sim_reception", w przerwaniu od timera mikroprocesora. #define SIM_TX_BUF_SIZE 32 #define SIM_RX_BUF_SIZE 32 volatile unsigned char sim_tx_buf[SIM_TX_BUF_SIZE]; volatile unsigned char sim_rx_buf[SIM_RX_BUF_SIZE]; volatile unsigned char sim_tx_len = 0; volatile unsigned char sim_rx_len = 0; enum { handshake, hdsh_resp }; volatile uint8_t sim_states = handshake; #define SIM_HNDSHK "AT\r\n" void sim_reception(void) { //RX //if Receiver not empty flag is set and we have space in rx buffer, we read byte while(USART1 -> SR & USART_SR_RXNE) { if(sim_rx_len < SIM_RX_BUF_SIZE) sim_rx_buf[sim_rx_len++] = USART1 -> DR; //after reading byte we increment buffer length else break; } //TX //if Transmitter empty flag is set and we have data in tx buffer, we send byte while(USART1 -> SR & USART_SR_TXE) { if(sim_tx_len != 0) { USART1 -> DR = sim_tx_buf[0];//send one byte sim_byte_up(sim_tx_buf); //shift one byte up } else break; } switch(sim_states) { case handshake: sim_tx_buf_put(SIM_HNDSHK); sim_states = hdsh_resp; break; case hdsh_resp: break; default: sim_states = handshake; break; } } Na razie chcę wysłać tylko jedno polecenie i odczytać odpowiedź modułu SIM800L. Krótko opiszę nadawanie, które działa bez zarzutu. Wywołuję funkcję "sim_tx_buf_put", która napełnia bufor komendą "SIM_HNDSHK", zdefiniowaną wyżej za pomocą #define. Funkcja bada ile miejsca mamy w buforze i czy wystarczy go jeszcze dla nowego polecenia. Jeśli tak to wstawia je bajt po bajcie i zwiększa odpowiednio zmnienną "sim_tx_len". Nie będe wstawiał kodu tej funkcji, ponieważ działa ona bardzo dobrze, nie mam z nią problemu. Wyżej w kodzie widać, że jest badany stan flagi "Transmitter Empty", gdy się ona pojawi i w buforze Tx są dane przystępuje się do wysyłania danych bajt po bajcie. Po każdym wysłanym bajcie wywoływana jest funkcja "sim_byte_up", która przesuwa bajty bufora "o jeden w górę", czyli sim_tx_buf[0] = sim_tx_buf[1], sim_tx_buf[1] = sim_tx_buf[2] itd. oraz zmniejsza wartość "sim_tx_len" o jeden, następnie wysyłany jest kolejny bajt i tak do opróżnienia bufora. Nadajnik działa jak należy, co pokazuje screenshot z analizatora stanów logicznych. Po wysłaniu wiadomości "AT\r\n" moduł SIM800L odsyła odpowiedź. Zatem flaga "Receiver Not Empty" powinna zostać aktywowana, bufor Rx jest pusty, tak więc coś powinno zostać zapisane w buforze. Jednak nic takiego się nie dzieje. Screenshoty z debugowania. W buforze Rx pojawia się tylko jeden bajt o wartości 127. Dalszych procedur odbioru nawet nie opracowałem, bo skoro w buforze odbiorczym nic nie ma... Nie bardzo wiem z czym jest związany ten problem, próbowałem wyprowadzić Tx i Rx we wszystkich możliwych konfiguracjach, jednak zawsze jest tak samo, nadajnik wysyła bez żadnego problemu a odbiornik nie może nic odczytać. Jeśli ktoś ma jakiś pomysł co może być przyczyną problemu i jak to rozwiązać to byłbym bardzo wdzięczny za pomoc Pozdrawiam mw
  15. Dzień dobry, Piszę program do swojego mikrokontrolera STM32F303VCT6, który wysyła sygnał sinusoidalny na rampie do sterowania laserem. Dodatkowo, ten sam mikrokontroler mierzy napięcie na fotodetektorze oraz mnoży te dwa sygnały - sinusoidalny i odbierany. Do tej pory wygląda na to, że wszystko działa. Potrzebuję jednak zaimplementować jeszcze filtr dolnoprzepustowy, który po wymnożeniu tych dwóch sygnałów, przefiltruje mi sygnał tak, aby na innym pinie skonfigurowanym pod przetwornik cyfrowo-analogowy "obetnie" mi żądane częstotliwości. Szukając w internecie implementacji takiego filtra nie uzyskałem satysfakcjonującej mnie odpowiedzi. Czy ktoś mógłby dokładnie wyjaśnić jak to zrobić? A może jest jakaś tego typu funkcja przy wykorzystaniu bibliotek HAL? Z góry dziękuję za pomoc!
  16. To mój pierwszy post na forum więc witam wszystkich Chociaż czytelnikiem jestem już od dawna;p Posiadam płytkę stm32f411re i próbuję przetestować akcelerometr jak w tytule(Był dołączony do kursu STM32F1). Do pomocy korzystałem z obu kursów STM32: F4 (HAL) oraz Kurs F1. Próbowałem już różnych konfiguracji interfejsu, różnych pinów itp. jednak ciągle nie mogę nawet odczytać rejestru WHO_AM_I. Konfiguracja I2C: static void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } } Adres układu: #define LSM303D_ADDR (0x3a << 1) (Próbowałem też 0x3c oraz opcji bez przesunięcia bitowego) Pętla główna: while (1) { HAL_I2C_Mem_Read(&hi2c1, LSM303D_ADDR, 0x0f, 1, &who_am_i, 1, 100); } Gdzie 0x0f to adres rejestru WHO_AM_I z dokumentacji zamieszczonej w kursie, a &who_am_i to adres zmiennej globalnej typu uint_8t. Dokumentacja LSM303D Układ zmontowany poprawnie, SDA i SCL podpinam pod piny ustawione w Cube, dodatkowo masa i 3v3 do Vin. Oczekiwana wartość rejestru to wg dokumentacji 0x49, jednak śledzenie zmiennej w STMStudio zawsze wskazuje 0. Zaznaczę jeszcze, że próbowałem też odczytywać konkretne wartości z akcelerometru po uprzedniej konfiguracji, ale nigdy nie zadziałało. Zakładam więc, że jest jakiś podstawowy problem z komunikacją układów, dlatego chcę zacząć od poprawnego odczytu who_am_i. Dajcie znać, jeśli czegoś brakuje w opisie. Dzięki.
  17. Mam teraz problem z ostatnim zadaniem. Wprawdzie mam inna plytke (stm32f303re) lecz poza innym maksymalnym taktowaniem zegara wydaje mi sie ze nie ma roznicy w wykorzystywanych funkcjach. Zmienilem odpowiednio wartosci prescaler. Tak wygladają wartości zmiennych: Zmienia sie tylko wartość Duty zaś reszta pozostaje niezmienna. KOd kopiowalem z pliku wprowadzajac potrzebne zmiany. I co najbardziej ciekawe wartości zaczynaja sie zmieniac gdy dotkne niektorych miejsc na plytce (np. styki piny do ktorych sa przypisane timery, ale nie tylko). Wartości zatrzymuje sie wtedy na przykladowo takich liczbach. Wyniki te poza procentem wypelnienia wydaja sie nawet poprawne, lecz ta jedna wartosc nie reaguje wogole na zmiany polozenia pokretla enkodera. Przede wszystkim nie mam pojecia dlaczego niektore miejsca reaguja na dotyk. Jesli to moze pomóc w rozwiazaniu tej zagadki moge zaznaczyc te punkty.
  18. Aktualnie pracuję nad tym kursem: https://forbot.pl/blog/kurs-stm32-f4-5-pomiar-napiecia-adc-dma-stmstudio-id13099 tylko na plytce stm32f303re Znalazłem w dokumentacji mikrokontrolera dane do przeliczania wyniku na stopnie celsjusza, nie wiem czy błąd jest w danych czy w czymś innym ale temperatura znacznie odbiega od tej jaka powinna wyjść według kursu. U mnie jest 40 stopni zas wedlug kursu powinna byc chyba zblizona do temperatury otoczenia. Czy ktos moze mi sprawdzic czy znalazlem poprawne dane i napisalem dobrze kod? Największe wątpliwości mam co do maksymalnego napiecia zasilania. Poniżej zamieszczam screeny ze znalezionymi danymi oraz kod.
  19. Chcę napisac program ktory bedzie zaswiecal stopniowo diode a nastepnie ja gasil w czasie 4s. Znalazlem w reference manual takie informacje na temat przerwania SysTick: więc ustawilem zegar procesora takim taktowaniem jak jest opisane Obliczylem rowniez ze aby dioda zaswiecala sie cyklicznie w czasie 4s trzeba aktualizowac stannapiecia PWM co 40cyklow przerwań, zas timer ustawilem tak aby sygnal PWM przyjmowal 100 wartosci posrednich. Po odpaleniu programu w debugerze wyskakuje mi jakis błąd ktorego nawet nie wiem jak interpretować. Zamieszczam rowniez kod, ktos moze bedzie wiedzial w czym jest problem. main.rar https://forbot.pl/blog/kurs-stm32-f4-8-zaawansowane-funkcje-licznikow-id13473 Robie ten kurs tylko na innym mikrokontrolerze, STM32F303re
  20. Cześć! od wielu dnia(tak) walczę z tym, co na AVRkach można było zrobić w 5 minut z notą katalogową. A mowa o multipleksowaniu wejść przetwornika ADC. Chciałbym w dodatku wykorzystać przerwania bądź DMA. Procek na jakim pracuję to STM32F030F4. W tym momencie konfiguracja wygląda jak na załączonym screenie. Natomiast kod wygląda tak: W sekcji UC(usercode) 2 mam dopisany start: /* USER CODE BEGIN 2 */ HAL_ADC_Start_IT(&hadc); /* USER CODE END 2 */ Natomiast w sekcji PFP dopisałem taką funkcję: /* USER CODE BEGIN PFP */ void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){ reading[0] = HAL_ADC_GetValue(&hadc); } /* USER CODE END PFP */ Efekt? Nic nie działa. Odczyt z przetwornika to wciąż 0. Kiedy robiłem to pollingiem w pętli while, funkcja GetValue zwracała naprzemiennie(ale bez ładu) wyniki z 3 potencjometrów. Natomiast to słaba metoda. jak to ugryźć? Dodam, że w F0 jedyną metodą jest opcja ScanMode.
  21. Próbował może ktoś korzystać z STM32CubeIDE? Zgodnie z tym opisem to jest All-in-one. Czyli może w końcu wszystko działa tak jak powinno?
  22. Witam! Pewnie problem na zasadzie początkującego użytkownika STM32 ale co poradzę - wystąpił ! Mianowicie, mam podłączone 3 przyciski do STM32F103CBT6, przyciski podłączone z rezystorem pull-up i poprzez kondensator filtrujący. W procesorze włączony również pull-up na wejściu od przycisku. Reakcja na wciśnięcie (zbocze opadające) miała być uruchamiana z wykorzystaniem przerwań zewnętrznych. I tu pojawia się problem bo o ile program nie zajmuje się czymś innym albo tylko jakimiś drobnymi rzeczami to wszystko jest w porządku - reakcja następuje od razu. Jeśli tylko zacznę np. więcej rzeczy wyświetlać na OLEDzie to reakcja następuje losowo. Wszystkie przerwania mają priorytety i grupy ustawione na 0. Dodam, że sprawdzałem na oscyloskopie czy występują jakiekolwiek drgania styków - zbocze opadające jest gładziutkie, nie ma możliwości, ze tu coś jest nie halo. Program pisany w HALu tak jak w kursie Forbota. Jeśli będzie potrzeba to dodam listing. Dodatkowo również w ten sam sposób uruchomione są przerwania z zewnętrznego urządzenia i tam wszystko śmiga, no a na przyciskach nie chce - ciekawe. Proszę o jakieś sugestie co to może być, czy ktoś się z czymś spotkał.
  23. Cześć. Mam mały problem z modułem MPU9250, a konkretniej AK8963 (Magnetometr). Odczyt wartości zmierzonych przez akcelerometr i żyroskop nie sprawiły mi większych problemów i wszystko działa prawidłowo. Sprawa skomplikowała się kiedy zechciałem odczytać wartości zmierzone przez magnetometr. Ogólnie komunikacja z magnetometrem działa prawidłowo (WHO_AM_I itd.). Problem polega na tym, że po odczytaniu wartości z rejestrów HXL, HXH... cały czas otrzymuję wartość -1 dla każdej osi. Fragment kodu z inicjalizacją magnetometru: MPU9250_Error_code MPU9250_Magnetometer_Configuration(I2C_HandleTypeDef *I2Cx, struct MPU9250 *DataStructure) { uint8_t Byte_temp = 0x00; uint8_t Bytes_temp[3] = {0}; DataStructure->Magnetometer_addres = 0x0C << 1; // Case 2: Disable the I2C master interface Byte_temp = 0x00; if( HAL_I2C_Mem_Write(I2Cx, DataStructure->Device_addres, MPU9250_USER_CTRL, 1, &Byte_temp, 1, 1000) != HAL_OK ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Magnetometer_Config_FAIL; } // Case 3: Enable the bypass multiplexer Byte_temp = 0x02; if( HAL_I2C_Mem_Write(I2Cx, DataStructure->Device_addres, MPU9250_INT_PIN_CFG, 1, &Byte_temp, 1, 1000) != HAL_OK ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Magnetometer_Config_FAIL; } // Case 1: Is device connected ? if( HAL_I2C_IsDeviceReady(I2Cx, DataStructure->Magnetometer_addres, 1, 1000) != HAL_OK ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Magnetometer_Config_FAIL; } // Case 2: Who am i test if( HAL_I2C_Mem_Read(I2Cx, DataStructure->Magnetometer_addres, MPU9250_WIA, 1, &Byte_temp, 1, 1000) != HAL_OK ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Init_FAIL; } if( Byte_temp != 0x48 ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Init_FAIL; } // Case 4: Setup to fuse ROM access mode and 16-bit output Byte_temp = 0x1F; if( HAL_I2C_Mem_Write(I2Cx, DataStructure->Magnetometer_addres, MPU9250_CNTL1, 1, &Byte_temp, 1, 1000) != HAL_OK ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Magnetometer_Config_FAIL; } HAL_Delay(100); // Case 5: Read from the fuse ROM sensitivity adjustment values if( HAL_I2C_Mem_Read(I2Cx, DataStructure->Magnetometer_addres, MPU9250_ASAX | 0x80, 1, Bytes_temp, 3, 1000) != HAL_OK ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Magnetometer_Config_FAIL; } DataStructure->Magnetometer_ASAX = ( ( (Bytes_temp[0] - 128) * 0.5 ) / 128 ) + 1; DataStructure->Magnetometer_ASAY = ( ( (Bytes_temp[1] - 128) * 0.5 ) / 128 ) + 1; DataStructure->Magnetometer_ASAZ = ( ( (Bytes_temp[2] - 128) * 0.5 ) / 128 ) + 1; // Case 6: Reset to power down mode Byte_temp = 0x00; if( HAL_I2C_Mem_Write(I2Cx, DataStructure->Magnetometer_addres, MPU9250_CNTL1, 1, &Byte_temp, 1, 1000) != HAL_OK ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Magnetometer_Config_FAIL; } // Case 7: Enable continuous mode 2 and 16-bit output Byte_temp = 0x16; if( HAL_I2C_Mem_Write(I2Cx, DataStructure->Magnetometer_addres, MPU9250_CNTL1, 1, &Byte_temp, 1, 1000) != HAL_OK ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Magnetometer_Config_FAIL; } HAL_Delay(100); return MPU9250_Magnetometer_Config_OK; } Fragment kodu z odczytaniem zmierzonych wartości: MPU9250_Error_code MPU9250_Read_Magnetometer(I2C_HandleTypeDef *I2Cx, struct MPU9250 *DataStructure) { uint8_t Bytes_temp[7] = { 0x00 }; if( HAL_I2C_Mem_Read(I2Cx, DataStructure->Magnetometer_addres, MPU9250_HXL | 0x80, 1, Bytes_temp, 7, 1000) != HAL_OK ) { //HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); return MPU9250_Read_Magnetometer_FAIL; } DataStructure->Magnetometer_X = Bytes_temp[0] | Bytes_temp[1] << 8; DataStructure->Magnetometer_Y = Bytes_temp[2] | Bytes_temp[3] << 8; DataStructure->Magnetometer_Z = Bytes_temp[4] | Bytes_temp[5] << 8; return MPU9250_Read_Magnetometer_OK; }
  24. Hej, robiłem tą część kursu na płytce NUCLEO-F334R8 i mam problem z odczytem temperatury. W UserManual znalazłem takie wartości: AVG_slop = 0,0043 V25 = 1,43 https://www.st.com/resource/en/datasheet/stm32f334k4.pdf Rozdzial 6.3.23: Pomiar ADC zwraca mi wartosc w okolicach 1360, co daje wynik ok -77C... 0_o Czujnik zepsuty czy cos robie nie tak?
  25. Tradycyjna kostka do gry jest prosta w budowie, tania ale ma pewne wady. Potrafi potoczyć się w trudno dostępne miejsce. Kolejna wada jest hałas przez nią wytwarzany oraz fakt, że potrafi wpaść na planszę gry i przewrócić pionki. Wad tych pozbawiona jest kostka elektroniczna. W Internecie można znaleźć wiele konstrukcji, wszystkie jakie widziałem wyświetlają wynik losowania na diodach LED. Prezentowana konstrukcja posiada nowoczesny wyświetlacz OLED co pozwala na wyświetlanie różnorodnych grafik. Ponadto może zastąpić cztery kostki o praktycznie dowolnej liczbie liczbie ścian. Kostka została zbudowana na mikrokontrolerze STM32F103RBT8, który można znaleźć na płytkach STM32 NUCLEOSTM32-NUCLEO i innych dostępnych w Botlandzie . Można użyć taniej płytki "Blue pill". Prototyp został zamontowany na dedykowanej do tego PCB. Do wyświetlania grafiki służy kolorowy wyświetlacz OLED o rozdzielczości 96x64 np OLED 0,96". Grafika jest zapisana na karcie SD, dzięki czemu łatwo ją zmienić. Karta jest umieszczona w gnieździe podobnym do Gniazago karty SD z wyrzutnikiem tyle, że bez wyrzutnika. Na PCB przewidziano miejsce na pamięć DataFlash ale w prototypie nie jest ona używana. Jej przeznaczeniem była możliwość skopiowania grafiki z karty pamięci do DataFlash skąd mogą one bardzo szybko być wyświetlane na OLEDzie. Czas odczytu grafik z karty SD jest stosunkowo długi ale wystarczający do wyświetlania statycznych obrazów. Jeśli miałyby być wyświetlane grafiki (np. podczas losowania) to wykorzystanie DataFlasch będzie konieczne. Jak to działa? Naciśnięcie przycisku SET, wybudza mikrokontroler. Program sprawdza, czy reset spowodowany jest włączeniem zasilania czy wybudzeniem. Jeśli włączeniem zasilania wybiera kostkę numer 1 (na karcie SD można umieścić grafiki czterech kostek, każda do 250 ścian) ponadto inicjalizuje generator pseudolosu (ziarno). Jeżeli reset był wywołany wybudzeniem, ustawiana jest ostatnio używana kostka. W kolejnym kroku, przeszukiwane są pliki graficzne, na podstawie których, wiadomo ile ścian ma każda z czterech kostek. Po przeszukaniu plików pojawia się napis „Gotowy do gry” albo „SD Error” jeśli nie wykryto karty lub wymaganych plików graficznych. Przyciskami UP, DOWN, LEFT, RIGHT, można wybrać jedną z kostek, SET uruchamia losowanie. W czasie losowania, zależnie od tego czy znajduje się grafika reprezentująca wybraną kostkę (w prototypie „Orzeł czy reszka” i „Ruletka”) czy nie (w prototypie kostka 6 ścian, kostka 12 ścian), wyświetlana jest grafika lub migający ekran. Po zwolnieniu przycisku SET, po sekundzie, pojawia się wylosowana ściana kostki. Bezczynność przez dwie minuty spowoduje przejście kostki w stan uśpienia. Program został napisany w KEIL uV 5, zajmuje 31kB FLASH, 18kB RAM więc można go skompilować darmową wersją programu. Przy pisaniu softu, wspomagano się programem CubeMX. Program w dużej mierze korzysta z HAL tylko nieliczne fragmenty operują bezpośrednio na rejestrach mikrokontrolera. W pamięci mikrokontrolera stworzono bufor na dane wyświetlacza. Wszystkie operacje graficzne są wykonywane na nim, po czym wysyłana z wykorzystaniem DMA. Dzięki temu, transmisja zajmuje ok 12ms, w efekcie grafika na ekranie pojawia się niezauważalnie szybko. Zawartość karty SD stanową pliki graficzne w formacie BMP zapisane są w głównym katalogu. Nazwy plików kolejnych kostek zaczynają się od liter a, b, c, d. Cyfra za nazwą informuje o numerze ściany. Pierwsza kostka 6 ścian będzie wymagała plików: "a1.bmp" ... "a6.bmp". Ruletka to pliki:" b1.bmp" … "b37.bmp". Jeśli w czasie losowania ma pojawiać się grafika należy dodać plik z sufiksem 00, np.: "a00.bmp", "b00.bmp". Grafika w formacie BMP powinna mieć wymiary 94x64 piksele. Głębia kolorów 24-bit, bez kompresji. Grafika może mieć inne wymiary, jeśli będzie za duża, to zostanie obcięta, jeśli za mała, nie wypełni całego wyświetlacza. Do tworzenia /obróbki grafik w prototypie korzystano z Windows'owego Paint'a i programu IrfanView. W załączniku archiwum z programem i schemat. Kostka12.zip Protel Schematic.pdf
×
×
  • Utwórz nowe...