الثلاثاء، 26 يناير 2021

توليد الموجة بالاردوينو

 استخدم Arduino وشاشة OLED ووحدة AD9833 DDS لإنشاء مولد موجات مفيد لمختبرك.


الأشياء المستخدمة في هذا المشروع

اردوينو ناونو 

شاشة    Display SH1106 I2C Oled 128x64 pixel (4 pin)

AD9833 DDS Module

مقاومة متغيرة

ريلي 5 فولت

مكثف 10 µF

مكثف   Polyester capacitor  10 nF   


 قصة

لقد تأجلت لفترة طويلة شراء راسم الذبذبات. الآن ، بعد شرائه ، حان الوقت أيضًا للحصول على مولد إشارة غير مكلف للاستخدام في هواية ، وهو ضروري لتشخيص الدوائر الصوتية والمزيد. من خلال الجمع بين شغفي الإلكترونيات والحوسبة ، فإن أفضل شيء هو القيام بذلك بنفسك باستخدام Arduino.


المميزات

تردد الخرج بين 1 هرتز و 999999 هرتز

وضعان لتغير التردد: لوغاريتمي ورقم واحد

ثلاثة أنواع من الموجة والجيب والمثلث والمربعة

AC أو DC اقتران الإخراج

القدرة على الكنس باستمرار بين قيمتين ترددات محددة مسبقًا

يتم التحكم فيها حصريًا بمقبض واحد

شاشة توقف مدمجة للحفاظ على عمر شاشة oled





ملحوظة
سعة الخرج من وحدة DDS ليست عالية جدًا ، في المتوسط بين 550 و 650 مللي فولت من الذروة إلى الذروة للأشكال الموجية الجيبية والمثلثية ، أعلى بكثير للأشكال المربعة ، حوالي 4.5 فولت من الذروة إلى الذروة. بالطبع هذا يميل إلى الانخفاض متناسبًا عكسياً مع التردد ولكن يصل إلى 1 ميجا هرتز فهو خطي تمامًا. وحدة AD9833 DDS قادرة على توليد إشارات تصل إلى 12 ميجاهرتز والتي ليست ضرورية بالنسبة لي. إذا كنت بحاجة إلى تجاوز 1 ميغا هرتز ، فيمكنك محاولة التجربة وإضافة رقم أو رقمين إلى الشاشة ، ولكن هذا ينطوي على إعادة تصميم جزئي للواجهة الرسومية.
تعتمد جودة إشارة الخرج أيضًا على جودة مصدر الطاقة ، لذا سيكون من الأفضل استخدام مصدر طاقة خطي بدلاً من مصدر تبديل كما فعلت لتقليل حجم الحالة التي استردتها من مشروع قديم. ولكن حتى مع وحدة التحويل ، فإن خطية أشكال الموجة جيدة جدًا.
مراجع سريعة



النموذج المبدئي


استخدم وحدة ترحيل مُجمَّعة مسبقًا بها جهات اتصال 10 أمبير ، لأنه في الوقت الحالي لم يكن لدي مرحل صغير من القصب ، سيكون هذا هو الخيار الأفضل لتجنب ثنائيات الترانزستور والمقاومة ، عندما لا تكون هناك حاجة لمرحلات الطاقة. يمكن أن يوفر رقم التعريف الشخصي الرقمي من Arduino 40 مللي أمبير كحد أقصى لذلك لا يمكنك توصيل مرحل كهروميكانيكي بملف 120/150 أوم مباشرة.

كن حذرًا جدًا دائمًا عند استخدام الجهد الكهربائي!

خطر التعرض لصدمة كهربائية و / أو تلف الجلد والعينين.

 



#include "JXWG_Defs.h"
#include "JXWG_Graphics.h"

void setup()   {

  //if you are using a simple encoder and 3x10K pullup resistors, apply this settings below
  pinMode(PinA, INPUT);
  pinMode(PinB, INPUT);
  pinMode(PinS, INPUT);

  //if you are using a simple encoder whitout 3x10K resistors use next tree row
  //pinMode(PinA, INPUT_PULLUP);
  //pinMode(PinB, INPUT_PULLUP);
  //pinMode(PinS, INPUT_PULLUP);

  //MOST PCB WELDED ENCODERS ALREADY HAVE PULLUP RESISTORS ON PIN A AND B BUT NOT ON SWITCH PIN
  //then use the settings below
  //pinMode(PinA, INPUT);
  //pinMode(PinB, INPUT);
  //pinMode(PinS, INPUT_PULLUP);

  digitalWrite(PinA, HIGH);
  digitalWrite(PinB, HIGH);
  digitalWrite(PinS, HIGH);

  pinMode(PinCoupling, OUTPUT);                  //Coupling Mode

  Encoder.setDebounceDelay(5);

  display.begin(SH1106_SWITCHCAPVCC, 0x3C);     //initialize with the I2C addr 0x3C (for the 128x64)
  display.clearDisplay();
  //display.setRotation(2);                     //uncomment this line if you want to mount the display upside down

  Wire.begin();                                 // join i2c bus as master
  TWBR = 5;                                     // freq=615kHz period=1.625uS

  //Assigns encoder switch push event to interrupt 1 Pin 3 of Arduino
  attachInterrupt(digitalPinToInterrupt(PinS), encoderSwitch, FALLING);

  DDS_Init();                                   //Initialize the DDS module;
  setConfig();                                  //Load config and set startup values

} //---> end setup()

