simba92 Napisano Kwiecień 28, 2017 Udostępnij Napisano Kwiecień 28, 2017 Witam! Od kilku dni walczę z obsługą czujnika HC SR04 na stm32f103 na płytce NUCLEO. Cały czas uzyskuje błędne odczyty, wyświetlane na interfejsie UART ( zdjęcia z terminala zamieszczane w załącznikach). Po przeszukaniu internetu i różnych rozwiązań napisałem własny program do obsługi tego czujnika, który polega na : 1. wysłaniu impulsu na pinie TRIG o czasie 10[us], pin -> PA1 2. odczycie impulsu naa pinie ECHO z wykorzystaniem przerwania o zboczu narastajacym i opadajacym, pin -> PA6 3.wysłaniu wyniku na UART Poniżej zamieszczam kod programu : #include "stm32f10x.h" #define GPIO_Trig GPIOA #define Trig_Pin GPIO_Pin_1 #define GPIO_Echo GPIOA #define Echo_Pin GPIO_Pin_6 #define EchoLine EXTI_Line6 #define EchoIRQ EXTI9_5_IRQn #define SR04_Prescaler 31 #define FallingEdge 0 #define RisingEdge 1 //Volatile interrupts volatile uint8_t PulseTriggerEnded, InterruptEdge, PulseEchoEnded; volatile uint32_t PulseTime=0; /*-------------------Konfiguracja zewnetrznego zegara HSE jako zrodla taktujacego system--------------------------------*/ void RCC_Conf(void) { ErrorStatus HSEStartUpStatus; //Reset ustawien RCC RCC_DeInit(); //Wlacz HSE RCC_HSEConfig(RCC_HSE_ON); //Czekaj az HSE bedzie gotowy HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); //zwloka dla pamieci Flash FLASH_SetLatency(FLASH_Latency_2); //HCLK = SYSCLK RCC_HCLKConfig(RCC_SYSCLK_Div1); //PCLK2 = HCLK RCC_PCLK2Config(RCC_HCLK_Div1); //PCLK1 = HCLK/2 RCC_PCLK1Config(RCC_HCLK_Div2); //PPLCLK = 16MHZ * 4 = 64 MHZ - do plytki wlutowano kwarc 16MHz z kondenasorami po 22pF i wykorzystano petle PLL x4 RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_4); //Wlacz PLL RCC_PLLCmd(ENABLE); //Czekaj az PLL poprawnnie sie uruchomi while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); //PLL bedzie zrodlem sygnalu zegarowego RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //Czekaj az PLL bedzie sygnalem zegarowym systemu while(RCC_GetSYSCLKSource() != 0x08); } } /*----------------------------------------------------------------------------------------------------------------------------------------------*/ /*-------------Podprogramy do obslugi funkcji printf dla interfejsu UART do wyswietlania wyniku pomiaru na terminalu----------*/ void send_char(char c) { while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); USART_SendData(USART2, c); } void send_string(const char* s) { while (*s) send_char(*s++); } int __io_putchar(int c) { if (c=='\n') { send_char('\r'); } send_char(c); return c; } volatile uint32_t timer_ms = 0; void SysTick_Handler() { if (timer_ms) { timer_ms--; } } /*------------------------------------------------------------------------------------------------------------------------------*/ void delay_us(int time) //funkcja opozniajaca bazujaca na zegarze systemowym { timer_ms = time; while (timer_ms) {}; } /*----------------Obsluga przerwania od zegara TIM2 dla pinu TRIGGER----------------------*/ void TIM2_IRQHandler() { if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //gdy licznik doliczy do 1000 tj 1000us generujemu przerwanie update { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } if (TIM_GetITStatus(TIM2, TIM_IT_CC1) == SET) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC1); //gdy licznik doliczy do 10 wystawimy na pinie Trig stan wysoki GPIO_SetBits(GPIO_Trig, Trig_Pin); } if (TIM_GetITStatus(TIM2, TIM_IT_CC2) == SET) { TIM_ClearITPendingBit(TIM2, TIM_IT_CC2); //gdy licznik doliczy do 20 wystawimy na pinie Trig stan niski GPIO_ResetBits(GPIO_Trig, Trig_Pin); } } /*-----------------------------------------------------------------------------------------------------------------------------------------------*/ void send_pulse(void) //funkcja do generowania pojedynczego impulsu TRIG = 10us { TIM_Cmd(TIM2,DISABLE); TIM_SetCounter(TIM2,0); TIM_Cmd(TIM2, ENABLE); delay_us(30); PulseTriggerEnded = 1; //flaga konca impulsu trig TIM_SetCounter(TIM2,0); //wylaczmy i skasujmy licznik , wyslalismy juz potrzebny impuls TIM_Cmd(TIM2,DISABLE); InterruptEdge = RisingEdge; //informacja dla przerwania od echo ze czekamy na przerwanie ze zboczem narastajacym } void EXTI9_5_IRQHandler(void) { if(EXTI_GetITStatus(EchoLine) == SET) { EXTI_ClearITPendingBit(EchoLine); if(InterruptEdge == RisingEdge) //impuls powrotny echa ze zboczem narastajacym - zaczynamy odliczac czas impulsu dla echa { GPIO_SetBits(GPIOA, GPIO_Pin_5); PulseEchoEnded = 0; TIM_SetCounter(TIM3,0); TIM_Cmd(TIM3, DISABLE); TIM_Cmd(TIM3, ENABLE); //start liczymy czas dla impulsu echa InterruptEdge = FallingEdge; //informacja dla przerwania ze zboczem opadajacym } else { PulseTime = TIM_GetCounter(TIM3); //pobieramy czas impulsu echa TIM_Cmd(TIM3, DISABLE); //wylaczamy licznik GPIO_ResetBits(GPIOA, GPIO_Pin_5); PulseEchoEnded = 1; //flaga informujaca o koncu pomiaru } } } int main(void) { GPIO_InitTypeDef gpio; TIM_TimeBaseInitTypeDef tim; TIM_OCInitTypeDef channel; NVIC_InitTypeDef nvic; EXTI_InitTypeDef exti; RCC_ClocksTypeDef rc; USART_InitTypeDef uart; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); SysTick_Config(SystemCoreClock / 1000000); //Konfiguracja UART GPIO_StructInit(&gpio); 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); USART_StructInit(&uart); uart.USART_BaudRate = 115200; USART_Init(USART2, &uart); USART_Cmd(USART2, ENABLE); gpio.GPIO_Pin = GPIO_Pin_6; gpio.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(GPIOA, &gpio); gpio.GPIO_Pin = GPIO_Pin_1; gpio.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &gpio); /*----------------------------TIM2 dla TRIG----------------------------------*/ TIM_TimeBaseStructInit(&tim); tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Prescaler = 32 - 1; //zegar na lini z TIM2 ma czestotliwosc PCLk1 = HCLK/2 = 32MHZ dzielac przez 32 fTim2 = 1MHZ ->Ttim2=1us tim.TIM_Period = 1000 - 1; //zliczamy do 1000us TIM_TimeBaseInit(TIM2, &tim); TIM_OCStructInit(&channel); channel.TIM_OCMode = TIM_OCMode_Timing; channel.TIM_Pulse = 10; //generuj przerwanie jak zliczy do 10 TIM_OC1Init(TIM2, &channel); channel.TIM_OCMode = TIM_OCMode_Timing; channel.TIM_Pulse = 20; //generuj przerwanie jak zliczy do 20 TIM_OC2Init(TIM2, &channel); TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC1|TIM_IT_CC2, ENABLE); // nvic.NVIC_IRQChannel = TIM2_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 0; nvic.NVIC_IRQChannelSubPriority = 0; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); /*----------------------------TIM2 dla TRIG----------------------------------*/ /*---------------------------------------------------------------------------*/ /*----------------------------TIM3 dla ECHO----------------------------------*/ tim.TIM_CounterMode = TIM_CounterMode_Up; tim.TIM_Prescaler = 32 - 1; //zegar na lini z TIM3 ma czestotliwosc PCLk1 = HCLK/2 = 32MHZ dzielac przez 32 fTim3 = 1MHZ ->Ttim2=1us tim.TIM_Period = 2000 - 1; //zliczamy do 2000us TIM_TimeBaseInit(TIM3, &tim); nvic.NVIC_IRQChannel = EchoIRQ; nvic.NVIC_IRQChannelPreemptionPriority = 0; nvic.NVIC_IRQChannelSubPriority = 0; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6); exti.EXTI_Line = EchoLine; exti.EXTI_LineCmd = ENABLE; exti.EXTI_Mode = EXTI_Mode_Interrupt; exti.EXTI_Trigger = EXTI_Trigger_Rising_Falling; EXTI_Init(&exti); /*----------------------------TIM3 dla ECHO----------------------------------*/ RCC_GetClocksFreq(&rc); float Distance = 0.0f, TimeOfFlight = 0.0f; float TTimePerCnt = (SR04_Prescaler+1)/(float)rc.PCLK1_Frequency; //(31+1)/32 MHZ = 1us rowny okresowi zegara TIM2 const float SpeedOfSound = 340.3f; //predkosc dzwieku przy 15 stC uint8_t PulseTriggerSent = 0; /*GPIO_SetBits(GPIOA, GPIO_Pin_5); delay_us(5000000); GPIO_ResetBits(GPIOA, GPIO_Pin_5); delay_us(5000000);*/ while(1) { if(PulseTriggerSent == 0) { PulseTriggerSent = 1; send_pulse(); } if(PulseTriggerSent && PulseTriggerEnded && PulseEchoEnded) { TimeOfFlight = TTimePerCnt*((float)PulseTime); //okres zegara x ilosc zliczonych impulsow Distance = ((SpeedOfSound*TimeOfFlight)/2.0f); //d = 340 [m/s] * x [us] /2 delay_us(200000); //200ms PulseTriggerSent = 0; } printf("czas = %f [s] ,odleglosc od przeszkody = (%f [m]) \n",TimeOfFlight,Distance); } } Bardzo proszę o pomoc w rozwiązaniu mojego problemu z obsługą tego czujnika, która przecież nie może być taka trudna. Podsumowując : - bardzo złe wyniki pomiarów - powyżej 15 cm w ogóle czujnik nic nie widzi i często się zawiesza PS: dodatkowo pod zasilanie podpiąłem równolegle kondensatory 2x100uf (bez kondensatorów w sumie nie ma różnicy w odczycie) Pomiar dla 10 cm : Pomiar dla 20 cm : 1 Cytuj Link do komentarza Share on other sites More sharing options...
Lukaszm Kwiecień 28, 2017 Udostępnij Kwiecień 28, 2017 Sprawdzałeś na oscyloskopie albo analizatorze logicznym czy sensor w ogóle działa poprawnie? Cytuj Link do komentarza Share on other sites More sharing options...
simba92 Kwiecień 28, 2017 Autor tematu Udostępnij Kwiecień 28, 2017 Niestety nie posiadam oscyloskopu ani analizatora logicznego. Kupiłem nowy 2 czujnik ale on też wskazuje podobne odczyty. Moduły, które są teraz produkowane zawierają mniej elementów pasywnych, może coś poszło nie tak. Siedzę nad tym czujnikiem już kilka dni i cały czas nie mogę zrozumieć dlaczego nie działa jak powinien 🙁 1 Cytuj Link do komentarza Share on other sites More sharing options...
Lukaszm Kwiecień 28, 2017 Udostępnij Kwiecień 28, 2017 Szkoda że nie masz sprzętu. Podróbka Saleae kosztuje 40zł, a daje bardzo duże możliwości w zakresie diagnostyki. Załóżmy, że chociaż jeden z sensorów jest ok. Próbowałeś napisać sobie program, który mierzyłby czas trwania impulsu echo poprzez zwykły polling pinu? Cytuj Link do komentarza Share on other sites More sharing options...
Polecacz 101 Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Zarejestruj się lub zaloguj, aby ukryć tę reklamę. Produkcja i montaż PCB - wybierz sprawdzone PCBWay! • Darmowe płytki dla studentów i projektów non-profit • Tylko 5$ za 10 prototypów PCB w 24 godziny • Usługa projektowania PCB na zlecenie • Montaż PCB od 30$ + bezpłatna dostawa i szablony • Darmowe narzędzie do podglądu plików Gerber Zobacz również » Film z fabryki PCBWay
Elvis Kwiecień 28, 2017 Udostępnij Kwiecień 28, 2017 Wygląda na to, że w programie czai się kilka błędów. Zacznijmy od podstawowego: tim.TIM_Prescaler = 32 - 1 Lepiej użyć preskalera o wartości 64, wtedy faktycznie częstotliwość wyniesie 1MHz, a nie 2MHz. To powinno sporo poprawić w działaniu. Kolejna poprawka to wywołanie RCC_Conf(). Całkiem ładna funkcja, ale jakoś mało używana. Na szczęście domyślny kod wygenerowany dla Nucleo też używa 64MHz, więc i tak działa - ale mniej stabilnie, bo na RC zamiast używać kwarca. Na mojej płytce udało mi się wygenerować impuls wyzwalający - niestety nie mam w tej chwili czujnika HC-SR04, więc więcej teraz nie przetestuję. Cytuj Link do komentarza Share on other sites More sharing options...
deshipu Kwiecień 29, 2017 Udostępnij Kwiecień 29, 2017 Ja się jeszcze zastanawiam, czy nie zasilasz czasem tego HC SR-04 napięciem 3.3V? Bo on wymaga 5V do działania. HC SR-04+ działa na 3V. Cytuj Link do komentarza Share on other sites More sharing options...
simba92 Kwiecień 29, 2017 Autor tematu Udostępnij Kwiecień 29, 2017 Ja się jeszcze zastanawiam, czy nie zasilasz czasem tego HC SR-04 napięciem 3.3V? Bo on wymaga 5V do działania. HC SR-04+ działa na 3V. Układ jest zasilany napięciem 5V, a na pinie echo zastosowałem dodatkowo dzielnik napięciowy aby do STM podawać max 3V3. Wygląda na to, że w programie czai się kilka błędów. Zacznijmy od podstawowego: tim.TIM_Prescaler = 32 - 1 url][/url][/center] Dzielnik jest dobry ponieważ szyna APB1 taktowana jest z częstotliwością(max 36MHZ) a tu HCLK/2 = 64/2 =32MHZ, zewnetrzny kwac działa z fHSE=64MHZ (doczytałem o tym w książce K.Paprocki Mikrokontrolery STM32 w praktyce). Funkcja RCE_conf() musi działać pisałem ją w oparciu o listing z powyższej książki. Jakie są inne błędy w programie Elvis? Szkoda że nie masz sprzętu. Podróbka Saleae kosztuje 40zł, a daje bardzo duże możliwości w zakresie diagnostyki.Załóżmy, że chociaż jeden z sensorów jest ok. Próbowałeś napisać sobie program, który mierzyłby czas trwania impulsu echo poprzez zwykły polling pinu? Jeszcze nie pisałem programu na pooling, spróbuje to zrobić i zamówię sobie analizer z allegro 😉 1 Cytuj Link do komentarza Share on other sites More sharing options...
Elvis Kwiecień 29, 2017 Udostępnij Kwiecień 29, 2017 Dzielnik jest niestety zły. To prawda, że APB1 jest taktowana częstotliwością HCLK/2, ale timery są wyjątkiem - pracują z pełną prędkością APB1 x 2, czyli HCLK. Ale żeby nie było, że bezgranicznie wierzę dokumentacji - uruchamiałem Twój program i sprawdzałem analizatorem wyniki. Trzeba ustawić preskaler na 64, żeby działało poprawnie, tym razem producent nie oszukał. Nie twierdzę, że RCE_conf() jest zła - po prostu nie jest używana. Dla mnie była to zaleta, bo program działał pomimo braku kwarca 16MHz 🙂 Na początek spróbuj poprawić preskaler i zwiększ szerokość impulsu wyzwalającego, teraz generujesz ok. 5-6 us, więc sporo za mało (minimum 10us). 1 Cytuj Link do komentarza Share on other sites More sharing options...
simba92 Kwiecień 29, 2017 Autor tematu Udostępnij Kwiecień 29, 2017 Elvis dzięki, zmieniłem wartość preskalera na 64 i czujnik działa poprawnie. Generuje dobry impuls 10us ,a nie jak wczesniej 5-6 us. Temat zamknięty, wszystko działa ! 😉 1 Cytuj Link do komentarza Share on other sites More sharing options...
Pomocna odpowiedź
Dołącz do dyskusji, napisz odpowiedź!
Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!