//----------------------------------------------------------------------------
// HACDIM                                                     19951102 CHG
// Controls 5 channels of dimmers. Each channel can be set to an individual
// value.
//
// HACBUS:
//   Device type: 0x05             ; HACDIM type
//   Address    : 0x01..0x0F       ; Address 1..15
//   Commands   : 0x00             ; Poll
//          Resp: 0x80             ; No data
//                0x01 L1 L2 L3 L4 L5    ; Set levels for all outputs
//          Resp: 0x81                   ; Levels 0x00..0x19,
//                                       ; 0=off, 1=min, 25=max
//                0x02 nn YY             ; Set level YY for channel nn (1..5)
//          Resp: 0x82                   ; Level 0x00..0x19,
//                                       ; 0=off, 1=min, 25=max
//                0x03 nn YY             ; For channel nn, increase/decrease level
//          Resp: 0x83                   ; YY=0 decrease, YY=1 increase
//
//
// Principle of operation:
//
// First of all, the 50 Hz sinus curve:
//
// +      ..              ..              ..              ..
//       .  .            .  .            .  .            .  .
//      .    .          .    .          .    .          .    .  ÚÄÄÄÄÄZero cross point
//     .      .        .      .        .      .        .      . ³     (feed to INT1*)
// 0ÄÄ.ÄÄÄÄÄÄÄÄ.ÄÄÄÄÄÄ.ÄÄÄÄÄÄÄÄ.ÄÄÄÄÄÄ.ÄÄÄÄÄÄÄÄ.ÄÄÄÄÄÄ.ÄÄÄÄÄÄÄÄ.ÄÄÄÄÄÄÄÄÄÄÄÄÄ
//              .    .          .    .          .    .          .
//              |.  .|            .  .            .  .            .
// -            ||..||              ..              ..              .
//              ||||||
//                The point on the curve where each Triac is activated, controls the amount
//                of light emitted by the connected lamps. The quicker after the Zero
//                crossing the Triac is activated, the more "Light" we will have.
//
// Definitions:
//   Whenever the sinus is crossing the 0 V line, there will be an external interrupt to
//   the processor, via INT1* input. Note however that the interrupt will not be exactly
//   at the ZC point, because we have to "travel" a bit up(down) the sinus curve to get
//   enough energi to turn the Optocoupler on. That doesn't matter, because we have to
//   be a bit up(down) on the curve, so there will be sufficiently current thru the triacs
//   so they can hold them self turned on for the rest of the period.
//
//   For each channel there exists a TriggerCount_n, which is an 8-bit counter used to
//   determine at what point of the sinus curve the Triac should have a firing pulse.
//
//   When an external interrupt occurs on INT1*, Zero crossing occured,
//   The 8-bit counter, PhaseCounter, is reset to 1, and the Timer is being set
//   up for 314 uSec timeout.
//
//   Whenever an Timer interrupt is generated, all outputs to the Triac's are
//   deactivated. If any TriggerCount_n equals the PhaseCounter's value,
//   the corrosponding Triac is activated. This is done for all channels.
//   The PhaseCounter is incremented, and if > 25 it will be reset to 1 again.
//
//----------------------------------------------------------------------------
#include <reg2051.h>
#include <stdio.h>
#include "..\HACNET\HACNET.H"

//----------------------------------------------------------------------------
// Constants...
//----------------------------------------------------------------------------
#define TRUE   1
#define FALSE  0

#define MyDeviceType 0x05 // My device type on HACNET...
#define RxSize 6+3        // Maximum size of RxBuffer

//----------------------------------------------------------------------------
// I/O definitions...
//----------------------------------------------------------------------------
#define CH1 P33   // Triac for each of the 5 channels
#define CH2 P34
#define CH3 P35
#define CH4 P37
#define CH5 P10

#define Enable P12 // Enable driver (HC245 G* input) to Triac's

#define ZC  P32   // Zero crossing detector, external int only, INT0*
                  // Triggered by an Optocoupler direct driven from 220 V AC

//----------------------------------------------------------------------------
// Variables...
//----------------------------------------------------------------------------
unsigned char RxBuffer[RxSize];       // RxBuffer
extern bit RxAvail;                   // Message available in RX Buffer
extern bit Resend;                    // Resend last response

