/* * File: serwomechanizm-main-skokowo-z-tablica-i-przyciskami.c * Author: elektronikawsrode.pl * Program: uruchamia serwomechanizm przycisk w lewo i przycisk w prawo. */ // Bity konfiguracyjne // CONFIG1 #pragma config FOSC = INTOSC // (INTOSC oscillator; I/O function on CLKIN pin) #pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled) #pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is MCLR) #pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled) #pragma config BOREN = OFF // Brown-out Reset Enable (Brown-out Reset disabled) #pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin) // CONFIG2 #pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off) #pragma config PLLEN = OFF // PLL Enable (4x PLL disabled) #pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset) #pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.) #pragma config LPBOREN = OFF // Low Power Brown-out Reset enable bit (LPBOR is disabled) #pragma config DEBUG = OFF // In-Circuit Debugger Mode (In-Circuit Debugger disabled, ICSPCLK and ICSPDAT are general purpose I/O pins) #pragma config LVP = OFF // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming) #define _XTAL_FREQ 500000 // Wewnetrzny oscylator ustawiono na 500 kHz #include // --- DEFINICJE DLA RUCHU CIAGLEGO --- // Jak dlugo (w ms) trzeba przytrzymac przycisk, aby wlaczyl sie ruch ciagly // (Po wykonaniu pierwszego kroku) #define OPOZNIENIE_STARTU_RUCHU_MS 400 // Szybkosc ruchu ciaglego (pauza w ms miedzy kolejnymi krokami) #define SZYBKOSC_RUCHU_CIAGLEGO_MS 100 // --- DEFINICJE PRZYCISKOW --- #define PRZYCISK_LEWO PORTAbits.RA3 // Pin 4 (Ruch w strone -90) #define PRZYCISK_PRAWO PORTAbits.RA4 // Pin 3 (Ruch w strone +90) #define PRZYCISNIETY 0 // Przycisk zwiera do GND (bo uzywamy WPU) // --- TABLICA SEKWENCJI RUCHU --- const unsigned int sekwencja_ruchu[] = { 300, // Pozycja 0: _m90 (Stopien -90.0) 338, // Pozycja 1: _m82 (Stopien -82.5) 375, // Pozycja 2: _m75 (Stopien -75.0) 413, // Pozycja 3: _m67 (Stopien -67.5) 450, // Pozycja 4: _m60 (Stopien -60.0) 488, // Pozycja 5: _m52 (Stopien -52.5) 525, // Pozycja 6: _m45 (Stopien -45.0) 563, // Pozycja 7: _m37 (Stopien -37.5) 600, // Pozycja 8: _m30 (Stopien -30.0) 638, // Pozycja 9: _m22 (Stopien -22.5) 675, // Pozycja 10: _m15 (Stopien -15.0) 713, // Pozycja 11: _m7 (Stopien -7.5) 750, // Pozycja 12: _0 (Stopien 0.0) 790, // Pozycja 13: _7 (Stopien +7.5) 829, // Pozycja 14: _15 (Stopien +15.0) 869, // Pozycja 15: _22 (Stopien +22.5) 908, // Pozycja 16: _30 (Stopien +30.0) 948, // Pozycja 17: _37 (Stopien +37.5) 987, // Pozycja 18: _45 (Stopien +45.0) 1027, // Pozycja 19: _52 (Stopien +52.5) 1067, // Pozycja 20: _60 (Stopien +60.0) 1106, // Pozycja 21: _67 (Stopien +67.5) 1146, // Pozycja 22: _75 (Stopien +75.0) 1185, // Pozycja 23: _82 (Stopien +82.5) 1225 // Pozycja 24: _90 (Stopien +90.0) }; // Makro automatycznie obliczy rozmiar tablicy #define LICZBA_POZYCJI (sizeof(sekwencja_ruchu) / sizeof(sekwencja_ruchu[0])) void inicjalizacja_systemu(void) { // --- KONFIGURACJA OSCYLATORA --- // OSCCON: IRCF<3:0> = 0111 (500 kHz MF) // SCS<1:0> = 10 (Internal oscillator block) OSCCON = 0b00111010; // --- KONFIGURACJA PINOW PRZYCISKOW --- // Wlaczamy globalnie rezystory Weak Pull-Up // Bit WPUEN = 0 (znajduje sie w Banku 1) OPTION_REGbits.nWPUEN = 0; // Konfiguracja RA3 (Pin 4) jako wejscie cyfrowe z pull-up TRISAbits.TRISA3 = 1; // Wejscie (chociaz jest 'input-only') WPUAbits.WPUA3 = 1; // Wlacz pull-up dla RA3 // RA3 nie ma trybu analogowego (ANSELA nie ma bitu ANSA3) // Konfiguracja RA4 (Pin 3) jako wejscie cyfrowe z pull-up TRISAbits.TRISA4 = 1; // Wejscie ANSELAbits.ANSA4 = 0; // Wylacz tryb analogowy (ustaw na cyfrowy) WPUAbits.WPUA4 = 1; // Wlacz pull-up dla RA4 // --- KONFIGURACJA PWM3 --- // 1. Konfiguracja pinu RA2 (PWM3) TRISAbits.TRISA2 = 0; // RA2 jako wyjscie ANSELAbits.ANSA2 = 0; // RA2 jako cyfrowe I/O // 2. Ustawienie zegara PWM3 // CS<1:0> = 00 (FOSC), PS<2:0> = 000 (Preskaler 1:1) PWM3CLKCON = 0b00000000; // 3. Ustawienie okresu (Period = 20 ms) // PWM3PR = 9999 (binarnie 0b0010011100001111) PWM3PRH = 0b00100111; PWM3PRL = 0b00001111; // 4. Ustawienie fazy (Phase = 0) // PWM3PH = 0 (binarnie 0b0000000000000000) PWM3PHH = 0b00000000; PWM3PHL = 0b00000000; // 5. Ustawienie poczatkowego wypelnienia (pozycja 0 stopni) // PWM3DC = 750 (binarnie 0b0000001011101110) PWM3DCH = (sekwencja_ruchu[12] >> 8); // Pozycja 0 stopni (0b00000010) PWM3DCL = (sekwencja_ruchu[12] & 0b11111111); // (0b11101110) // 6. Konfiguracja trybu PWM // EN=1 (Wlaczony), OE=1 (Wyjscie wlaczone) // POL=0 (Aktywny-wysoki), MODE=00 (Standard) PWM3CON = 0b11000000; // 7. Zaladowanie wartosci (Immediate Reload) // Ustawienie bitu LDA (Load Buffer Armed) PWM3LDCONbits.LDA = 1; } void ustaw_wypelnienie(unsigned int wypelnienie) { PWM3DCH = (wypelnienie >> 8); // Zapisz gorny bajt PWM3DCL = (wypelnienie & 0b11111111); // Maska binarna dla dolnego bajtu // Zaladuj nowe wartosci do buforow operacyjnych // Uzywamy rejestru PWM3LDCON zgodnie z ustaleniami PWM3LDCONbits.LDA = 1; } void main(void) { // Uzywamy 'int' (signed), aby latwiej sprawdzac warunek >= 0 int indeks_pozycji; inicjalizacja_systemu(); // Ustaw pozycje poczatkowa na srodku (indeks 12) indeks_pozycji = 12; ustaw_wypelnienie(sekwencja_ruchu[indeks_pozycji]); __delay_ms(1000); // Poczekaj 1 sekunde na stabilizacje serwa while (1) { // --- Obsluga przycisku LEWO (RA3) --- if (PRZYCISK_LEWO == PRZYCISNIETY) { __delay_ms(50); // 1. Debouncing (odczekaj na drgania stykow) if (PRZYCISK_LEWO == PRZYCISNIETY) { // 2. Potwierdz wcisniecie // 3. Wykonaj PIERWSZY KROK (dla "pojedynczego klikniecia") if (indeks_pozycji > 0) { indeks_pozycji--; ustaw_wypelnienie(sekwencja_ruchu[indeks_pozycji]); } // 4. Sprawdz, czy przycisk jest trzymany (auto-repeat) unsigned int licznik_czekania = 0; // Czekaj OPOZNIENIE_STARTU_RUCHU_MS, ale sprawdzajac co 20ms while (licznik_czekania < OPOZNIENIE_STARTU_RUCHU_MS && (PRZYCISK_LEWO == PRZYCISNIETY)) { __delay_ms(20); licznik_czekania += 20; } // 5. Jesli po tym czasie nadal jest wcisniety - wlacz RUCH CIAGLY if (PRZYCISK_LEWO == PRZYCISNIETY) { while(PRZYCISK_LEWO == PRZYCISNIETY) { // Zabezpieczenie zakresu if (indeks_pozycji > 0) { indeks_pozycji--; ustaw_wypelnienie(sekwencja_ruchu[indeks_pozycji]); } else { break; // Przerwij petle 'while', jestesmy na koncu } // Ustaw szybkosc ruchu ciaglego __delay_ms(SZYBKOSC_RUCHU_CIAGLEGO_MS); } } // Jesli przycisk zostal puszczony w trakcie "licznik_czekania" // (w kroku 4), program po prostu idzie dalej (to bylo pojedyncze klikniecie). } } // Koniec obslugi LEWO // --- Obsluga przycisku PRAWO (RA4) --- if (PRZYCISK_PRAWO == PRZYCISNIETY) { __delay_ms(50); // 1. Debouncing if (PRZYCISK_PRAWO == PRZYCISNIETY) { // 2. Potwierdzenie // 3. Wykonaj PIERWSZY KROK if (indeks_pozycji < (LICZBA_POZYCJI - 1)) { indeks_pozycji++; ustaw_wypelnienie(sekwencja_ruchu[indeks_pozycji]); } // 4. Sprawdz, czy przycisk jest trzymany unsigned int licznik_czekania = 0; while (licznik_czekania < OPOZNIENIE_STARTU_RUCHU_MS && (PRZYCISK_PRAWO == PRZYCISNIETY)) { __delay_ms(20); licznik_czekania += 20; } // 5. Jesli nadal wcisniety - wlacz RUCH CIAGLY if (PRZYCISK_PRAWO == PRZYCISNIETY) { while(PRZYCISK_PRAWO == PRZYCISNIETY) { // Zabezpieczenie zakresu if (indeks_pozycji < (LICZBA_POZYCJI - 1)) { indeks_pozycji++; ustaw_wypelnienie(sekwencja_ruchu[indeks_pozycji]); } else { break; // Przerwij petle 'while', jestesmy na koncu } // Ustaw szybkosc ruchu ciaglego __delay_ms(SZYBKOSC_RUCHU_CIAGLEGO_MS); } } } } // Koniec obslugi PRAWO // Mala pauza, aby nie obciazac CPU w 100% __delay_ms(10); } // Koniec while(1) } // Koniec main()