program ADAC;
(*
DDS-Slave mit ATmega8

(c) by c't magazin und Carsten Meyer, cm@ctmagazin.de

18.09.2006 erste Implementierung aus Labor-DDS

*)

{$NOSHADOW}
{$W- Warnings}            {Warnings off}
{$TYPEDCONST OFF}
{$DEBDELAY}

//Defines aktivieren durch Entfernen des 1. Leerzeichens!

//sonst AD9833-DDS Anpassungen und mega168 auf MIDIvice-Platine


{ $DEFINE minEEPROM}         //TEST: minimale EEPROM-Presets während Entwicklung
{ $DEFINE MenuTest}          //TEST: Keine HW-IO-Routinen, zum Testen der Menüs
{$DEFINE yi3}         //MP3-Player Yampp Industrial an serieller Schnittstelle
{ $DEFINE bournsincr}  //Bourns-Incrementalgeber mit doppelter AktuelleFrequenz, sonst ALPS

Device = mega8, VCC = 5;

Define_Fuses
Override_Fuses; // optional, always replaces fuses in ISPE
LockBits0 = [];
FuseBits0 = [BODLEVEL];
ProgMode = SPI;

Import SysTick, SerPort;

From System Import LongInt;

Define
  ProcClock      = 8000000;        {Hertz}


  SerPort        = 9600, Stop2;    {Baud, StopBits|Parity}
  RxBuffer       = 16, iData;
  TxBuffer       = 16, iData;

  SysTick        = 1;              //msec
  StackSize      = $0080, iData;
  FrameSize      = $0100, iData;

Implementation


{--------------------------------------------------------------}
const
{ Keyboard-Interface, Taster und LEDs PortBits }

  DDRBinit                   = %00111111;            {PortB dir }
  PortBinit                  = %11101111;            {PortB, b_STROBE=0 }

  DDRCinit                   = %00001100;            {PortC dir }
  PortCinit                  = %11111111;            {PortC mit IncrEncoder}

  DDRDinit                   = %00000100;            {PortD dir }
  PortDinit                  = %11111100;            {PortD }

  LEDOutPort                 = @PortD;   {LED-Port}
  DDSOutPort                 = @PortB;   {DDS-Port}

  b_SCLK                     = 0; //Takt für alle
  b_SDATA                    = 1; //Daten für alle
  b_FSYNC                    = 2; //DSS-Frame
  b_LOADPOT                  = 3; //LM1972 LOADS
  b_STROBE                   = 4; //4094 SR Strobe
  b_SerAux                   = 5; //PB5 ist SerAux zweiter serieller Ausgang
  

//Lineal     .----.----.----.----.----.----.----.----.----
  Vers1Str                   = 'ADA-C V#0.02';    //Vers1
  Vers2Str                   = 'C.Meyer 09/2006';    //Vers2

  StopStr                    = 'STOP';

  high                       = true;
  low                        = false;


{$EEPROM}
structconst
  EEPatchArray   : array[0..12] of Byte =
  (
  $10,$27,0,0, 10, 0, 8, 0,  //Frequ 1000 Hz (LongInt), MP3sec, MP3min, dBkorr, Out
  0, 10, 0, 1, 70);     //SweepOct SweepTime Brst Wav dB

{--------------------------------------------------------------}

var
{$EEPROM}

{$DATA} {Schnelle Register-Variablen}
  i,
  k           : Byte;  // k in HW-Prozess benutzt
  m: Byte;

{$IDATA}  {Langsamere SRAM-Variablen}


  b_LEDaux[LEDOutPort, 2]    : bit; {Bit 2 ist Memory-LED, separat}

  SweepStarted  : Boolean;
  DDSfrequ,                       //Frequenzwert für DSS
  AktuelleFrequenz, FrequFak: LongInt;    //Absolutfrequenz real, Skalierungsfaktor
  BurstCount     : Byte;
  SweepOnce,
  BurstPrime, Modified,
  aBool: boolean;
  MemoryNum       : byte;

  SweepUpper:LongInt;

//Reihenfolge wichtig nur für Preset-Datei
  EditArray      : Table[0..15] of Byte;
  Edit1[@EditArray]    : byte;

  Frequenz[@EditArray] : LongInt;      // Absolutwert in 1/10 Hz

  SweepRange[@EditArray+8]    : Byte; // Korrekturwert Lautstärke
  SweepTime[@EditArray+9]      : byte;
  BurstMode[@EditArray+10]   : byte;
  Wave[@EditArray+11] : byte;

  AttnStep[@EditArray+12]     : byte; //0...7= -60 ... +10dB

  SaveArray      : Table[0..15] of Byte;
  Save1[@SaveArray]    : byte;

  EditString     : String[16];
  EditString1[@EditString+1]       : char;  //erstes Zeichen nach Längenbyte
  SaveEditString : String[16];
  LevelStr       : string[7];
  FreqStr        : String[8];
  BurstTimer      : SysTimer8;


