Dlaczego zbudowałem stoper
Stoper został zbudowany ponieważ nie udało mi się znaleźć i kupić urządzenia które by mierzyło czas zamknięci lub otwarcia styku i było zasilane z baterii.
Próbowałem również znaleźć gotowy projekt DIY w Internecie, ale i to się nie udało. Z tego powodu przygotowałem ten artykuł.
Opis działania stopera
Stoper posiada dwa tryby pracy i dwa zakresy pomiarowe, które zmieniają się automatycznie. Zakres pierwszy 0,001 – 9,999 s i zakres drugi 0,1 – 999,9 s
W pierwszym trybie aby uruchomić stoper trzeba nacisnąć przycisk, drugie naciśnięcie zatrzymuje stoper , przyciśniecie na dłużej niż 3 sekundy zeruje stoper.
W drugim trybie trzeba do listwy zaciskowej podłączyć styk otwarty lub zamknięty i, następnie wyzerować stoper.
Stoper rozpocznie mierzyć czas w przypadku podłączenia styku otwartego gdy styk się zamknie i zatrzyma się gdy styk się otworzy , w przypadku podłączenia styku zamkniętego stoper rozpocznie pomiar gdy styk się otworzy i zakończy gdy styk się zamknie.
Poniżej opis słowny zamieniony na rysunki. Należy pamiętać że przed rozpoczęciem pomiaru należy wyzerować stoper.
Budowa stopera
Do budowy stopera użyłem taniego mikrokontrolera PIC16F19174 który posiada wbudowany sterownik wyświetlacza LCD. Jako zasilanie wybrałem ogniwo litowo jonowe 18650 o pojemności 2000mA , wybrałem 4 cyfrowy wyświetlacz serii DE 119 o dużych cyfrach. Widok płytki drukowanej, schemat stopera na zdjęciach poniżej.
Schemat w formacie pdf i oraz spakowany projekt wykonany w Kicad w plikach poniżej.
W projekcie wykorzystałem wszystkie nóżki mikrokontrolera posiadające funkcję sterowania wyświetlaczem LCD, dlatego źródłem sygnału zegarowego w mikrokontrolerze jest wewnętrzny generator HFINTOSC którego dokładność wynosi około 1%, i taka też jest dokładność stopera około 1% wartości zmierzonej. Można poprawić dokładność stopera kalibrując sygnał zegarowy rejestrem OSCTUNE. Dla moich zastosowań taka dokładność jest wystarczająca. Pozostałe 2 wolne nóżki wykorzystałem do podłączenia przycisku oraz styku zewnętrznego.
Program do stopera został napisany w języku C w środowisku MPLABX. Poniżej kod programu.
Stoper będzie przeze mnie używany do pomiaru czasu zadziałania zabezpieczeń zwarciowych i przeciążeniowych wyłączników. Poniżej przykład wykorzystania stopera
Poniżej krótki filmik ilustrujący działanie stopera
Działanie i Uruchamianie
Wyświetlacz który wybrałem ma tylko jedno podłoże i jest sterowany w trybie statycznym.
Żeby element wyświetlacza był widoczny (czarny) należy przyłożyć przemienne napięcie pomiędzy podłoże (BP) a elektrodę segmentu. Tą funkcję realizuje sterownik w mikrokontrolerze. Wystarczy tylko ustawić odpowiednie bity w rejestrach LCDDATA0 – LCDDATA4 (jeden bit jeden segment). W moim przypadku częstotliwość napięcia sterującego wyświetlaczem została ustawiona na około 60 Hz i została uzyskana z drugiego wewnętrznego generatora LFINTOSC o częstotliwości 31kHz. Dla mojego modelu wyświetlacza może się ona zawierać między 30 Hz a 100 Hz, a wyświetlacz może pracować w zakresie 3V do 5V. Dzięki temu przy zasilaniu z ogniwa nie ma potrzeby stosowania stabilizatora napięcia. Przykładowe przebiegi napięcia na sterowanych segmentach wyświetlacza na zdjęciach poniżej. Należy pamiętać że wyświetlacza LCD nie można sterować napięciem stałym. Stałe napięcie powoduje zjawisko elektrolizy które prowadzi do uszkodzenia wyświetlacza.
Podczas uruchamiana spotkało mnie parę niespodzianek. Ponieważ niezbyt często programuję w C skorzystałem z podpowiedzi ogólnodostępnej AI, która deklarowała że mogę podłączyć do PIC16F19175 32 segmenty w trybie statycznym, w rzeczywistości można ich podłączyć 30. Musiałem zrezygnować z jednej kropki stąd dwa zakresy. AI również nie trafiała z nazwami rejestrów dla mojego mikrokontrolera, ale po niewielkich poprawkach udało się doprowadzić kod do porządku.
Następnie okazało się że nie mogę zaprogramować mojego mikrokontrolera. Próbowałem 3 programatorów PicKit BASIC, SNAP, PICKIT3- otrzymywałem komunikat że być może mój mikrokontroler ma ustawiony bit programowania wysokim napięciem. Rzeczywistość okazała się bardziej banalna na płytce PCB zabrakło kawałka połączenia do resetu mikrokontrolera.
Podczas testów kiedy konfigurowałem sterownik LCD w tryb „LCD CHARGE PUMP” zauważyłem że między podłożem a segmentami SEG2D-PIN11, SEG2C-PIN10 i SEG3D-PIN44 nie mam przebiegu napięcia przemiennego bez tych trzech pionów nie mógłbym wyświetlać wszystkich cyfr. Z tego powodu skonfigurowałem sterownik LCD w trybie NTERNAL RESISTANCE LADDER POWER MODES.
Kiedy już miałem na wszystkich segmentach przemienne przebiegi i stoper zaczął liczyć , resetował się przy około 2 sekundach i liczył od nowa . Okazało się że w mikrokontrolerze domyślnie jest włączony watchdog , który musiałem wyłączyć.
Stoper pobiera mniej niż 1 mA prądu (800µA). Poniżej załączam notę AN1354 w której dociekliwe osoby mogą znaleźć informacje na temat konfiguracji sterownika.
Ja ucieszyłem się że wreszcie udało mi się skonfigurować sterownik LCD i nie drążyłem dalej tematu.
Nie spodziewałem się że przy tak prostym projekcie mogą się przytrafić takie przypadki
Stoper został zbudowany ponieważ nie udało mi się znaleźć i kupić urządzenia które by mierzyło czas zamknięci lub otwarcia styku i było zasilane z baterii.
Próbowałem również znaleźć gotowy projekt DIY w Internecie, ale i to się nie udało. Z tego powodu przygotowałem ten artykuł.
Opis działania stopera
Stoper posiada dwa tryby pracy i dwa zakresy pomiarowe, które zmieniają się automatycznie. Zakres pierwszy 0,001 – 9,999 s i zakres drugi 0,1 – 999,9 s
W pierwszym trybie aby uruchomić stoper trzeba nacisnąć przycisk, drugie naciśnięcie zatrzymuje stoper , przyciśniecie na dłużej niż 3 sekundy zeruje stoper.
W drugim trybie trzeba do listwy zaciskowej podłączyć styk otwarty lub zamknięty i, następnie wyzerować stoper.
Stoper rozpocznie mierzyć czas w przypadku podłączenia styku otwartego gdy styk się zamknie i zatrzyma się gdy styk się otworzy , w przypadku podłączenia styku zamkniętego stoper rozpocznie pomiar gdy styk się otworzy i zakończy gdy styk się zamknie.
Poniżej opis słowny zamieniony na rysunki. Należy pamiętać że przed rozpoczęciem pomiaru należy wyzerować stoper.
Budowa stopera
Do budowy stopera użyłem taniego mikrokontrolera PIC16F19174 który posiada wbudowany sterownik wyświetlacza LCD. Jako zasilanie wybrałem ogniwo litowo jonowe 18650 o pojemności 2000mA , wybrałem 4 cyfrowy wyświetlacz serii DE 119 o dużych cyfrach. Widok płytki drukowanej, schemat stopera na zdjęciach poniżej.
Schemat w formacie pdf i oraz spakowany projekt wykonany w Kicad w plikach poniżej.
W projekcie wykorzystałem wszystkie nóżki mikrokontrolera posiadające funkcję sterowania wyświetlaczem LCD, dlatego źródłem sygnału zegarowego w mikrokontrolerze jest wewnętrzny generator HFINTOSC którego dokładność wynosi około 1%, i taka też jest dokładność stopera około 1% wartości zmierzonej. Można poprawić dokładność stopera kalibrując sygnał zegarowy rejestrem OSCTUNE. Dla moich zastosowań taka dokładność jest wystarczająca. Pozostałe 2 wolne nóżki wykorzystałem do podłączenia przycisku oraz styku zewnętrznego.
Program do stopera został napisany w języku C w środowisku MPLABX. Poniżej kod programu.
#include <xc.h>
#include <stdint.h>
// Konfiguracja (uproszczona)
#pragma config FEXTOSC = OFF // Wy??cz zewn?trzny kwarc
#pragma config RSTOSC = HFINT32 // Po starcie u?yj HFINTOSC 32MHz
#pragma config CLKOUTEN = OFF // Wylacz wyjscie zegara na pinie
#pragma config LCDPEN = OFF// WYLACZ POMPE
#pragma config CSWEN = ON // Pozwól na zmian? zegara w kodzie
#pragma config FCMEN = ON // Monitorowanie zegara
#pragma config WDTE = OFF // Monitorowanie zegara
#define _XTAL_FREQ 4000000 // Poinformuj kompilator o zmianie
void OSC_Init_4MHz() {
OSCCON1 = 0x60; // HFINTOSC, NDIV = 1
OSCFRQ = 0x02; // HFFRQ = 4 MHz
}
void Int_IOC_config(){
// 2. Konfiguracja IOC (Interrupt-on-Change)
IOCCNbits.IOCCN1 = 1; // W??cz przerwanie na zbocze opadaj?ce (Negative edge) dla RC1
IOCCPbits.IOCCP1 = 1; // Opcjonalnie: zbocze narastaj?ce (Positive edge)
// 3. Konfiguracja przerwa? globalnych
IOCCFbits.IOCCF1 = 0; // Wyczyszczenie flagi przed startem
PIE0bits.IOCIE = 1; // W??czenie przerwa? IOC
}
void Timer0_Init_4MHz() {
T0CON0 = 0x90; // 16-bit mode
T0CON1 = 0x42; // Fosc/4, Prescaler 1:4
// Dla 1ms: (4MHz/4)/4 = 250 000 ticks/s. 1ms = 250 ticks.
// 65536 - 250 = 65286 (0xFF06)
TMR0H = 0xFF;
TMR0L = 0x06;
Int_IOC_config();
PIE0bits.TMR0IE = 1;
INTCONbits.GIE = 1;
}
// Zmienne stopera (jednostka: 1 ms dla lepszej p?ynno?ci)
volatile uint32_t ms_counter = 0;
volatile __bit is_running = 0; // Uzywamy __bit zamiast bit
volatile __bit auto_mode_active = 0; // Flaga trybu automatycznego
volatile __bit initial_rc1_state = 0;
const uint8_t segment_map[] = {
0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F // 0-9
};
void LCD_Init() {
// 3. LCDPS: Preskaler (odswiezanie)
LCDPS = 0x03;
// 4. LCDREF: VLCD3 = VDD (najprostszy tryb zasilania LCD)
LCDREF = 0x07; //max kontrast
LCDRL = 0xF0; // lader max midle min 0xF0 ,0xA0, 0x50
LCDVCON1 = 0x07; // BIAS 5.01V; EN5V enabled; LPEN enabled;
LCDVCON2 = 0x02; // CPWDT disabled; LCDVSRC Charge Pump only;
LCDCON = 0x01; // CS LFINTOSC; SLPEN disabled; static;
LCDCONbits.LCDEN = 1; // Enable Module;
// 5. LCDSE (Segment Enable) - wlaczamy piny zgodnie ze schematem
LCDSE0 = 0xFF; // SEG0-SEG7 (bez SEG5 na RA5)
LCDSE1 = 0xFF; // SEG8-SEG15
LCDSE2 = 0xFF; // SEG18-SEG23 (SEG16 i 17 to piny CFLY - nie uzywamy)
LCDSE3 = 0xFF; // SEG24-SEG31
LCDSE4 = 0xFF; // SEG32-SEG34 (RE0-RE2)
LCDDATA0 =0x00;
LCDDATA1 =0x00;
LCDDATA2 =0x00;
LCDDATA3 =0x00;
LCDDATA4 =0x00;
}
// Funkcja pomocnicza do bitów
#define GET_BIT(val, bit) ((segment_map[val] >> bit) & 0x01)
void LCD_Display(uint8_t d1, uint8_t d2, uint8_t d3, uint8_t d4, uint8_t dp1, uint8_t dp3) {
uint8_t cyfra1,cyfra2,cyfra3,cyfra4,cyfra5;
// Cyfra 1 (SEG0-4, 6-7)
cyfra1 = (uint8_t)((GET_BIT(d1,0) << 0) | (GET_BIT(d1,1) << 1) | (GET_BIT(d1,2) << 2) |
(GET_BIT(d1,3) << 3) | (GET_BIT(d1,4) << 4) | (GET_BIT(d1,5) << 6) |
(GET_BIT(d1,6) << 7));
// Cyfra 2 (SEG8-11, 13-15)
cyfra2 = (uint8_t)((GET_BIT(d2,0) << 0) | (GET_BIT(d2,1) << 1) | (GET_BIT(d2,2) << 2) |
(GET_BIT(d2,3) << 3) | (GET_BIT(d2,4) << 5) | (GET_BIT(d2,5) << 6) |
(GET_BIT(d2,6) << 7));
// Cyfra 3 (SEG18-20, 22-25)
cyfra3 = (uint8_t)((GET_BIT(d3,0) << 2) | (GET_BIT(d3,1) << 3) | (GET_BIT(d3,2) << 4) |
(GET_BIT(d3,3) << 6) | (GET_BIT(d3,4) << 7));
cyfra4 = (uint8_t)((GET_BIT(d3,5) << 0) | (GET_BIT(d3,6) << 1));
// Cyfra 4 (SEG26-32)
cyfra4 |= (uint8_t)((GET_BIT(d4,0) << 2) | (GET_BIT(d4,1) << 3) | (GET_BIT(d4,2) << 4) |
(GET_BIT(d4,3) << 5) | (GET_BIT(d4,4) << 6) | (GET_BIT(d4,5) << 7));
cyfra5 = (uint8_t)(GET_BIT(d4,6) << 0);
// --- OBS?UGA KROPEK ---
// DP1 to SEG33 (LCDDATA12 bit 1), DP3 to SEG34 (LCDDATA12 bit 2)
if (dp1) cyfra5 |= (1 << 1); else cyfra5 &= ~(1 << 1);
if (dp3) cyfra5 |= (1 << 2); else cyfra5 &= ~(1 << 2);
LCDDATA0 = cyfra1;
LCDDATA1 = cyfra2;
LCDDATA2 = cyfra3;
LCDDATA3 = cyfra4;
LCDDATA4 = cyfra5;
}
//WATCHGOG MUSI BYC WYLACZONY
void main(void) {
OSC_Init_4MHz();
// Kalibracja:
// Jesli stoper spóznia sie -> zwieksz warto?? (np. 0x01, 0x02)
// Jesli stoper spieszy sie -> zmniejsz warto?? (np. 0x3F, 0x3E)
// OSCTUNE = 0x00;
// Porty i Przycisk RC0
TRISCbits.TRISC0 = 1;
TRISCbits.TRISC1 = 1; // Wejscie Urzadzenia (RC1)
// ANSELC = 0x00; // ROZWIAZANIE BLEDU: Wylaczenie analogów na ca?ym PORCIE C
// WPUCbits.WPUC0 = 1; // Pull-up na przycisku
__delay_ms(1000);
LCD_Init(); // inicjowanie wswietlacza
Timer0_Init_4MHz();
// ms_counter = 1234;
volatile uint16_t val;
uint8_t d1, d2, d3, d4,d1_,d2_,d3_,d4_;
uint16_t press_timer;
// Zapamietanie stanu pinu RC1 zaraz po uruchomieniu
initial_rc1_state = PORTCbits.RC1;
auto_mode_active = 1;
while (1) {
// Obsluga przycisku
if (PORTCbits.RC0 == 0) {
__delay_ms(20); // Antydrgania
press_timer = 0;
while (PORTCbits.RC0 == 0) {
__delay_ms(10);
press_timer++;
if (press_timer >= 300) { // 3 sekundy
is_running = 0;
ms_counter = 0;
// while(PORTCbits.RC0 == 0); // Czekaj na puszczenie
val = (uint16_t)ms_counter;
d1 = (val / 1000) % 10;
d2 = (val / 100) % 10;
d3 = (val / 10) % 10;
d4 = val % 10;
LCD_Display(d1, d2, d3, d4, 1, 0);
auto_mode_active = 1;
}
}
if (press_timer > 2 && press_timer < 300) {
is_running = !is_running; // Start/Stop
// if(is_running)auto_mode_active = 0;//wylaczenie auto mode
// else auto_mode_active = 1;// wlaczenie auto mode
}
}
if (ms_counter < 10000) {
// ZAKRES 1: 0,000 - 9,999s (Kropka DP1)
val = (uint16_t)ms_counter;
d1 = (val / 1000) % 10;
d2 = (val / 100) % 10;
d3 = (val / 10) % 10;
d4 = val % 10;
LCD_Display(d1, d2, d3, d4, 1, 0);
} else {
// ZAKRES 3: 10,0 - 999,9s (Kropka DP3)
// Wy?wietlamy sekundy i jedn? dziesi?t?
uint32_t ds = ms_counter / 100; // dziesi?te cz??ci sekundy
if (ds > 9999) ds = 9999; // Limit wyswietlacza
d1_ = (ds / 1000) % 10;
d2_ = (ds / 100) % 10;
d3_ = (ds / 10) % 10;
d4_ = ds % 10;
LCD_Display(d1_, d2_, d3_, d4_, 0, 1);
}
}
}
void __interrupt() ISR(void) {
if (PIR0bits.TMR0IF) {
PIR0bits.TMR0IF = 0;
TMR0H = 0xFF;
TMR0L = 0x06;
if (is_running) ms_counter++;
if (PIE0bits.IOCIE && IOCCFbits.IOCCF1 && auto_mode_active){
is_running = !is_running; // Start/Stop
//ms_counter = 0;
IOCCFbits.IOCCF1 = 0;
}
}
}
Stoper będzie przeze mnie używany do pomiaru czasu zadziałania zabezpieczeń zwarciowych i przeciążeniowych wyłączników. Poniżej przykład wykorzystania stopera
Poniżej krótki filmik ilustrujący działanie stopera
Działanie i Uruchamianie
Wyświetlacz który wybrałem ma tylko jedno podłoże i jest sterowany w trybie statycznym.
Żeby element wyświetlacza był widoczny (czarny) należy przyłożyć przemienne napięcie pomiędzy podłoże (BP) a elektrodę segmentu. Tą funkcję realizuje sterownik w mikrokontrolerze. Wystarczy tylko ustawić odpowiednie bity w rejestrach LCDDATA0 – LCDDATA4 (jeden bit jeden segment). W moim przypadku częstotliwość napięcia sterującego wyświetlaczem została ustawiona na około 60 Hz i została uzyskana z drugiego wewnętrznego generatora LFINTOSC o częstotliwości 31kHz. Dla mojego modelu wyświetlacza może się ona zawierać między 30 Hz a 100 Hz, a wyświetlacz może pracować w zakresie 3V do 5V. Dzięki temu przy zasilaniu z ogniwa nie ma potrzeby stosowania stabilizatora napięcia. Przykładowe przebiegi napięcia na sterowanych segmentach wyświetlacza na zdjęciach poniżej. Należy pamiętać że wyświetlacza LCD nie można sterować napięciem stałym. Stałe napięcie powoduje zjawisko elektrolizy które prowadzi do uszkodzenia wyświetlacza.
Podczas uruchamiana spotkało mnie parę niespodzianek. Ponieważ niezbyt często programuję w C skorzystałem z podpowiedzi ogólnodostępnej AI, która deklarowała że mogę podłączyć do PIC16F19175 32 segmenty w trybie statycznym, w rzeczywistości można ich podłączyć 30. Musiałem zrezygnować z jednej kropki stąd dwa zakresy. AI również nie trafiała z nazwami rejestrów dla mojego mikrokontrolera, ale po niewielkich poprawkach udało się doprowadzić kod do porządku.
Następnie okazało się że nie mogę zaprogramować mojego mikrokontrolera. Próbowałem 3 programatorów PicKit BASIC, SNAP, PICKIT3- otrzymywałem komunikat że być może mój mikrokontroler ma ustawiony bit programowania wysokim napięciem. Rzeczywistość okazała się bardziej banalna na płytce PCB zabrakło kawałka połączenia do resetu mikrokontrolera.
Podczas testów kiedy konfigurowałem sterownik LCD w tryb „LCD CHARGE PUMP” zauważyłem że między podłożem a segmentami SEG2D-PIN11, SEG2C-PIN10 i SEG3D-PIN44 nie mam przebiegu napięcia przemiennego bez tych trzech pionów nie mógłbym wyświetlać wszystkich cyfr. Z tego powodu skonfigurowałem sterownik LCD w trybie NTERNAL RESISTANCE LADDER POWER MODES.
Kiedy już miałem na wszystkich segmentach przemienne przebiegi i stoper zaczął liczyć , resetował się przy około 2 sekundach i liczył od nowa . Okazało się że w mikrokontrolerze domyślnie jest włączony watchdog , który musiałem wyłączyć.
Stoper pobiera mniej niż 1 mA prądu (800µA). Poniżej załączam notę AN1354 w której dociekliwe osoby mogą znaleźć informacje na temat konfiguracji sterownika.
Ja ucieszyłem się że wreszcie udało mi się skonfigurować sterownik LCD i nie drążyłem dalej tematu.
Nie spodziewałem się że przy tak prostym projekcie mogą się przytrafić takie przypadki
Fajne? Ranking DIY