цифровая электроника
вычислительная техника
встраиваемые системы

 
» » » Генератор синусоидального сигнала на основе Arduino своими руками


Генератор синусоидального сигнала на основе Arduino своими руками

Автор: Mike(admin) от 15-08-2021, 23:55

Генератор сигналов может генерировать различные сигналы, такие как синусоидальные, прямоугольные и пилообразные. У некоторых из них имеется функция развертки и организации произвольной формы сигнала. Это полезные инструменты в мастерской. Их можно использовать для проверки аудиосхем, схем на основе операционных усилителей и проверки отклика схемы. Большинство современных функциональных генераторов могут легко выдавать частоты до 1 МГц.


Генератор синусоидального сигнала на основе Arduino своими руками

Если вам нужен простой генератор синусоидального сигнала, но неохота тратить денег на полноценный генератор сигналов, то можно сделать такой своими руками на основе Arduino, и в данном материале будет рассказано как.


Наш простой генератор синусоидального сигнала основан на Я решил, справочной таблицы значений, которые определяют синусоидальном выходе. Чем больше значений вы укажете в таблице, тем лучше выходной сигнал будет приближаться к истинной синусоиде. Также очень удобно использовать степень двойки при создании такой таблицы. Важно также выбрать количество значений. Так, 8 значений не дадут требуемого результата, а 128, вероятно, будут нагружать ядро микроконтроллера, поэтому мы выбрали 32 значения для начала в качестве оптимального варианта.


Для формирования выходного сигнала мы взяли ЦАП MCP4725, который представляет собой 12-битное устройство. На следующем изображении представлена схема подключения Arduino к ЦАП по интерфейсу I2C.


Генератор синусоидального сигнала на основе Arduino своими руками

Генератор синусоидального сигнала на основе Arduino своими руками

Для формирования таблицы был открыт Excel, куда были записаны числа от 0 до 31. Следующим столбцом, который нам нужен, был угол в радианах. Это просто индекс, умноженный на 2, умноженный на Pi, деленный на 32. Следующий столбец – это нормализованная амплитуда сигнала. Мы просто взяли синус угла из предыдущего столбца. Это создало сигнал, который составлял 2 единицы от пика до пика и был сосредоточен на нуле. Его максимум был +1, а минимум -1.


Имея выходной диапазон от 0 до 5 В, нам понадобился сигнал с центром на 2,5 В и амплитудой ±2,5 В. Следующий столбец просто умножил сигнал на 2,5. Затем компенсируем его на 2,5 В. Сигнал показал максимум 5В и минимум 0В. Теперь нам просто нужно было преобразовать это в 12-битное число, чтобы поместить в нашу таблицу. Поскольку мы хотели, чтобы значение было 4095 при напряжении 5 В, мы умножаем на 4095 и делим на 5. Чтобы проверить, посмотрите на строку 8. Выходное напряжение составляет 5 В, а количество битов – 4095.


32 2-байтовых числа – это не так много данных, и их можно легко сохранить в ОЗУ, но мы будем сохранять таблицу во FLASH и читать ее по мере необходимости. Преимущество в том, что если мы решим повысить точность сигнала и добавить больше данных, нам может не хватить ОЗУ и нужно будет использовать флэш-память. В итоге воспользуемся командой PROGMEM. Код программы нашего генератора синусоидального сигнала на основе Arduino следующий:



#include<Wire.h>
#include<avr/pgmspace.h>


#define addr 0b1100011

#define Conver_reg      0X00
#define Config_reg      0x01
#define Low_lim_reg     0X02
#define High_lim_reg    0X03

#define FM      0x00
#define LD      0x02
#define DaEP    0x03

unsigned char incomingByte = 0;         
unsigned char DataH = 0;             
unsigned char DataL = 0;                 
unsigned char Cmd = 0x00;                
unsigned char PDM = 0;                 
unsigned char Array[6] = {0, 0, 0, 0, 0}; 
unsigned char i = 0;                    
unsigned char val = 0;                  
unsigned int temp = 0;                  
unsigned char wait = 0;                
unsigned char x = 0;                    
const PROGMEM unsigned int mysine[] = {
  2048,
  2447,
  2831,
  3185,
  3495,
  3750,
  3939,
  4056,
  4095,
  4056,
  3939,
  3750,
  3495,
  3185,
  2831,
  2447,
  2048,
  1648,
  1264,
  910,
  600,
  345,
  156,
  39,
  0,
  39,
  156,
  345,
  600,
  910,
  1264,
  1648
};

