Skocz do zawartości

Problem z poprawnymi odczytami z czujnika HC SR04 na płytce NUCLEOf103


simba92

Pomocna odpowiedź

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 :

  • Lubię! 1
Link do komentarza
Share on other sites

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 🙁

  • Lubię! 1
Link do komentarza
Share on other sites

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?

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

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ę.

Link do komentarza
Share on other sites

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 😉

  • Lubię! 1
Link do komentarza
Share on other sites

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).

  • Lubię! 1
Link do komentarza
Share on other sites

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!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

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