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?