void setup() {
  Wire.begin();
  Serial.begin(9600);
  banner(); 
}

void loop() {

  if (Serial.available() > 0)        
  {
    incomingByte = Serial.read();

    //====================
    if (incomingByte == '1')        
    {
      incomingByte = 0;               

      Wire.requestFrom(addr, 5, 1);
      i = 0;
      while (Wire.available())        
      {
        i++;                        
        Array[i] = Wire.read();     
      }
      Serial.print("\n This is the Command Byte: "); Serial.print(Array[1], HEX);
      Serial.print("\n DAC Reg MSB Byte: "); Serial.print(Array[2], HEX);
      Serial.print("\n DAC REG LSB Byte: "); Serial.print(Array[3], HEX);
      Serial.print("\n EEPROM Power down + MSB Byte: "); Serial.print(Array[4], HEX);
      Serial.print("\n EEPROM LSB Byte: "); Serial.println(Array[5], HEX);
      banner();
    }

    //====================
    if (incomingByte == '2')       
    {
      incomingByte = 0;              

      Serial.print("\nSelect the power down mode\n");
      Serial.print("1 - Normal Mode (0)\n");
      Serial.print("2 - 1K Resistor to GND (1)\n");
      Serial.print("3 - 100K Resistor to GND (2)\n");
      Serial.print("4 - 500K Resistor to GND (3)\n");
      wait = 1;  
      PDM = 0;  
      while (wait) {   
        if (Serial.available() > 0)     
        {
          incomingByte = Serial.read();
          if ((incomingByte > 47) && (incomingByte < 53)) {
            wait = 0;
          }
        }
      }
      switch (incomingByte) {
        case 49:
          PDM = 0x00;
          break;
        case 50:
          PDM = 0x01;
          break;
        case 51:
          PDM = 0x02;
          break;
        case 52:
          PDM = 0x03;
          break;
      }
      incomingByte = 0;
      Serial.print("\nPDM mode: ");
      Serial.print(PDM);
      Serial.print("\n");
      banner();
    }

    //====================
    if (incomingByte == '3')     
    {
      incomingByte = 0;        

      Serial.print("\nSelect the Command Mode\n");
      Serial.print("1 - Fast (0)\n");
      Serial.print("2 - Write to DAC (2)\n");
      Serial.print("3 - Write to DAC and EEPROM (3)\n");

      wait = 1;                
      Cmd = 0;                  
      while (wait) {           
        if (Serial.available() > 0)  
        {
          incomingByte = Serial.read();
          if ((incomingByte > 48) && (incomingByte < 52)) {
            wait = 0;
          }
        }
      }
      switch (incomingByte) {
        case 49:
          Cmd = 0x00;
          break;
        case 50:
          Cmd = 0x02;
          break;
        case 51:
          Cmd = 0x03;
          break;
      }
      Serial.print("\nCMD mode: ");
      Serial.print(Cmd);
      Serial.print("\n");
      banner();
    }

    //====================
    if (incomingByte == '4')      
    {
      incomingByte = 0;          

      Serial.print("\nEnter a value between 0 and 4095\n");
      Serial.print("You must use leading zeros\n");
      Serial.print("Value: ");

      wait = 1;
      i = 0;
      while (wait) {
        if (Serial.available() > 0)     
        {
          incomingByte = Serial.read();
          if ((incomingByte) != 10) {
            i++;
            Array[i] = (incomingByte - 48);
            if (i >= 4) {
              wait = 0;
            }
          }
        }
      }

      temp = 0;
      temp = 1000 * Array[1] + 100 * Array[2] + 10 * Array[3] + Array[4];
      if (temp > 4095) {
        Serial.print("\nError: Value too large -> ");
        Serial.print(temp);
      }
      else { 
        DataL = ((temp << 4) & 0x00F0);
        DataH = ((temp >> 4) & 0x00FF); 
      }
      Serial.print("\n");
      Serial.print(temp);
      Serial.print("\n");
      banner();
    }

    //====================
    if (incomingByte == '5')      
    {
      incomingByte = 0;      

      if (Cmd & 0x06) 
      {
        val = (((Cmd << 5) & 0xE0)  | ((PDM < 1) & 0x06));
        Wire.beginTransmission(addr);
        Wire.write(val);              
        Wire.write(DataH);         
        Wire.write(DataL);            
        Wire.endTransmission();

        Serial.print("\n MSB: "); Serial.print(DataH, HEX);
        Serial.print("\n LSB: "); Serial.println(DataL, HEX);
      }

      else     
      {
        val = (((PDM << 4) & 0x30) | (( DataH >> 4) & 0x0F)); 
        Wire.beginTransmission(addr);
        Wire.write(val);
        val = (((DataH << 4) & 0xF0) | ((DataL >> 4) & 0x0F)); 
        Wire.write(val);
        Wire.endTransmission();
        Serial.print("\nFast MSB: "); Serial.print(DataH, HEX);
        Serial.print("\n LSB: "); Serial.println(DataL, HEX);
      }
      banner();
    }

    //====================
    if (incomingByte == '6')      
    {
      incomingByte = 0;          
      Serial.print("\nOutputs a sine wave\n");

      Serial.print("Enter the 'e' key to exit\n");
      Serial.print("+ to increase period and - to decrease period\n");
      Serial.print("Delay time will be printed to Serial Monitor \n");
      Serial.print("Outputing wave now\n");

      wait = 1;
      i = 0;

      while (wait)
      {
        if (Serial.available() > 0)
        {
          incomingByte = Serial.read();
          if ((incomingByte == 'e') | (incomingByte == 'E')) {
            wait = 0;
          }
          if (incomingByte == 43) { 
            x++;
            Serial.println(x);
          }
          else if (incomingByte == 45) {  
            x--;
            Serial.println(x);
          }
          incomingByte = 0;       
        }
        delay(x);

        temp = pgm_read_word_near(mysine + i);
        //temp = (mysine[i]);
        DataL = ((temp << 4) & 0x00F0);  
        DataH = ((temp >> 4) & 0x00FF);  

        val = (((PDM << 4) & 0x30) | (( DataH >> 4) & 0x0F)); 
        Wire.beginTransmission(addr);
        Wire.write(val);
        val = (((DataH << 4) & 0xF0) | ((DataL >> 4) & 0x0F)); 
        Wire.write(val);
        Wire.endTransmission();

        i++;              
        i = i & 0x1F;         
      }
      banner();
    }

    //====================
    if ((incomingByte == 'H') | (incomingByte == 'h')) 
    {
      incomingByte = 0;           

      Serial.print("\n");
      banner();                 
    }
  }
}

