program SoftDDS;
(*
DDS-Funktionsgenerator mit AD98833, kompakt OHNE MP3-Spieler-Steuerung
als universelle Audio-Quelle, gekürzt für ATmega8
(c) by c't magazin und Carsten Meyer, cm@ctmagazin.de

-- MIDIvice-Platine mit ATmega8 (8KByte Code!) und
   AD9833-DDS-Platine an ManualScan-Port, Pegeleinstellung
   in 1-dB-Schritten per LM1972, Ausgangspegel asym./sym. mit
   entsprechender Korrektur
-- ALPS- oder Bourns-Incrementalgeber an MIDIvice AnalogIO-Port PC0 und PC1
-- LCD über I2C mit 2x16 Zeichen, für Preset- und Default-Anzeige
   5 Taster zur Bedienung, Up/Down, Left/Right, Aux
   
*)

{$NOSHADOW}
{ $W+ Warnings}            {Warnings off}
{$TYPEDCONST OFF}
{$DEBDELAY}

//Defines aktivieren durch Entfernen des 1. Leerzeichens!

{ $DEFINE minEEPROM}  //TEST: minimale EEPROM-Presets während Entwicklung
{ $DEFINE MenuTest}   //TEST: Keine HW-IO-Routinen, zum Testen der Menüs
{$DEFINE bournsincr} //Bourns-Incrementalgeber mit doppelter Frequenz, sonst ALPS


Device = mega8, VCC = 5;
Import SysTick, TWImaster, LCDmultiPort, IncrPort4;

From System Import LongInt;

Define
  ProcClock      = 16000000;        {Hertz}

  TWIpresc       = TWI_BR400;
  LCDmultiPort   = I2C_TWI;
  LCDTYPE_M      = 44780;          // 66712;
  LCDrows_M      = 2;              //2 rows
  LCDcolumns_M   = 16;             //16 chars per line
  IncrPort4      = PinC, 1, 16; // pin-reg used, channels, 16 or 32bit integer
  IncrScan4      = Timer1, 4; // timer used, scan rate 4kHz (1..100)

  SysTick        = 10;              //msec
  StackSize      = $0080, iData;
  FrameSize      = $0060, iData;

Implementation

{--------------------------------------------------------------}
type
  TEditWhich     = (m_fr1, m_fr2, m_fr3, m_fr4, m_fr5, m_fr6, m_fr7, m_frdec,
                    m_patch, m_db, m_wav, m_rpt, m_out, m_brst, m_frequ, m_mv);

{--------------------------------------------------------------}
const
//Wave = Sinus, Dreieck/Sägezahn, Rechteck, Audio 1..99
  c_sinw                     = 0;
  c_triw                     = 1;
  c_squw                     = 2;
  c_audio                    = 3;

//OutputMode Normal,
  c_nrmo                     = 0;
  c_symo                     = 1;
  c_pwro                     = 2;
//  c_psymo                    = 3;

{ Keyboard-Interface, Taster und LEDs PortBits }

  m_lastmenu                 = m_brst;

  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 }

  BtnInPortReg               = @PinD;   {Taster-Eingangsport}
  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

//für AD9833-DDS
//                         Bit    5432109876543210
  c_ddsresetcmd              = %0010000100000000;
//                         Bit              O D M
  c_ddssinecmd               = %0010000000000000;
  c_ddstrianglecmd           = %0010000000000010;
  c_ddssquarecmd             = %0010000000101000;

{AD9833-DDS-Konstanten für Frequenzen, addiert für DDS, abhängig von CLK, hier 4.194304 Mhz}
  fhz            : Table[0..7] of LongInt = (64000000, 6400000, 640000, 64000, 6400, 640, 64, 6);
{Dezimalstelle gerundet, nur die ersten 10 benutzt}
  fdhz           : Table[0..15] of LongInt = (0, 6, 13, 19, 26, 32, 38, 45, 51, 58, 0, 6, 13, 19, 26, 32);
  dBadj          : Table[0..63] of byte = (
{$I dBadj.txt}
  );
  offsetAdj = 30; // neg. Offset für Adjust-Berechnung
  symAdj = 0;    // + Fine-Adjust für -6dB SymOut-Einstellung
  dBfac          : Table[0..31] of Word = (
{$I dB775mV.txt}
  );


