DDS AD9850

777Andrej
Offline
Зарегистрирован: 09.04.2014

Может кто помочь ?
встроил в код генератора вольтметр. 
делаю измерения пока постоянного напряжения по входа А1 от 0-5в 
почему то показания вольтметра скачат, при том. если я выбираю диапазон от 0-100кгц они пляшут , если от 100кгц до 10мгц они стоят ровно.
такое ощущение что работу ацп кто то сбивает, и оно не успевает считать. 
как победить эту проблему не могу. 
 

#include <Rotary.h>


/*
Main code by Igor Krepsky - <a href="http://www.frompinskto.wordpress.com" title="www.frompinskto.wordpress.com" rel="nofollow">www.frompinskto.wordpress.com</a>
based on fragments of code by Richard Visokey AD7C - <a href="http://www.ad7c.com" title="www.ad7c.com" rel="nofollow">www.ad7c.com</a>
Rev. 2.2 - Spt., 2016  for AD9851 chip.
*/
// Подключение библиотек
#include <LiquidCrystal.h>
#include <rotary.h>


//Определения
#define W_CLK A2     // A2 - connect to AD9851 module word load clock pin (CLK)
#define FQ_UD A3     // A3 - connect to freq update pin (FQ)
#define DATA A4      // A4 - connect to serial data load pin (DATA)
#define RESET A5     // A5 - connect to reset pin (RST)
#define ENC_A 2      // Pin 2 - 1 канал валкодера 
#define ENC_B 3      // Pin 3 - 2 канал валкодера
#define ENC_KEY 4    // Pin 4 - кнопка валкодера
#define MODE_1 0     // Pin 0 - переключатель mode vfo
#define MODE_2 1     // Pin 1 - переключатель mode sweep
#define KEY_1 5      // Pin 5 - доп. кнопка
const int analogInPin = A1;  // Analog input pin 
     
float outputValue = A1;  

#define pulseHigh(pin) {digitalWrite(pin, HIGH); digitalWrite(pin, LOW); }

Rotary r = Rotary(2,3); // Устанавливает пины для каналов энкодера.  Должны поддерживать прерывания.

LiquidCrystal lcd(8, 9, 10, 11, 12, 13); // ПРИСВОЕНИЕ ДЛЯ LCD (RS_E_4_5_6_7)R/W=gnd

// Переменные
int_fast32_t rx=0000000;        // Стартовая частота VFO
int_fast32_t rif=0;             // Значение IF
int_fast32_t wif=450000;        // рабочее значение IF
int_fast32_t rx2=1;             // переменная для сохранения обновлённой частоты
int_fast32_t increment = 1;  // начальный VFO инкремент в HZ.
int_fast32_t delta=1000;        // Стартовая величина ширины качания sweep в Hz
int_fast32_t sstep=1;           // Стартовая величина шага качания sweep в Hz
int buttonstate = 0;            // Переменная для чтения состояния кнопки
String hertz = "1 Hz";
int  hertzPosition = 0;
byte ones,tens,hundreds,thousands,tenthousands,hundredthousands,millions ;  //Разряды для частоты
byte a_1, a_2;            // разряды для отображения IF
String freq;              // string для получения частоты
boolean mod_1;            // текущее значение переключателя mode vfo
boolean mod_1_old;        // сохранённое значение переключателя mode vfo
boolean mod_2;            // текущее значение переключателя mode sweep
boolean mod_2_old;        // сохранённое значение переключателя mode sweep

void setup() {
    // put your setup code here, to run once:
  pinMode(ENC_KEY,INPUT); // кнопка валкодера, 0 при нажатии
  digitalWrite(ENC_KEY,HIGH);
  lcd.begin(16, 2);
  pinMode(ENC_A, INPUT);
  pinMode(ENC_B, INPUT);
  pinMode(MODE_1, INPUT);
  pinMode(MODE_2, INPUT);
  PCICR |= (1 << PCIE2);
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19);
  sei();
  pinMode(FQ_UD, OUTPUT);
  pinMode(W_CLK, OUTPUT);
  pinMode(DATA, OUTPUT);
  pinMode(RESET, OUTPUT); 
  pulseHigh(RESET);
  pulseHigh(W_CLK);
  pulseHigh(FQ_UD);  // this pulse enables serial mode on the AD9851 - see datasheet
  lcd.setCursor(hertzPosition,1); 
  lcd.print(hertz);
}// END SETUP

