Skocz do zawartości

[STM32] HC-SR04


paweleh

Pomocna odpowiedź

Witam,
Od paru dni mam problem ze skonfigurowaniem owego czujnika.

Kod pisze w oparciu o biblioteki HALa i CubeMx.

/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"

/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;

UART_HandleTypeDef huart2;

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
       uint32_t cnt;
       uint32_t distance;
       uint32_t time;


       char buffer[100];
       int a;
       uint8_t x;

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);
static void MX_TIM3_Init(void);
static void MX_USART2_UART_Init(void);

/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/

/* USER CODE END PFP */

/* USER CODE BEGIN 0 */


void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
               __HAL_TIM_SET_COUNTER(&htim2,0);        // ****** Konfiguracja przerwania
}
/* USER CODE END 0 */

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();

 /* Configure the system clock */
 SystemClock_Config();

 /* Initialize all configured peripherals */
 MX_GPIO_Init();
 MX_TIM2_Init();
 MX_TIM3_Init();
 MX_USART2_UART_Init();

 /* USER CODE BEGIN 2 */
 HAL_TIM_PWMBITS_1;
 huart2.Init.Parity = UART_PARITY_NONE;
 huart2.Init.Mode = UART_MODE_TX_RX;
 huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
 huart2.Init.OverSampling = UART_OVERSAMPLING_16;
 HAL_UART_Init(&huart2);

}

/** Configure pins as
       * Analog
       * Input
       * Output
       * EVENT_OUT
       * EXTI
*/
void MX_GPIO_Init(void)
{

 GPIO_InitTypeDef GPIO_InitStruct;

 /* GPIO Ports Clock Enable */
 __GPIOC_CLK_ENABLE();
 __GPIOD_CLK_ENABLE();
 __GPIOA_CLK_ENABLE();
 __GPIOB_CLK_ENABLE();

 /*Configure GPIO pin : B1_Pin */
 GPIO_InitStruct.Pin = B1_Pin;
 GPIO_InitStruct.Mode = GPIO_MODE_EVT_RISING;
 GPIO_InitStruct.Pull = GPIO_NOPULL;
 HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

 /*Configure GPIO pin : LD2_Pin */
 GPIO_InitStruct.Pin = LD2_Pin;
 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
 GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
 HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

#ifdef USE_FULL_ASSERT

/**
  * @brief Reports the name of the source file and the source line number
  * where the assert_param error has occurred.
  * @param file: pointer to the source file name
  * @param line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t* file, uint32_t line)
{
 /* USER CODE BEGIN 6 */
 /* User can add his own implementation to report the file name and line number,
   ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
 /* USER CODE END 6 */

}

#endif

/**
 * @}
 */

/**
 * @}
*/

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

Prosze o pomoc.

__________

Komentarz dodany przez: Treker

Kody programów proszę umieszczać w treści postów w tagach .

Link do komentarza
Share on other sites

"..problem ze skonfigurowaniem owego czujnika.."

Co to znaczy? Tego czujnika nie konfiguruje się. Wystarczy wysłać impuls i poczekać na echo mierząc przy tym czas. Której z tych rzeczy Twój program nie robi?

Przecież nikt nie będzie analizował 300 linii kodu żeby znaleźć za Ciebie błąd. Ponieważ mówienie pomaga - to jest dowiedzione, może opowiedz własnymi słowami jak chcesz obsługiwać ten czujnik w swoim kodzie. Po kolei: co robisz, co to za konstrukcja, do których pinów podłączyłeś HC, który timer co ma robić, w jakim trybie i kiedy, jak uzyskujesz wyniki itd. Dokładnie, ze szczegółami dot. częstotliwości taktowania itp. Może kogoś tym zaciekawisz i pochyli się nad programem. Jeżeli błędy są w idei stosowania tego czujnika - opisz ją a szybko okaże się czy miałeś dobry pomysł. Nie licz, że wrzucenie kodu plus prośba o pomoc zadziałają. Forum to nie Google, nie ma tu gotowych odpowiedzi tylko normalni ludzie, którzy muszą ją wymyślić i napisać. Mogą odpowiadać na konkretne pytania bo wiedzą naprawdę dużo, ale dlaczego mieliby analizować czyjś długi kod? To Twoje zadanie - masz nie tylko wklepać, ale także uruchomić i przetestować program.

Link do komentarza
Share on other sites

Wszystkie zegary mam ustawione na taktowanie 36MHz

Idea dzialania:

Zegar 3 ustawiony na czestotliwosc odpowiadajaca 60ms generuje sygnal PWM z wypelnieniem 10us.

