W poprzednim odcinku zaprezentowałem prosty obrotomierz bazujący na pomiarze napięcia efektu Halla. Pomimo swojej użyteczności, układ był jednak zbyt duży i nieporęczny aby móc go bezpośrednio zastosować w rowerze. W tym odcinku dokonamy jego miniaturzacji (Fig. 1) oraz zmienimy koncepcję szkicu obsługującego urządzenie. W szkicu z projektu 35 wartości kadencji były wyświetlane w kilkusekundowych przedziałach czasowych definiowanych zmienną sampleTime
. W nowym szkicu, wartości kadencji będą wyświetlane na bieżąco, w czasie rzeczywistym.
W związku z tym, że hallotron AH49E znajdując się poza oddziaływaniem pola magnetycznego podaje na swoje wyjście napięcie około 2,5V, które czasami może być interpretowane przez mikrokontroler jako stan wysoki, zdecydowano się na zmianę konstrukcji układu. Wykorzystano komparator napięć LM393 (Fig. 2) - układ porównujący ze sobą dwa napięcia wejściowe i w zależności od ich stosunku do siebie, zwierający pin wyjściowy do masy. Krótką charakterystykę tego układu przedstawiono w projekcie 37.
Komparator LM393 będzie zasilany z układu Arduino napięciem 5V. Napięcie wyjściowe hallotronu w warunkach występowania silnego pola magnetycznego wynosi 4-5V. W nocie katalogowej układu LM393 przeczytamy, że napięcie zasilania komparatora powinno być o ok. 1,5V wyższe niż porównywane napięcia wejściowe. Komparator będzie porównywał napięcie z detektora pola magnetycznego (pin 2) z napięciem pochodzącym z regulowanego dzielnika napięcia utworzonego na potencjometrze R3 (pin 3). Regulacja tego napięcia umożliwi kalibrację układu do warunków pracy konkretnego rozwiązania aplikacyjnego.
Jeżeli nie ma pola magnetycznego, napięcie na wejściu nieodwracającym komparatora UIn1(+)
dostarczane z potencjometru R3 na pin 3 komparatora jest większe od napięcia wyjściowego hallotronu podawanego na wejście odwracające komparatora UIn1(-)
(pin 2).
UIn1(+) > UIn1(-)
Wyjście układu LM393 jest wtedy rozłączone od masy. Prąd nie płynie także przez rezystor R4 i diodę LED1. Przez krótką chwilę prąd płynący od zacisku +5V przez R5 ładuje okładki kondensatora C1. Prąd ładowania kondensatora wraz z ilością zgromadzonego ładunku, z czasem zanika. Na wejściu D2 układu Arduino cały czas występuje wtedy stan wysoki.
W chwili kiedy hallotron dostanie się w strefę oddziaływania pola magnetycznego, napięcie na jego wyjściu wzrasta do ok. 4-5V. Napięcie na wejściu odwracającym komparatora UIn1(-)
(pin 2) jest teraz wyższe niż na wejściu nieodwracającycm UIn1(+)
(pin 3).
UIn1(-) > UIn1(+)
Wyjście komparatora zostaje zwarte do masy. Przez R4 oraz LED1 zaczyna płynąć prąd, co powoduje zaświecenie diody LED. Jednocześnie dochodzi do rozładowania kondensatora C1. Na wejściu D2 układu Arduino pojawia się stan niski. Sygnały te będą zliczane przez układ mikrokontrolera. Dodatkowo do układu dodano kondensator filtrujący C2. Wyświetlaczo OLED podłączono do pinów SDA (A4) i SCL (A5) układu Arduino Nano.
Zmontujmy układ z Fig. 3.
W szkicu obsługującym urządzenie, na wstępie zostanie utworzona jednowymiarowa i numReadings
-elementowa tablica. Gdy na wyjściu OUT czujnika Halla pojawi się stan wysoki, do tablicy zostanie zapisany czas wykrycia pola magnetycznego. Gdy liczba pomiarów przekroczy liczbę elementów tablicy, najstarszy pomiar zostanie podmieniony najmłodszym. Na podstawie wyników zapisanych w tablicy będzie obliczany przedział czasu jaki upłynął pomiędzy najstarszym pomiarem a najmłodszym. Jeżeli w obliczanym przedziale czasu wystąpiło numReadings
impulsów, to z proporcji zostanie obliczona liczba impulsów na minutę (RPM). Wartość ta będzie się zmieniała co impuls i na bieżąco będzie wyświetlana na wyświetlaczu OLED.
/* The Bicycle Cadence Sensor Cadence Sensor measures the number of pedal revolutions per minute. Tomasz Bartuś bartus@agh.edu.pl 2018-10-18 */ #include <Arduino.h> #include <SPI.h> #include <U8g2lib.h> U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ SCL, /* data=*/ SDA); // pin remapping with ESP8266 HW I2C #define numReadings 4 // number of the hall efect readings int hall_din = 2; int pomiar = 0; // total measurement number (from 0 to infinity) long readings[numReadings]; // readings table initialization int i = 0; // no of measurements counted from 0 to numReadings int j = 0; int index_min; int cadence_limit = 90; // cadence limit on which depends the direction of the displayed arrows int mean_cadence = 0; long currentTime; //unsigned void setup() { pinMode(hall_din, INPUT); Serial.begin(9600); u8g2.begin(); u8g2.firstPage(); do { u8g2.clearBuffer(); u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.drawStr(40, 15, "Cadence"); u8g2.drawStr(40, 32, "Sensor"); u8g2.drawLine(30, 0, 30, 32); u8g2.setFont(u8g2_font_ncenR10_tf); u8g2.setDisplayRotation(U8G2_R3); u8g2.drawStr(0, 25, "Bike"); } while ( u8g2.nextPage() ); delay(3000); } long minimum() { // the function that calculates a time of the oldest measurement long currentMinimum = 2147483647; // we start really high, so that any element in the array will be lower that this for (int i = 0; i < numReadings; i++) { if ( readings[i] < currentMinimum ) { currentMinimum = readings[i]; } } return currentMinimum; } long maximum() { // the function that calculates a time of the youngest measurement long currentMaximum = 0; // we start really low, so that any element in the array will be higher that this for (int i = 0; i < numReadings; i++) { if ( readings[i] > currentMaximum ) { currentMaximum = readings[i]; } } return currentMaximum; } void loop() { if (digitalRead(hall_din) == LOW) { // if the hall sensor detected a magnetic field: currentTime = millis(); // save the current time to the currentTime variable if (pomiar < numReadings) { // if the total measurement number is < numReadings readings[j] = currentTime; // save the currentTime variable to the readings[j] table element long min = minimum(); // calculate the minimum time value from the readings[numReadings] table long max = maximum(); // calculate the maximum time value from the readings[numReadings] table for (int i = 0; i < numReadings; i++) { // print the array Serial.print("readings["); Serial.print(i); Serial.print("] ma wartosc: "); Serial.println(readings[i]); } j = j + 1; } else { // if the total measurement number is > numReadings long min = minimum(); // calculate the minimum time value from the readings[numReadings] table long max = maximum(); // calculate the maximum time value from the readings[numReadings] table for (int i = 0; i < numReadings; i++) { // if ( readings[i] == min ) { // check the index of the minimum value from the array index_min = i; readings[index_min] = currentTime; for (int i = 0; i < numReadings; i++) { // print the array Serial.print("readings["); Serial.print(i); Serial.print("] ma wartosc: "); Serial.println(readings[i]); } long deltaT = max - min; long count2rpm = (60000 * numReadings) / deltaT; // count the RPM value Serial.println(count2rpm); u8g2.firstPage(); do { u8g2.clearBuffer(); u8g2.setDisplayRotation(U8G2_R0); u8g2.setFont(u8g2_font_fub30_tf); char buf[50]; sprintf (buf, "%d", count2rpm); u8g2.drawStr(45, 32, buf); u8g2.setFont(u8g2_font_ncenR10_tf); u8g2.drawLine(30, 0, 30, 32); if (count2rpm <= cadence_limit) { u8g2.drawTriangle(8,0, 11,16, 5,16); u8g2.drawTriangle(5,17, 11,17, 8,29); u8g2.drawTriangle(0,32, 7,29, 5,17); u8g2.drawTriangle(9,29, 16,32, 11,17); } else { u8g2.drawTriangle(0,0, 7,3, 5,15); u8g2.drawTriangle(5,15, 11,15, 8,3); u8g2.drawTriangle(5,16, 11,16, 8,32); u8g2.drawTriangle(16,0, 12,15, 9,3); } } while ( u8g2.nextPage() ); } } } pomiar = pomiar + 1; } else { // Serial.println("Far field"); } }