void loop() {
  JX_WaveGenerator_MAIN();
}

//-----------------------------------------------------------------------------------------------------------------------------------------
// JX WaveGenerator MAIN function
//-----------------------------------------------------------------------------------------------------------------------------------------
void JX_WaveGenerator_MAIN() {

  byte encoderSpin = Encoder.rotate();                                             //Encoder rotation direction 1=CW, 2=CCW
  byte encoderLongPush = Encoder.pushLong(1000);                                   //encoder long push event
  long lStep = 0;                                                                  //current frequency step value
  long wTime = 600000; //10 min

  if (encoderPush) delay(250);

  if (encoderSpin) {
    cTime = millis();
  } else {
    if (millis() - cTime > wTime) {
      ScreenSaver();
    }
  }

  switch (mode) {

    case LOGARITHMIC: //0
      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode LOGARITHMIC: Encoder rotation change frequency in logaritmic step 1,10,100,1000,10000,100000 Hz
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {

        if (lFreq >= 1) {
          lStep = AutoStep(lFreq, encoderSpin);                             //Calculate logaritmic step
        } else if (_CouplingMode == OFF) {                                  //if coupling mode is set to OFF
          resetCouplingMode();                                              //set default coupling mode when Frequency is not 0
          encoderSpin = 0;                                                  //skip first spin
        }

        if (encoderSpin == CW && lFreq <= 999999 - lStep) {                 //spin CW increment the frequency
          lFreq += lStep;
        }
        if (encoderSpin == CCW && lFreq >= lStep + 1) {                     //spin CCW decrement the frequency
          lFreq -= lStep;
        }

        DDS_FrequencySet(lFreq, Wave[_WaveType]);                           //send the frequency value to DDS module
        displayFrequency(lFreq);                                            //send formatted freq to display
        lLastFreq = lFreq;                                                  //save current freq
      }

      //-----------------------------------------------------------------------------------------------------------------------------------
      // workmode LOGARITHMIC: Encoder push switch to OPTIONS mode
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;                                                //Clear push flag
        drawSymbol(1);                                                      //draw arrow symbol
        selectIcon(0, WHITE);                                               //draw a border around first icon
        idx = 0; idy = 0;                                                   //reset pointers var
        mode = OPTIONS;                                                     //go to mode OPTIONS
      }

      break; //end mode LOGARITHMIC

    case SINGLEDIGIT: //1
      //-------------------------------------------------------------------------------------------------------------
      // submode SINGLEDIGIT: Encoder rotation move cursor left and right
      //-------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idx < MAXDIGIT + 2) idx++;            //clockwise increase pointer
        if (encoderSpin == CCW && idx > 0) idx--;                      //counterclockwise decrease pointer

        //------------------------------------------------------------------------------------------------------------
        // when idx is from 0 to 5 select frequency digits
        //------------------------------------------------------------------------------------------------------------
        if (idx >= 0 && idx < MAXDIGIT) {                               //if the current position is within the digits
          drawSymbol(0);                                                //draw up arrow
          selectDigit(idx);                                             //show cursor at current position and delete previous
          if (idx == 5) selectIcon(0, BLACK);                           //hide first icon selection
        }

        //------------------------------------------------------------------------------------------------------------
        // when idx is from 6 to 8 select icons
        //------------------------------------------------------------------------------------------------------------
        if (idx >= MAXDIGIT && idx <= MAXDIGIT + 2) {                   //if the current position is beyond the digits
          hideCursor(MAXDIGIT - 1);                                     //hide cursor at last digit
          drawSymbol(1);                                                //draw dn arrow
          selectIcon(idx - MAXDIGIT, WHITE);                            //select icon
        }
      }

      //-----------------------------------------------------------------------------------------------------------------------------------
      // submode SINGLEDIGIT: Encoder push event
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;

        //-------------------------------------------------------------------------------------------------------------
        // if a digit from 0 to 5 are selected go to mode DIGITCHANGE
        //-------------------------------------------------------------------------------------------------------------
        if (idx <= MAXDIGIT - 1) {
          hideCursor(idx);                                                              //flash cursor
          delay(250);                                                                   //for
          selectDigit(idx);                                                             //visual confirmation
          drawSymbol(2);                                                                //draw turn icon
          mode = DIGITCHANGE;                                                           //change mode
        }
        //-------------------------------------------------------------------------------------------------------------
        // otherwise there is an icon selected then go to OPTIONS
        //-------------------------------------------------------------------------------------------------------------
        else {
          if (idx >= MAXDIGIT && idx <= MAXDIGIT + 2) {
            idy = idx - MAXDIGIT;
            selectOption(idy);
            idy = options[idy];
          }
        }
      }
      break; //end mode SINGLEDIGIT


    case SWEEP://2
      //----------------------------------------------------------------------------------------------------------------
      // workmode SWEEP: Encoder rotation move cursor left and right for option selection
      //----------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      if (encoderPush) {
        //-----------------------------------------------------------------------------------------------------------------------------------
        // workmode SWEEP: Encoder push go to OPTIONS, SWEEP OPTIONS or START/STOP sweep
        //-----------------------------------------------------------------------------------------------------------------------------------
        encoderPush = false;

        switch (idy) {
          case 0:
            lFreq = atol(Freq);
            selectOption(idy);
            idy = options[idy];
            delay(100);
            if (_WorkMode != 2) displayFrequency(lLastFreq);
            SweepReset();
            break;

          case 1:
            lFreq = atol(Freq);
            drawSymbol(9);
            drawSymbol(1);
            displaySweepIcons();
            selectIcon(0, WHITE);
            idy = 0;
            displayFrequency(_Sweep(idy));
            delay(100);
            SweepReset();
            mode = OPTSWEEP;
            break;

          case 2:
            //** sweepStatus: STILL 0 (never started), 1 BREAK, 2 PAUSE **
            if (sweepStatus == STILL || sweepStatus == PAUSE) {
              drawSymbol(3);                            //draw pause icon
              selectIcon(2, WHITE);                     //select icon
              FrequencySweep();                         //run sweep
            }
            else {                                      //if paused
              drawSymbol(4);                            //draw play icon
              sweepStatus = PAUSE;
            }
            break;
        }
      }
      if (sweepStatus == PAUSE) flashIcon(250);           //flashing pause text
      break; //end mode SWEEP

    case OPTIONS://3
      if (encoderLongPush) reset();

      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode OPTIONS: Encoder spin select option to change (workmode, wavetype, couplingmode)
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode OPTIONS: Encoder push switch to relative mode
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;
        //selectIcon(idy, BLACK);
        selectOption(idy);
        hideCursor(0);
        idy = options[idy];
      }
      break; //end mode OPTIONS


    case OPTMODE://4
      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode OPTMODE: Encoder spin select mode icons (logarithmic, singledigit, sweep)
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      //-----------------------------------------------------------------------------------------------------------------------------------
      // mode OPTMODE: Encoder push select workmode to set (stored in option[0])
      //-----------------------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;
        byte pMode = _WorkMode;
        switch (idy) {
          case 0:
            hideCursor(0);
            drawSymbol(0);
            lFreq = lLastFreq;
            displayFrequency(lFreq);
            _setWorkMode(LOGARITHMIC);
            break;

          case 1:
            drawSymbol(0);
            selectDigit(0);
            lFreq = lLastFreq;
            displayFrequency(lFreq);
            _setWorkMode(SINGLEDIGIT);
            break;

          case 2:
            lLastFreq = lFreq;
            displayFrequency(_SweepMin); //ready to start
            _setWorkMode(SWEEP);
            break;
        }
        mode = _WorkMode;
        if (pMode != _WorkMode) saveConfig();
        if (_CouplingMode == OFF) resetCouplingMode();
        idx = 0;
        drawAllIcons();
      }
      break; //end mode OPTMODE


    case OPTWAVE://5
      //----------------------------------------------------------------------------------------------------------------
      // mode OPTWAVE: Encoder rotation move cursor left and right for wave selection (sqr, sin, tri)
      //----------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      //--------------------------------------------------------------------------------------------------------------------
      // mode OPTWAVE: Encoder push set new wave type
      //--------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;
        if (_WaveType != idy) {
          _setWaveType(idy);
          saveConfig();
        }
        UpdateFrequency(); //updates wavetype
        drawAllIcons();
        idx = 0;
        mode = _WorkMode;

      }
      break; //end mode OPTWAVE


    case OPTCOUP://6
      //----------------------------------------------------------------------------------------------------------------
      // mode OPTCOUP: Encoder rotation move cursor left and right for coupling mode selection
      //----------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;
        if (encoderSpin == CCW && idy > 0) idy--;
        selectIcon(idy, WHITE);
      }

      //--------------------------------------------------------------------------------------------------------------------
      // mode OPTCOUP: Encoder push select current coupling mode
      //--------------------------------------------------------------------------------------------------------------------
      if (encoderPush) {
        encoderPush = false;
        setCouplingMode(idy);
        drawAllIcons();
        idx = 0;
        mode = _WorkMode;
      }
      break; //end mode OPTCOUP



    case OPTSWEEP://7
      //--------------------------------------------------------------------------------------------------------------------
      // mode OPTSWEEP: Encoder spin select sweep values to edit
      //--------------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idy < 2) idy++;                                          //clockwise increase pointer
        if (encoderSpin == CCW && idy > 0) idy--;                                         //counterclockwise decrease pointer
        selectIcon(idy, WHITE);                                                           //select first icon
        displayFrequency(_Sweep(idy));                                                    //display current sweep value
      }


      if (encoderPush) {
        encoderPush = false;
        //--------------------------------------------------------------------------------------------------------------------
        // mode OPTSWEEP: Encoder push confirm sweep values to edit
        //--------------------------------------------------------------------------------------------------------------------
        drawSymbol(0);
        selectDigit(0);
        selectIcon(idy, WHITE);
        idx = 0;
        displayFrequency(_Sweep(idy));
        mode = SWEEPEDIT;
      }
      break; //end mode OPTSWEEP


    case SWEEPEDIT://8

      //--------------------------------------------------------------------------------------------------------------
      // mode SWEEPEDIT: Encoder spin select digit to change
      //--------------------------------------------------------------------------------------------------------------
      if (encoderSpin) {
        if (encoderSpin == CW && idx < MAXDIGIT - 1) idx++;                        //clockwise increase pointer
        if (encoderSpin == CCW && idx > 0) idx--;                                  //counterclockwise decrease pointer
        selectDigit(idx);
      }

      //--------------------------------------------------------------------------------------------------------------
      // mode SWEEPEDIT: Encoder longpush exit from edit and return to SWEEP
      //--------------------------------------------------------------------------------------------------------------
      if (encoderLongPush == LONGPUSH) {
        encoderPush = false;
        drawAllIcons();
        displayFrequency(_SweepMin);
        hideCursor(idx);
        _setWorkMode(SWEEP);
        SweepReset();
        mode = _WorkMode;
        delay(250);
      }

      if (encoderPush) {
        encoderPush = false;
        //-------------------------------------------------------------------------------------------------------------
        // mode SWEEPEDIT: Encoder push go to mode DIGITCHANGE
        //-------------------------------------------------------------------------------------------------------------
        hideCursor(idx);                                                              //flash cursor
        delay(250);                                                                   //for
        selectDigit(idx);                                                             //visual confirmation
        drawSymbol(2);                                                                //draw turn icon
        mode = DIGITCHANGE;                                                           //change mode
      }
      break;//end mode SWEEPEDIT

    case DIGITCHANGE://9
      //---------------------------------------------------------------------------------------------------------
      // mode DIGITCHANGE: Encoder longpush exit from DIGITCHANGE when in SWEEP mode
      //---------------------------------------------------------------------------------------------------------
      if (encoderLongPush == LONGPUSH && _WorkMode == SWEEP) {
        encoderPush = false;
        hideCursor(idx);
        drawSymbol(1);        
        mode = OPTSWEEP;
        delay(250);
      }

      //---------------------------------------------------------------------------------------------------------
      // mode DIGITCHANGE: Encoder rotation change digit value (0 -> 9 ->0 and so on)
      //---------------------------------------------------------------------------------------------------------
      if (encoderSpin) {                                                  //encoder rotation
        if (encoderSpin == CW) {                                          //clockwise direction
          Freq[idx]++; if (Freq[idx] > '9') Freq[idx] = '0';
        } else {                                                          //counter clockwise direction
          Freq[idx]--; if (Freq[idx] < '0') Freq[idx] = '9';
        }
        updateDigit(idx, Freq[idx]);                                      //update digit on display
      }

      //---------------------------------------------------------------------------------------------------------
      // mode DIGITCHANGE: Encoder push return to mode SINGLEDIGIT or SWEEPEDIT
      //---------------------------------------------------------------------------------------------------------
      if (encoderPush) {                                                  //encoder push flag set by interrupt
        encoderPush = false;                                              //reset event flag
        hideCursor(idx);                                                  //flash cursor
        delay(250);                                                       //for
        selectDigit(idx);                                                 //visual confirmation
        drawSymbol(0);

        if (_WorkMode == SWEEP) {
          long ltemp = _Sweep(idy) ;                                       //save value
          _setSweep(idy, atol(Freq));                                      //convert new value from array to long
          if (_SweepMax > 0 && _SweepMax > _SweepMin && _SweepStep > 0) {  //check congruency of the new sweep value
            if (_Sweep(idy) != ltemp) saveConfig();                        //if value has changed write new value in EEPROM
            displayFrequency(_Sweep(idy));
            mode = SWEEPEDIT;                                              //change mode
          } else {            
            _displayErrMsg;                                                //display error message stored in flash mem
            delay(1000);
            _setSweep(idy, ltemp);                                         //restore saved value
            displayFrequency(_Sweep(idy));                                 //redisplay value
            drawSymbol(2);                                                 //redraw turn icon
          }

        } else {                                                           //if not in sweep mode
          if (_CouplingMode == OFF) {                                      //if coupling mode if OFF
            lLastFreq = atol(Freq);                                        //save current frequency
            resetCouplingMode();                                           //set default coupling mode
          }
          UpdateFrequency();                                               //send frequency to DDS Module
          mode = SINGLEDIGIT;                                              //change mode
        }
      }
      break; //end mode DIGITCHANGE

    default:
      break;
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------
// Push encoder event - Called by interrupt
//-----------------------------------------------------------------------------------------------------------------------------------
void encoderSwitch(void) {
  encoderPush = true;
}