unsigned char TriggerCount_1;         // Holds count for triggering Triac 1
unsigned char TriggerCount_2;         // Holds count for triggering Triac 2
unsigned char TriggerCount_3;         // Holds count for triggering Triac 3
unsigned char TriggerCount_4;         // Holds count for triggering Triac 4
unsigned char TriggerCount_5;         // Holds count for triggering Triac 5

unsigned char Pre_1;                  // Holds new preset count for channel
unsigned char Pre_2;                  // Holds new preset count for channel
unsigned char Pre_3;                  // Holds new preset count for channel
unsigned char Pre_4;                  // Holds new preset count for channel
unsigned char Pre_5;                  // Holds new preset count for channel

unsigned char Hold_1;           
unsigned char Hold_2;           
unsigned char Hold_3;           
unsigned char Hold_4;           
unsigned char Hold_5;           

//----------------------------------------------------------------------------
// wait for a specific time in 10 mSec
// Based on a 11.059 MHz crystal
//----------------------------------------------------------------------------
void Delay(unsigned char t) {
  unsigned int i;
  while (t--) for(i=0;i<774; i++);
}


//----------------------------------------------------------------------------
// Zero crossing detector input, generates interrupt on every zero crossing of
// the sine-wave. (Actually a bit after the ZC, se top of file)
//----------------------------------------------------------------------------
void Int0(void) interrupt 0 using 2 {
  TH0=0xFE;    // Timeout value for 314 uSec.
  TL0=0xDE;
  TR0=TRUE;    // Start timer 0
}

//----------------------------------------------------------------------------
// Timer 0 interrupt.
// Timer preloads:
//           0xFEDE => 314 uSec (PhaseCounter max = 25)
//           0xFF52 => 188 uSec (PhaseCounter max = 43)
//           0xFFAC =>  90 uSec (PhaseCounter max = 86)
//----------------------------------------------------------------------------
void Timer0(void) interrupt 1 using 2 {
  static unsigned char PhaseCounter=1;
  // Load Timer again...
  TH0=0xFE;    // Timeout value for 314 uSec.
  TL0=0xDE;

  P11=!P11;
  // We stop a little before the next ZC...
  if (PhaseCounter>26) { // 26 * 314 uSec=8164 uSec
    PhaseCounter=1;
    // Update the values, this is a safe "flicker free" place to do so...
    TriggerCount_1=Pre_1;
    TriggerCount_2=Pre_2;
    TriggerCount_3=Pre_3;
    TriggerCount_4=Pre_4;
    TriggerCount_5=Pre_5;
    TR0=FALSE; // Stop timer, and wait for next ZC
  }
//CH1=CH2=CH3=CH4=CH5=0; // Kill all the Triacs
 
  if (Hold_1) Hold_1--;
  if (Hold_2) Hold_2--;
  if (Hold_3) Hold_3--;
  if (Hold_4) Hold_4--;
  if (Hold_5) Hold_5--;
 
  if (Hold_1==0) CH1=0;
  if (Hold_2==0) CH2=0;
  if (Hold_3==0) CH3=0;
  if (Hold_4==0) CH4=0;
  if (Hold_5==0) CH5=0;
  // Check if we are at a point on sinus curve, where we have to fire the Triac...
  if (PhaseCounter==TriggerCount_1) { CH1=TRUE; Hold_1=2; }
  if (PhaseCounter==TriggerCount_2) { CH2=TRUE; Hold_2=2; }
  if (PhaseCounter==TriggerCount_3) { CH3=TRUE; Hold_3=2; }
  if (PhaseCounter==TriggerCount_4) { CH4=TRUE; Hold_4=2; }
  if (PhaseCounter==TriggerCount_5) { CH5=TRUE; Hold_5=2; }
  // Next step
  PhaseCounter++;
}

