Скачет задержка.

Al_dr
Offline
Зарегистрирован: 25.06.2019

Добрый день. Сразу замечу что я не очень разбираюсь в програмирование и, надеюсь, что в этом разделе форума это допускается.

Мой вопрос в следующем. Я решил сделать простенькое устройство, генератор импульсов с задержкой на Arduino nano (китайская копия, если это важно) и что я не пытался делать, задержка, необходимая мне между двумя импульсами всегда скачет в диапазоне около 10мкс. Сначала я пытался сделать это считывая micros() в цикле while(), задержка скакала, я решил, что это возможно из-за случайного времени выполнения всех мат. операций и считываний в цикле, в общем, не буду излагать здесь всю трогательную историю моей борьбы, скажу лишь, что закончил я так

    PORTD |=1<<2;
asm("nop");
// повторяется сто раз просто скопированная строка
asm("nop");
  PORTB |=1<<3;

То есть в моём понимание болтаться просто нечему. Но на деле выходит картина приведённая на иллюстрации ниже

Здесь красный первый импульс и жёлтый второй видно, что он бывает в двух разных местах, разница между которыми 6,26 мкс. С более сложными (но более удобными и понятными мне кодами) типа циклов while(t<T){t=micros();...} ,разброс достигает 10 мкс и положения не 2, а больше. Болтанка от абсолютной величины задержки, на мой взгляд, не зависит.

Подскажите, пожалуйста, что я делаю не так. 

Спасибо

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

всё так, так и должно быть, если болтанка критична, переходить на авр студию

svm
Offline
Зарегистрирован: 06.11.2016

Запретить все прерывания. Не запускать никакие библиотеки. И лучше выложить полный код, а не кусочек, который теоретически должен работать корректно.

Al_dr
Offline
Зарегистрирован: 25.06.2019

Код я могу выложить без проблем, но он у меня в отвратительном тестовом виде с кучей процедур не имеющих отношения к делу и, мне кажется, что просто как-то не вежливо его так выкладывать. Но, на всякий случай, я его приложу, но никому смотреть и разбираться в полном коде не советую :-)

Из билиотек я использую 

#include <Arduino.h>
#include <TM1637Display.h>
#include <EEPROM.h>
Но они мне нужны. 
Отключить прерывания попробую.
#include <Arduino.h>
#include <TM1637Display.h>
#include <EEPROM.h>

//PINS
byte out1=2;
byte out2=11;
byte CLK1=3;//valcoder1
byte DT1=4;
byte SW1=5;
byte CLK2=10;//valcoder2
byte DT2=9;
byte SW2=8;

unsigned char encoder_A1, encoder_B1, encoder_A_prev1; // valcoder1
unsigned char SW_State1,SW_State_prev1;
unsigned char encoder_A2, encoder_B2, encoder_A_prev2; // valcoder2
unsigned char SW_State2,SW_State_prev2;


int sw_counter;
int sw1_counter;
int sw_delay=5000;
byte adr_freq=3;
byte adr_del=7;
byte freq_mass[3]={10,25,50};  //frequences
byte freq;
byte freq_i; //frequency nomber
int T_cor=70;
//float mult=1;
//int d=2;
int del;//delay
int del_mult=1;
byte pulse_wide=25;
unsigned long t1,t2,dt;//,tee1,tee2,dtee;
unsigned long td1,td2,dt_del,dt_wide,i,j;
float T,dT;
//int dTee=10000; //delay before puting to eepromt
//long dTd=10000;//delay for d set
//int d[4];

byte u1_flag=0;
byte u2_flag=0;
byte eet_flag=1;
byte d_flag=0;
byte beg_flag=1;
byte work_flag=0;

TM1637Display display(7, 6);

void setup() {
//Serial.begin(9600);

    display.setBrightness(0x0f);
EEPROM.get(adr_freq,freq_i);
EEPROM.get(adr_del,del);
DDRD = B00000100;
DDRB = B00001000;
//pinMode(out1,OUTPUT);
//pinMode(out2,OUTPUT);
freq=freq_mass[freq_i];
T=1000000/freq;
//t1=micros();
display.showNumberDec(freq);
delay(1500);
display.showNumberDec(del);
delay(1500);
display.setBrightness(0);
display.showNumberDec(work_flag);
delay(1500);
display.showNumberDec(freq);

}