//-----------------------------------------------------------------------------------------------------------------------------------
// Utility functions
//-----------------------------------------------------------------------------------------------------------------------------------
// Draw graphics interface
//----------------------------------------------------------------------------------------------------------------
void drawInterface() {

  display.clearDisplay();
  display.display();
  delay(1000);

  display.drawRoundRect(0, 0, 128, 64, 3, WHITE);                                       //draw external frame
  display.fillRect(1, 1, 126, 14, WHITE);                                               //draw caption frame

  displayText(12, 4, strFromFlash(0), BLACK, WHITE, SMALL);                             //print caption title
  delay(1000);

  if (cTime == 1) {                                                                     //only on power on 
    displayText(XPOS - 6, YPOS + 10, strFromFlash(1), WHITE, BLACK, BIG);               //show Welcom message
    delay(1000);
    display.fillRect(2, 16, display.width() - 3, 35, BLACK);                            //clear Welcome message
    cTime = 0;
  }

  display.display();

  displayText(XPOS + 84, YPOS + 4, strFromFlash(2), WHITE, BLACK, SMALL);              //print "Hz"

  sprintf(Freq, "%06li", lFreq);                                                       //put frequency value into char array with template "000000"
  for (int i = MAXDIGIT - 1; i >= 0; i--) {
    display.drawChar(XPOS + 2 + i * DELTAX, YPOS, Freq[i] , WHITE, BLACK, BIG);        //Display with animation effect from right to left
    display.display();
  }

}//end drawInterface()