//LCDXY-Positionen der Menüs und der zugehörigen Cursor-Position auf dem Display
//Reihenfolge wie EditWhich:
//m_fr1, m_fr2, m_fr3, m_fr4, m_fr5, m_fr6, m_fr7, m_frdec,
//m_patch, m_db, m_wav, m_rpt, m_out, m_brst, m_frequ, m_mv
  posx           : Table[0..15] of byte = (0, 2, 4, 6, 8, 10, 12, 14, 7, 9, 0, 5, 8, 13, 4, 4);
  cursx          : Table[0..15] of byte = (1, 3, 5, 7, 9, 11, 13, 15, 9, 13, 3, 7, 11, 15, 0, 0);
  posy           : Table[0..15] of byte = (1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1);

//Lineal     .----.----.----.----.----.----.----.----.----
  Vers1Str                   = 'DDS9833 V#1.12';    //Vers1
  Vers2Str                   = 'C. Meyer 11/05';    //Vers2

  HzStr                      = ' Hz';
  dBStr                      = ' dB';
  SinStr                     = 'Sin';
  SquStr                     = 'Sqr';
  TriStr                     = 'Tri';
  ExtStr                     = 'Ext';

  NrmStr                     = 'Nrm';
  SymStr                     = 'Sym';

  MemStr                     = 'Memory ';     //MemStr
  PresetStr                  = 'Preset ';     //MemStr
  TerzStr                    = 'TerzFr ';     //MemStr
  FrequStr                   = 'Frequency Hz    ';
  OptStr                     = 'Wav Adj Out Brst';

  high                       = true;
  low                        = false;


//Preset: Terzfrequenzen
//20, 25, 31,5, 40, 50, 63, 80, 100, 125, 160, 250, 315, 400, 500,
//630, 800, 1k, 1,25k, 1,6k, 2k, 2,5k, 3,15k, 4k, 5k, 6,3k, 8k, 10k, 12.5k, 16k, 20k
  TerzArray      : Table[0..31] of integer =
  (
  1000,   20,      25,     31,      40,     50,      63,     80,
  100,    125,     160,    200,     250,    315,     400,    500,
  630,    800,     1000,   1250,    1600,   2000,    2500,   3150,
  4000,   5000,    6300,   8000,    10000,  12500,   16000,  20000
  );

//Default-EEPROM-Werte:
{$EEPROM}
structconst

{$IFDEF minEEPROM}
  EEPatchArray   : array[0..63] of Byte =
  (
  0, 0, 0, 1, 0, 0, 0, 0,    //m_frequ
  0, 30, 0, 0, 70, 0, 0, 20,     //Wav m_rpt m_out m_brst m_db - - GainOffs
  0, 0, 0, 1, 0, 0, 0, 1,    //m_frequ
  0, 30, 0, 0, 70, 0, 0, 20,     //Wav m_rpt m_out m_brst m_db - - -
  0, 0, 0, 1, 0, 0, 0, 2,    //m_frequ
  0, 30, 0, 0, 70, 0, 0, 20,     //Wav m_rpt m_out m_brst m_db - - -
  0, 0, 0, 1, 0, 0, 0, 3,    //m_frequ
  0, 30, 0, 0, 70, 0, 0, 20      //Wav m_rpt m_out m_brst m_db - - -
  );
{$ELSE}
{$I mem-70dB.txt}
{$ENDIF}

{--------------------------------------------------------------}

var
{$EEPROM}

{$DATA} {Schnelle Register-Variablen}
  i, k           : Byte;
//  m, DDSL, DDSM, DDSH  : Byte;