Zegar 2 ma za zadanie odbierac sygnal. Ustawilem go na Input Capture (z czestotliwoscia rowniez odpowiadajaca 60ms) z funkcja przerwan. W przerwaniu czytam wartosc zegara a nastepnie go zeruje.

Link do komentarza
Share on other sites

Jak rozumiem słowo "zegar" trzeba wszędzie zamienić na słowo "timer". Prosiłem o szczegóły tzn. z jakim podzielnikiem pracuje każdy timer, na którym pinie ma we/wy itd. Chcemy wiedzieć co chciałeś zrobić. Przecież musisz wiedzieć takie rzeczy pisząc kod - sprzedaj tę wiedzę, to nie boli. Dopiero to będzie można porównać z kodem.

Acha, czyli chcesz by impuls 10us pojawiał się z okresem 60ms. Czy sprawdziłeś, że timer 3 generuje ten przebieg? Tylko nie mów, że nie masz oscyloskopu. Możesz użyć diodki LED podłączonej do wyjścia. 10us to za mało żeby coś zobaczyć? To zwiększ wypełnienie do 10% - na pewno będzie coś widać. Nawiasem mówiąc wypełnienia nie wyrażamy w jednostkach czasu tylko w procentach lub innym ułamku. Zawsze możesz też użyć kolejnego timera do zmierzenia przebiegu generowanego przez timer 3 - sam sobie zrób oscyloskop.

Input Capture działa na zbocza - od którego do którego chcesz mierzyć, który sygnał, pisz wszystko. Dlaczego mam to z Ciebie wyciągać? Od tego zależy czy przekonasz nas, że rozumiesz działanie tego czujnika. Problem może wcale nie być w kodzie tylko w niezrozumieniu timingów.

Tak więc, jeszcze raz. Tylko tym razem postaraj się bardziej. Wyobraź sobie, że tłumaczysz to kompletnie niekumatemu gościowi, który chce się tego nauczyć ale nie wie jak działają timery, jak w ogóle mierzy się odległości ultradźwiękami itd..

Link do komentarza
Share on other sites

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

jlcpcb.jpg

jlcpcb.jpg

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

Dzięki Marku za chęć współpracy i cierpliwość.

Jak mam być szczery to STM32 to dla mnie nowość, z którą mam krótkie doświadczenie. (początki są zawsze trudne 😳 )

Moja płytka to NUCLEO F103RB.

Timer 3, który generuje sygnał PWM podłączyłem do Arduino i faktycznie wysyła sygnał 10us (zbadałem go pulseIN).

Konfiguracja timera 3:

PWM Output

Prescaler: 35

Period: 59999;

PIN PA6 (TIM3 CHANNEL1)

Przy ustawieniu taktowania każdego zegara na 36MHz taka konfiguracja daje okres okolo 60ms.

Timer 2:

Input Capture z przerwaniami

Prescaler: 35;

Period: 59999;

PA1 (TIM2, CHANNEL2)

Tutaj także okres wyliczony na okolo ~60ms.

Chcę zmierzyć długość sygnału wejściowego, który generuje czujnik.

Nie rozumiem działania "na zbocza". Po każdym przerwaniu mam resetować licznik? Różnica między zboczami to moja pożądana długość sygnału?

Link do komentarza
Share on other sites

Skoro wiesz, że impulsy wyzwalające są na 100% poprawnie generowane, to tą samą metodą upewnij się, że dostajesz też coś z czujnika. Ten nawet bez niczego przed nosem powinien produkować impuls o długości ok. 38ms. Oczywiście przeszkoda powinna skracać ten impuls proporcjonalnie do jej odległości.

Kiedy już będziesz wiedział, że cokolwiek na pin procesora wraca, możesz się zabrać za pomiar długości. Czytałeś o trybie Input Capture? Jeśli nie bardzo czaisz pojęcie zboczy sygnału wejściowego i ich wpływu na system timera pracującego w tym trybie, to mam niejasne przeczucie, że tu możesz mieć kłopoty. Opisz pokrótce (5 zdań) jak rozumiesz działanie timerów 2-5 w trybie Input Capture. Oczywiście zupełnie spokojnie możesz posługiwać się obszerną dokumentacją procesora - polecam w tym przypadku "RM0008 Reference Manual". To nie egzamin maturalny - można ściągać 🙂

Link do komentarza
Share on other sites

Oczywiście sprawdziłem działanie w "drugą stronę" i jestem pewny, że do procesora wraca sygnał.

Mój punkt widzenia na Input Capture 😳 :

