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?
Schéma je až primitivně jednoduché.
Všechno obstará jediná součástka - mikroprocesor. Zapojení vstupního konektoru umožní jak připojit řídící kanál z přijímače, tak připojit libovolný ISP programátor. Pro jednoduchost jsem použil jednostrannou destičku plošných spojů. Dvoustranná by sice zmenšila celkový rozměr ale celé řešení přeci jen prodražila.


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-BEC
Zdrojový 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.
Není-li při zapnutí RCswitche rídící kanál v maximu, přejde se rovnou do pracovního režimu.
//======================================================================
//
// 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?