/*******************************************************************************
Project : Rotate Clock (mechanically scanned digital LED clock)
Version : 1.0 (803 words)
Date    : 27.01.2007
Author  : Vershinin Ivan                            
Company : Home, Ryazan, Russia, http://www.cadhouse.nm.ru  
Compiler: CodeVisionAVR, Ver. 1.24.8, http://www.hpinfotech.ro
Comments: Eight light emitting diodes spin, giving the illusion of chars in the air.
          IR reciever (RC5, Philips) make possible to set current time.

Chip type           : ATtiny2313
Clock frequency     : 14.31818 MHz
Memory model        : Tiny
External SRAM size  : 0
Data Stack size     : 32
*******************************************************************************/

#include <tiny2313.h>

#define True           0x01
#define False          0x00
// bit operations
#define SetBit(x) |= (1<<x) 
#define ClrBit(x) &= ~(1<<x) 
#define InvBit(x) ^= (1<<x)
#define IsBit(x) & (1<<x)

typedef unsigned char byte; // short synonym for 'unsigned char'
typedef unsigned int word;  // short synonym for 'unsigned int'

#define MaxHrs         23
#define MaxMin         59
#define MaxSec         59

#define PulseRatio     5                         // on-off time ratio
#define Kpr            (PulseRatio - 1)          // on-off time coefficient
#define RealColCnt     120                       // number of glowing columns
#define ColCnt         (RealColCnt * PulseRatio) // number of columns subject to the on-off time ratio

// once a (LapCnt+1) revolutions calculating number of ticks of Timer1 per turn
#define LapCnt         49

#define _OCIE1A        6
#define _INT1          7

// character generator
flash const byte CharMap[12, 6] = {{0x7C, 0x82, 0x82, 0x82, 0x7C, 0x00},   // 0 
                                   {0x00, 0x42, 0xFE, 0x02, 0x00, 0x00},   // 1 
                                   {0x4E, 0x92, 0x92, 0x92, 0x62, 0x00},   // 2 
                                   {0x44, 0x82, 0x92, 0x92, 0x6C, 0x00},   // 3
                                   {0xF0, 0x10, 0x10, 0x10, 0xFE, 0x00},   // 4
                                   {0xE4, 0xA2, 0xA2, 0xA2, 0x9C, 0x00},   // 5
                                   {0x7C, 0x92, 0x92, 0x92, 0x4C, 0x00},   // 6
                                   {0x80, 0x80, 0x8E, 0x90, 0xE0, 0x00},   // 7
                                   {0x6C, 0x92, 0x92, 0x92, 0x6C, 0x00},   // 8
                                   {0x64, 0x92, 0x92, 0x92, 0x7C, 0x00},   // 9
                                   {0x00, 0x00, 0x28, 0x00, 0x00, 0x00},   // :
                                   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};  // space

struct TTimeRec                       // time
{
  byte Sec, Min, Hrs;                 // counters of secs, mins and hours
  byte Htens, Hunits, Mtens, Munits;  // tens and units of hours and mins
} TimeRec;

word Delta;                   // reg  Timer1 additive
byte ColNum;                  // number of current column in current symbol (form 0 to 5)
byte CharNum;                 // number of current symbol (from 0 to 4: 0,1-hours; 2-dots; 3,4-mins)
bit Additive;
bit DisplayOn;

bit PointsOn;                 // resets every sec for dots blinking
bit PointsStop;
byte DotOff;                  // if it = 1..4 then col is 'off', if it = 0 then col is 'on'
byte EventsFlag;              // reg for interrupt flags

byte IR_HalfPeriodCounter;    // half cycle counter received IR-packet
word IR_Interval;             // interval between interrupts and initial time for IR-timeout
word IR_Command;              // received IR command
bit IR_Detect, IR_Error;      // flags 'IR-detecting' and 'IR-error'
byte SetTimePos;              // number of current symbol for set time (1,2-hours; 3-dots; 4,5-mins)

#define RC5Standby       12
#define RC5VolumePlus    16
#define RC5VolumeMinus   17
 
#define IR_Pin           PIND.3