Input Capture mierzy sygnał zewnętrzny. Po wystąpieniu narastającego zbocza na wejściu wyzerowany w przerwaniu timer zaczyna liczyć co jest podstawą do zmierzenia czasu. Następnie po tym jak sygnał przestanie narastać szczytujemy wartość (i ponownie zerujemy licznik(?)). Proces przebiega w kółko w wyznaczonym przez nas okresie wyskalowanym "prescalerem" i "periodem".

Widać, że dużo mi jeszcze brakuje 🙂

Link do komentarza
Share on other sites

To teraz ja opiszę moją wersję:

1. Tryb Input Capture został wymyślony po to, by procesor miał szansę łapać czas pewnych zdarzeń całkowicie sprzętowo, bez udziału programu. Dzięki temu a) program może zajmować się ciekawszymi rzeczami niż oczekiwanie na zdarzenie, b) unikamy wszelkich dziwnych opóźnień związanych z działaniem programu wynikających np. z obsługi przerwań, c) dedykowany sprzęt jest zwykle szybszy niż program wykonywany przez wielki procesor dzięki czemu można mierzyć zdarzenia szybsze i/lub częstsze.

2. Zdarzeniem w sensie pktu 1 może być zmiana stanu sygnału wejściowego - czyli zbocze. Zbocze może być narastające - gdy sygnał zmienia się z 0 na 1 i opadające - gdy mamy zmianę z 1 na 0. Jeżeli impuls wyjściowy czujnika jest "dodatni" (czyli zwykle jest stan 0 a mierzymy czas trwania stanu 1) to oczywistym jest, że najpierw wystąpi zbocze narastające a w chwilę później zbocze opadające. Interesuje nas odległość w czasie, liczona np. w us między tymi zboczami.

3. Ponieważ program nie powinien mieszać się do pomiarów czasu bo tylko by przeszkadzał, timer w trybie Input Capture jest całkowicie samowystarczalny. Powinien sobie liczyć z zadaną prędkością (ustawioną jego prescalerem) w niczym nieograniczonym cyklu 0..65535..0..65535 itd. Do łapania "pozycji w czasie", timer ma 4 niezależne rejestry TIMx_CCRx. Każdy z nich ma - tak jak sam timer - 16 bitów i służy do "zatrzaśnięcia" zawartości timera w locie, oczywiście na skutek wystąpienia jakiegoś zdarzenia.

4. Wiedząc tyle pewnie już się domyślasz jak powinieneś zrobić swój pomiar. Musisz wykorzystać dwa dowolne rejestry CCRx: jeden z nich "podłączyć" do sygnału z czujnika tak, by łapał chwilę pojawienia się zbocza narastającego a drugi tak, by zatrzaskiwał moment wystąpienia zbocza opadającego na tym samym wejściu. Oczywiście podłączanie rejestrów CCRx do zdarzeń robi się przez ustawianie odpowiednich bitów konfiguracyjnych, których jest mnóstwo. Np. TIM2_CCR1 ma swoją konfigurację w TIM2_CCMR1. Ponieważ rejestry te służą także do opisywania trybu Output Compare - trzeba uważać i wybrać odpowiednie znaczenia pól.

5. Pierwsze zdarzenie (czyli zbocze narastające rozpoczynające mierzony impuls) nie musi o niczym informować programu więc nie musi zgłaszać żadnego przerwania. Informacja zapisana do pierwszego rejestru CCRx jest sama z siebie bezużyteczna. Dopiero zbocze opadające i związany z nim zapis do drugiego rejestru powinien wywoływać przerwanie. W jego obsłudze wystarczy odczytać oba rejestry i policzyć różnicę. To będzie poszukiwana długość. Zauważ, że dopóki odstęp czasu nie przekroczy pojemności timera, nie jest ważne co znalazłeś w rejestrach. Nawet jeśli "po drodze" timer się przepełnił, nic nie szkodzi. Zawsze odejmując zawartość drugiego rejestru od pierwszego dostaniesz długość liczoną w "tickach" timera. Arytmetyka modulo 2^16 załatwi poprawny wynik 🙂 Pracując w trybie Input Capture zawartość timera jest "święta" - nie powinieneś go ani zatrzymywać, ani zerować, ani zapisywać. To twój zegarek. Nawet w trybie pomiaru sygnału PWM (odmiana tego co opisałem) to sprzęt zajmuje się manipulacją stanu timera.

Szczegóły tego co powinieneś zrobić i jakie pola bitowe ustawić masz krok po kroku opisane w rozdziale 15.3.5 wspomnianego dokumentu. Powodzenia 🙂

Link do komentarza
Share on other sites

Zrobilem tak jak mowiles.

