Ten post chciałbym poświęcić na projekt realizujący pomiar odległości czujnikiem HC-SR04, wyposażony w dwa czujniki temperatury DS18B20, które pozwolą na dostosowanie wyników pomiarów do warunków zewnętrznych, komunikację poprzez USART z komputerem.
Dodatkowe funkcje jakie chciałbym wprowadzić to pomiar wielkości pomieszczenia czy pudła, wraz z mierzeniem jej zawartości.
Do projektu należy skonfigurować takie elementy jak:
Czujnik odległości HC-SR04, który działa na dwóch timerach generujących odpowiednie czasy oraz zliczający czas wystąpienia stanu wysokiego na pinie Echo. Jest on następnie zamieniany na czas, który zostaje przeliczony na odległość układu od przeszkody.
Czujniki temperatury DS18B20, połączonych w jednej linii. Dane z nich są następnie uśredniane i ich wartość jest pobierana do obliczenia odległości.
Poprzez USART dane będą wysyłane do komputera. Głównie w celu debugowania, ale także z informacjami zebranymi z czujników.
Wyświetlacz Nokia, na którym będą prezentowane wyniki działania programu tzn, aktualny czas, odległość od przeszkody oraz temperatura.
W tej części chciałbym trochę opisać czujnik HC-SR04. Podstawowe parametry:
Ja korzystam z czujnika zawierającego dwa piny przeznaczone do sterowania. Na jednym z nich generowany jest impuls 10us, drugi natomiast odczytuje odpowiedź. Występują też układy posiadające tylko jeden pin sterujący. Zasada działa jest taka sama. Takie same sygnały należy podać w celu inicjalizacji oraz odczytu.
Należy także mieć na uwadze, że mogą występować sytuacje w których dana przeszkoda nie zostanie poprawnie zinterpretowana. Może się to zdarzyć gdy odległość od przeszkody przekracza maksymalny dopuszczalny zakres pomiarowy. W takim przypadku, jeśli jest to niewielkie przekroczenie to powinna wystąpić tylko pogorszenie dokładności. Druga sytuacja dotyczy za małego kąta pomiarowego. Fala ultradźwiękowa trafi na przeszkodę natomiast nie zostanie zarejestrowana fala powrotna, która po odbiciu nie wróci do odbiornika. Ostatnim problemem jest wielkość obiektu mierzonego. Ten z kolei jeśli będzie za mały to czujnik nie zarejestruje jego obecności. Im dalej znajduje się on od przeszkody tym większe prawdopodobieństwo wystąpienia takiej sytuacji.
Czujnik HC-SR04 posiada pewną rozbieżność, która w zależności od jego zastosowania może mieć, większy lub mniejszy wpływ na jego skuteczność działania. W celu jej maksymalnej poprawy zastosowałem uśrednianie dziesięciu wyników pomiarów, które wyeliminują występujące duże niedokładności. Następnie wyeliminowałem wyniki, znacząco odbiegające od pozostałych. Każdy z wyników zanim przejdzie do uśredniania zostaje obliczony poprzez zmodyfikowany wzór obliczania odległości, który pobiera wcześniej obliczoną prędkość dźwięku w powietrzu. Te wartości wahają się w następujący sposób:
Taki wzór można stosować na dwa sposoby. Pierwszy z nich jest obliczanie każdorazowo tej prędkości dla zmierzonej temperatury. Drugi sposób dotyczy wykonania obliczeń wcześniej i przygotowania tablicy zawierającej wartości już obliczone. Jest on dosyć kłopotliwy jeśli zależy nam na dużej dokładności dla szerokiego zakresu temperatur. Ponieważ wtedy rozmiar tablicy byłby dosyć znaczący.
Poniżej przejdę przez ważniejsze funkcje dla każdego z modułów. Cały program będzie do pobrania pod linkiem na dole strony.
Jak już wspomniałem wcześniej czujnik HC-SR04 został oprogramowany z użyciem dwóch timerów.
Dodatkowe funkcje jakie chciałbym wprowadzić to pomiar wielkości pomieszczenia czy pudła, wraz z mierzeniem jej zawartości.
Elementy oraz podłączenie:
- HC-SR04 domyślnie PD0;
- DS18B20 domyślnie PB8. Można go dowolnie modyfikować ponieważ procedura obsługi transmisji OneWire została oparta o funkcje opóźniające.
- Konwerter USART USB - RX PC10; TX PC11;
- Wyświetlacz Nokia5110 DC - GPIOC_14 | CE - GPIOC_13 | RST - GPIOC_15 | DIN - GPIOC_3 | CLK - GPIOB_10 ;
Programowanie:
Do projektu należy skonfigurować takie elementy jak:
Czujnik odległości HC-SR04, który działa na dwóch timerach generujących odpowiednie czasy oraz zliczający czas wystąpienia stanu wysokiego na pinie Echo. Jest on następnie zamieniany na czas, który zostaje przeliczony na odległość układu od przeszkody.
Czujniki temperatury DS18B20, połączonych w jednej linii. Dane z nich są następnie uśredniane i ich wartość jest pobierana do obliczenia odległości.
Poprzez USART dane będą wysyłane do komputera. Głównie w celu debugowania, ale także z informacjami zebranymi z czujników.
Wyświetlacz Nokia, na którym będą prezentowane wyniki działania programu tzn, aktualny czas, odległość od przeszkody oraz temperatura.
HC-SR04 opis:
W tej części chciałbym trochę opisać czujnik HC-SR04. Podstawowe parametry:
- Napięcie zasilania 5V (z 3.3V nie działa). Zgodnie z dokumentacją tylko 5V akceptuje. Do STMa najlepiej podłączyć przez rezystor, 500Ohm do 1kOhm spokojnie powinno wystarczyć.
- Pobór prądu wynosi 15mA;
- Zakres od 2 do 200cm. Daje rade do 230cm z przyzwoitą dokładnością i nie skomplikowanych celów pomiarowych. Większy zakres oferuje np US-015 bądź HC-SR04 od SparkFun (taki czerwony).
- Częstotliwość pracy wynosi 40kHz
- Kąt pracy pomiędzy 15 a 30 st.
Ja korzystam z czujnika zawierającego dwa piny przeznaczone do sterowania. Na jednym z nich generowany jest impuls 10us, drugi natomiast odczytuje odpowiedź. Występują też układy posiadające tylko jeden pin sterujący. Zasada działa jest taka sama. Takie same sygnały należy podać w celu inicjalizacji oraz odczytu.
Należy także mieć na uwadze, że mogą występować sytuacje w których dana przeszkoda nie zostanie poprawnie zinterpretowana. Może się to zdarzyć gdy odległość od przeszkody przekracza maksymalny dopuszczalny zakres pomiarowy. W takim przypadku, jeśli jest to niewielkie przekroczenie to powinna wystąpić tylko pogorszenie dokładności. Druga sytuacja dotyczy za małego kąta pomiarowego. Fala ultradźwiękowa trafi na przeszkodę natomiast nie zostanie zarejestrowana fala powrotna, która po odbiciu nie wróci do odbiornika. Ostatnim problemem jest wielkość obiektu mierzonego. Ten z kolei jeśli będzie za mały to czujnik nie zarejestruje jego obecności. Im dalej znajduje się on od przeszkody tym większe prawdopodobieństwo wystąpienia takiej sytuacji.
Dokładność HC-SR04:
Czujnik HC-SR04 posiada pewną rozbieżność, która w zależności od jego zastosowania może mieć, większy lub mniejszy wpływ na jego skuteczność działania. W celu jej maksymalnej poprawy zastosowałem uśrednianie dziesięciu wyników pomiarów, które wyeliminują występujące duże niedokładności. Następnie wyeliminowałem wyniki, znacząco odbiegające od pozostałych. Każdy z wyników zanim przejdzie do uśredniania zostaje obliczony poprzez zmodyfikowany wzór obliczania odległości, który pobiera wcześniej obliczoną prędkość dźwięku w powietrzu. Te wartości wahają się w następujący sposób:
- -40 st. C - 306.5 m/s
- -30 st. C - 312.9 m/s
- -20 st. C - 319.3 m/s
- -10 st. C - 325.6 m/s
- 0 st. C - 331.8 m/s
- 10 st. C - 337.8 m/s
- 15 st. C - 340.3 m/s
- 20 st. C - 343.8 m/s
- 30 st. C - 349.6 m/s
- 40 st. C - 355.3 m/s
v = 331.5*sqrt(1+(temperatura[st. C]/273,15) [m/s]
Taki wzór można stosować na dwa sposoby. Pierwszy z nich jest obliczanie każdorazowo tej prędkości dla zmierzonej temperatury. Drugi sposób dotyczy wykonania obliczeń wcześniej i przygotowania tablicy zawierającej wartości już obliczone. Jest on dosyć kłopotliwy jeśli zależy nam na dużej dokładności dla szerokiego zakresu temperatur. Ponieważ wtedy rozmiar tablicy byłby dosyć znaczący.
Program:
Poniżej przejdę przez ważniejsze funkcje dla każdego z modułów. Cały program będzie do pobrania pod linkiem na dole strony.
HC-SR04
Jak już wspomniałem wcześniej czujnik HC-SR04 został oprogramowany z użyciem dwóch timerów.
W pierwszej kolejności należy zainicjalizować porty GPIO. Takie funkcje można wywołać jako funkcje wspólną razem z inicjalizacją timera oraz przerwań. Dzięki czemu te podstawowe elemen ty można zdefiniować jako prywatne:
- // Echo Pin HC-SR04 (PA0)
- #define HCSR04_ECHO_PORT GPIOA
- #define HCSR04_ECHO_CLK RCC_AHB1Periph_GPIOA
- #define HCSR04_ECHO_PIN GPIO_Pin_0
- #define HCSR04_ECHO_SOURCE GPIO_PinSource0
- // Trigger Pin (PD3)
- #define HCSR04_TRIGGER_PORT GPIOD
- #define HCSR04_TRIGGER_CLK RCC_AHB1Periph_GPIOD
- #define HCSR04_TRIGGER_PIN GPIO_Pin_3
- static void Priv_HCSR04_GPIO_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- //Pin - Trigger
- RCC_AHB1PeriphClockCmd(HCSR04_TRIGGER_CLK, ENABLE);
- RCC_AHB1PeriphClockCmd(HCSR04_ECHO_CLK, ENABLE);
- GPIO_InitStructure.GPIO_Pin = HCSR04_TRIGGER_PIN;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(HCSR04_TRIGGER_PORT, &GPIO_InitStructure);
- //Ustawienie pinu na low
- HCSR04_TRIGGER_PORT->BSRRH = HCSR04_TRIGGER_PIN;
- //Echo - Pin
- GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO_PIN;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_Init(HCSR04_ECHO_PORT, &GPIO_InitStructure);
- GPIO_PinAFConfig(HCSR04_ECHO_PORT, HCSR04_ECHO_SOURCE, GPIO_AF_TIM2);
- }
Funkcja pozwalająca na włączenie wykorzystywanych timerów:
- static void Priv_HCSR04_Init_TIM2_TIM7(void)
- {
- TIM_ICInitTypeDef TIM_ICInitStructure;
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- //==========================
- // Timer-7 wlaczenie, odpowiada za opoznienie
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
- TIM_Cmd(TIM7, DISABLE);
- // Ustawienie timera
- TIM_TimeBaseStructure.TIM_Period=9; //10us
- TIM_TimeBaseStructure.TIM_Prescaler=83; //1Mhz
- TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
- TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
- TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure);
- TIM_ARRPreloadConfig(TIM7, ENABLE);
- // Timer -2 ustawienie trybu input capture
- TIM_PrescalerConfig(TIM2, 83, TIM_PSCReloadMode_Immediate);
- TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
- TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
- TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
- TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
- TIM_ICInitStructure.TIM_ICFilter = 0x0;
- TIM_PWMIConfig(TIM2, &TIM_ICInitStructure);
- TIM_SelectInputTrigger(TIM2, TIM_TS_TI1FP1); //TIM_Internal_Trigger_Selection
- //Triger input
- // Tryb slave_mode reset
- TIM_SelectSlaveMode(TIM2, TIM_SlaveMode_Reset);
- TIM_SelectMasterSlaveMode(TIM2,TIM_MasterSlaveMode_Enable);
- }
Dodatkowo do układu należy jeszcze włączyć przerwania dla timerów 2 oraz 7. Wykonuje się to poprzez następującą funkcję:
- static void Priv_HCSR04_InitNVIC(void)
- {
- NVIC_InitTypeDef NVIC_InitStructure;
- //Przerwanie wewnetrzne od timera 2
- NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
- TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);
- //Przerwanie wewnetrzne od timera 7
- NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStructure);
- TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE);
- }
Dane jakie są potrzebne dla układu są przetrzymywane w strukturze:
- typedef struct {
- uint32_t time_tim2;
- uint32_t time_tim7;
- uint32_t delay_u_sec;
- }HCSR04_Ultra_t;
Przedstawione powyżej funkcje są prywatne zainicjalizowane jako static. Ma to za zadanie ograniczyć ich zasięg tylko do plików należących do układu HC-SR04. Ich wywołanie następuje poprzez jedną publiczną funkcję przedstawioną poniżej.
- static void Priv_SetData_Struct(void)
- {
- HCSR04.time_tim2= 0;
- HCSR04.time_tim7=0;
- HCSR04.delay_u_sec=0;
- }
- //===============================================================
- //Inicjalizacja HCSR04
- void HCSR04_Init(void)
- {
- Priv_SetData_Struct();
- Priv_HCSR04_GPIO_Init();
- Priv_HCSR04_Init_TIM2_TIM7();
- Priv_HCSR04_InitNVIC();
- }
W pierwszej kolejności ustawieniu ulegają parametry dla struktury. Następnie następuje przejście przez wszystkie procedury inicjalizacji.
Sygnał włączający pomiar musi trwać 10us. Za jego generację odpowiada timer 7:
- static void Priv_HCSR04_Trigger(void)
- {
- HCSR04.time_tim7=0;
- //Pin Trigger ustawiony w stan high
- HCSR04_TRIGGER_PORT->BSRRL = HCSR04_TRIGGER_PIN;
- TIM_Cmd(TIM7, ENABLE); //Wlaczenie timera
- while(HCSR04.time_tim7<10); //Odliczanie od 0 do 9, aktualizacja w
- //obsludze przerwania
- TIM_Cmd(TIM7, DISABLE); //Wylaczenie timera
- //Pin triggera ustawiony na low
- HCSR04_TRIGGER_PORT->BSRRH = HCSR04_TRIGGER_PIN;
- }
Pojedynczy pomiar bez kompensacji temperaturowej wygląda następująco:
- float HCSR04_Distance_cm(void)
- {
- float return_value=0.0;
- uint32_t check_ok=0;
- HCSR04.time_tim2=0; //czyszczenie danych w strukturze
- TIM_SetCounter(TIM2, 0); //zerowanie licznika
- TIM_Cmd(TIM2, ENABLE); //Wlaczenie timer2
- Priv_HCSR04_Trigger_Send(); //Wyslanie sygnalu Send
- check_ok=0;
- HCSR04.time_tim7=0;
- TIM_Cmd(TIM7, ENABLE); //Wlaczenie timera
- do
- {
- if(HCSR04.time_tim2!=0) { check_ok=1; } // pomiar zakonczony powodzeniem
- if(HCSR04.time_tim7>=HCSR04_TIMEOUT) { check_ok=2; } // niepowodzenie pomiaru
- }while(check_ok==0);
- TIM_Cmd(TIM7, DISABLE);
- TIM_Cmd(TIM2, DISABLE);
- HCSR04.time_tim7=0;
- TIM_Cmd(TIM7, ENABLE);
- while(HCSR04.time_tim7<HCSR04_DELAY);
- TIM_Cmd(TIM7, DISABLE);
- if(check_ok==1)
- {
- //konwersja czasu na centymetry(w us) na centymetry
- return_value=(float)(HCSR04.delay_us)*(float)(0.017);
- }
- //Blad pomiaru
- else { return_value=-1.0; }
- return return_value;
- }
Pojedynczy pomiar z kompensacją temperaturową z dwóch czujników temperatury:
- float HCSR04_Single_With_Temp_cm(float temp)
- {
- float return_value = 0.0;
- float c = 0.0;
- HCSR04_Distance_cm();
- c = 331.5 + (0.6 * temp);
- c = c * 0.0001;
- return return_value += ((float)(HCSR04.delay_u_sec*0.5) * c);
- }
Wykonywanie kilku pomiarów wraz z ich uśrednieniem poprzez zastosowanie podstawowej metody czyli średniej arytmetycznej:
- typedef enum {
- Use_Temp_Value_False = 0, //Nie korzysta z kompensacji temperatury
- Use_Temp_Value_True = 1 //Wykorzystuje kompensacje temperaturowa
- }Use_Temp_Value_t;
- float HC_SR04_Distance_Few_Data(uint8_t sample_number, Use_Temp_Value_t use_temp_value)
- {
- float return_value = 0.0;
- uint8_t i = 0;
- //Bez kompensacji temperaturowej
- if(use_temp_value == Use_Temp_Value_False)
- {
- for(i=0;i<sample_number;i++)
- {
- return_value += HCSR04_Distance_cm();
- }
- }
- //Z kompensacją temperaturową
- if(use_temp_value == Use_Temp_Value_True)
- {
- for(i=0;i<sample_number;i++)
- {
- return_value += HCSR04_Distance_With_Temp_cm();
- }
- }
- return_value = (return_value/(float)sample_number);
- return return_value;
- }
Ta funkcja pozwala na wykonanie zadanej ilości pomiarów, oraz zdecydowanie czy do pomiaru ma być brana wartość temperatury od czujnika DS18B20. W tym przypadku zdecydowałem się wynik odległości przechowywać w zmiennej float. Jeśli prób pomiarowych będzie bardzo dużo, chociaż uważam, że jest to mało potrzebne bo nie polepszy to aż tak dokładności natomiast znacząco zwiększy czas pomiaru, to należałoby zastosować np dwie zmienne typu float, i z nich na końcu wyciągnąć średnią.
Na samym końcu przedstawię funkcje odpowiedzialne za obsługę przerwania. Działanie jest dosyć proste w TIM7 następuje sprawdzenie wygenerowanego przerwania, po czym flaga od przerwania jest czyszczona. Jedno wywołanie i przejście przez funkcję pozwoli na odliczenie około 10us, potrzebnych do wygenerowania odpowiedniego sygnału inicjalizującego.
- void TIM7_IRQHandler(void)
- {
- if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)
- {
- //Czyszczenie flagi
- TIM_ClearITPendingBit(TIM7, TIM_IT_Update);
- HCSR04.time_tim7+=10;
- }
- }
Timer 2 podstawowe działanie ma takie samo. Główna część obsługi przerwania pobiera dane z rejestrów CCR1 oraz CCR2, Rejestry te przechowują wartości jakie mają być porównywane z rejestrem CNT. W kolejnym kroku wartości te są od siebie odejmowane i przekazywane do wartości opóźnienia.
- void TIM2_IRQHandler(void)
- {
- uint32_t start,stop;
- if(TIM_GetITStatus(TIM2, TIM_IT_CC1) == SET)
- {
- //Usuniecie flagi przerwania
- TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
- //Odczyt wskazan
- start=TIM_GetCapture1(TIM2);
- stop=TIM_GetCapture2(TIM2);
- HCSR04.delay_u_sec=start-stop;
- HCSR04.time_tim2++;
- }
- }
Do pobrania danych z rejestrów CCR1 i 2 wykorzystuje funkcje zawarte w pliku stm32f4xx_tim.
DS18B20
Na samym początku opiszę trochę komunikację poprzez interfejs One Wire, po czym przejdę do krótkiego opisania komunikacji z czujnikiem DS18B20.
Zacznę od transmisji One Wire. Jest ona ściśle określona i zawiera poszczególne sekwencje działań jakie należy wykonać aby uzyskać komunikację z układem. Na początku wysyłana jest inicjalizacja, po czym przesyłana jest komenda ROM na końcu przesyłane są poszczególne funkcje. Wyjątek dla czujnika stanowią komendy Search ROM 0x0F oraz Alarm Search 0xEC. Dla nich po ich wysłaniu należy wrócić do inicjalizacji.
Polega ona na wysłaniu sygnału reset od mikrokontrolera po czym czujnik odsyła impuls zwany presence pulse. Dopiero po tej odpowiedzi można przejść do wysyłania komend ROM. DO wyboru są następujące działania:
- Search ROM (0xF0) - powoduje identyfikację kodów ROM jakie są podłączone do linii. Kiedy jest podłączone tylko jeden układ podrzędny to tą komendę można pominąć.
- Read ROM (0x33) - odczyt ROM z podrzędnego układu. Wykorzystywana tylko gdy podłączone jest na linii jedno urządzenie.
- Match ROM (0x55) - do zaadresowanie podrzędnego układu. Na tą komendę powinien odpowiedzieć jedynie układ o wysłanym kodzie ROM.
- Skip ROM (0xCC) - czyli pominięcie wysłania kodu ROM. Pozwala na równoczesne adresowanie urządzeń podłączonych na linii.
- Alarm Search (0xEC) - na tą komendę odpowiadają układy na których została ustawiona flaga alarmu. Pozwala to na sprawdzenie czy wystąpił sygnał alarmu z czujników, Jak wspomniałem wcześniej po tej operacji
Na samym początku zdefiniowanie funkcji opóźniających, odpowiedzialne za generowanie opóźnienia pomiędzy funkcjami.
- void Delay_Init(void)
- {
- RCC_ClocksTypeDef RCC_Clocks;
- RCC_GetClocksFreq(&RCC_Clocks); //Pobranie zegara systemowego
- data_delay = RCC_Clocks.HCLK_Frequency / 5000000; //1us Delay
- }
- void DelayMicros(uint32_t micros)
- {
- micros = micros * data_delay - 10;
- while (micros--);
- }
- void DelayMillis(uint32_t millis)
- {
- millis = 1000 * millis * data_delay - 10;
- while (millis--);
- }
- void OneWire_Init(OneWire_DS_t* OneWire_DS_St, GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
- {
- GPIO_InitTypeDef GPIO_InitDef;
- Delay_Init();
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); //Wlaczenie zegara
- GPIO_InitDef.GPIO_Pin = GPIO_Pin; //Wybranie pinów
- GPIO_InitDef.GPIO_OType = GPIO_OType_PP; //Wyjscie jako push-pull
- GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT; //Ustawienie pinu jako wyjscie
- GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_UP; //Bez rezystora podciagajacego
- GPIO_InitDef.GPIO_Speed = GPIO_Speed_50MHz; //Ustawienie czestotliwosci
- GPIO_Init(GPIOx, &GPIO_InitDef); //Wlaczenie lini GPIOD
- OneWire_DS_St->GPIOx = GPIOx;
- OneWire_DS_St->GPIO_Pin = GPIO_Pin;
- }
- void OneWire_WriteBit(OneWire_DS_t* OneWireStruct, uint8_t bit)
- {
- if (bit)
- {
- //Ustaw stan LOW
- ONEWIRE_LOW(OneWireStruct);
- ONEWIRE_OUTPUT(OneWireStruct);
- DelayMicros(10);
- //Bit high
- ONEWIRE_INPUT(OneWireStruct);
- DelayMicros(55);
- ONEWIRE_INPUT(OneWireStruct);
- }
- else
- {
- //Ustaw linie na LOW
- ONEWIRE_LOW(OneWireStruct);
- ONEWIRE_OUTPUT(OneWireStruct);
- DelayMicros(65);
- //Bit high
- ONEWIRE_INPUT(OneWireStruct);
- //Odczekaj 5us i zwolnij linie
- DelayMicros(5);
- ONEWIRE_INPUT(OneWireStruct);
- }
- }
- uint8_t OneWire_ReadBit(OneWire_DS_t* OneWireStruct)
- {
- uint8_t bit = 0;
- //Linia na low
- ONEWIRE_LOW(OneWireStruct);
- ONEWIRE_OUTPUT(OneWireStruct);
- DelayMicros(3);
- //Zwolnij linie
- ONEWIRE_INPUT(OneWireStruct);
- DelayMicros(10);
- //Odczytanie stanu linii
- if ((((OneWireStruct->GPIOx)->IDR & (OneWireStruct->GPIO_Pin)) == 0 ? 0 : 1)) { bit = 1; }
- //Odczekaj 50us
- DelayMicros(50);
- //Zwroc wartosc rejestru
- return bit;
- }
Teraz przejdę do opisu komunikacji z czujnikiem DS18B20:
Dane od czujnika przechowywane są scratchpadzie, które zawierają 2 bajty rejestr przechowujący informacje dotyczącą temperatury dostarczoną z czujnika. Z niego uzyskuje się dostęp do jedno bajtowego rejestru porównań TH oraz TL. Dodatkowo pozwala na uzyskanie dostępu do rejestru konfiguracyjnego
W pierwszej kolejności wprowadzenie danych do rejestru konfiguracyjnego dotyczące dokładności czujnika. Do dyspozycji są 4 wartości, 9, 10, 11 lub 12 bitów. TH, TL oraz rejestr konfiguracyjny są typu EEPROM, wobec tego przechowują one dane w przypadku braku zasilania.
Adresowanie urządzenia odbywa się poprzez wykorzystywanie kodów 64-bit kod umieszczony w pamięci ROM. Osiem najmłodszych bitów układu to tzw. family code czyli 0x28. Kolejne 48 bitów zawierają unikalny numer urządzenia. Osiem najstarszych bitów przechowuje CRC, które zostały obliczone z pierwszych 56 bitów.
Wracając do rozdzielczości to każda z dostępnych wartości pozwala na uzyskanie przybliżenia z dokładnością wynoszącą 0,5 st. C, 0,25 st. C, 0,125 st.C, 0,0625 st. C. Im większa rozdzielczość tym czas konwersji zostaje wydłużony do maksymalnie 750ms przy 12 bitach. Jest ona domyślnie ustawiana po włączeniu urządzenia. Aby ustawić rozdzielczość trzeba się odnieść do bit 5 i 6 rejestru konfiguracyjnego. Tam od 00 (9 bit) do 11(12 bit) można przechodzić po rozdzielczości.
Rozpoczęcie konwersji rozpoczyna się od wysłania wartości 0x44 do czujnika. Po konwersji układ przechodzi w stan bezczynności. Następnie mikrokontroler może wysłać zapytanie odnośnie stanu wykonywania operacji. Jeśli jeszcze konwersja trwa to w odpowiedzi zostaną uzyskane same zera. Jedynki natomiast będą oznaczać zakończenie transmisji.
Dane po konwersji są przechowywane na 16 bitach z rozszerzeniem obejmującym znak. W zależności od rozdzielczości zajęte są wszystkie bity początkowe dla 12 bitów. Dla pozostałych są powoli zwalniane np. dla 9 bitów bit 0, 1 i 2 zostają wolne. Bity od 15 do 11 pozwalają na określenie czy temperatura jest dodatnia czy ujemna.
Istnieje możliwość także zasilania pasożytniczego bezpośrednio z linii magistrali. Natomiast wtedy należy wprowadzać poprawki do kodu ponieważ pewne operacje trzeba wykonać inaczej.
Wracająć do scratchpada, to składa się on z 8 bytów. Bajt 0 oraz 1 przechowuje wynik pomiaru temperatury. Bajt 2 oraz 3 pozwala na uzyskanie dostępu do TH i TL przechowywanych w EEPROM. Bajt 4 pozwala na uzyskanie informacji odnośnie rejestrów konfiguracyjnych. Bajt 5, 6, 7 są zarezerwowane i niedostępne. Można je jedynie odczytać, powoduje to otrzymanie wartości 1. Bajt 8 zawiera CRC z bajtów od 0 do 7.
Wprowadzanie danych do scratchpada musi być poprzedzone komendą 0x4E. Dane muszą być wysłane od najmłodszego bitu od 2 bajtu. Aby odczytać poprawność danych odczytuje się dane za pomocą komendy 0xBE. Dane w odpowiedzi przesyłane są od pierwszego bitu bajtu 0. Aby dane przesłać ze scratchpada do pamięci EEPROM należy wykonać polecenie 0x48. Można je także pobrać ze scratchpada poleceniem 0xB8. Po ty można sprawdzić jej status, 0 jeśli procedura się wykonuje lub 1 jeśli została zakończona.
Poniżej przedstawiam zebrany zestaw komend wykorzystywanych przy komunikację z czujnikiem:
- Convert T - 0x44 - pojedyncza konwersja temperatury.
- Write scratchpad - 0x4E - zapis trzech bajtów do układu (1: TH, 2: TL, 3: rejestr konfiguracyjny). Dane przesyłane od najmłodszego do najstarszego bajtu. Zapis musi nastąpić przed wystawieniem sygnału reset.
- Read scratchpad - 0xBE - odczytanie danych z pamięci scratchpada. Układ master zakończy odczyt sygnałem reset.
- Copy scratchpad - 0x48 - skopiowanie danych do pamięci EEPROM.
- Recal E2 - 0xB8 - wywołuje alarm TH, TL oraz rejestry konfiguracyjne z pamięci EEPROM.
- Read power supply - 0xB4.
Teraz pokaże opis funkcji odpowiedzialnych za komunikację z czujnikiem:
Na samym początku sprawdzenie czy family code się zgadza:
- uint8_t DS18B20_Check_Family_Code(uint8_t *ROM)
- {
- if (*ROM == DS18B20_FAMILY_CODE) { return 1; }
- return 0;
- }
Druga funckja odpowiada za zaadresowanie konkretnego czujnika:
- uint8_t DS18B20_Single_Sensor_Start(OneWire_DS_t* OneWire, uint8_t *ROM)
- {
- if (!DS18B20_Check_Family_Code(ROM)) { return 0; } //Sprawdzenie czy na linii podlaczone DS18B20
- OneWire_Reset(OneWire); //Reset
- OneWire_SelectWithPointer(OneWire, ROM); //Wybranie numeru ROM
- OneWire_WriteByte(OneWire, DS18B20_CMD_CONVERTTEMP); //Rozpoczecie konwersji temperatury
- return 1;
- }
Pozostałe funkcje będą opisane bezpośrednio w plikach od projektu.
NOKIA5110
Ten układ był juz przezemnie poruszany na forum dla tego mikrokontrolera wobec tego na szybko przedstawię tylko najważniejsze funkcje z których będę korzystał.
Funkcje włączające GPIO, SPI i sam wyświetlacz.
- void NOKIA_INIT(void)
- {
- GPIO_ResetBits(LCD_RST_PORT, LCD_RST);
- DelayMillis(2);
- GPIO_SetBits(LCD_RST_PORT, LCD_RST);
- NOKIA_CMD(0x21);
- NOKIA_CMD(0xc8);
- NOKIA_CMD(0x06);
- NOKIA_CMD(0x13);
- NOKIA_CMD(0x20);
- NOKIA_CMD(0x0c);
- NOKIA_CMD(0x40);
- NOKIA_CMD(0x80);
- }
- void GPIO_NOKIA_INIT(void)
- {
- GPIO_InitTypeDef GPIOInit;
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
- GPIO_PinAFConfig(GPIOC, GPIO_PinSource3, GPIO_AF_SPI2);
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_SPI2);
- //Inicjalizacja pinów MOSI
- GPIO_StructInit(&GPIOInit);
- GPIOInit.GPIO_Pin = LCD_DIN;
- GPIOInit.GPIO_Mode = GPIO_Mode_AF;
- GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIOInit.GPIO_OType = GPIO_OType_PP;
- GPIOInit.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_Init(LCD_DIN_PORT, &GPIOInit);
- //Inicjalizacja SCK
- GPIO_StructInit(&GPIOInit);
- GPIOInit.GPIO_Pin = LCD_CLK;
- GPIOInit.GPIO_Mode = GPIO_Mode_AF;
- GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIOInit.GPIO_OType = GPIO_OType_PP;
- GPIOInit.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_Init(LCD_CLK_LINE, &GPIOInit);
- //Inicjalizacja MISO, niewykorzystywane
- GPIOInit.GPIO_Pin = MISO_PIN;
- GPIOInit.GPIO_Mode = GPIO_Mode_AF;
- GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIOInit.GPIO_OType = GPIO_OType_PP;
- GPIOInit.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_Init(MISO_PORT, &GPIOInit);
- //Wlaczenie pozostalych linii
- GPIOInit.GPIO_Pin = LCD_DC|LCD_CE|LCD_RST;
- GPIOInit.GPIO_Mode = GPIO_Mode_OUT;
- GPIOInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIOInit.GPIO_OType = GPIO_OType_PP;
- GPIOInit.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_Init(LCD_DC_PORT, &GPIOInit);
- GPIO_SetBits(LCD_CE_PORT, LCD_CE|LCD_RST);
- }
- //Inicjalizacja SPI
- void SPI_NOKIA_INIT(void)
- {
- SPI_InitTypeDef SPIInit;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
- SPI_StructInit(&SPIInit);
- SPIInit.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
- SPIInit.SPI_Mode = SPI_Mode_Master;
- SPIInit.SPI_CPOL = SPI_CPOL_Low;
- SPIInit.SPI_CPHA = SPI_CPHA_1Edge;
- SPIInit.SPI_NSS = SPI_NSS_Soft;
- SPIInit.SPI_DataSize = SPI_DataSize_8b;
- SPIInit.SPI_FirstBit = SPI_FirstBit_MSB;
- SPIInit.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
- SPI_NOKIA->CR1 &= ~SPI_CR1_SPE;
- SPI_Init(SPI_NOKIA, &SPIInit);
- SPI_Cmd(SPI_NOKIA, ENABLE);
- }
- //Wyslanie komendy na wyswietlacz
- static uint8_t SPI_SEND_NOKIA(uint8_t byte)
- {
- while (SPI_I2S_GetFlagStatus(SPI_NOKIA, SPI_I2S_FLAG_TXE) == RESET);
- GPIO_ResetBits(LCD_CE_PORT, LCD_CE);
- SPI_I2S_SendData(SPI_NOKIA, byte);
- while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET);
- GPIO_SetBits(LCD_CE_PORT, LCD_CE);
- return SPI_I2S_ReceiveData(SPI_NOKIA);
- }
- //Funkcja pozwalajaca na przesylanie danych do ukladu
- static void NOKIA_CMD(uint8_t cmd)
- {
- GPIO_ResetBits(LCD_CE_PORT, LCD_CE|LCD_DC);
- SPI_SEND_NOKIA(cmd);
- GPIO_SetBits(LCD_CE_PORT, LCD_CE);
- }
- void LCD_CLEAR(void)
- {
- memset(lcd_buffer, 0, (84 * 48 / 8));
- }
Poniżej funkcja odpowiedzialna za wyświetlanie tekstu na wyświetlaczu. I to właściwie tylko z niej korzystam aby wyświetlić dane
- void LCD_DRAW(int row, int col, const char* text)
- {
- //Deklaracja zmiennych
- int i;
- uint8_t* pbuf = &lcd_buffer[row * 84 + col];
- const uint8_t* font = 0;
- //Dopóki bedzie jakis tekst do wyswietlania oraz bedzie mozliwosc
- //wpisania danych wtedy funkcja bedzie wykonywana
- while ((*text) && (pbuf < &lcd_buffer[(84 * 48 / 8) - 6]))
- {
- //Wskaznik na wartosc, zostaje ona zwiekszona o 1
- int ch = *text++;
- //Deklaracja zmiennej w kodzie ASCII
- //z wskaznikiem na jej adres.
- //Liczenie od 0 odejmnowanie znaku spacji
- font = &font_ASCII[ch - ' '][0];
- //Przejscie w tablicy po poszczególnych
- //wartosciach podanego znaku
- for (i = 0; i < 5; i++)
- {
- *pbuf++ = *font++;
- }
- *pbuf++ = 0;
- }
- i=0;
- //Ustawienie lini DC na wysoki
- GPIO_SetBits(GPIOC, LCD_DC);
- //Wybranie urzadzenia, niski na CE
- GPIO_ResetBits(GPIOC, LCD_CE);
- for (i = 0; i < (84 * 48 / 8); i++)
- {
- SPI_SEND_NOKIA(lcd_buffer[i]);
- }
- GPIO_SetBits(LCD_CE_PORT, LCD_CE);
- }
USART
Transmisja od USART'a jest właściwie standardowa. Poniżej potrzebne funkcje:- void GPIO_Initialize(void)
- {
- //konfigurowanie portow GPIO
- GPIO_InitTypeDef GPIO_In;
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
- //TX dla pinu PB6
- GPIO_In.GPIO_Pin = GPIO_Pin_10;
- GPIO_In.GPIO_PuPd = GPIO_PuPd_UP;
- GPIO_In.GPIO_OType = GPIO_OType_PP;
- GPIO_In.GPIO_Mode = GPIO_Mode_AF;
- GPIO_In.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_Init(GPIOC, &GPIO_In);
- //RX dla pinu PB7
- GPIO_In.GPIO_Pin = GPIO_Pin_11;
- GPIO_In.GPIO_Mode = GPIO_Mode_AF;
- GPIO_In.GPIO_PuPd = GPIO_PuPd_UP;
- GPIO_In.GPIO_OType = GPIO_OType_PP;
- GPIO_In.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_Init(GPIOC, &GPIO_In);
- //Wlaczenie transmisji na podanych pinach
- GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_USART3);
- GPIO_PinAFConfig(GPIOC, GPIO_PinSource11, GPIO_AF_USART3);
- }
- void USART_Initialize(void)
- {
- GPIO_Initialize();
- USART_InitTypeDef USART_InitStructure;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
- USART_InitStructure.USART_BaudRate = 115200;
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
- USART_InitStructure.USART_Parity = USART_Parity_No;
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
- USART_Init(USART3, &USART_InitStructure);
- USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
- USART_Cmd(USART3, ENABLE);
- }
- void USART_puts(volatile char *c)
- {
- while (*c != 0)
- {
- SendChar(*c);
- c++;
- }
- }
- void SendChar(char c)
- {
- while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
- USART_SendData(USART3, c);
- }
Obsługa przycisku:
Przycisk odpowiedzialny jest za włączenie pomiaru pojedynczego, ponowne włączenie inicjalizuje kolejny pomiar. Oba są brane do obliczenia powierzchni danego pudła czy np. pokoju. Po tym w przypadku pudła bądź innego pojemnika można obliczyć jaka jest wartość jego zapełnienia.Ja wykorzystałem pin PA3 wraz z zewnętrznym przyciskiem. Nie wykorzystałem pinu PA0 z przyciskiem ponieważ do niego mam podłaczoną jedną z nóg od HCSR04. W przerwaniu zwiększana jest wartość flagi która następnie w pętli głównej w zależności od jej wartości wyświetla dane.
- void ButtonInit(void)
- {
- EXTI_InitTypeDef EXTI_InitStruct;
- NVIC_InitTypeDef NVIC_InitStruct;
- GPIO_InitTypeDef GPIO_InitDef;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
- GPIO_InitDef.GPIO_Pin = GPIO_Pin_3;
- GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
- //Ustawienie jako wejście
- GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;
- GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIO_InitDef.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_Init(GPIOA, &GPIO_InitDef);
- //Wybranie EXTI dla konkretnego pinu
- SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource3);
- EXTI_InitStruct.EXTI_Line = EXTI_Line3;
- EXTI_InitStruct.EXTI_LineCmd = ENABLE;
- EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
- EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
- EXTI_Init(&EXTI_InitStruct);
- NVIC_InitStruct.NVIC_IRQChannel = EXTI3_IRQn;
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00;
- NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
- NVIC_Init(&NVIC_InitStruct);
- }
Obsługa przerwania:
- void EXTI3_IRQHandler(void)
- {
- if (EXTI_GetITStatus(EXTI_Line3) != RESET)
- {
- EXTI_ClearITPendingBit(EXTI_Line3);
- if (Button_flag == 0)
- {
- Wymiar_Dane.szerokosc1 = 0;
- Wymiar_Dane.szerokosc2 = 0;
- Wymiar_Dane.wysokosc = 0;
- Wymiar_Dane.rozmiar = 0;
- //Wykonanie pierwszego pomiaru
- Wymiar_Dane.szerokosc1 = HC_SR04_Distance_Few_Data((float)10.0, 1, Srednia_temp);
- }
- else if(Button_flag == 1)
- {
- //Wykoanie drugiego pomiaru i wyswietlenie danych
- Wymiar_Dane.szerokosc2 = HC_SR04_Distance_Few_Data((float)10.0, 1, Srednia_temp);
- }
- else if(Button_flag == 2)
- {
- //Wykoanie drugiego pomiaru i wyswietlenie danych
- Wymiar_Dane.wysokosc = HC_SR04_Distance_Few_Data((float)10.0, 1, Srednia_temp);
- Wymiar_Dane.rozmiar = Wymiar_Dane.szerokosc1 * Wymiar_Dane.szerokosc2 * Wymiar_Dane.wysokosc; //obliczona objetosc
- sprintf(bufer, "Szero1: %f | Szero2: %f %\n\r", Wymiar_Dane.szerokosc1, Wymiar_Dane.szerokosc2);
- USART_puts(bufer);
- sprintf(bufer, "Wysokosc: %f | Rozmiar: %f %\n\r", Wymiar_Dane.wysokosc, Wymiar_Dane.rozmiar);
- USART_puts(bufer);
- }
- else if(Button_flag == 3)
- {
- Wymiar_Dane.szerokosc1 = 0.0;
- Wymiar_Dane.szerokosc2 = 0.0;
- Wymiar_Dane.wysokosc = 0;
- }
- Button_flag++;
- if(Button_flag == 4) { Button_flag = 0; }
- }
- }
Dane dotyczące rozmiarów oraz zapełnienia przechowywane są w przygotowanej strukturze:
- typedef struct Wymiar{
- float szerokosc1;
- float szerokosc2;
- float wysokosc;
- float rozmiar;
- float zapelnienie;
- }wymiar_t;
- wymiar_t Wymiar_Dane;
MAIN
W głównej części programu włączane są wszystkie potrzebne elementy, po nich sprawdzane są czujniki podłączone na linii onewire. W dalszej części następuje wykonanie pomiarów oraz wyświetlenie informacji.- int main(void)
- {
- float distance = 0.0;
- float temps[SENSORS];
- ErrorStatus check;
- char buf[40];
- uint8_t devices, i, count;
- uint8_t device[SENSORS][8];
- //---------------------------------------------------------------
- SystemInit();
- SYSTICK_ON(0);
- //---------------------------------------------------------------
- LedInit();
- ButtonInit();
- USART_Initialize();
- USART_puts("LED USART INIT\n\r");
- //---------------------------------------------------------------
- OneWire_DS_t OneWire1_Sensors;
- OneWire_Init(&OneWire1_Sensors, GPIOB, GPIO_Pin_8);
- USART_puts("ONE_WIRE_INIT\n\r");
- //---------------------------------------------------------------
- HCSR04_Init();
- USART_puts("HCSR04\n\r");
- //---------------------------------------------------------------
- count = 0;
- devices = OneWire_First(&OneWire1_Sensors);
- USART_puts("One wire init\n\r");
- //---------------------------------------------------------------
- GPIO_NOKIA_INIT();
- SPI_NOKIA_INIT();
- NOKIA_INIT();
- LCD_CLEAR();
- LCD_DRAW(1, 1, "Init Ok");
- USART_puts("LCD INIT\n\r");
- //---------------------------------------------------------------
- while (devices)
- {
- count++;
- OneWire_GetFullROM(&OneWire1_Sensors, device[count - 1]);
- devices = OneWire_Next(&OneWire1_Sensors);
- }
- if (count > 0)
- {
- sprintf(buf, "Urzadzenia obecne na lini: %d\n", count);
- USART_puts(buf);
- for (i = 0; i < count; i++)
- {
- DS18B20_Set_Resolution(&OneWire1_Sensors, device[i], DS18B20_Resolution_12bits);
- }
- }
- while (1)
- {
- //Rozpoczecie konwersji na wszystkich urzadzeniach podlaczonych na linii
- DS18B20_Start_All_Sensors(&OneWire1_Sensors);
- //Odczekanie na zakonczenie pobierania temperatury
- while (!DS18B20_Check_Conversion_Complete(&OneWire1_Sensors));
- //Odczytaj temperaturę z każdego urządzenia oddzielnie
- for (i = 0; i < count; i++)
- {
- // Read temperature from ROM address and store it to temps variable
- if (DS18B20_Read_Data(&OneWire1_Sensors, device[i], &temps[i]))
- {
- //Print temperature
- sprintf(buf, "Temp %d: %3.5f ", i, temps[i]);
- USART_puts(buf);
- sprintf(buf, "T %d:%3.5f", i, temps[i]);
- LCD_DRAW(i+2, 1, buf);
- Srednia_temp += temps[i];
- }
- else
- {
- //Blad odczytu
- USART_puts("Reading error;\n");
- }
- }
- //-------------------------------------------------------------------------
- Srednia_temp = (Srednia_temp*0.5); //Obliczenie sredniej temperatury
- sprintf(buf, "Ts:%3.5f", Srednia_temp); //Przygotowanie wartosci
- LCD_DRAW(4, 1, buf); //Wypisanie bufora
- //-------------------------------------------------------------------------
- //Obliczenie średniej wartosci
- distance = HC_SR04_Distance_Few_Data((float)10.0, 1, Srednia_temp);
- sprintf(bufer, " Dis: %f\n\r", distance);
- USART_puts(bufer);
- sprintf(bufer, "Dis %.4f", distance);
- LCD_DRAW(1, 1, bufer);
- Srednia_temp = 0.0;
- //---------------------------------------------------------------
- //Ustawienie diod swiecacych
- if(distance<10)
- {
- GPIO_SetBits(GPIOD, GPIO_Pin_12);
- GPIO_ResetBits(GPIOD, GPIO_Pin_13);
- GPIO_ResetBits(GPIOD, GPIO_Pin_14);
- GPIO_ResetBits(GPIOD, GPIO_Pin_15);
- }
- else if(distance>50)
- {
- GPIO_SetBits(GPIOD, GPIO_Pin_12);
- GPIO_SetBits(GPIOD, GPIO_Pin_13);
- GPIO_ResetBits(GPIOD, GPIO_Pin_14);
- GPIO_ResetBits(GPIOD, GPIO_Pin_15);
- }
- else if(distance>100)
- {
- GPIO_SetBits(GPIOD, GPIO_Pin_12);
- GPIO_SetBits(GPIOD, GPIO_Pin_13);
- GPIO_SetBits(GPIOD, GPIO_Pin_14);
- GPIO_ResetBits(GPIOD, GPIO_Pin_15);
- }
- else if(distance>150)
- {
- GPIO_SetBits(GPIOD, GPIO_Pin_12);
- GPIO_SetBits(GPIOD, GPIO_Pin_13);
- GPIO_SetBits(GPIOD, GPIO_Pin_14);
- GPIO_SetBits(GPIOD, GPIO_Pin_15);
- }
- //--------------------------------------------------------------
- if(Button_flag == 2)
- {
- Wymiar_Dane.zapelnienie = distance * Wymiar_Dane.szerokosc1 * Wymiar_Dane.szerokosc2;
- Wymiar_Dane.zapelnienie = ((Wymiar_Dane.zapelnienie)*100)/Wymiar_Dane.rozmiar;
- sprintf(bufer, "Zapelnienie: %f %%\n\r", Wymiar_Dane.zapelnienie);
- USART_puts(bufer);
- sprintf(bufer, "Zap: %.4f %%", Wymiar_Dane.zapelnienie);
- LCD_DRAW(5, 1, bufer);
- }
- }
- }
Pliki z projektem można znaleźć na dysku Google pod tym linkiem.