void loop() {

 outputValue = float(analogRead(analogInPin)) / 204.6; //вольтметр
 lcd.setCursor(6, 1);
lcd.print(" Vout=          ");
lcd.setCursor(6, 1);
lcd.print(" Vout=");
int mv = outputValue * 1000;
if(mv<1000)
{
  lcd.print(mv);
}
else
{
  lcd.print(outputValue);
  lcd.print("  ");
}

  delay(100);

// ИЗМЕНЕНИЕ ИНКРЕМЕНТА (кнопка валкодера)
      buttonstate = digitalRead(ENC_KEY); // если нажатие кнопки валкодера - изменить инкремент
      if(buttonstate == LOW) {
      setincrement();        
      }
// ВОЗВРАТ ШАГА ИЗМЕНЕНИЯ ЧАСТОТЫ к 1КГЦ в РЕЖИМЕ ГЕНЕРАТОРА
      if ((mod_1 == 1)&&(mod_2 == 1)){
      buttonstate = digitalRead(KEY_1);
      if (buttonstate == LOW){increment = 1; hertz="1Hz"; hertzPosition=0;
      lcd.setCursor(hertzPosition,1);
      lcd.print("       ");
      lcd.setCursor(hertzPosition,1); 
      lcd.print(hertz);
      delay(250); // Adjust this delay to speed up/slow down the button menu scroll speed.  
      }
      }    
// ПЕРЕКЛЮЧАТЕЛЬ MODE
      mod_1 = digitalRead (MODE_1); // чтение значения переключателя mode vfo.
      mod_2 = digitalRead (MODE_2); // чтение значения переключателя mode sweep.
      
// ДЕЙСТВИЯ ПОСЛЕ ПЕРЕКЛЮЧЕНИЯ MODE 
      if ((mod_1 != mod_1_old)||(mod_2 != mod_2_old)){// если произошло переключение mode
      // и вывод на дисплей
      
      // 1.очистка
      
      lcd.setCursor(0,1);
      lcd.print("                ");
      
      // 2.вывод значения
      
      if ((mod_1 == 1)&&(mod_2 == 0)){// если режим генератора
        rif=0;
        lcd.setCursor(hertzPosition,1); 
        lcd.print(hertz);
      }
 
                                                                    
      if ((mod_1 == 0)&&(mod_2 == 1)){// если режим SWEEP
      rif=0;
      lcd.setCursor(hertzPosition,1); 
      lcd.print(hertz);
      if (delta < 10000){
      lcd.setCursor(5,1);  
      lcd.print("  SWP:");                       
      lcd.print(delta/1000);
      lcd.print("KHz ");
      }
      else if (delta >= 100000){
      lcd.setCursor(5,1);  
      lcd.print("SWP:");                       
      lcd.print(delta/1000);
      lcd.print("KHz ");
      }
      else
      {
      lcd.setCursor(5,1);  
      lcd.print(" SWP:");                       
      lcd.print(delta/1000);
      lcd.print("KHz ");
      } 
      } 
      
// ВЫВОД НА ДИСПЛЕЙ ЧАСТОТЫ И ПЕРЕДАЧА ЕЁ В DDS ПОСЛЕ ПЕРЕКЛЮЧЕНИЯ MODE      
      showFreq();
      sendFrequency(rx+rif);
      rx2 = rx;
      mod_1_old = mod_1;
      mod_2_old = mod_2;  
      } 
// ОКОНЧАНИЕ ДЕЙСТВИЯ ПОСЛЕ ПЕРЕКЛЮЧЕНИЯ MODE 
      
// ВЫЧИСЛЕНИЕ ЧАСТОТЫ ДЛЯ РЕЖИМА SWEEP
      if ((mod_1 == 0)&&(mod_2 == 1)){
      rif = rif + sstep;
      if (rif > delta) {rif = 0;}
      sendFrequency(rx+rif);// отправить частоту в синтезатор
      // переключение delta 
      buttonstate = digitalRead(KEY_1);
      if (buttonstate == LOW) {
      if (delta == 1000){delta = 5000;}
      else if (delta == 5000){delta = 10000; sstep = 2;}
      else if (delta == 10000){delta = 50000; sstep = 5;}
      else if (delta == 50000){delta = 100000; sstep = 10;}
      else {delta = 1000; sstep = 1;}
      if (delta < 10000){
      lcd.setCursor(5,1);  
      lcd.print("  SWP:");                       
      lcd.print(delta/1000);
      lcd.print("kHz ");
      }
      else if (delta == 100000){
      lcd.setCursor(5,1);  
      lcd.print("SWP:");                       
      lcd.print(delta/1000);
      lcd.print("kHz ");
      }
      else
      {
      lcd.setCursor(5,1);  
      lcd.print(" SWP:");                       
      lcd.print(delta/1000);
      lcd.print("kHz ");
      } 
     // delay(500);
      }  
      }
// ВЫЧИСЛЕНИЕ ЧАСТОТЫ ДЛЯ РЕЖИМА УЧЁТА ПРОМЕЖУТОЧНОЙ ЧАСТОТЫ
    if ((mod_1 == 1)&&(mod_2 == 0)){
      rif = wif;
      
     
      buttonstate = digitalRead(KEY_1);
      if (buttonstate == LOW);  // read the analog in value:

 
      }
    
// ВЫВОД НА ДИСПЛЕЙ ЧАСТОТЫ И ПЕРЕДАЧА ЕЁ В DDS В ОБЩЕМ СЛУЧАЕ 
          
        if (rx != rx2)
        {
        showFreq();// при изменении частоты вывести на дисплей
        sendFrequency(rx+rif);// отправить частоту в синтезатор
        rx2 = rx;
        }
            
}// END LOOP