//----------------------------------------------------------------------------------------------------------------
// Print string in x,y pos with specified colors and size
//----------------------------------------------------------------------------------------------------------------
void displayText(byte x, byte y, const char *str, byte foreColor, byte backColor, byte textSize) {
  display.setTextSize(textSize);                                  //textsize: SMALL or BIG global const
  display.setTextColor(foreColor, backColor);                     //colors WHITE or BLACK global const of the library
  display.setCursor(x, y);                                        //set the cursor position
  display.print(str);                                             //str is the pointer to the string of chars
  display.display();                                              //update display
}

//----------------------------------------------------------------------------------------------------------------
// Copies element [i] of the string_table array from flash memory to the ram buffer and returns the pointer to the buffer
//----------------------------------------------------------------------------------------------------------------
char* strFromFlash(byte i) {
  strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i])));
  return (char*)buffer;
}

//----------------------------------------------------------------------------------------------------------------
// Draw or clear a border around selected icon after clearing border of the previous one
//----------------------------------------------------------------------------------------------------------------
void selectIcon(byte icon, byte color) {
  static byte prevIcon;
  display.drawRect(XPOS - 10 + prevIcon * 32, YPOS + 19, 29, 20, BLACK);
  display.drawRect(XPOS - 10 + icon * 32, YPOS + 19, 29, 20, color);
  display.display();
  prevIcon = icon;
}