const
//Anpassen bei Änderungen der Reihenfolge: Letzter - Erster Eintrag
  EditArrayLen               = 1 + @AttnStep - @EditArray;


{$NOSAVE}

{###########################################################################}


{###########################################################################}
//EEPROM-Routinen

procedure PatchCopyFromEE;
//AktuelleFrequenz und Settings aus EEPROM holen
var myPatchAdr: Word;
begin
  myPatchAdr :=  word(MemoryNum)*EditArrayLen;
  for i := 0 to EditArrayLen - 1 do
    settable(EditArray, i, (EEPatchArray[myPatchAdr + word(i)]));
  endfor;
end;

{###########################################################################}

procedure BurstGen;
//Burst-Generierung per Software, während auf Tastatureingabe gewartet wird
begin
  if BurstMode <> 0 then
    if isSysTimerZero(BurstTimer) then
      setSystimer(BurstTimer, 10);
      dec(BurstCount);
      if BurstCount = 0 then
//        dss_cmd:= c_ddsresetcmd;
//        SendDDS;
        BurstCount:= BurstMode + 1;
        BurstPrime:= true;
      endif;
    endif;
    if BurstCount = 1 then
      if BurstPrime then
//        dss_cmd:= wave_cmd;
//        SendDDS;
        BurstPrime:= false;
      endif;
    endif;
  endif;
end;


{###########################################################################}



procedure SerCRLF;
begin
  Serout(#$0D);
  Serout(#$0A);
end;

procedure serprompt(ok:boolean);
begin
  if ok then
   Serout(#$3D);  //"="
  else
    Serout(#$3F);   //"?"
  endif;
  Serout(#$3E);   //">"
  SerCRLF;
  aBool:=ok;
end;




procedure ParseCmd;
var
  InpStr:string[63];
  ParamStr:string[15];
  Param:LongInt;
  ParamByte:byte;
  ParamDez:byte;
  Request:boolean;
  CmdChar1, CmdChar2:char;
  
begin
  readln(SerInp, InpStr);
  InpStr:=uppercase(InpStr);
  if length(InpStr) = 0 then
    serprompt(true);
    return;
  endif;
//Parse einzelnen Befehl
  i:=pos(' ',InpStr); // Trenner
  Request:=(pos('?',InpStr)>1); // Request
  CmdChar1:=InpStr[1];
  CmdChar2:=InpStr[2];
  ParamStr:=copy(InpStr,i+1,15);
  i:=pos('.',ParamStr);
  ParamDez:=0;
  if i > 1 then
    ParamStr:=copy(ParamStr,1,i-1);
    ParamDez:=byte(ParamStr[i+1])-48;  // 1. Nachkommastelle
  endif;
  Param:=StrtoInt(ParamStr);
  ParamByte:=byte(Param);
  aBool:=true;

  if request then
    serprompt(false);
  else  // kein Request, also Parameter setzen
    case  CmdChar1 of
      'F':
        AktuelleFrequenz:= Param * 10 + longInt(ParamDez);
        Frequenz:=AktuelleFrequenz;
        |
      'W':
        wave:= ParamByte;
        |
      'L':
        AttnStep:= ParamByte;
        |
      'B':
        BurstMode:= ParamByte;
        |
      'S': // SweepRange
        SweepRange:= ParamByte;
        SweepOnce:=true;
//        SetSweepUpper;
//        SerOutFreq(Frequenz);
        serout(#44); //","
        serout(#32);
//        SerOutFreq(SweepUpper);
        SerCRLF;
        |
      'T': // SweepTime
        SweepTime:= ParamByte;
        |
    else
      serprompt(false);
    endcase;
  endif;
  
  if aBool then
    serprompt(true);
  endif;
//  SetLevelDDS;
end;


{###########################################################################}


procedure initall;
//nach Reset aufgerufen
begin
  DDRB:=  DDRBinit;            {PortB dir}
  PortB:= PortBinit;           {PortB}
  DDRC:=  DDRCinit;            {PortC dir}
  PortC:= PortCinit;           {PortC}
  DDRD:=  DDRDinit;            {PortD dir}
  PortD:= PortDinit;           {PortD}

  ADMUX:= ADMUX OR %11000000;  {Internal ADC Reference}


  MemoryNum:= 0;
  PatchCopyFromEE;       // Optionen-Defaults

  Modified:= false;
  EnableInts;
  BurstCount:= 1;
  setSystimer(BurstTimer,1);
  AktuelleFrequenz:=Frequenz;
//  SetLevelDDS;
end;

{###########################################################################}

begin
  Initall;
  loop
    if serstat then
      parseCmd;
    endif;

//    SweepGen;
    BurstGen;
      
  endloop;
end ADAC.