Wartosci z CCR2 sa w sumie ok, natomiast CCR1 "wypluwa" caly czas zera :/

Ustawienia bibliotek Halla

void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
{
 /* Capture compare 1 event */
 if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET)
 {
   if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) !=RESET)
   {
     {
       __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1);
       htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1;

       /* Input capture event */
       if((htim->Instance->CCMR1 & TIM_CCMR1_CC1S) != 0x00)      // CC1S jest różny od zera
       {
         HAL_TIM_IC_CaptureCallback(htim);                // generuje sie przerwanie
       }
       /* Output compare event */
       else
       {
         HAL_TIM_OC_DelayElapsedCallback(htim);
         HAL_TIM_PWM_PulseFinishedCallback(htim);
       }
       htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED;
     }
   }
 }

W sumie miało by to jakiś sens odnośnie tego co jest napisane w reference manualu.

W przerwaniu czytam obie wartości

****** CALLBACK

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){
       cnt = TIM2->CCR1;      // czytam wartosci rejestru CCR1
cnt1 = TIM2->CCR2;    // czytam wartosci rejestru CCR2

}

Link do komentarza
Share on other sites

To znaczy jak? Ponieważ nie widać jak skonfigurowałeś system Input Capture, to co możemy poradzić? Ponieważ jeden rejestr coś łapie to znaczy, że widzi jedno ze zboczy. Jest oczywistym, że drugie musi także się pojawiać. Jeśli drugi rejestr nie widzi odwrotnego zbocza to znaczy, że coś tam skopałeś. Pracuj aż oba wyniki będą sensowne. Potem odejmij i policz długość impulsu 🙂

Link do komentarza
Share on other sites

Konfiguruje zgodnie z tym co jest napisane w reference manual:

czyli

Select the active input: TIMx_CCR1 must be linked to the TI1 input, so write the CC1S bits to 01 in the TIMx_CCMR1 register. As soon as CC1S becomes different from 00, the channel is configured in input and the TIMx_CCR1 register becomes read-only.
 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; 
czyli w bitach  (0x00000001)
Select the edge of the active transition on the TI1 channel by writing the CC1P bit to 0 in the TIMx_CCER register (rising edge in this case).
 sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;  

przy czym TIM_INPUTCHANNELPOLARITY_RISING = (0x00000000) w bitach

Program the input prescaler. In our example, we wish the capture to be performed at each valid transition, so the prescaler is disabled (write IC1PS bits to 00 in the TIMx_CCMR1 register).
 sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; 

czyli ustawione na 0x0000;

Filtr ustawilem na zero bo chyba nie jest mi potrzebny?

sConfigIC.ICFilter = 3;

Enable capture from the counter into the capture register by setting the CC1E bit in the TIMx_CCER register.
 HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_2); 

Naprawde nie rozumiem jak to mozliwe ze czyta tylko jedno zbocze :/

Link do komentarza
Share on other sites

Jeden rejestr czyta jedno zbocze a drugi rejestr drugie. Jak rozumiem drugi ustawiłeś identycznie z dokładnością do kierunku zbocza, tak?

To teraz jeszcze upewnij się, czy oba rejestry korzystają z tego samego wejścia TI1 i czy jest tam doprowadzony sygnał z pinu do którego podłączyłeś wyjście czujnika.

Filtr służy do usuwania krótkich zakłóceń impulsowych lub "wyostrzania" zboczy zaszumionych sygnałów. Możesz go włączyć - na pewno nie zaszkodzi.

Link do komentarza
Share on other sites

Drugi rejestr wraz z pierwszym korzystają z tego samego wejścia oraz są skonfigurowane tak samo (z tym, że jeden ma Falling Edge). Mimo tego dalej wartości zwraca dalej tylko jeden :/

Link do komentarza
Share on other sites

Proszę o to mój kod:


void MX_TIM2_Init(void)
{

 TIM_ClockConfigTypeDef sClockSourceConfig;
 TIM_MasterConfigTypeDef sMasterConfig;
 TIM_IC_InitTypeDef sConfigIC;

 htim2.Instance = TIM2;
 htim2.Init.Prescaler = 0;       
 htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
 htim2.Init.Period = 65535;
 htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
 HAL_TIM_Base_Init(&htim2);

 sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
 HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig);

 HAL_TIM_IC_Init(&htim2);

 sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
 sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
 HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig);

 sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;     
 sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
 sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
 sConfigIC.ICFilter = 0;
 HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1);

 sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
 HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_2);

}

Link do komentarza
Share on other sites

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ę »
×
×
  • 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.