#define RC5FullPeriodNom 398                        // nominal T = 1.7(7) ms
#define RC5FullPeriodErr (RC5FullPeriodNom / 4)     // tolerance T
#define RC5HalfPeriodNom (RC5FullPeriodNom / 2)     // nominal T/2
#define RC5HalfPeriodErr (RC5FullPeriodNom / 8)     // tolerance T/2
#define RC5Timeout       18295                      // timeout interval = 64T-2T-14T-2T = 46T = 81.7(7) ms
#define RC5FullPeriodMax (RC5FullPeriodNom + RC5FullPeriodErr)
#define RC5FullPeriodMin (RC5FullPeriodNom - RC5FullPeriodErr)
#define RC5HalfPeriodMax (RC5HalfPeriodNom + RC5HalfPeriodErr)
#define RC5HalfPeriodMin (RC5HalfPeriodNom - RC5HalfPeriodErr)

#define LoByte(x) (*(byte*)&x)

// GIMSK operations
#define ExternalInterrupt1_Enable GIMSK SetBit(_INT1)
#define ExternalInterrupt1_Disable GIMSK ClrBit(_INT1)
#define Is_ExternalInterrupt1_Enable GIMSK IsBit(_INT1)
// External_interrupt_0 flag operations
#define Set_External_Interrupt0_Flag EventsFlag SetBit(0)
#define Clr_External_Interrupt0_Flag EventsFlag ClrBit(0)
#define Is_External_Interrupt0_Flag EventsFlag IsBit(0)
// Timer_0_overflow flag operations
#define Set_Timer0_Overflow_Flag EventsFlag SetBit(1)
#define Clr_Timer0_Overflow_Flag EventsFlag ClrBit(1)
#define Is_Timer0_Overflow_Flag EventsFlag IsBit(1)
// External_interrupt_1 flag operations
#define Set_External_Interrupt1_Flag EventsFlag SetBit(3)
#define Clr_External_Interrupt1_Flag EventsFlag ClrBit(3)
#define Is_External_Interrupt1_Flag EventsFlag IsBit(3)

byte TensAndUnitsToByte(byte Tens, byte Units)
{
  byte Tmp, i;
  
  i = 0;
  Tmp = 0;
  while (i < Tens)
  {
    Tmp += 10;
    i++;
  };
  Tmp += Units;
  return Tmp;
}

void ByteToTensAndUnits(byte *Arg, byte *Tens, byte *Units)
{
  *Units = *Arg;
  *Tens = 0;
  while (*Units > 9)
  {
    (*Tens)++;
    *Units -= 10;
  };
}

// calcs values of tens and units of hours and mins
void Extract(void)
{
  ByteToTensAndUnits(&TimeRec.Hrs, &TimeRec.Htens, &TimeRec.Hunits);
  ByteToTensAndUnits(&TimeRec.Min, &TimeRec.Mtens, &TimeRec.Munits);
}

// time counting
void Time(void)
{
  TimeRec.Sec++;
  if (TimeRec.Sec > MaxSec)
  {
    TimeRec.Sec = 0;
    TimeRec.Min++;
    if (TimeRec.Min > MaxMin)
    {
      TimeRec.Min = 0;
      TimeRec.Hrs++;
      if (TimeRec.Hrs > MaxHrs) TimeRec.Hrs = 0;
    };
  };
  Extract();
}

// displays symbols and calcs current col and symbol
void Display(void)
{    
  byte Num, Tmp, Flag;
  
  Flag = 0;
  if (SetTimePos == (CharNum + 1)) Flag = 1;
  
  DotOff++;
  if (DotOff > Kpr) DotOff = 0;
  if (DotOff)
  {
    if (!Flag)
      PORTB = 0xFF; // underlining
    else
      PORTB = 0xFE; // blanking
    return;
  };
  // if not 'DotOff' then display symbol
  switch (CharNum)
  {
    case 0: Num = TimeRec.Htens; break;
    case 1: Num = TimeRec.Hunits; break;
    case 2: if (PointsStop) Num = 10; else {if (PointsOn) Num = 10; else Num = 11;} break;
    case 3: Num = TimeRec.Mtens; break;               
    case 4: Num = TimeRec.Munits; break;
  };
  
  if (SetTimePos == (CharNum + 1)) Flag = 1;
  
  Tmp = ~(CharMap[Num, ColNum]);
  if (Flag && (ColNum < 5)) Tmp ClrBit(0);
  PORTB = Tmp;
  
  ColNum++;
  if (ColNum > 5)    // display of last col of curr symbol is completed
  { 
    ColNum = 0;
    CharNum++;
    if (CharNum > 4) // display of last symbol is completed - reset 'DisplayOn' flag
    {
      CharNum = 0;
      DisplayOn = False;
    };  
  }
}