void loop() {
  while(1)
 {if(work_flag==1)
  cycle();
  //cycle1();
 encoder1();
 encoder2();
 }
}
void cycle1()
{
 
  
  if(beg_flag==1)  
   {t1=micros();
    PORTD |=1<<2;
  
PORTD &=~(1<<2);
  beg_flag=0;  }

   t2=micros();
  dt=t2-t1;
   if(dt>T)
  beg_flag=1;
}

void cycle()
{

  
  if(beg_flag==1)
  {
    t1=micros();
  dt_wide=t1+pulse_wide;
  dt_del=t1+del;
  u1_flag=1;
    PORTD |=1<<2;
     // digitalWrite(out1,HIGH);
 /*  while(t2<dt_del)
  {t2=micros();
  if(u1_flag==1)
  if(t2>dt_wide)
  {u1_flag=0;
  //PORTD &=~(1<<2);
  digitalWrite(out1,LOW);
  }
  }*/

asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
asm("nop");
  PORTB |=1<<3;
// digitalWrite(out2,HIGH);
  u2_flag=1;
  beg_flag=0;
  }
  t2=micros();
  dt=t2-t1;
  
  if(u2_flag==1)
  if(dt>del+pulse_wide)
  {u2_flag=0;
 // PORTB &=~(1<<3);
   digitalWrite(out2,LOW);
 }
  
  if(u1_flag==1)
  if(dt>pulse_wide)
  {u1_flag=0;
 // PORTD &=~(1<<2);
    digitalWrite(out1,LOW);}

  if(dt>=T-T_cor)
  beg_flag=1;
}
/*t2=micros();
dt=t2-t1;
if(dt-puls_wide<=0)
{if(u1_flag==0)
{
  PORTD |=1<<2;
u_flag=1;}

}
else
if(u_flag==1)
{
  PORTD &=~(1<<2);  
u_flag=0;
{
  if(dT<dt-T/d)
  dT=dt-T/d;
}
}
if(dt-T>=0)
{t1=micros()-dt+T-47;
}*/
  

  void encoder1()
 {              
 
  encoder_A1 = digitalRead(CLK1);
  encoder_B1 = digitalRead(DT1);
  SW_State1 = digitalRead(SW1);

   if(!encoder_A1 && encoder_A_prev1)
  {
  /*  tee1=millis();
    tee2=tee1;*/
    //eet_flag=0;
     if(encoder_B1)
    {
freq_i=freq_i-1;

    }
       else
    {
freq_i++;

      }
if(freq_i<0||freq_i>250)
freq_i=0;
if(freq_i>1)
freq_i=2;

      freq=freq_mass[freq_i];
      T=1000000/freq;
      display.showNumberDec(freq);       
    }
    encoder_A_prev1 = encoder_A1;

 if(!SW_State1)
 {
 if(SW_State_prev1)
  {
work_flag=0;
EEPROM.put(adr_freq,freq_i);
EEPROM.put(adr_del,del);
display.showNumberDec(work_flag); 
display.setBrightness(0);
sw1_counter=0;
  }
  if(!SW_State_prev1)
  sw1_counter++;
  if(sw1_counter==sw_delay)
  {
    sw1_counter=0;
    
    display.setBrightness(7);
    work_flag=1;
    display.showNumberDec(freq); 
EEPROM.put(adr_freq,freq_i);
EEPROM.put(adr_del,del);
 }

}  
  SW_State_prev1=SW_State1;            
 }
  void encoder2()
 {              
 
  encoder_A2 = digitalRead(CLK2);
  encoder_B2 = digitalRead(DT2);
  SW_State2 = digitalRead(SW2);

   if(!encoder_A2 && encoder_A_prev2)
  {
   /* tee1=millis();
    tee2=tee1;*/
    //eet_flag=0;
     if(encoder_B2)
    {
      while(del_mult>del)
      del_mult=del_mult/10;
      if(del_mult<1)
      del_mult=1;
      del=del-del_mult;
      if(del<0)
      del=0;
    }
       else
    {
      del=del+del_mult;
      if(del>9999)
      del=9999;
      if(del<pulse_wide)
      del=pulse_wide;
      }

      display.showNumberDec(del);       
    }
    encoder_A_prev2 = encoder_A2;

 if(!SW_State2)
 sw_counter++;
 {
 if(sw_counter==sw_delay)
  {
del_mult=del_mult*10;
if(del_mult>1000)
del_mult=1;
sw_counter=0;
display.showNumberDec(del_mult); 
  }  
 } 
 if(sw_counter!=0)
 if(SW_State2)
 sw_counter=0;
 //SW_State_prev2=SW_State2;
 }            

 

 

Al_dr
Offline
Зарегистрирован: 25.06.2019