// ПОДПРОГРАММЫ
// Обработка прерывания
ISR(PCINT2_vect) {
  
    // обработка энкодера
    unsigned char result = r.process();
    if (result) {    
    if (result == DIR_CW){rx=rx+increment;}
    else {rx=rx-increment;};       
    // конец обработки валкодера
    if ((rx+rif) >40000000){rx=(40000000-rif);}; // ВЕРХНИЙ VFO LIMIT
    if (rx <1){rx=0;}; // НИЖНИЙ VFO LIMIT
    }
    }

    
// расчёт частоты на основе докумментации на микросхему = <sys clock> * <frequency tuning word>/2^32
void sendFrequency(double frequency) {  
  int32_t freq = frequency * 4294967296./125000997;  // note 180 MHz clock on 9851. если генератор на 125мгц, то вписать . тут же можно и подогнать частоту генератора
  for (int b=0; b<4; b++, freq>>=8) 
  {
   tfr_byte(freq & 0xFF);
  }
  tfr_byte(0x000);   // Final control byte, LSB 1 to enable 6 x xtal multiplier on 9851 set to 0x000 for 9850
  pulseHigh(FQ_UD);  // Сделано!  Должен увидеть выход.
  }

// передаёт байт, по биту за раз, начиная с LSB  на 9851 через serial DATA line
void tfr_byte(byte data)
{
  for (int i=0; i<8; i++, data>>=1) {
    digitalWrite(DATA, data & 0x01);
    pulseHigh(W_CLK);   //после передачи каждого бита, CLK is pulsed high
  }
}

void setincrement(){// установка значения инкремента частоты
  if(increment == 1){increment = 10; hertz = "10Hz"; hertzPosition=0;} 
  else if(increment == 10){increment = 100; hertz = "100Hz"; hertzPosition=0;}
  else if (increment == 100){increment = 1000; hertz="1Khz"; hertzPosition=0;}
  else if (increment == 1000){increment = 10000; hertz="10Khz"; hertzPosition=0;}
  else if (increment == 10000){increment = 100000; hertz="100Khz"; hertzPosition=0;}
  else if (increment == 100000){increment = 1000000; hertz="1Mhz"; hertzPosition=0;} 
  else if (increment == 1000000){increment = 10000000; hertz="10Mhz"; hertzPosition=0;} 
  else{increment = 1; hertz = "1Hz"; hertzPosition=0;};  
   lcd.setCursor(hertzPosition,1);
   lcd.print("       ");
   lcd.setCursor(hertzPosition,1); 
   lcd.print(hertz);
   delay(100); // Adjust this delay to speed up/slow down the button menu scroll speed.
};