{$IDATA}  {Langsamere SRAM-Variablen}

  dss_cmd        : word;
  wave_cmd       : word;
  MP3switchTemp, MP3State, SwitchState: Byte; //Zwischenspeicher für 4094 SR oder PortB miniDDS
  BtnInState     : Byte;  //Sample-Wert der Panel-Tasten

  b_TasteRechts[@BtnInState, 4]: bit; {Bit 0 ist Taste Enter, ganz rechts}
  b_TasteAb[@BtnInState, 6]  : bit; {Bit 6 ist Taste Down}
  b_TasteAuf[@BtnInState, 5] : bit; {Bit 5 ist Taste Up}
  b_TasteLinks[@BtnInState, 3]: bit; {Bit 3 ist Taste Esc, ganz links}
  b_TasteAux[@BtnInState, 7] : bit; {Bit 7 ist Taste Aux, separat}

  b_LEDaux[LEDOutPort, 2]    : bit; {Bit 2 ist LED, separat}

  b_SquareSw[@SwitchState, 0]: bit;
  b_AttnSw[@SwitchState, 1]  : bit;
  b_SymSw[@SwitchState, 2]   : bit;
  b_ExtOnMP3Power[@SwitchState, 3]: bit;

  IncrValue      : integer;   // Vergleichswert für Incremental-Encoder
  IncrVerdreht   : Boolean;
  EditWhich, DispWhich, LastMenu: TEditWhich;
  CurItemNumber,
  DisplayValue, EditValue,
  ItemUpperLimit, ItemLowerLimit, x, y, {x,y Koordinaten LCD}
  GainOffset,                    //0 oder 20 für NormOut/SymOut oder PowerOut/PowerSym
  digits,
  BtnTime, BtnTime2,
  BurstCount     : Byte;
  prime, BurstPrime, Modified, aBool, PresetOn: boolean;

  PresetNum, PatchNum   : byte;


//Edit-Item-Reihenfolge
  EditArray      : Table[0..15] of Byte;
  Edit1[@EditArray]    : byte;

  Frequ1[@Edit1] : byte;      // 1 Mhz
  Frequ2[@Frequ1+1]    : byte;       // 100kHz
  Frequ3[@Frequ1+2]    : byte;
  Frequ4[@Frequ1+3]    : byte;
  Frequ5[@Frequ1+4]    : byte;
  Frequ6[@Frequ1+5]    : byte;
  Frequ7[@Frequ1+6]    : byte;       //1 Hz
  FrequDez[@Frequ1+7]  : byte;     //0.1 Hz

  Wave[@Edit1+8] : byte;
  Adj[@Wave+1]   : byte;
  OutputMode[@Wave+2]  : byte;
  BurstMode[@Wave+3]   : byte;
  LeveldB[@Wave+4]     : byte; //0...7= -60 ... +10dB
  PwrGain[@Wave+7]     : byte; //Nachverstärkung extern Default (kein Edit-Wert!)
  SaveArray      : Table[0..15] of Byte;
  Save1[@SaveArray]    : byte;

  LevelStr       : string[7];
  FreqStr        : String[8];
  BurstTimer     : SysTimer8;

const
//Anpassen bei Änderungen der Reihenfolge: Letzter - Erster Eintrag
  EditArrayLen               = @PwrGain - @EditArray;