void SetHours(byte Hours)
{
  if (Hours <= MaxHrs) TimeRec.Hrs = Hours;
}

void SetMinutes(byte Minutes)
{
  if (Minutes <= MaxMin) TimeRec.Min = Minutes;
}

void SetTime(void)
{ 
  byte Command, Tmp;
  
  Command = LoByte(IR_Command);
  
  if (Command == RC5VolumePlus)
  { 
    if (SetTimePos < 5) SetTimePos++; else SetTimePos = 0;
    if (SetTimePos == 3) PointsStop = True; else PointsStop = False;
  } else
  if (Command == RC5VolumeMinus)
  {
    if (SetTimePos > 0) SetTimePos--; else SetTimePos = 5;
    if (SetTimePos == 3) PointsStop = True; else PointsStop = False;
  } else
  if (Command == RC5Standby)
  {
    if (SetTimePos == 3) TimeRec.Sec = 0;
    PointsStop = False;
  } else
  if (Command <= 9)
  { 
    if (SetTimePos == 1)
    { 
      Tmp = TensAndUnitsToByte(Command, TimeRec.Hunits);
      SetHours(Tmp);
    } else
    if (SetTimePos == 2)
    { 
      Tmp = TensAndUnitsToByte(TimeRec.Htens, Command);
      SetHours(Tmp);
    } else
    if (SetTimePos == 4)
    {                             
      Tmp = TensAndUnitsToByte(Command, TimeRec.Munits);
      SetMinutes(Tmp);
    } else
    if (SetTimePos == 5)
    {                             
      Tmp = TensAndUnitsToByte(TimeRec.Mtens, Command);
      SetMinutes(Tmp);
    };
    Extract();
  };
}

void IR_Cancel(void)
{
  ExternalInterrupt1_Disable; // ext_int_1 turn off
  IR_Interval = TCNT1;        // to note the time and after RC5Timeout ticks of Timer1 ext_int_1 turn on
  IR_HalfPeriodCounter = 0;
  IR_Command = 0; 
  IR_Detect = False;
  IR_Error = True;
}

void IR_Process(void)
{ 
  byte Tmp;
  
  Tmp = IR_Command;

  if ((IR_Interval >= RC5HalfPeriodMin) && (IR_Interval <= RC5HalfPeriodMax))
    IR_HalfPeriodCounter++;
  else
    if ((IR_Interval >= RC5FullPeriodMin) && (IR_Interval <= RC5FullPeriodMax))
    {
      if ((IR_HalfPeriodCounter & 0x01) == 0)
      {
        IR_HalfPeriodCounter += 2;
        Tmp = ~Tmp;
      } else goto ERR;
    } else goto ERR;
  if ((IR_HalfPeriodCounter & 0x01) == 0) IR_Command = (IR_Command << 1) | (Tmp & 0x01);
  // test for 2 starting bits is 1
  if ((IR_HalfPeriodCounter == 2) && (IR_Command = 0x00)) goto ERR;
  if (IR_HalfPeriodCounter >= 26)
  { 
    IR_Command = ~IR_Command & 0b00111111; // mask 2 MSB
    SetTime();
    IR_Detect = False;
    IR_Error = False;
  };
  return;
ERR:
  IR_Cancel();
}

void OnOverflowTimer0(void)

{
  static byte Tick;
  Tick++;
  if (Tick > 58)   // Timer0 interrupts frequency is 59 Hz
  {
    Time();        // here hits every sec and increments time counter
    Tick = 0;
  }
  // dots blinking: half of sec is blank and other is glowing
  if (!PointsStop)
  {
    if (Tick > 29)
      PointsOn = True;
    else
      PointsOn = False;
  };  
}

