elektronika
RC switch
jednoduché řešení z diskrétních součástek
Někdy potřebujeme RC soupravou sepnout dálkově nějaký kontakt. Může to být zapnutí nějakého osvětlení nebo sepnutí spouště fotoaparátu. Na řesšní podobné problematiky jsou přímo předurčeny moderní jednočipové mikroprocesory. Já jsem si zvolil procesor od firmy Atmel s označením ATtiny13.Co takové řešení přinese?
- spínání až dvou výstupů
- naprogramování úrovně sepnutí pro každý výstup
v rozlišení 600dpi
Použité součástky:
C1 100n IO1 ATtiny13 LED1 LED0805 (zelená) LED2 LED0805 (červená) R1 10k R0805 R2 10k R0805 R3 2k2 R0805 R4 10k R0805 R5 10k R0805 R6 2k2 R0805 T1,T3 BSP52 NPN SOT-223 T2,T4 BC817 NPN-SOT23-BECZdrojový kód by šel ještě zkrátit. Vycházel jsem ze své zavedené praxe, kdy definuji nastavení procesoru pomocí samostatných funkcí. Kód bych optimalizoval teprve tehdy, kdybych měl potíže s místem.
Uvádím zde celý zdrojový kód protože je tak jednoduchý, že nepředpokládám jeho komerční zneužití. Sám jsem při svých začátcích hledal nějaký jednoduchý příklad který by mě uvedl do problematiky. Většina funkcí je okomentována, tak je snad netřeba dalšího komentáře.
Programování vypínače.
- výstup vybraného kanálu a nastavíme na maximum
- připojíme řídící kanál k RCswitch
- počkám až obě diody 10x bliknou (10 sekund)
- pokud během těchto deseti vteřin přepnu výstupní kanál na nulu, ukončím programový mód
- rozvítí se levá (zelená) LED po dobu
- během 10ti sekund nastavím řídící kanál na požadovanou polohu
- rozvítí se pravá (červená) LED po dobu
- během 10ti sekund nastavím řídící kanál na požadovanou polohu
- obě diody rychle 3x bliknou
- programování je u konce, RCswitch spíná výstupní tranzistory a sepnutí indikuje rozsvícením příslušné LED
//====================================================================== // // Projekt : RC switch // Program pro spinani dvojic tranzistoru v zavislosti // na dvou uzivatelem definovanych polohach ridiciho // kanalu RC vysilace. // Pro rizeni je mozno pouzit jak tripolohovy prepinac, // tak kanal s plynulou regulaci, pripadne jen vypinac, // kdy je vyuzit pouze jeden spinany vystup. // // Soubor : main.c // // Autor : RNDr.Vladimir Pribyl, CSc // // e-mail : vladimir__p@volny.cz // // Firma : private // Copyright (c) 2007, All rights reserved // // Historie : 22.VIII.2007 VPR - uvodni verze // // Konfigurace : ------------------------------------ // Chip type : ATtiny13 // Program type : Application // Clock frequency : 9.600000 MHz // Memory model : Small // External SRAM size : 0 // Data Stack size : 256 // ------------------------------------ // // Poznamka : Presnost rizeni kanalu je 200 dilku na celou drahu serva // //====================================================================== // // nastaveni PonyProg // - : (1) prazne // + : (0) zacelke // novy procesor // SELFPRGEN - // DWEN - // BODLEVEL1 - // BODLEVEL0 - // EESAVE - // WDTOM - // CKDIV8 + // SUT1 - (1) // SUT0 + (0) // CLKSEL1 - (1) // CLKSEL0 + (0) // // CLKSEL : 9.6MHz internal // SUT : 14CK + 64 ms Slowly rising power /********************************* * aplication ATtiny13 board * *********************************/ #include <tiny13.h> #include <delay.h> #define DDB7 7 #define DDB6 6 #define DDB5 5 #define DDB4 4 #define DDB3 3 #define DDB2 2 #define DDB1 1 #define DDB0 0 #define SIGNAL PINB.0 #define OUTPORT PORTB #define OUT_1 4 #define OUT_2 3 #define ZAPNOUT 1 #define VYPNOUT 0 /******************************* * INIT_FN.C * *******************************/ void INIT_bootup (); void INIT_watch_dog (); void INIT_ports (); void INIT_timers (); void INIT_ext_interrupt (); void INIT_AD_con (); /******************************* * MAIN.C * *******************************/ void READ_eepromValue (); char GET_pulseLength (); void processPGMmode (); /*************************************************************************** * GLOBAL VARIABLES * ***************************************************************************/ unsigned char g_pulseLenCounter = 0; unsigned char g_1secCounter = 0; unsigned char g_200msCounter = 0; bit g_bIsPgmMode = 0; unsigned char g_dolniSwitch; unsigned char g_horniSwitch; eeprom unsigned char g_eeprom_DolniHodnota = 200; eeprom unsigned char g_eeprom_HorniHodnota = 200; /*************************************************************************** * FUNCTION: main() 22.Aug.07 * * VPR * * PURPOSE: hlavni programova smycka * * * * RETURN : * ***************************************************************************/ void main(void) { // prubeh ridiciho signalu serva // | 2ms | // | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | // 1 ms (servo na jednom kraji) // ___________________________________ // | | // --------- -------------------------------------------- // // 1.5ms (servo uprostred) // _______________________________________________________ // | | // --------- ------------------------ // // 2 ms (servo na druhem kraji) // ___________________________________________________________________________ // | | // --------- ---- // // unsigned char delkaPulzu; unsigned char second; // inicializace pri zapnuti zarizeni nebo po resetu INIT_bootup (); // zjistim, jestli jsem v programovem modu // programovy mod znamena, ze po dobu 10s je delka pulzu maximalni (> 180) second = g_1secCounter; do { if (GET_pulseLength () < 180) { // delka pulzu je kratka, koncim detekcni smycku g_bIsPgmMode = 0; break; } else { g_bIsPgmMode = 1; } // pri kazde zmene sekundy bliknu diodama if (second != g_1secCounter) { second = g_1secCounter; OUTPORT.OUT_1 ^= 1; OUTPORT.OUT_2 ^= 1; } } while (g_1secCounter < 10); // osetreni programoveho modu if (g_bIsPgmMode) { processPGMmode (); } READ_eepromValue (); // nekoncici programova smycka while (1) { delkaPulzu = GET_pulseLength (); // sepnu vystupni tranzistory OUTPORT.OUT_1 = (delkaPulzu > g_dolniSwitch) ? ZAPNOUT : VYPNOUT; OUTPORT.OUT_2 = (delkaPulzu > g_horniSwitch) ? ZAPNOUT : VYPNOUT; }; } /*************************************************************************** * FUNCTION: READ_eepromValue () 23.Aug.07 * * VPR * * PURPOSE: Prectu ulozene hodnoty z EEPROM * * * * RETURN : * ***************************************************************************/ void READ_eepromValue () { int eeprom *ptr_to_eeprom; ptr_to_eeprom = &g_eeprom_DolniHodnota; g_dolniSwitch = *ptr_to_eeprom; ptr_to_eeprom = &g_eeprom_HorniHodnota; g_horniSwitch = *ptr_to_eeprom; } /*************************************************************************** * FUNCTION: GET_pulseLength () 23.Aug.07 * * VPR * * PURPOSE: Zjisti delku pulzu 100 - 200 * * * * RETURN : delka pulzu 100..00 * ***************************************************************************/ char GET_pulseLength () { // cekam na hranu 0->1 // ujistim se, ze mam skutecne 0 while (SIGNAL == 1) ; // cekam na hranu while (SIGNAL == 0) ; // vynuluju citac 10us g_pulseLenCounter = 0; // cekam na hranu 1->0 konec pulzu while (SIGNAL == 1) ; return (g_pulseLenCounter); } /*************************************************************************** * FUNCTION: processPGMmode () 23.Aug.07 * * VPR * * PURPOSE: Osetreni programoveho modu * * * * RETURN : * ***************************************************************************/ void processPGMmode () { char loopIdx, pulseLength; int eeprom * ptr_to_eeprom; // programovani delky pulzu pro prvni FET OUTPORT.OUT_1 = ZAPNOUT; g_1secCounter = 0; ptr_to_eeprom = &g_eeprom_DolniHodnota; while (g_1secCounter < 10) { pulseLength = GET_pulseLength (); } pulseLength -= 2; *ptr_to_eeprom = pulseLength; OUTPORT.OUT_1 = VYPNOUT; // programovani delky pulzu pro druhy FET OUTPORT.OUT_2 = ZAPNOUT; g_1secCounter = 0; ptr_to_eeprom = &g_eeprom_HorniHodnota; while (g_1secCounter < 10) { pulseLength = GET_pulseLength (); } pulseLength -= 2; *ptr_to_eeprom = pulseLength; OUTPORT.OUT_2 = VYPNOUT; // potvrzeni ukonceni modu for (loopIdx = 0; loopIdx < 3; loopIdx++) { g_200msCounter = 0; while (g_200msCounter == 0) ; OUTPORT.OUT_1 = ZAPNOUT; OUTPORT.OUT_2 = ZAPNOUT; g_200msCounter = 0; while (g_200msCounter == 0) ; OUTPORT.OUT_1 = VYPNOUT; OUTPORT.OUT_2 = VYPNOUT; } } /*************************************************************************** * FUNCTION: INIT_ports() 22.Aug.07 * * VPR * * PURPOSE: * * * * RETURN : * ***************************************************************************/ void INIT_bootup () { #ifdef _TINY13_INCLUDED_ #pragma optsize- CLKPR = 0x80; CLKPR = 0x00; // nastaveni frekvence oscilatoru // prectene z firemniho nastaveni OSCCAL = 0x57; #ifdef _OPTIMIZE_SIZE_ #pragma optsize+ #endif #endif // inicializace portu INIT_ports (); // inicializace citacu/casovacu INIT_timers (); // inicializace externich preruseni INIT_ext_interrupt (); // inicializace watch-dog INIT_watch_dog (); // inicializace AD prevodniku INIT_AD_con (); // Global enable interrupts #asm("sei") } /*************************************************************************** * FUNCTION: INIT_ports() 22.Aug.07 * * VPR * * PURPOSE: * * * * RETURN : * ***************************************************************************/ void INIT_ports () { // Port B initialization // Registr DDRB oídí smir poenosu dat. // v úrovni H pracuje port jako výstup, v úrovni L jako vstup DDRB = (1<<DDB3)|(1<<DDB4); // bity 3 (OUT_1) a 4 (OUT_2) jsou vystupni // je-li bit nastaven jako vstupni, pak 1 aktivuje pull-up rezistor PORTB = 0x01; } /*************************************************************************** * FUNCTION: INIT_timers() 22.Aug.07 * * VPR * * PURPOSE: * * * * RETURN : * ***************************************************************************/ void INIT_timers () { // nastaveni citace 0 // FOC0 WGM00 COM01 COM00 WGM01 CS02 CS01 CS00 // . . . . . 0 0 0 - zadny zdroj pro citac // . . . . . 0 0 1 - osc // . . . . . 0 1 0 - osc / 8 // . . . . . 0 1 1 - osc / 64 // . . . . . 1 0 0 - osc / 256 // . . . . . 1 0 1 - osc / 1024 // . 0 . . 0 . . . - mod 0 (normal) // . 1 . . 0 . . . - mod 1 (PWM) // . 0 . . 1 . . . - mod 2 (CTC) // . 1 . . 1 . . . - mod 3 (PWM fast) // ----------------------------------------------------------------- // zdroj hodin : Systemove hodiny 9.6 MHz - ATtiny13 // preddelic : 8 => 1200 kHz // CTC : 12 => CMP preruseni po 10 us // mod 2 od nuly do 12 a preruseni CMP, zadne preruseni OVF TCCR0A = 0x02; TCCR0B = 0x01; TCNT0 = 0x00; OCR0A = 12; OCR0B = 0x00; // Timer(s)/Counter(s) Interrupt(s) initialization TIMSK0 = 0x04; } /*************************************************************************** * FUNCTION: INIT_ext_interrupt() 22.Aug.07 * * VPR * * PURPOSE: * * * * RETURN : * ***************************************************************************/ void INIT_ext_interrupt () { // External Interrupt(s) initialization // INT0: Off // Interrupt on any change on pins PCINT0-5: Off GIMSK = 0x00; MCUCR = 0x00; } /*************************************************************************** * FUNCTION: INIT_watch_dog() 22.Aug.07 * * VPR * * PURPOSE: * * * * RETURN : * ***************************************************************************/ void INIT_watch_dog () { } /*************************************************************************** * FUNCTION: INIT_AD_con() 22.Aug.07 * * VPR * * PURPOSE: * * * * RETURN : * ***************************************************************************/ void INIT_AD_con () { // Analog Comparator initialization // Analog Comparator: Off ACSR = 0x80; ADCSRB = 0x00; } /*************************************************************************** * FUNCTION: timer0_comp_isr() 22.Aug.07 * * VPR * * PURPOSE: Timer 0 output compare interrupt service routine * * 1 us * * * * RETURN : * ***************************************************************************/ interrupt [TIM0_COMPA] void timer0_compa_isr(void) { static unsigned long g_1usCounter = 0; g_pulseLenCounter++; if (++g_1usCounter > 20000) { g_1usCounter = 0; if (++g_200msCounter > 5) { g_200msCounter = 0; g_1secCounter++; } } }
Řešení z diskrétních součástek
Na rozdíl od předešlého řešení, kdy potřebujeme naprogramovat mikroprocesor a je následující řešení mnohem jednodušší. Hlavní rozdíl je ale v tom, že spínáme pouze jeden výstup a nastavení spínací úrovně nastavíme mechanicky. Destičku plošného spoje vyleptáme z tenčího kuprextitu. Odporem R2 nastavíme úroveň vychýlení ovladače (pokud nepoužíváme kanál ovládaný přepínačem), kdy dojde k sepnutí výstupního tranzistoru.
Toto zapojení jsem použil při ovládání kontaktu 1Mpx miniaturního fotoaparátu GSmart Mini2. Úprava je triviální. Po otevření fotoaparátu (šroubek je pod nálepkou) jsem vyvedl dvojici kontaktů spouště.
Nevýhodou tohoto řešení je, že fotoaparát se po chvilce nečinnosti sám vypne. Chystám se vložit do těla fotoaprátu miniaturní mikroprocesor, který napojím přímo na výstup přijímače. Ale kde vzít čas?