{###########################################################################}

//DDS-Funktionen

{$IFNDEF MenuTest}
{$I DDS-HW.pas}
{$ELSE}
procedure SetupDSS;  //Freqenz und Pegel einstellen
begin
end;
procedure SetLevelDDS;  //Sende alle Daten
begin
end;
procedure SendDDS;  //Sende ein Wort an den DDS-Chip
begin
end;
procedure SendSR;  //Sende MP3- und SwitchState an den 4094 oder Ausgangsport
begin
end;
procedure miniDDS;
begin
end;
{$ENDIF}

{###########################################################################}
//EEPROM-Routinen

procedure PresetCopyFromROM;
//Frequenz aus ROM-Terz-Tabelle (Integer!) holen
begin
//Wert in String zwecks Auswertung (raffiniert!)
  FreqStr:= inttostr(gettable(TerzArray, PresetNum) : 7);
  FreqStr[8]:= #48;   //Zehntel
  for i := 0 to 7 do
    k:= byte(FreqStr[i + 1]);
    if k = 32 then  //führende Leerzeichen?
      k := 48;
    endif;
    settable(EditArray, i, k - 48);
  endfor;
end;

procedure PresetSave;
//Sichert aktuelles EditArray bei Wechsel auf Memory
begin
  CopyBlock(@Frequ1, @Save1, EditArrayLen + 1);
end;

procedure PresetRestore;
//Stellt EditArray nach Wechsel von Memory auf Presets wieder her
begin
  CopyBlock(@Save1, @Frequ1, EditArrayLen + 1);
end;

procedure PatchCopyFromEE;
//Frequenz und Settings aus EEPROM holen
var myPatchAdr: Word;
begin
  for i := 0 to EditArrayLen do
    myPatchAdr := word(i) + ((word(PatchNum)) shl 4);  //*16
    settable(EditArray, i, (EEPatchArray[myPatchAdr]));
  endfor;
end;

procedure PatchCopyToEE;
//Frequenz und Settings in EEPROM speichern
var myPatchAdr: Word;
begin
  for i := 0 to EditArrayLen do
    myPatchAdr := word(i) + ((word(PatchNum)) shl 4);  //*16
    EEPatchArray[myPatchAdr]:= gettable(EditArray, i);
  endfor;
end;

{###########################################################################}
//Menü-Prozeduren

procedure LCD1xy;
begin
  LCDxy_M(LCD_m1, x, y);
end;

procedure SetPos;
begin
  i:= ord(DispWhich);
  x:= gettable(posx, i);
  y:= gettable(posy, i);
  LCD1xy;
end;

procedure SetCursor;
begin
  i:= ord(EditWhich);
  x:= gettable(cursx, i);
  y:= gettable(posy, i);
  LCD1xy;
//Cursor-Zeichen "<" ist #0
  LCDout_M(#0);
end;


procedure LCD1x0y0;
begin
  LCDhome_M(LCD_m1);
  mdelay(1); //sonst manchmal Fehlfunktionen
end;

procedure LCD1x0y1;
begin
  x:= 0;
  y:= 1;
  LCD1xy;
end;

{$I buttons.pas}

procedure SetIncrValToEditValue;
begin
{$IFDEF bournsincr}
  SetIncrVal4(0, integer(EditValue) shl 2); // pro Raste vier Zählerpunkte
{$ELSE}
  SetIncrVal4(0, integer(EditValue) shl 1); // pro Raste zwei Zählerpunkte
{$ENDIF}
end;

procedure CheckItemLimits;
//EditValue eingrenzen
begin
  if EditValue = $ff then
    EditValue := 0;
  endif;
  if EditValue > ItemUpperLimit then
    EditValue := ItemUpperLimit;
    SetIncrValToEditValue; //ggf. Maximalwert neu setzen
  endif;
  if EditValue < ItemlowerLimit then
    EditValue := ItemlowerLimit;
    SetIncrValToEditValue; //ggf. Minimalwert neu setzen
  endif;
end;


procedure WriteItem;
//einzelnen Wert DisplayValue darstellen
begin
//Zahlenausgabe ein- bis zweistellig, ggf. mit führender Null:
//Äquivalent zu write(lcdout, ByteToStr(DisplayValue:digits));
  if digits > 1 then
    k:= DisplayValue mod 100;
    k:= (k div 10);
    LCDout_M(char(k + 48));
  endif;
  k:= DisplayValue mod 10;
  LCDout_M(char(k + 48));
end;


procedure WriteWave;
//Wellenform ausschreiben
begin
  DispWhich:= m_wav;
  setPos;
  case wave of
    c_sinw :
             WriteLCD(@SinStr);
           |
    c_triw :
             WriteLCD(@TriStr);
           |
    c_squw :
             WriteLCD(@SquStr);
           |
    c_audio :
             WriteLCD(@ExtStr);
           |
  endcase;
end;

procedure WriteOutputMode1;
//OutputMode ausschreiben: Normal, Symmetrisch, PowerAmp, SymPowerAmp
begin
  case OutputMode of
    c_nrmo  :
              WriteLCD(@NrmStr);
            |
    c_symo  :
              WriteLCD(@SymStr);
            |
  endcase;
end;

procedure WriteOutputMode;
//OutputMode ausschreiben: Normal, Symmetrisch, PowerAmp, SymPowerAmp
begin
  DispWhich:= m_out;
  setPos;
  WriteOutputMode1;
end;

procedure ShowFrequSpaced;
//Frequenz-Array (Teil von EditArray) mit Leerzeichen
//zwischen einzelnen Ziffern darstellen
begin
  LCD1x0y0;
  WriteLCD(@FrequStr);
  LCD1x0y1;
  for i:= 0 to 7 do
    k:= gettable(EditArray, i);
    LCDout_M(char(k + 48));
    if i = 6 then
      LCDout_M(#46); //Dezimalpunkt nach 7. Stelle
    else
      LCDout_M(#32);
    endif;
  endfor;
end;


procedure WriteFrequency;
//Frequenz-Array (Teil von EditArray) darstellen
var blank: boolean;
begin
  DispWhich:= m_frequ;
  setpos;
  blank := true;
  for i:= 0 to 6 do
    DisplayValue:= gettable(EditArray, i);
    if DisplayValue <> 0 then
      blank:= false;
    endif;
    if (DisplayValue = 0) and blank then
      LCDout_M(#32);
    else
      LCDout_M(char(DisplayValue + 48));
    endif;
  endfor;
  LCDout_M(#46); //Dezimalpunkt nach 7. Stelle
  DisplayValue:= FrequDez;
  LCDout_M(char(DisplayValue + 48));
  WriteLCD(@HzStr);
end;

procedure WriteLevelmV;
//m_db-Wert in LeveldB in mV umrechnen (nach mV-Tabelle) und darstellen
var blank: boolean;
  dBtab          : byte;
  Prefix         : char;
{mV aus m_db ausrechnen}
begin
  DispWhich:= m_mv;
  SetPos;

  dBtab := LeveldB + GainOffset + 10; // für AD9833-DDS mit LM1972

  i:= dBtab mod 20;
  k:= ((dBtab + 10) div 20) + 2;  //Komma-Position ermitteln
  Prefix:= 'm';   // für mV
  LevelStr:= inttostr(gettable(dBfac, i) : 6);

  //hier: 10...110 = -70 ... +30dB
  //eigener Regelbereich 0...80 = -70 ... +10dB
  case dBtab of
    10..29  :      //-70..-51
              k:= k + 3;
              insert('.', LevelStr, k);
              Prefix:= #228;   // für uV
            |
    30..89  :     //-50..+9
              insert('.', LevelStr, k);
            |
    90..109 :     //+10..+29
              insert('.', LevelStr, 4);
              Prefix:= #32;   //kein Prefix
            |
    110     :     //+30
              insert('.', LevelStr, 5);
              Prefix:= #32;   //kein Prefix
            |
  endcase;
  LCDout_M(#32);   //Leerzeichen
  LCDout_M(#32);   //Leerzeichen
  write(lcdout_m, LevelStr);
  LCDout_M(#32);   //Leerzeichen
  LCDout_M(Prefix);
  LCDout_M(#86);
end;

procedure WriteLeveldB;
//m_db-Wert LeveldB ausschreiben
var dBval: Integer;
begin
  DispWhich:= m_db;
  SetPos;
  //0...100 = -70 ... +30dB
  //eigener Regelbereich 0...80 = -70 ... +10dB
  dBval:= integer(LeveldB) - 70 + integer(GainOffset);
  write(lcdout_m, IntToStr(dBval : 4));
  WriteLCD(@dBStr);
end;



procedure ShowOptionsLower;
//Alle Optionen (Teil des EditArray, vier Werte) darstellen
begin
  WriteWave;
  LCDout_M(#32);   //Leerzeichen
  DispWhich:= m_rpt;
  setPos;
  DisplayValue:= Adj;
  WriteItem;
  LCDout_M(#32);   //Leerzeichen
  WriteOutputMode;
  LCDout_M(#32);   //Leerzeichen
  LCDout_M(#32);   //Leerzeichen
  DispWhich:= m_brst;
  setPos;
  DisplayValue:= BurstMode;
  digits:=1;
  WriteItem;
  LCDout_M(#48);   //Null
  LCDout_M(#32);   //Leerzeichen
  digits:=2;
end;

procedure ShowOptions;
//Überschrift und alle Optionen darstellen
begin
  LCD1x0y0;
  WriteLCD(@OptStr);
  ShowOptionsLower;
end;

procedure ShowDefaultUpper;
//m_patch/Preset-Nummer und Pegel ausschreiben
begin
  DispWhich:= m_patch;
  digits:= 2;
  if PresetOn Then
    DisplayValue:= PresetNum;
    LCD1x0y0;
    if PresetNum = 0 then
      WriteLCD(@PresetStr);
    else
      WriteLCD(@TerzStr);
    endif;
    WriteItem;
  else
    LCD1x0y0;
    WriteLCD(@MemStr);
    DisplayValue:= PatchNum;
    WriteItem;
  endif;
  LCDout_M(#32);
  WriteLeveldB;
  SetCursor;
end;

procedure ShowDefaultLower;
//Darstellung der unteren Zeile Hauptmenü: Frequenz, Pegel in mV oder Spielzeit
begin
  LCD1x0y1;
  LCDclrEOL_M(LCD_m1);
  if EditWhich = m_db then
    WriteOutputMode1;
    WriteLevelmV;
  else
    writewave;
    WriteFrequency;
  endif;
end;

procedure ShowDefault;
//komplettes Hauptmenü anzeigen
begin
  ShowDefaultUpper;
  ShowDefaultLower;
end;


procedure BurstGen;
//Burst-Generierung per Software hier, während auf Tastatureingabe gewartet wird
begin
  if BurstMode <> 0 then
    if isSysTimerZero(BurstTimer) then
      setSystimer(BurstTimer, 1);
      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 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}

  if LCDsetup_M(LCD_m1) then
    LCDcursor_M(LCD_m1, false, false);
    LCDCharSet_M(LCD_m1, #0, $02, $06, $0E, $1E, $0E, $06, $02, $00); {"<" Cursor}
  endif;
  LCDclr_M(LCD_m1);

  WriteLCD(@Vers1Str);
  LCD1x0y1;
  WriteLCD(@Vers2Str);
  mdelay(1000);

  PatchNum:= 0;
  PatchCopyFromEE;       // Optionen-Defaults
  PresetNum:= 0;

  BtnTime:= 40; // Initial timer value
  BtnTime2:= 10; // Anzahl Durchläufe bis fast forward

  GainOffset:= 0;
  PresetOn:= true;
  LCDclr_M(LCD_m1);
  Modified:= false;
  EnableInts;
  prime:= false;
  BurstCount:= 1;
  setSystimer(BurstTimer, 1);
  SetLevelDDS;
  IncrCount4start;
  ClearIncrAll4;
end;

{###########################################################################}

begin
  Initall;
  EditWhich:= m_patch;

  ShowDefaultUpper;
//  SetupMP3;
  ShowDefaultLower;
//Hauptschleife, wird bei jedem Druck eines Aux- oder Navi-Buttons durchlaufen
//sonst innerhalb der Up-Down-Button-Schleife
  loop
    SetLevelDDS;
    CurItemNumber:= ord(EditWhich);
    if EditWhich > m_patch then    //m_patch, m_db überspringen
      CurItemNumber:= CurItemNumber - 2;
    endif;
    EditValue:= gettable(EditArray, CurItemNumber);
    ItemLowerLimit:= 0;
    ItemUpperLimit:= 9;
    digits:= 2;
    case EditWhich of
      m_patch        :
                       ItemUpperLimit:= 31;
                       BtnTime:= 40; //kurz stehenbleiben bei Autorepeat
                       if PresetOn then
                         EditValue:= PresetNum;
                       else
                         EditValue:= PatchNum;
                       endif;
                       ShowDefault;
                     |
      m_dB           :
                       ItemUpperLimit:= 80;   // 80dB eigener Regelbereich
                       EditValue:= LeveldB;
                       Showdefault;
                     |
      m_fr1..m_frdec :
                       digits:= 1;
                       ShowFrequSpaced;
                     |
      m_wav          :
                       ItemUpperLimit:= 3;
                       ShowOptions;
                     |

      m_rpt          :
                       ItemLowerLimit:= 0;
                       ItemUpperLimit:= 99;
                       ShowOptions;
                     |
      m_out          :
                       ItemUpperLimit:= 1;
                       ShowOptions;
                     |
      m_brst         :
                       LCD1x0y0;
                       ShowOptions;
                     |
    endcase;
    SetCursor;
    SetIncrValToEditValue;
    LastMenu:= m_lastmenu;
    SlowRepeatRelease; // auf Loslassen oder Autorepeat warten
    Modified:= false;
//Up-Down-Unterschleife
    repeat
{$IFDEF bournsincr}
      IncrValue:= GetIncrVal4(0) div 4;  // pro Raste vier Zählerpunkte
{$ELSE}
      IncrValue:= GetIncrVal4(0) div 2;  // pro Raste zwei Zählerpunkte
{$ENDIF}
      if IncrValue < 0 then // negative Werte wg. Byte gar nicht erst zulassen
        IncrValue:= 0;
        SetIncrVal4(0, 0);
      endif;
      IncrVerdreht:= (byte(IncrValue) <> EditValue);
      if UpDownbutton or IncrVerdreht then
        if not b_TasteAuf then
          inc(EditValue);
          SetIncrValToEditValue;
        endif;
        if not b_TasteAb then
          dectolim(EditValue,0);
          SetIncrValToEditValue;
        endif;
        if IncrVerdreht then
          EditValue:= byte(IncrValue);
        endif;
        Modified:= true;
        CheckItemLimits;
        DispWhich:= EditWhich;
        SetPos;
        case EditWhich of
          m_patch        :
                           if PresetOn then
                             PresetNum:= EditValue;
                             PresetCopyFromROM;
                           else
                             PatchNum:= EditValue;
                             PatchCopyFromEE;
                           endif;
                           ShowDefault;
//                           SetupMP3;
                           Modified:= false;    //hier wird kein Wert verändert
                         |
          m_db           :
                           LeveldB:= EditValue;
                           ShowDefault;
                         |
          m_fr1..m_frdec :
                           // erste acht Bytes im EditArray sind Frequ.
                           settable(EditArray, byte(EditWhich), EditValue);
                           DisplayValue:= EditValue;
                           WriteItem;
                         |
          m_wav          :
                           Wave:= byte(EditValue);
                           WriteWave;
//                           SetupMP3;
                         |
          m_rpt          :
                           Adj:=  EditValue;
                           DisplayValue:= EditValue;
                           WriteItem;
                         |
          m_out          :
                           OutputMode:= byte(EditValue);
                           WriteOutputMode;
                         |
          m_brst         :
                           BurstMode:=  EditValue;
                           DisplayValue:= EditValue;
                           digits:= 1;
                           WriteItem;
                         |
        endcase;
        SetLevelDDS;
        FastRepeatRelease;
      endif;
//      MP3checkRepeat;
      BurstGen;
      if Modified then
        {neuen Patch senden}
        if not PresetOn then
          PatchCopyToEE;
        endif;
        if not UpDownbutton then
//          SetupMP3;
        endif;
      endif;
    until NaviButton;

    if not b_TasteLinks then
      if EditWhich = m_patch then
        EditWhich:= m_frdec;
      else
        EditWhich:= pred(EditWhich);
      endif;
    endif;
    if not b_TasteRechts then
      inc(EditWhich);
//      if MP3isOn then
//        LastMenu:= m_wav;
//      endif;
      if EditWhich > LastMenu then
        EditWhich:= LastMenu;
      endif;
    endif;
    if not b_TasteAux then
      if PresetOn then
        PresetOn:= false;
        b_LEDaux := low;
        PresetSave;
        PatchCopyFromEE;
      else
        PresetOn:= true;
        b_LEDaux := high;
        PresetRestore;
      endif;
    endif;
  endloop;
end SoftDDS.