void OnExternalInterrupt0(void)
{
  static word Last;
  static word Total;
  static word Latch;
  static byte Lap;
  // compute number of ticks of Timer1 per turn... and divide it into col count
  Latch = TCNT1;
  Total = Latch - Last;
  Last = Latch;
  Lap++;
  
  if (Lap > LapCnt)          // every (LapCnt+1) turns compute addition
  {
    Delta = Total / ColCnt;
    Lap = 0;
  };     
  ColNum = 0;
  CharNum = 0;
  DotOff = Kpr;
  OCR1A = Latch + Delta;     // set value OCR1A for timer_1_compare_A_isr
  DisplayOn = True;          // enable display
  Additive = False;
}

void OnExternalInterrupt1(void)
{
  static word Last;
  static word Latch;

  Latch = TCNT1;
  IR_Interval = Latch - Last;
  Last = Latch;
  
  if (!IR_Detect) // begin packet capture from IR-receiver
  { 
    if (IR_Pin == 0)
    {
      IR_Detect = True;
      IR_Command = 0;
      IR_HalfPeriodCounter = 0;
    } else
    { 
      IR_Cancel();
    };
  } else
  {
    IR_Process();
  };
}

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
  Set_External_Interrupt0_Flag;
}

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
  Set_External_Interrupt1_Flag; 
}

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{           
  TCNT0 = 0x13;    // reinitialize Timer 0 value
  Set_Timer0_Overflow_Flag;
}

// Timer 1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{   
  if (!DisplayOn) return;
  Additive = !Additive;
  OCR1A = TCNT1 + Delta + Additive;
  Display();
}

void Initialization(void)
{
  // Ports
  PORTA=0x07;      // all pins of PORTA as inputs
  DDRA=0x00;       // internal pull-up resistor connected to Vcc
  DDRB=0xFF;       // 0b11111111 - all pins of PORTB as outputs
  PORTB=0xFF;      // 0b11111111 - off all LEDs
  PORTD=0x7F;      // all pins of PORTD as inputs
  DDRD=0x00;       // internal pull-up resistor connected to Vcc
  // External Interrupts
  GIMSK=0xC0;      // 0b11000000 - external interrupts INT1, INT0 allowed
  MCUCR=0x06;      // 0b00000110 - interrupt when any change on INT1 and falling edge on INT0
  // Timer/Counter 0 initialization
  TCCR0A=0x00;
  TCCR0B=0x05;     // 0b00000101 - clock value: 13982,59765625 Hz (CK/1024)
  TCNT0=0x13;      // 0b00010011 - interrupts frequency is 59 Hz
  OCR0A=0x00;
  OCR0B=0x00;
  // Timer/Counter 1 initialization
  TCCR1A=0x00;
  TCCR1B=0x03;     // 0b00000011 - clock value: 223721.5625 Hz (CK/64)
  TCNT1H=0x00;
  TCNT1L=0x00;
  ICR1H=0x00;
  ICR1L=0x00;
  OCR1AH=0x00;
  OCR1AL=0x00;
  OCR1BH=0x00;
  OCR1BL=0x00;
  // Timer(s)/Counter(s) Interrupt(s) initialization
  TIMSK=0x42;      // 0b01000010 - Timer/Counter0 Overflow Interrupt Enable
  #asm("sei");     // enable interrupts
}

void main(void)
{
// Crystal Oscillator division factor: 1
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

Initialization();

while (1)
  {
    if (Is_External_Interrupt0_Flag)
    {
      OnExternalInterrupt0();
      Clr_External_Interrupt0_Flag;
    };
    if (Is_Timer0_Overflow_Flag)
    {
      OnOverflowTimer0();
      Clr_Timer0_Overflow_Flag;
    };
    if (Is_External_Interrupt1_Flag)
    {
      OnExternalInterrupt1();
      Clr_External_Interrupt1_Flag;
    };
    if (IR_Error)                         // if occur error during IR receiving…
    {
      if ((TCNT1 - IR_Interval) > RC5Timeout)  // and timeout is expired then
      { 
        IR_Error = False;                 // reset 'IR-error' flag
        EIFR = 0xC0;
        ExternalInterrupt1_Enable;        // ext_int_1 enable
      };
    };
  };
}
Используются технологии uCoz