void showFreq(){// вывод значения частоты на дисплей
    millions = int(rx/1000000);
    hundredthousands = ((rx/100000)%10);
    tenthousands = ((rx/10000)%10);
    thousands = ((rx/1000)%10);
    hundreds = ((rx/100)%10);
    tens = ((rx/10)%10);
    ones = ((rx/1)%10);
    lcd.setCursor(0,0);
    lcd.print("                ");
   if (millions > 9){lcd.setCursor(2,0);}
   else{lcd.setCursor(2,0);}
    lcd.print(millions);
    lcd.print(".");
    lcd.print(hundredthousands);
    lcd.print(tenthousands);
    lcd.print(thousands);
    lcd.print(".");
    lcd.print(hundreds);
    lcd.print(tens);
    lcd.print(ones);
    lcd.print(" Hz ");
      
};



 

777Andrej
Offline
Зарегистрирован: 09.04.2014

http://i.imgur.com/W6eKF8J.jpg как идет считывание это 64гц частота

http://i.imgur.com/eJ5nTYG.jpg более стабильно , и полностью стабильно будет если выбрать частоту более или равно 100кгц.
измеряю постоянное напряжение ацп А1 в районе от 0-5в
в данном случае должно быть 498мВ
но как видно по первому скрину, то ацп не до считывает.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

777Andrej, для измерения переменного тока существуют специальные алгоритмы, на форуме на эту тему неоднократно вели обсуждения, пользуйтесь поиском.

777Andrej
Offline
Зарегистрирован: 09.04.2014

тут нет измерения переменного!
измеряется постоянное напряжение в пределах от 0 до 5в
для теста выбран предел просто 498мВ

просто пределы переключения частоты а их там
1гц
10гц
100гц
и тд
каким то образом влияют на счет ацп.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

777Andrej, по скетчу не видно что вы измеряете, как это всё включено, итп. Очевидно наводки на вход идут -> аппаратный косяк.

777Andrej
Offline
Зарегистрирован: 09.04.2014

хорошо , если я на вход А1 подам 5в
почему напряжение обрывается и считает
0,24в
0,59в
1,23
2,57
3,05
и снова
0,24в
0,59в
1,23
2,57
3,05
но до 5в никогда не дойдет!?
 

777Andrej
Offline
Зарегистрирован: 09.04.2014

возможно обрыв счета ацп идет из за отправки частоты в синтезатор?
и вопрос, почему выше 100кгц если включен этот диапазон , то вольтметру хватает время на счет!?

AlexeySh
Offline
Зарегистрирован: 16.01.2017

Подробно ваш скетч не разбирал, но возможно все банально просто. Время измерения напряжения командой analogRead составляет 1/10000 секунды. А что произойдет если во время выполнения этого измерения будет вызвано прерывание? Которое в данной программе обрабатывается не с использованием стандартных библиотек типа TimerOne, а напрямую. Думаю из за этого процесс измерения может прерваться в любой момент и analogRead() выдаст некорректный результат.

Решение - или исправлять обработку прерывания так, чтобы она не мешала функции analogRead(). Или отказаться от использования analogRead() и делать измерения напрямую через регистры управления АЦП. Или заменить свой обработчик прерываний на стандарную библиотеку TimerOne или что-то аналогичное. Думаю что разработчики библиотеки позаботились о корректной работе её с analogRead().

777Andrej
Offline
Зарегистрирован: 09.04.2014

Полностью с вами согласен!  и тоже уверен в этом , что именно не хватает времени на измерение.
но вся проблема , я не очень так разбираюсь в этом пока, и сделать такие изменения я не смогу сам .