void banner() {
  Serial.print ("\nDigital to Analog Converter SF-5: Main Menu\n\n");
  Serial.print ("1. Read DAC Registers\n");
  Serial.print ("2. Power Down Select\n");
  Serial.print ("3. Command Mode Select\n");
  Serial.print ("4. Get value to write\n");
  Serial.print ("5. Execute a write\n");
  Serial.print ("6. Sine wave output\n");
  Serial.print ("H. Help Menu\n");
}

Максимальная частота, которую вы получите, составляет 92 Гц. Для настройки частоты используются delay() после каждой записи в таблице. Это будет 32 мс (примерно) за цикл. Итак, следующие частоты - 23 Гц, 13 Гц, 9 Гц. Вы можете увеличить задержку до 256. Это даст период в 8 секунд.


Генератор синусоидального сигнала на основе Arduino своими руками

Если вам не нравится качество выходного сигнала, вы можете либо увеличить количество значений в таблице, либо сгладить этот сигнал, используя фильтр. То есть мы можем отфильтровать высокие частоты, которые создают острые углы сигнала при изменении уровней напряжения. Например, мы использовали простой RC-фильтр низких частот со значениями R = 10K, C = 0,1 мкФ для организации частоты среза около 160 Гц. На следующем изображении показан результат фильтрации, где мы видим, что сигнал выглядит намного лучше и является лучшим приближением к истинной синусоиде.


Генератор синусоидального сигнала на основе Arduino своими руками



© digitrode.ru


Теги: Arduino




Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Комментарии:

Оставить комментарий