//----------------------------------------------------------------------------
// Main part
//----------------------------------------------------------------------------
void main() {
  unsigned char i;
  unsigned char Rsp[1];                       // Buffer for responses
  unsigned char MyAddress;                    // My Address on HACNET

 //----------------------------------------------------------------------------
 // Initalize HACNET Driver
 //----------------------------------------------------------------------------
  MyAddress=InitHACNET(MyDeviceType, RxSize);

 //---------------------------------
 // Initialize outputs...
 //---------------------------------
  CH1=CH2=CH3=CH4=CH5=0; // Kill all the Triacs
  Enable=0; // Enable driver

 //------------------------------
 // Configure onchip resources...
 //------------------------------
  EX0   = 1;                      /* External interrupt 0 enable              */
  IT0   = 1;                      /* External interrupt 0 Edge sesitive       */
  ET0   = 1;                      /* Enable Timer 0 interrupts                */
  PT0   = 1;                      /* Timer 0 high priority                    */
  PX0   = 1;                      /* External interrupt 0 high priority       */
  TMOD |= 0x01;                   /* TMOD: timer 0, mode 1, 16 timer          */

  EA=1;        // Global int enable

  //----------------------------------------------------------------------------
  // Forever
  //----------------------------------------------------------------------------

  while(1) {
    //----------------------------------------------------------------------------
    // Diagnostic mode, Address is 0x00
    //----------------------------------------------------------------------------
    if (MyAddress==0) {
  //    HacTransmit("DIAG\n\r",6);
      Pre_1=0x0C; // Set some value on the outputs...
      Pre_2=0x0C; // Approx half level...
      Pre_3=0x0C;
      Pre_4=0x0C;
      Pre_5=0x0C;
    } else {
    //----------------------------------------------------------------------------
    // Normal mode
    //----------------------------------------------------------------------------
      // Check if we have to resend last response...
      if (Resend) {
        Resend=FALSE;
    //    Delay(200);
        HacTransmit(Rsp, 0xFF);
      }
      if (RxAvail) { // New telegram from our Master...
   //     Delay(200);
        switch (RxBuffer[0]) {
          case 0:
            Rsp[0]=0x80; // Response code
            HacTransmit(Rsp, 1); // Send response to master
            break;
          case 1:
            // reverse values...
            for (i=1; i<6; i++) if (RxBuffer[i]) RxBuffer[i]=26-RxBuffer[i];
            // We have to store the new load values in temporary storage to avoid
            // flicker. They will be loaded at the end of a half-period (see timer int.)
            Pre_1=RxBuffer[1];
            Pre_2=RxBuffer[2];
            Pre_3=RxBuffer[3];
            Pre_4=RxBuffer[4];
            Pre_5=RxBuffer[5];
            Rsp[0]=0x81; // Response code
            HacTransmit(Rsp, 1); // Send response to master
            break;
          case 2:
            // reverse value...
            if (RxBuffer[2]) RxBuffer[2]=26-RxBuffer[2];
            switch (RxBuffer[1]) {
              case 1: Pre_1=RxBuffer[2]; break;
              case 2: Pre_2=RxBuffer[2]; break;
              case 3: Pre_3=RxBuffer[2]; break;
              case 4: Pre_4=RxBuffer[2]; break;
              case 5: Pre_5=RxBuffer[2]; break;
            }
            Rsp[0]=0x82; // Response code
            HacTransmit(Rsp, 1); // Send response to master
            break;
          case 3:
            // Get firing angle for selected channel...
            switch (RxBuffer[1]) {
              case 1: i=Pre_1; break; 
              case 2: i=Pre_2; break; 
              case 3: i=Pre_3; break; 
              case 4: i=Pre_4; break; 
              case 5: i=Pre_5; break; 
            }
            // Check if increase...
            if (RxBuffer[2]) {
              if (i>2) i-=2;
            // Else decrease...
            } else {
              if (i<25) i+=2;
            }
            // Store new firing angle for selected channel...
            switch (RxBuffer[1]) {
              case 1: Pre_1=i; break; 
              case 2: Pre_2=i; break; 
              case 3: Pre_3=i; break; 
              case 4: Pre_4=i; break; 
              case 5: Pre_5=i; break; 
            }
            Rsp[0]=0x83; // Response code
            HacTransmit(Rsp, 1); // Send response to master
            break;
        }
        RxAvail=0;
      }
    }
  }
}

