Skocz do zawartości

PrimeSoul

Użytkownicy
  • Zawartość

    37
  • Rejestracja

  • Ostatnio

Informacje

  • Płeć
    Mężczyzna
  • Lokalizacja
    Września
  • Zawód
    technik elektronik
  • Moje zainteresowania:
    Programowanie C/C++, Sport, Robotyka, Film

Ostatnio na profilu byli

Blok z ostatnio odwiedzającymi jest wyłączony i nie jest wyświetlany innym użytkownikom.

Osiągnięcia użytkownika PrimeSoul

Wynalazca

Wynalazca (6/19)

  • Za 5 postów
  • Za 25 postów
  • Młodszy Juror
  • Wschodząca gwiazda
  • To już rok!

Odznaki

9

Reputacja

  1. @jaceksz73 Wystarczy, że wstawisz swój kod w części "user code" w funkcji przerwania w pliku przerwań. Plik ten w drzewie projektu jest najczęściej obok pliku main.c (przynajmniej w środowisku SW4STM).
  2. Max. timer clock to maksymalna częstotliwość sygnału zegarowego podawana na timery "przylepione" do danej magistrali. Więcej niż ta wartość nie osiągniesz. Max. interface clock to maksymalna częstotliwość taktowania wszystkich pozostałych peryferiów "przylepionych" do tej magistrali, np. USART, I2C itp. Podejrzewam, że w złożonych projektach może mieć to znaczenie, ale w hobbystycznych rozwiązaniach z reguły tylko taktowanie timerów, tudzież jakichś middleware'ów nas tak naprawdę interesuje. Natomiast jeśli chodzi o tutorial, to trudno mi powiedzieć, co autor miał na myśli. Jeśli magistrala jest taktowana z 50MHz maksymalnie, to timery na niej przeważnie mogą hulać do 100MHz, o ile nie ustawi się inaczej w konfiguracji zegara (np. bez podwojenia).
  3. Najszybsza metoda to zerknięcie do strony konfiguracji zegara w CubeMXie. Masz tam po prawej stronie taktowanie poszczególnych magistral, w tym APB1 i wskazana dla niej wartość. Poniżej albo powyżej powinna znajdować się wartość dla timerów tej samej magistrali, najczęściej pomnożona przez 2. Przy czym w tej chwili piszę mając w głowie STM32F407VG, bo akurat ostatnio miałem go w ręku, a nie zestaw do kursu, który leży gdzieś w szafce, czekając na lepsze czasy ;). A szczegółowe informacje znajdziesz na 100% w nocie katalogowej mikrokontrolera / reference manualu / manualu do Cube'a (przyda się opcja Ctrl + F, bo to nie są lekkie lektury...). STMy mają to do siebie, że tych zegarów jest sporo i większość można całkiem spoko indywidualnie sobie poustawiać, w tym zegary timerów.
  4. @Zealota Oj sorki, mój błąd przy spakowaniu plików. Zaraz poprawię. UPDATE: Teraz załącznik powinien już zawierać wszystkie pliki projektowe.
  5. Cześć wszystkim, Walczę co nieco z serwonapędami na płytce Discovery z STM32F407VG na pokładzie (stary model, z programatorem poprzedniej generacji). Sprawa wygląda tak, że mam serwo wieloobrotowe z enkoderem w postaci czujnika szczelinowego i tarczy (sygnał dubluję 3 razy, na razie tylko do testów, później każdy kanał sterowany będzie oddzielnie). Dokładność pozycjonowania taka sobie, ale do potrzeb projektu wystarcza. Kwestia jest taka, że serwo poruszać się ma wg wyliczonej prędkości z regulatora (jeszcze nie zaimplementowany, dopiero w planach) w przerwaniu od timera systemowego. Wartość aktualnego sterowania serwem będzie zapisywana do zmiennej globalnej. No i teraz kluczowa sprawa - chciałbym, żeby timer, na którym jest sterowane to serwo, samoczynnie zmieniał swoje wypełnienie na wartość zmiennej globalnej z obliczonym sterowaniem. Idealnym rozwiązaniem moim skromnym zdaniem byłoby użycie DMA w kierunku Memory -> Peripheral w trybie kołowym (half-word). Niestety, całość nie chce zadziałać - startuje tylko pierwszy kanał timera i w dodatku z ustawieniem domyślnym, nie zmienia swojego wypełnienia przy zmianach zmiennej przypisanej do kanału w DMA, a pozostałe nawet nie startują z domyślnymi wartościami. W załączniku wrzuciłem okrojony tylko do tej kwestii projekt testowy i projekt z CubeMXa. Poniżej kod main'a: /* Private variables ---------------------------------------------------------*/ TIM_HandleTypeDef htim4; DMA_HandleTypeDef hdma_tim4_ch2; DMA_HandleTypeDef hdma_tim4_ch1; DMA_HandleTypeDef hdma_tim4_ch3; /* USER CODE BEGIN PV */ volatile uint16_t PwmPulse = 199; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM4_Init(void); static void MX_DMA_Init(void); /* USER CODE BEGIN PFP */ (...) int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM4_Init(); MX_DMA_Init(); /* USER CODE BEGIN 2 */ // HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1); // TIM4->CCR1 = PwmPulse; // HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_2); // TIM4->CCR2 = PwmPulse; // HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_3); // TIM4->CCR3 = PwmPulse; // HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4); // TIM4->CCR4 = PwmPulse; HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_1, (uint32_t*)&PwmPulse, 1); HAL_Delay(100); HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_2, (uint32_t*)&PwmPulse, 1); HAL_Delay(100); HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_3, (uint32_t*)&PwmPulse, 1); HAL_Delay(100); //HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_2, (uint32_t*)&PwmPulse, sizeof(PwmPulse)/2); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { // TIM4->CCR2 = PwmPulse; // HAL_Delay(100); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } Zakomentowana frakcja kodu bez DMA działa bez problemu i generalnie jest to awaryjna opcja, której użyję, jeśli nic z DMA nie uda mi się sensownego wypracować. No i jeszcze wygenerowane z CubeMXa funkcje konfiguracyjne dla timera i DMA: static void MX_TIM4_Init(void) { /* USER CODE BEGIN TIM4_Init 0 */ /* USER CODE END TIM4_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; /* USER CODE BEGIN TIM4_Init 1 */ /* USER CODE END TIM4_Init 1 */ htim4.Instance = TIM4; htim4.Init.Prescaler = 839; htim4.Init.CounterMode = TIM_COUNTERMODE_UP; htim4.Init.Period = 1999; htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim4) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_Init(&htim4) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 99; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } sConfigOC.Pulse = 189; if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_2) != HAL_OK) { Error_Handler(); } sConfigOC.Pulse = 199; if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_3) != HAL_OK) { Error_Handler(); } sConfigOC.Pulse = 0; if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_4) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM4_Init 2 */ /* USER CODE END TIM4_Init 2 */ HAL_TIM_MspPostInit(&htim4); } /** * Enable DMA controller clock */ static void MX_DMA_Init(void) { /* DMA controller clock enable */ __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA interrupt init */ /* DMA1_Stream0_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn); /* DMA1_Stream3_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Stream3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Stream3_IRQn); /* DMA1_Stream7_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn); } Podobny problem miałem też wcześniej, gdy rozwiązywałem zadania z kursu STM32, ale tam była to kwestia złej kierunkowości w DMA, tutaj ją sprawdziłem w Cube'ie chyba z 3 razy. Będę baaaardzo wdzięczny za wszelką pomoc w tym zakresie, przejrzenie kodu / projektu. Środowisko: SW4STM32 + STM32CubeMX Pozdrawiam Wrk_03_SERVO_LIB.zip
  6. Chyba 10k... Wziąłem pierwszy z brzegu, a teraz już nie pamiętam, bo układ musiałem rozmontować. Ale na pewno było to coś względnie dużego, bo dioda przy tym rezystorze świeciła szczątkowo. @RFM Tego nie wiedziałem, muszę poczytać na ten temat z czystej ciekawości 🙂.
  7. Co do sterowania - tak, 3V z STMa działa też bez zarzutu. Konwerter jest mi niepotrzebny do wysterowania tym serwem, co sprawdziłem zaraz po odkryciu tego fenomenu. Ciekaw jestem tylko, dlaczego i w jaki sposób pojawia się 3V amplituda za konwerterem, gdzie nominalnie powinno być 5V, tak jak na rezystorze. Jakąś diodką to ściąga w dół czy co? Dlatego traktuję to jako zagadkę w dziale "inne", bo jeśli chodzi o użycie to idzie względnie gładko 🙂
  8. W nocie katalogowej SG90, którą znalazłem. Link here. Ze zdjęcia to to samo, co mam na biurku (choć ten model to u mnie leży już od lat, nawet nie wiem skąd go wziąłem już).
  9. Cześć wszystkim, Mam pewną kwestię do omówienia, która mnie mocno zaciekawiła. Posiadam oryginalne serwo Tower SG90 i steruję nim z STM32F4 (z kursu). Standard napięciowy STMa to 3V, dla serwo wg specyfikacji wygoglowanej 5V, przy czym dotyczy to także sygnału sterującego. Z tego powodu łączę wyjście PWM z STMa do konwertera napięć i po drugiej stronie wlatuje wejście sterujące SG90. Zasilania 3V i 5V mają wspólną masę, przy czym serwo ciągnie prąd z zasilacza, a płytka po USB. I teraz zagadka: pomierzyłem na oscyloskopie przebiegi po obu stronach konwertera i na obu amplituda jest ... 3V. Z ciekawości odłączyłem serwo i podłączyłem rezystor - amplituda 5V, PWM pod względem czasowym bez zmian. Co tu się dzieje w tym układzie, bo mnie zamurowało? 😄 Pozdrawiam
  10. A tak swoją drogą, to nie widziałem, żeby ktoś wypisał odpowiedź na pytanie na końcu odcinka kursu, więc dla ciekawych (którzy odpowiedzi nie znali) wrzucam poniżej zrzut z manuala, który to wyjaśnia chyba dość preceyzyjnie.
  11. @Elvis, właśnie miałem pisać. Problem DMA rozwiązałem. Oczywiście moja własna głupota okazała się przyczyną. Podczas pierwszego testu nie używałem jednego kanału, a wszystkie 4 i 3 zdefiniowałem sobie na DMA. Okazało się, że tylko 1 ze zdefiniowanych przez DMA kanałów określiłem jako MemtoPeriph, a w pozostałych zapomniałem tego zrobić. A jak później testowałem, to konfiguracja robiona była w stanie lekkiego zdenerwowania i z głupoty... powieliłem ten sam błąd, tym razem już tylko przy 1 kanale. Ech, wstyd mi przed samym sobą. Dzięki za zaangażowanie wszystkim.
  12. @Treker Nie próbowałem żadnym narzędziem, bo takich w sumie nigdy nie używałem i żadnego z nich nie znam. Natomiast jak przeglądałem pliki samemu, to na pewno różnice widziałem, choćby wspomniany wyżej SYSTICK_Callback był inaczej zrobiony i wcale nie było to przypadkowe wg deweloperów ST. Fajnie byłoby jakby ktoś bardziej doświadczony ode mnie w STMach spróbował na najnowszym CubeMXie postawić taki PWM sterowany przez DMA, bo może widziałby więcej niż ja w kwestii jakichś nieprawidłowości itp.
  13. Mam jeszcze jedną kwestię do tego odcinka poradnika. Chodzi mi konkretnie o przykład z wygenerowaniem PWM przez DMA zamiast przekazania wypełnienia bezpośrednio do rejestru. Ostatnim razem to zostawiłem, bo nie chciało działać. Przeznaczyłem dzisiaj chyba z 4 h na szukanie rozwiązania i dalej się nie udało. Rzecz w tym, że robię krok po kroku wszystko jak w poleceniach i nie generuje mi PWMa na diodzie. Próbowałem wykonać diagnostykę i jedyne, co zdołałem wynaleźć, to kod błędu "1", czyli "Transfer error", na kanale DMA (przez STMStudio). Najlepsze jest to, że jak wrzucę projekt z paczki to działa. Ale jak skopiuję całego maina (który swoją drogą wygląda u mnie identycznie w części pisanej przez użytkownika) z działającego projektu do mojego, to znowu nie generuje PWM. Wnioskuję po tym, że problem leży gdzieś w plikach wygenerowanych przez CubeMXa i to nawet nie przez błędy w konfiguracji, bo sama konfiguracja w tym przypadku ograniczała się do ustawień, które potem zostały przepisane do funkcji konfigurujących poniżej maina. No i żeby była jasność, wersja bez DMA, a z wpisem bezpośrednio do rejestru timera działa. Zna ktoś może rozwiązanie tego problemu?
  14. Cześć, Co do tej części kursu, to znalazłem moim skromnym zdaniem ogromny babol w pierwszym programistycznym przykładzie, ale niekoniecznie związany z autorstwem kursu, a raczej CubeMXem. Niestety, użycie przerwania od timera systemowego jest niemożliwe w aktualnej wersji bibliotek generowanych w CubeMXie. Tu można znaleźć dyskusję na ten temat i jednocześnie sposób rozwiązania problemu (dodanie jednej linii kodu w pliku wygenerowanym przez Cube'a). Co ciekawe, z tego pomysłu ST, który rzekomo bug'iem nie jest, niezadowolonych jest baaaardzo wielu użytkowników CubeMXa ;D. I choć trochę deweloperzy ST w tym racji mają moim zdaniem, to jednak aż tak drastyczne kroki nie są na pewno mile widziane zwłaszcza w firmach pracujących na STMach. Pozdrawiam
  15. Z moich uwag, poza tym, co wcześniej opisał Elvis odnośnie końca linii i buforowania, to dorzuciłbym problematyczne działanie przykładu z 10 znakami w buforze. Na moim kompie RealTerm wyrzucał krzaczki na końcu stringa (w sensie 10 znaków normalnie i potem jakieś randomy). Wydaje mi się, że wynikało to z faktu braku zamknięcia tablicy znakiem '\0' (10-znakowy bufor i 10 znaków zapisanych). Jak uzupełniłem ten znak na dodanym przeze mnie 11. miejscu problem zniknął i wypisywanie odbywało się normalnie.
×
×
  • Utwórz nowe...

Ważne informacje

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