Спасибо, обязательно попробую при случае, но пока это выше моего понимания.

svm
Offline
Зарегистрирован: 06.11.2016

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

Al_dr
Offline
Зарегистрирован: 25.06.2019

Запретил прерывания. В приведённом варианте болтанка пропала. С циклом while(){micros();...} всё равно не работает, но, как я понимаю это уже из-за случайности момента считывания micros(); не так удобно, но зделаю цикл for(){nop}, наверное. Больше спасибо за совет. Проблема скорее решена

svm
Offline
Зарегистрирован: 06.11.2016

Al_dr пишет:

Запретил прерывания. В приведённом варианте болтанка пропала. С циклом while(){micros();...} всё равно не работает, но, как я понимаю это уже из-за случайности момента считывания micros(); не так удобно, но зделаю цикл for(){nop}, наверное. Больше спасибо за совет. Проблема скорее решена

Успехов!

Al_dr
Offline
Зарегистрирован: 25.06.2019

Спасибо

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Al_dr пишет:

Запретил прерывания. В приведённом варианте болтанка пропала. С циклом while(){micros();...} всё равно не работает, но, как я понимаю это уже из-за случайности момента считывания micros(); не так удобно, но зделаю цикл for(){nop}, наверное. Больше спасибо за совет. Проблема скорее решена

а микрос работает после запрета прерывания?

Al_dr
Offline
Зарегистрирован: 25.06.2019

Ну, как-то да - работает. Выдаёт примерно похожие на правду результаты. В интернетах пишут, что так делать нельзя, но мне, благо и не подходит по вышеописанным причинам, но когда я сейчас пробовал - работает. Ошибку не выдаёт в плату заливается, плата работает как нужно, но с болтанкой, которую я списываю на сучайность времени считывания.

nik182
Offline
Зарегистрирован: 04.05.2015

Болтанки быть не должно, если программа написана правильно. Для выдачи стабильного результата на высоких скоростях надо использовать таймер с прямой выдачей на ногу м.с. В этом случае результат не будет зависить от прерываний и команд внутри цикла loop. В интернете много примеров програмирования таймера. Даже здесь на форуме. Например http://arduino.ru/forum/proekty/generator-s-reguliruemoei-chastotoi-na-arduino

Al_dr
Offline
Зарегистрирован: 25.06.2019

Большое спасибо. Обязательно попробую что-то такое - давно хотел разобраться с таймерами. Сейчас требуемую мне задачу я решил, возможно, конечно, не самым прямым методом, но задержка совершенно стабильна для моих задач и регулируется с точностью, примерно в 1мкс от 20 до 1000. Абсолютное значение задержки и для меня не важно - в основном важна была стабильность.

Если интересно - вот конечный код, который работает

#include <Arduino.h>
#include <TM1637Display.h>
#include <EEPROM.h>

//PINS
byte out1=2;
byte out2=11;
byte CLK1=3;//valcoder1
byte DT1=4;
byte SW1=5;
byte CLK2=10;//valcoder2
byte DT2=9;
byte SW2=8;

unsigned char encoder_A1, encoder_B1, encoder_A_prev1; // valcoder1
unsigned char SW_State1,SW_State_prev1;
unsigned char encoder_A2, encoder_B2, encoder_A_prev2; // valcoder2
unsigned char SW_State2,SW_State_prev2;


int sw_counter;
int sw1_counter;
int sw_delay=5000;
byte adr_freq=3;
byte adr_del=7;
byte freq_mass[3]={10,25,50};  //frequences
byte freq;
byte freq_i; //frequency nomber
int T_cor=70;
//float mult=1;
//int d=2;
int del;//delay
int del_mult=10;
byte pulse_wide=25;
unsigned long t1,t2,dt;//,tee1,tee2,dtee;
unsigned long td1,td2,dt_del,dt_wide,i,a,b;
float T,dT;
float k=1.331;// recount mks to nop
int d=4;
//int dTee=10000; //delay before puting to eepromt
//long dTd=10000;//delay for d set
//int d[4];

byte u1_flag=0;
byte u2_flag=0;
byte eet_flag=1;
byte d_flag=0;
byte beg_flag=1;
byte work_flag=0;

TM1637Display display(7, 6);