//----------------------------------------------------------------------------------------------------------------
// Display all workmode icons
//----------------------------------------------------------------------------------------------------------------
void displayWorkModeIcons(void) {
  byte const *bitmap[3] = {imgLog, imgDigit, imgSweep};
  _clearIconsArea;
  for (byte i = 0; i <= 2; i++) {
    display.drawBitmap(XPOS -  8 + i * 32, YPOS + 21, bitmap[i],  25, 16, WHITE);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Display all wavetype icons
//----------------------------------------------------------------------------------------------------------------
void displayWaveTypeIcons(void) {
  byte const *bitmap[3] = {imgSqr, imgSin, imgTri};
  _clearIconsArea;
  for (byte i = 0; i <= 2; i++) {
    display.drawBitmap(XPOS -  8 + i * 32, YPOS + 21, bitmap[i],  25, 16, WHITE);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Display all coupling mode icons
//----------------------------------------------------------------------------------------------------------------
void displayCouplingModeIcons(void) {
  byte const *bitmap[3] = {imgCoAc, imgCoDc, imgCoOff};
  _clearIconsArea;
  for (byte i = 0; i <= 2; i++) {
    display.drawBitmap(XPOS -  8 + i * 32, YPOS + 21, bitmap[i],  25, 16, WHITE);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Display all sweep icons
//----------------------------------------------------------------------------------------------------------------
void displaySweepIcons(void) {
  byte const *bitmap[3] = {imgSwMax, imgSwMin, imgSwStep};
  _clearIconsArea;
  for (byte i = 0; i <= 2; i++) {
    display.drawBitmap(XPOS -  8 + i * 32, YPOS + 21, bitmap[i],  25, 16, WHITE);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Draw all icons
//----------------------------------------------------------------------------------------------------------------
void drawAllIcons(void) {
  _clearIconsArea;
  drawModeIcon();

  if (_WorkMode == SWEEP || _WorkMode == SWEEPEDIT ) {
    display.drawBitmap(XPOS + 24, YPOS + 21, imgSwOpt, 25, 16, WHITE);
    display.drawBitmap(XPOS + 56, YPOS + 21, imgSwStart, 25, 16, WHITE);
    drawSymbol(1);
    idy = 2;
    selectIcon(idy, WHITE); //ready to sweep
    drawSmallWaveIcon();
    drawSmallCouplingIcon();
  }
  else {
    drawWaveIcon();
    drawCouplingIcon();
    drawSymbol(0);
    if (_WorkMode == SINGLEDIGIT) selectDigit(0);
  }
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Draws the icon based on the value of relative option
//----------------------------------------------------------------------------------------------------------------
void drawModeIcon(void) {
  byte x = XPOS - 8, y = YPOS + 21;
  byte const *bitmap[3] = {imgLog, imgDigit, imgSweep};
  display.fillRect(x, y, 25, 16, BLACK);
  display.drawBitmap(x, y, bitmap[_WorkMode], 25, 16, WHITE);
  display.display();
}

void drawWaveIcon(void) {
  byte x = XPOS + 24, y = YPOS + 21;
  const byte *bitmap[3] = {imgSqr, imgSin, imgTri};
  display.fillRect(x, y, 25, 16, BLACK);
  display.drawBitmap(x, y, bitmap[_WaveType], 25, 16, WHITE);
  display.display();
  drawSmallWaveIcon();
}

void drawCouplingIcon(void) {
  byte x = XPOS + 56, y = YPOS + 21;
  const byte *bitmap[3] = {imgCoAc, imgCoDc, imgCoOff};
  display.fillRect(x, y, 25, 16, BLACK);
  display.drawBitmap(x, y, bitmap[_CouplingMode], 25, 16, WHITE);
  display.display();
  drawSmallCouplingIcon();
}


//----------------------------------------------------------------------------------------------------------------
// Draws small wave icon based on the value of relative option
//----------------------------------------------------------------------------------------------------------------
void drawSmallWaveIcon(void) {
  byte x = 114, y = 41;
  const byte *bitmap[3] = {imgSqrSmall, imgSinSmall, imgTriSmall};
  display.fillRect(x, y, 9, 8, BLACK);
  display.drawBitmap(x, y, bitmap[_WaveType], 9, 8, WHITE);
  display.display();
}


//----------------------------------------------------------------------------------------------------------------
// Draws small coupling icon based on the value of relative option
//----------------------------------------------------------------------------------------------------------------
void drawSmallCouplingIcon(void) {
  byte x = 114, y = 50;
  const byte *bitmap[3] = {imgAcSmall, imgDcSmall, imgOffSmall};
  display.fillRect(x, y, 9, 8, BLACK);
  display.drawBitmap(x, y, bitmap[_CouplingMode], 9, 8, WHITE);
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Show cursor at x position
//----------------------------------------------------------------------------------------------------------------
void showCursor(byte x) {
  display.drawChar(XPOS + 2 + x * DELTAX, YPOS + DELTAY, CURSOR, WHITE, WHITE, BIG);
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Hide cursor at x position
//----------------------------------------------------------------------------------------------------------------
void hideCursor(byte x) {
  display.drawChar(XPOS + 2 + x * DELTAX, YPOS + DELTAY, CURSOR, BLACK, BLACK, BIG);
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Show cursor at x position after hiding previous one
//----------------------------------------------------------------------------------------------------------------
void selectDigit(byte x) {
  static byte lastDigit;
  hideCursor(lastDigit);
  display.drawChar(XPOS + 2 + x * DELTAX, YPOS + DELTAY, CURSOR, WHITE, WHITE, BIG);
  display.display();
  lastDigit = x;
}

//----------------------------------------------------------------------------------------------------------------
// Update single digit frequency to chr value
//----------------------------------------------------------------------------------------------------------------
void updateDigit(byte digit, char chr) {
  display.drawChar(XPOS + 2 + digit * DELTAX, YPOS, chr , WHITE, BLACK, BIG);
  display.display();
}

//----------------------------------------------------------------------------------------------------------------
// Drwaw or clear some symbols/icons
//----------------------------------------------------------------------------------------------------------------
void drawSymbol(byte symbol) {

  switch (symbol) {
    case 0: //Top arrow
      display.fillRect(2, 20, 25, 16, BLACK);
      display.fillRect(2, 43, 14, 16, BLACK);
      display.drawChar(XPOS - 20 , YPOS + 4, ARROW, WHITE, BLACK, SMALL);  //draw top arrow top
      break;

    case 1: //Bottom arrow
      display.fillRect(2, 20, 25, 16, BLACK);
      display.fillRect(2, 43, 14, 16, BLACK);
      display.drawChar(XPOS - 20 , YPOS + 25, ARROW, WHITE, BLACK, SMALL); //draw bottom arrow
      break;

    case 2: //Turn icon
      display.fillRect(2, 20, 25, 16, BLACK);
      display.drawBitmap(XPOS - 21, YPOS + 1, imgTurn, 13, 13, WHITE);      //draw turn icon
      break;

    case 3: //Play icons
      display.fillRect(4, 23, 23, 11, BLACK);                              //clear pause icon
      display.drawBitmap(4, 23, imgSwRun, 23, 11, WHITE);                  //draw sweep icon
      display.fillRect(XPOS + 56, YPOS + 21, 25, 16, BLACK);               //clear icon area
      display.drawBitmap(XPOS + 56, YPOS + 21, imgSwPause, 25, 16, WHITE); //drwaw sweep play symbol icon
      break;

    case 4: //Pause icons
      display.fillRect(4, 23, 23, 11, BLACK);                              //clear sweep icon
      display.drawBitmap(4, 23, imgSwPsd, 23, 11, WHITE);                  //draw pause icon
      display.fillRect(XPOS + 56, YPOS + 21, 25, 16, BLACK);               //clear icon area
      display.drawBitmap(XPOS + 56, YPOS + 21, imgSwStart, 25, 16, WHITE); //draw sweep pause symbol icon
      break;

    case 9: //Simply clear symbol area
      display.fillRect(2, 20, 25, 16, BLACK);                              //clear top symbol area
      display.fillRect(2, 43, 14, 16, BLACK);                              //clear bottom symbol area
      break;

    default:
      break;
  }
  display.display();
}

//---------------------------------------------------------------------------------------------------------------
// Set current frequency in DDS module, if frequency is 0 it will be set to 1
//---------------------------------------------------------------------------------------------------------------
void UpdateFrequency(void) {
  lFreq = atol(Freq);                                         //convert char array to long
  if (lFreq < 1) {                                            //the frequency at zero makes no sense
    ++Freq[MAXDIGIT - 1];                                     //increase the right most digit
    lFreq = 1;                                                //set frequency to 1
  }
  displayFrequency(lFreq);                                    //update the display
  DDS_FrequencySet(lFreq, Wave[_WaveType]);                   //send the frequency value to DDS module
  lLastFreq = lFreq;                                          //save current freq
}

//---------------------------------------------------------------------------------------------------------------
// Display the frequency with the six-zero template
//---------------------------------------------------------------------------------------------------------------
void displayFrequency(long f) {
  sprintf(Freq, "%06li", f);                                       //convert long to char with template '000000'
  displayText(XPOS + 2, YPOS, Freq , WHITE, BLACK, BIG);           //print frequency on display
  display.display();                                               //refresh display
}

//---------------------------------------------------------------------------------------------------------------
// Reset coupling mode to default
//---------------------------------------------------------------------------------------------------------------
void resetCouplingMode(void) {
  if (lFreq == 0 && _CouplingMode == OFF) {
    setCouplingMode(AC);
    drawCouplingIcon();
  }
}

//---------------------------------------------------------------------------------------------------------------
// Set a specific coupling mode
//---------------------------------------------------------------------------------------------------------------
void setCouplingMode(byte cMode) {
  byte pMode = _CouplingMode;
  switch (cMode) {
    case 0:
      if (lLastFreq > 0) lFreq = lLastFreq;
      digitalWrite(PinCoupling, LOW);
      _setCouplingMode(AC);
      break;

    case 1:
      if (lLastFreq > 0) lFreq = lLastFreq;
      digitalWrite(PinCoupling, HIGH);
      _setCouplingMode(DC);
      break;

    case 2:
      lLastFreq = lFreq;
      lFreq = 0;
      digitalWrite(PinCoupling, LOW);
      _setCouplingMode(OFF);
      break;
  }
  DDS_FrequencySet(lFreq, Wave[_WaveType]);
  displayFrequency(lFreq);
  if (cMode != pMode) saveConfig();
}


//---------------------------------------------------------------------------------------------------------------
// Select options
//---------------------------------------------------------------------------------------------------------------
void selectOption(byte opt) {

  selectIcon(opt, BLACK);

  switch (opt) {
    case 0:                           //workMode;
      displayWorkModeIcons();
      selectIcon(_WorkMode, WHITE);
      mode = OPTMODE;
      break;

    case 1:                           //waveType;
      displayWaveTypeIcons();
      selectIcon(_WaveType, WHITE);
      mode = OPTWAVE;
      break;

    case 2:                          //couplingMode;
      displayCouplingModeIcons();
      selectIcon(_CouplingMode, WHITE);
      mode = OPTCOUP;
      break;
  }
}

//----------------------------------------------------------------------------------------------------------------
// Calculate logarithmic steps of the frequency
//----------------------------------------------------------------------------------------------------------------
long AutoStep(long value, byte spin) {
  if (spin == CW) {
    if (value >= 100000) return 100000;
    if (value >= 10000) return 10000;
    if (value >= 1000) return 1000;
    if (value >= 100) return 100;
    if (value >= 10) return 10;
    if (value >= 1) return 1;
    return 0;  // Invalid value
  }
  else {
    if (value <= 10) return 1;
    if (value <= 100) return 10;
    if (value <= 1000) return 100;
    if (value <= 10000) return 1000;
    if (value <= 100000) return 10000;
    if (value <= 1000000) return 100000;
    return 0;  // Invalid value
  }
}

//-------------------------------------------------------------------------------------------
// Start Sweep or restart it from where it came from before the pause
//-------------------------------------------------------------------------------------------
void FrequencySweep() {
  do {
    if (sweepDnPausedVal == 0) {                       //if sweepDown has not been stopped
      if (sweepUpPausedVal > 0) {                      //and sweepUp has been stopped
        sweepUpPausedVal = SweepUp(sweepUpPausedVal);  //continues from current value
      }
      else {
        sweepUpPausedVal = SweepUp(_SweepMin);         //else start from min
      }
    }
    if (sweepStatus != BREAK) {                        //if sweep has been stopped
      if (sweepDnPausedVal > 0) {                      //and sweepDn has been stopped
        sweepDnPausedVal = SweepDn(sweepDnPausedVal);  //continues from current value
      }
      else {
        sweepDnPausedVal = SweepDn(_SweepMax);         //else start from max
      }
    }
  } while (sweepStatus != BREAK);                      //continues sweep until stopped
}

//-----------------------------------------------------------------------------------------
// Sweep Up from sweepmin push encoder to pause
//-----------------------------------------------------------------------------------------
long SweepUp(long sweepmin) {
  long f;

  for (f = sweepmin ; f < _SweepMax ; f += _SweepStep) {
    DDS_FrequencySet(f, Wave[_WaveType]);
    displayFrequency(f);
    if (encoderPush) {
      sweepStatus = BREAK;
      break;
    }
  }
  if (sweepStatus == BREAK) return f;
  return 0;
}

//-----------------------------------------------------------------------------------------
// Sweep down from sweepmax push encoder to pause
//-----------------------------------------------------------------------------------------
long SweepDn(long sweepmax) {
  long f;

  for (f = sweepmax; f > _SweepMin ; f -= _SweepStep) {
    DDS_FrequencySet(f, Wave[_WaveType]);
    displayFrequency(f);
    if (encoderPush) {
      sweepStatus = BREAK;
      break;
    }
  }
  if (sweepStatus == BREAK)return f;
  return 0;
}

//-----------------------------------------------------------------------------------------
// Clear global sweep vars and restore display
//-----------------------------------------------------------------------------------------
void SweepReset(void) {
  sweepStatus = STILL;
  sweepUpPausedVal = 0;
  sweepDnPausedVal = 0;
  display.fillRect(4, 23, 23, 11, BLACK);              //clear sweep text
  display.display();
}

//-----------------------------------------------------------------------------------------
// Flash sweep pause icon
//-----------------------------------------------------------------------------------------
void flashIcon(int interval) {
  static long previousMillis;
  static boolean picShow;
  if (millis() - previousMillis >= interval) {
    previousMillis = millis();
    picShow = !picShow;
    if (picShow) {
      display.drawBitmap(4, 23, imgSwPsd, 23, 11, WHITE);      //drwaw sweep pause icon
      display.display();
    } else {
      display.fillRect(4, 23, 23, 11, BLACK);                 //clear sweep pause icon
      display.display();
    }
  }
}

//-----------------------------------------------------------------------------------------
// Arduino software reset
//-----------------------------------------------------------------------------------------
void reset(void) {
  display.fillRect(1, 16, 125, 46, BLACK);
  display.display();
  displayText(30, 30, strFromFlash(4), WHITE, BLACK, BIG);
  char str[2];
  for (byte i = 3; i > 0; i--) {
    sprintf(str, "%d", i);
    displayText(95, 30, str, WHITE, BLACK, BIG);
...