program GeneHF_90MHz;
{===============================================================================
par Silicium628
versions: voir plus bas dans la partie "const" - derniere mise à jour 22 aout 2008
================================================================================
PRINCIPE:
--------------------------------------------------------------------------------

================================================================================
La carte electronique répond aux signaux d'une télécommande TV infrarouge universelle
Beaucoup de modèles conviennent, il faudra adapter les codes des touches dans ce
programme le cas écheant (voir les 'case octet_IR of') dans la partie "main"
Pour ma part, j'utilise une PHILIPS type 'UNIVERSAL SBC RU 252 II'

================================================================================
}

{ $W+ Warnings}            {Warnings off}

Device = mega8, VCC = 5;

Import SysTick, LCDport, {RTclock,} {TickTimer,} RC5Rxport, { FreqCount,} ADCPort;

// From RTclock Import RTCtimer;

From System Import Int64, float;

Define
  ProcClock      = 16000000;        {Hertz}
  SysTick        = 10, Timer0;     {msec}   // ATTENTION: necessaire pour l'horloge RTC  et LCD

//  RTclock        = iData, DateTime; {Time, DateTime}
//  RTCsource      = SysTick {, adj}; { adj = +/-100}
//  RTCtimer       = 4; // 4 cannaux

//  FreqTimer      = Timer1; // (used 16bit Timer}

  StackSize      = 256, iData;     // (voir affi en runtime)
  FrameSize      = 128, iData;

  LCDport        = PortB, 0, PortC, 0;  // control port, bit, Data port, bit  ;  voir le docuDriver.pdf (3.4.3 LCD-Split)