void setup() {
//Serial.begin(9600);

    display.setBrightness(0x0f);
EEPROM.get(adr_freq,freq_i);
EEPROM.get(adr_del,del);
DDRD = B00000100;
DDRB = B00001000;
//pinMode(out1,OUTPUT);
//pinMode(out2,OUTPUT);
freq=freq_mass[freq_i];
T=1000000/freq;
a=k*pulse_wide-d;
b=del*k-d;
//t1=micros();
display.showNumberDec(freq);
delay(1500);
display.showNumberDec(del);
delay(1500);
display.setBrightness(0);
display.showNumberDec(work_flag);
delay(1500);
display.showNumberDec(freq);

}

void loop() {
  while(1)
 {if(work_flag==1)
  cycle();
  //cycle1();
 encoder1();
 encoder2();
 }
}


void cycle()
{

  
  if(beg_flag==1)
  {
    t1=micros();
  dt_wide=t1+pulse_wide;
  dt_del=t1+del;
  u1_flag=1;
        noInterrupts();
      PORTD |=1<<2;
      //digitalWrite(out1,HIGH);
      for(i=0;i<a;i++)
      asm("nop");
      PORTD &=~(1<<2);
      for(i=0;i<b-a;i++)
      asm("nop");
      //PORTD |=1<<2;
      PORTB |=1<<3;
      for(i=0;i<a;i++)
      asm("nop");
      PORTB &=~(1<<3);
      interrupts();
  //PORTB |=1<<3;
 //digitalWrite(out2,HIGH);
  u2_flag=1;
  beg_flag=0;
  }
  t2=micros();
  dt=t2-t1;


  if(dt>=T-T_cor)
  beg_flag=1;
}
/*t2=micros();
dt=t2-t1;
if(dt-puls_wide<=0)
{if(u1_flag==0)
{
  PORTD |=1<<2;
u_flag=1;}

}
else
if(u_flag==1)
{
  PORTD &=~(1<<2);  
u_flag=0;
{
  if(dT<dt-T/d)
  dT=dt-T/d;
}
}
if(dt-T>=0)
{t1=micros()-dt+T-47;
}*/
  

  void encoder1()
 {              
 
  encoder_A1 = digitalRead(CLK1);
  encoder_B1 = digitalRead(DT1);
  SW_State1 = digitalRead(SW1);

   if(!encoder_A1 && encoder_A_prev1)
  {
  /*  tee1=millis();
    tee2=tee1;*/
    //eet_flag=0;
     if(encoder_B1)
    {
freq_i=freq_i-1;

    }
       else
    {
freq_i++;

      }
if(freq_i<0||freq_i>250)
freq_i=0;
if(freq_i>1)
freq_i=2;

      freq=freq_mass[freq_i];
      T=1000000/freq;
      display.showNumberDec(freq);       
    }
    encoder_A_prev1 = encoder_A1;

 if(!SW_State1)
 {
 if(SW_State_prev1)
  {
work_flag=0;
EEPROM.put(adr_freq,freq_i);
EEPROM.put(adr_del,del);
display.showNumberDec(work_flag); 
display.setBrightness(0);
sw1_counter=0;
  }
  if(!SW_State_prev1)
  sw1_counter++;
  if(sw1_counter==sw_delay*10)
  {
    sw1_counter=0;
    
    display.setBrightness(7);
    work_flag=1;
    display.showNumberDec(freq); 
EEPROM.put(adr_freq,freq_i);
EEPROM.put(adr_del,del);
 }

}  
  SW_State_prev1=SW_State1;            
 }
  void encoder2()
 {              
 
  encoder_A2 = digitalRead(CLK2);
  encoder_B2 = digitalRead(DT2);
  SW_State2 = digitalRead(SW2);

   if(!encoder_A2 && encoder_A_prev2)
  {
   /* tee1=millis();
    tee2=tee1;*/
    //eet_flag=0;
     if(encoder_B2)
    {
      while(del_mult>del)
      del_mult=del_mult/10;
      if(del_mult<1)
      del_mult=1;
      del=del-del_mult;
      if(del<pulse_wide)
      del=pulse_wide;
    }
       else
    {
      del=del+del_mult;
      if(del>9999)
      del=9999;
      if(del<pulse_wide)
      del=pulse_wide;
      }
b=del*k-d;
      display.showNumberDec(del);       
    }
    encoder_A_prev2 = encoder_A2;

 if(!SW_State2)
 sw_counter++;
 {
 if(sw_counter==sw_delay)
  {
del_mult=del_mult*10;
if(del_mult>1000)
del_mult=1;
sw_counter=0;
display.showNumberDec(del_mult); 
  }  
 } 
 if(sw_counter!=0)
 if(SW_State2)
 sw_counter=0;
 //SW_State_prev2=SW_State2;
 }            

Ещё раз всем большое спасибо за помощь