//The first parameter defines the control port with its first used bit. The order of the bits are fixed but
//different to the non-split driver: RS, RW, E (, Enable2 optional).
  LCDtype        = 44780;
  LCDrows        = 4;               {rows}
  LCDcolumns     = 20;              {columns per line}

  RC5Rxport      = PinC, 4, negative; {Port, Pin#, polarity}
  RC5mode        = rc_6bit;        {command bit count}

  ADCchans       = [6], iData; {use only 1 Channel, ADC5}
  ADCpresc       = 16;



Implementation

//==============================================================================
{$IDATA}

type


const
  version        : string    = '4.0';

{ Type Declarations }


//==============================================================================
{$IDATA}

var
//  delay          : SysTimer8;
  ticked         : boolean;
//  timeout1       : boolean; // pour RTCtimer
//  timeout2       : boolean; // pour RTCtimer
//  timeout3       : boolean; // pour RTCtimer
//  timeout4       : boolean; // pour RTCtimer
  stk1           : word;
  itps           : word;

  OCR2_real      : float;

  rxAdr          : byte;
  rxCmd          : byte;
  adr1           : integer;
  cmd1           : integer;
  octet_IR       : byte;       //octet recu par le recepteur IR
  bitsix         : byte;
  memobitsix     : byte;
  nouveau        : boolean;    // pour l'anti rebond signaux IR
  acqui_ADC      : word;
  pos_bt         : byte;
  memo_pos_bt    : byte;
  digit_x        : byte;
  increment      : word;
  puiss10        : array[1..4] of word;
  pos_cur        : array[1..4] of byte;

//  Frequence      : longword;
//  F_affi         : longword;
  F_base1        : word;
  consigne_F     : longword;
  gamme          : byte;
  memo_gamme     : byte;
  manuel         : boolean;

{--------------------------------------------------------------}
{functions }

procedure init_ports;  // ports perso
// 0 = entree, 1=sortie   ;   les 1 sur les pins en entrees activent les R de Pull Up (tirage à VCC)
begin
  PortB:= %00000000;
  DDRB:= DDRB or %00001000;            // portB[3] = sortie (OC2)

  DDRC:= DDRC and %001111;     //PC4 en entree  (IR)

  portD:= %00100001;
  DDRD:=  %11111111;
end;


procedure interroge_IR;
begin
  if RecvRC5(rxAdr, rxCmd) then    // interroge IR
    adr1:= integer(rxAdr);
    cmd1:= {%00111111 and} integer(rxCmd);
    octet_IR:= byte(cmd1);  // le bit6 (=64 decimal) est=1 un appui de touche de la zapette sur deux
    memobitsix:= bitsix;
    bitsix:= octet_IR and %01000000;
    if bitsix <> memobitsix then
      nouveau:= true;
    else nouveau:= false;
    endif;
    octet_IR:= octet_IR and %00111111; // on supprime l'info de repetition de l'octet
//    portx:= portx or ......;  // allume LED
  else
    octet_IR:= $FF;
//    portx:= portx and %......;  // eteint LED
  endif;
if nouveau = false then octet_IR:= $FF; endif; // pas de repetition auto dans cette application
end;


procedure InitINTs;    // A ADAPTER au ATmega8
begin
{  TCCR2:
wgm21,20 =11 ->Fast PMW
com21,com20=11 ->Set OC2 on Compare Match, clear OC2 at TOP ;
bits2,1,0:  prescaler = 1/256

  TCCR2:= %01111110;
}

  TCCR2:= %00000000;  // Timer2 non utilisé. OC2 = sortie (portB,3) normale

  TIMSK := TIMSK or  %00000000;  // INT Timer2 comp disable; INT Timer2 overflow disable;
  GICR  := GICR  or  %00000000;  // gere les INTs voir page 67 du pdf
  MCUCR := MCUCR or  %00000010;  // The falling edge of INT0 generates an interrupt request. p:67 du pdf
end;



procedure Affiche_STACK_free;  // attention: ne pas appeller depuis une INT sinon fait planter
begin
  LCDxy(16, 3);
  Write(LCDout, IntToStr(stk1 : 3 : '0') );
//  LCDxy(16, 3);
//  Write(LCDout, IntToStr(fram_free : 3 : '0') ); fonction bugguée? chaque lecture consomme 1 octet!
end;


(*
procedure RTCtickSecond;       // CallBack from RTClock
begin

  LCDxy(8, 0);
  LCDclrEOL;
  Write(LCDout, IntToStr(F_base1));

  LCDxy(0, 0);
//      LCDclrEOL;
  Write(LCDout, ByteToStr(pos_bt : 2));

end;
*)

procedure active_ENB;
begin
  portD:= portD and %11011111;  // ENB/=0 (active le transfert entre le uC et le MC145170)
  udelay(1); // 10us
end;


procedure desactive_ENB;
begin
  portD:= portD or  %00100000;  // ENB/=1 (desactive ENB/ ce qui effectue le transfert dans les registres internes)
  mdelay(1);
end;


procedure init_variables;
begin
  puiss10[1]:= 1;
  puiss10[2]:= 10;
  puiss10[3]:= 100;
  puiss10[4]:= 1000;

  pos_cur[1]:= 0;
  pos_cur[2]:= 1;
  pos_cur[3]:= 3;
  pos_cur[4]:= 4;

  gamme:= 0;
  manuel:= false;
end;


procedure clk_PLL;
begin
  portD:= portD or  %01000000;
  udelay(1); // 10us
  portD:= portD and %10111111;
  udelay(1); // 10us
end;


procedure out_registre_C(C_data : byte);
//c'est le nombre de clocks qui determine le registre de destination
// en envoie le bit de poids fort en premier
var
  n, p           : byte;
begin
  active_ENB;
  for n:= 7 downto 0 do  // 8 bits
//    p:= 1 shl (n - 1);
//    if  (C_data and p) <> 0 then
    if bit(C_data, n) then
      portD:= portD or  %10000000;
    else
      portD:= portD and %01111111;
    endif;
    clk_PLL;
  endfor;
  desactive_ENB;
end;


procedure out_registre_N(N_data : word); // vers la PLL MC145170
//c'est le nombre de clocks qui determine le registre de destination
// en envoie le bit de poids fort en premier
var
  n              : byte;
  p              : word;
begin
  active_ENB;
  for n:= 15 downto 0 do   // 16 bits
    if bit(N_data, n) then
      portD:= portD or  %10000000;
    else
      portD:= portD and %01111111;
    endif;
    clk_PLL;
  endfor;
  desactive_ENB;
end;


procedure out_registre_R(R_data : word); // vers la PLL MC145170
//c'est le nombre de clocks qui determine le registre de destination
// en envoie le bit de poids fort en premier
var
  n              : byte;
  p              : word;
begin
  active_ENB;
  for n:= 14 downto 0 do   // 15 bits
    if bit(R_data, n) then
      portD:= portD or  %10000000;
    else
      portD:= portD and %01111111;
    endif;
    clk_PLL;
  endfor;
  desactive_ENB;
end;


procedure init_registre_C;  // de la PLL
{-------------------------------------------------------------------------------------------------------------
RAPPEL: programmation du registre C (8 bits) de la PLL MC145170:
C7 - POL:Select the output polarity of the phase/frequency detectors.
When set high, this bit inverts PDout and interchanges the fR function with fV as depicted in Figure 19.
Also see the phase detector output pin descriptions for more information. This bit is cleared low at power up.

C6 - PDA/B:Selects which phase/frequency detector is to be used.
When set high, enables the output of phase/frequency detector A (PDout) and disables phase/frequency detector B
by forcing fR and fV to the static high state.
When cleared low, phase/frequency detector B is enabled (fR and fV) and phase/frequency detector A is disabled
with PDout forced to the high-impedance state. This bit is cleared low at power up.

C5 - LDE:Enables the lock detector output when set high.
When the bit is cleared low, the LD output is forced to a static low level.
This bit is cleared low at power up.

C4 - C2, OSC2 - OSC0: Reference output controls which determines the REFout characteristics as shown below.

C4 C3 C2 REFout Frequency  ; Upon power up, the bits are initialized such that OSCin/8 is selected.
000dc (Static Low)
001 OSCin
010 OSCin /2
011 OSCin /4
100 OSCin /8 (par Defaut)
101 OSCin /16
110 OSCin /8
111 OSCin /16

C1 - fVE:Enables the fV output when set high.
When cleared low, the fV output is forced to a static low level.
The bit is cleared low upon power up.

C0 - fRE:Enables the fR output when set high. When cleared low, the fR output is forced to a static low level.
The bit is cleared low upon power up.
-------------------------------------------------------------------------------------------------------------}
begin
//           bits 76543210
  out_registre_C(%01110011);
end;


procedure test_IR;
var
  chr1           : char;
begin
  LCDclr;
  LCDxy(0, 0);
  Write(LCDout, 'GeneHF 90MHz ' + version );
  LCDxy(0, 1);
  Write(LCDout, 'Test IR ok = out' );

  repeat
    interroge_IR;
    if nouveau                 = true then
      chr1:= '*';
    else chr1:= ' ';
    endif;
    if octet_IR <> 255 then
      LCDxy(0, 0);
      LCDclrEOL;
      Write(LCDout, ByteToStr(octet_IR : 3 : ' ') + ' ' + chr1);
      mdelay(200);
    endif;
  until (octet_IR = 0) or (octet_IR = 23);
end;


procedure acqui_pos_btn;
// lit la position d'un potentiometre à resistances CMS discretes  (63k au total)
begin
//  LCDclr;

  acqui_ADC:= GetADC;  // resolution 10bits

  case acqui_ADC of

    881..900 : pos_bt:= 0;   //   884
             |
    875..880 : pos_bt:= 1;  //    878
             |
    869..874 : pos_bt:= 2; //     871
             |
    860..868 : pos_bt:= 3;  //    862
             |
    851..859 : pos_bt:= 4;   //   854
             |
    839..850 : pos_bt:= 5;   //   844
             |
    826..838 : pos_bt:= 6;   //   831
             |
    811..825 : pos_bt:= 7;   //   819
             |
    791..810 : pos_bt:= 8;    //  804
             |
    776..790 : pos_bt:= 9;  //    787
             |
    756..775 : pos_bt:= 10;   //  768
             |
    736..755 : pos_bt:= 11;   //  744
             |
    701..735 : pos_bt:= 12;   //   717
             |
    671..700 : pos_bt:= 13;    //  683
             |
    631..670 : pos_bt:= 14;   //   639
             |
    551..630 : pos_bt:= 15;   //   585
             |
    451..550 : pos_bt:= 16;   //  512
             |
    350..450 : pos_bt:= 17;   //  409
             |
    200..300 : pos_bt:= 18;  //   255
             |
    0..100   : pos_bt:= 19;  //   0
             |
  endcase;
//    LCDxy(0, 1);
//    Write(LCDout, ByteToStr(n : 3));
end;


procedure commute_selfs(gamme1 : byte);
// gamme = 1..7
// chaque bit commute une des trois selfs. Les selfs peuvent donc etre utilisees en //
begin
  portD:= portD and %11111000;
  portD:= portD or (gamme1 and %00000111);
end;


procedure select_gamme;
// a finaliser, sans trous, apres avoir ajuste toutes les selfs
begin
  memo_gamme:= gamme;
  case F_base1 of
    8001..9000 : gamme:= 7;
               |
    7301..8000 : gamme:= 6;
               |
    6801..7300 : gamme:= 5;
               |
    6301..6800 : gamme:= 4;
               |
    5501..6300 : gamme:= 3;
               |
    4800..5500 : gamme:= 2;
               |
    3200..3800 : gamme:= 1;
               |
  endcase;

//  if gamme <> memo_gamme then
  commute_selfs(gamme);


  LCDxy(7, 2);
  Write(LCDout, ByteToStr(gamme));
//  endif;
end;


procedure increment_F;
begin
  F_base1:= F_base1 + increment;
  LCDxy(4, 0);
  LCDclrEOL;
  Write(LCDout, IntToStr(F_base1 : 3 : 2) + ' MHz');
  LCDxy(8 - digit_x, 0);
  out_registre_N(F_base1);
  if not(manuel) then
    select_gamme;
  endif;
end;


procedure decrement_F;
begin
  F_base1:= F_base1 - increment;
  LCDxy(4, 0);
  LCDclrEOL;
  Write(LCDout, IntToStr(F_base1 : 3 : 2) + ' MHz');
  LCDxy(8 - digit_x, 0);
  out_registre_N(F_base1);
  if not(manuel) then
    select_gamme;
  endif;
end;


procedure affiche_increment;
begin
  case digit_x of
    1 : LCDxy(4, 1);
        Write(LCDout, ' 10 kHz');
      |
    2 : LCDxy(4, 1);
        Write(LCDout, '100 kHz');
      |
    3 : LCDxy(4, 1);
        Write(LCDout, '  1 MHz');
      |
    4 : LCDxy(4, 1);
        Write(LCDout, ' 10 MHz');
      |
  endcase;
end;


//==============================================================================
{ Main Program }
{$IDATA}

begin
  init_variables;
  init_ports;
  InitINTs;
  init_registre_C;

  out_registre_R(400);  // 4MHz/400 = 10kHz  (ref)
  out_registre_N(7000);

  ticked := true;
  LCDclr;                                  { clear display }
  LCDcursor(false, false);                 { display on, cursor off & no blink }
  Write(LCDout, 'RESET');

  mdelay(300);
  LCDclr;
  Write(LCDout, 'GeneHF 90MHz ' + version );
  mdelay(1000);
  LCDclr;

  LCDxy(0, 0);
  Write(LCDout, 'Out');
  LCDxy(0, 1);
  Write(LCDout, 'pas');
  LCDxy(0, 2);
  Write(LCDout, 'gamme');

//  SetFreqCountMode(TFreqBase1MHz); // mode frequencemetre  TFreqBase1MHz Frequ = 100Hz...6.5535MHz gate time = 10msec

  octet_IR:= 255;

  F_base1:= 7000;
  gamme:= 1;
  commute_selfs(gamme);
  LCDxy(7, 2);
  Write(LCDout, ByteToStr(gamme));

  EnableInts;

  digit_x:= 3;
  increment := puiss10[digit_x];
  affiche_increment;

  loop
    memo_pos_bt:= pos_bt;
    acqui_pos_btn;
    if (pos_bt < memo_pos_bt) and (pos_bt + memo_pos_bt <> 19) then
      decrement_F;
    elsif (pos_bt > memo_pos_bt)and (pos_bt + memo_pos_bt <> 19) then
      increment_F;
    elsif  (( pos_bt = 0) and (memo_pos_bt = 19) ) then
      increment_F;
    elsif  (( pos_bt = 19) and (memo_pos_bt = 0) ) then
      decrement_F;
    endif;

    interroge_IR;

    if octet_IR <> 255 then
      case octet_IR of
        0    : manuel:= false;
               LCDxy(10, 2);
               Write(LCDout, '( auto )');
             |
        1..8 : manuel:= true;
               gamme:= octet_IR;
               commute_selfs(gamme);
               LCDxy(7, 2);
               Write(LCDout, ByteToStr(gamme));
               LCDxy(10, 2);
               Write(LCDout, '(manuel)');
             |
        9    : test_IR
             |
        12   : system_reset;
             |
        17   : LCDcursor(true, false);
               if digit_x < 4  then    // (fleche <)
                 inc(digit_x);
                 LCDxy(8 - pos_cur[digit_x], 0);
                 increment := puiss10[digit_x];
                 affiche_increment;
               endif;
             |
        16   : if digit_x > 1 then    // (fleche >)
                 dec(digit_x);
                 LCDxy(8 - pos_cur[digit_x], 0);
                 increment := puiss10[digit_x];
                 affiche_increment;
               endif;
             |
        32   : increment_F;   // (fleche haut)
             |
        33   : decrement_F;   // (fleche bas)
             |
        43   : nop;   // (<< rouge)
             |
        46   : nop;   //(>> bleue)
             |
        47   : nop;   // (.)
             |
        56   : nop;   // (lecture >)
             |
        23   : nop;   // (Ok)
             |
      endcase;
    endif;


//    Frequence:= 16 * GetFreqCounterL;

    mdelay(2);

  endloop;

//  RTCtimer_Stop(0);
end GeneHF_90MHz.




