Генератор сигналов на Arduino, проблема с Serial

beautiful mind
Offline
Зарегистрирован: 06.09.2012

Здравствуйте! Собрал я тут генератор сигналов на Arduino подобный тому, что в переведенной статье http://cxem.net/arduino/arduino62.php . Но немного переделал его, чтобы был свободый Serial, чтобы управлять устройством с компьютера. И тут встала проблема, что данный код не позволяет принимать и отправлять сообщения по Serial. Если отправить что-либо, то все просто подвисает, а пришедшие сообщения не обрабатываются. Я подозреваю что все дело в прерываниях, благодаря которым и работает генератор. Но т.к. сам в них мало что понимаю, прошу помощи. Полностью код не буду копировать, можете по ссылке посмотреть.

Что я пытаюсь сделать:


void loop() {
  if (Serial.available() > 0) 
  {
    // get incoming byte:
    char inChar = (char)Serial.read();
    if (inChar == 'n') Serial.println("generator");       
  }
  
  checkFreq();
  checkShape();
  checkPW();
}

так вообще ничего не работает, сообщения пришедшие не обрабатываются.

После этого пытался просто отправить что-нибудь на компьютер.

  if ((millis() % 100000) == 0) 
  {
    Serial.println("generator");
  }

тогда все просто зависало, никаких сигналов небыло.

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

void setup() {
  
  //set port/pin  mode
  DDRD = B11111110;//all outputs
  DDRC = 0x00;//all inputs
  DDRB = 0xFF;//all outputs
  //TIMER INTERRUPT SETUP
  
  cli();//disable interrupts
  //timer 1:
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  //set compare match register- 100khz to start
  OCR1A = 159; // = (16 000 000 / 100 000) - 1 = 159
  //turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS10 bit for 0 prescaler
  TCCR1B |= (1 << CS10);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A); 
  
  samplerate = 100000;
  
  PORTB = 0;
  PORTB = 1<<type;
  
  //initialize variables
  frequency = analogRead(A5);//initialize frequency
  freqscaled = 48*frequency+1;//from 1 to ~50,000\
  period = samplerate/freqscaled;
   
  pulseWidth = analogRead(A4);//initalize pulse width
  pulseWidthScaled = int(pulseWidth/1023*period);
  
  triInc = 511/period;
  sawInc = 255/period;
  sinInc = 20000/period;
  
  sei();//enable interrupts
  
  Serial.begin(9600);
}

Подскажите, пожалуйста, как это можно обойти. Информацию по Serial можно обрабатывать не мгновенно. Через него будут просто переключаться режимы. Можно ли как-то остановить основную программу, считать данные, отправить ответ, и запустить дальше генератор? Я пробовал между noInterrupts() и interrupts() код вставлять, не помогло.

maksim
Offline
Зарегистрирован: 12.02.2012

Дело не в прерываниях, а в том что если вы посмотрите на схему, то увидите, что для генерации сигнала используется весь порт D (все 8 выводов порта), а на 0 и 1 выводах которого как раз и реализованаппаратный UART. У ATmega328 нет больше полных свободных портов, поэтому либо два бита выводите на другой порт, либо используйте программный UART на других выводах, но для этого вам прийдется перепаять FTDI на эти выводы, освободив 0 и 1 выводы порта D.

beautiful mind
Offline
Зарегистрирован: 06.09.2012

Я же напсал, что переделал схему. у меня ножки 0 и 1 свободны. И Serial работает, если я закомментирую представленный выше текст в setup(). И чтобы он работал порт не весь на выходы делаю, а так DDRD = B11111110;

maksim
Offline
Зарегистрирован: 12.02.2012

Так покажите что переделали или вы думаете здесь есть экстрасенсы? потому как если вы ничего не поменяли в обработчике ISR(TIMER1_COMPA_vect){ то ваша переделка бесполезна так как там есть строка PORTD = wave;

beautiful mind
Offline
Зарегистрирован: 06.09.2012

Да ну я же говорю, что освободил 0 и 1 ножку. Думал все понятно. 

Вот как переделал строку PORTD = wave;:

  PORTB = (wave >> 6) | (1<<(type+2));
  PORTD = wave << 2;

 

maksim
Offline
Зарегистрирован: 12.02.2012

Так не пойдет. Попробуйте так:

PORTB |= (wave >> 6) | (1<<(type+2));
PORTD |= wave << 2;

 

maksim
Offline
Зарегистрирован: 12.02.2012

Или даже так

PORTB |= (wave >> 6) | (1<<(type+2));
PORTD &= ~(255 << 2);
PORTD |= wave << 2;

 

maksim
Offline
Зарегистрирован: 12.02.2012

И еще даже вот так

PORTB &= ~(255 >> 6);
PORTB |= (wave >> 6) | (1<<(type+2));
PORTD &= ~(255 << 2);
PORTD |= wave << 2;

 

maksim
Offline
Зарегистрирован: 12.02.2012

Есть результат?

beautiful mind
Offline
Зарегистрирован: 06.09.2012

Ни к чему полезному это не привело) Только на осцилографе стал график рваный.

maksim
Offline
Зарегистрирован: 12.02.2012

Не привело, но прийдется оставить так или UART не будет работать на передачу. Так а виснуть перестало? А так пробовали? 

  if (Serial.available() > 0) 
  {
    cli();//disable interrupts
    char inChar = (char)Serial.read();
    if (inChar == 'n') Serial.println("generator"); 
    sei();//enable interrupts
  }

 

beautiful mind
Offline
Зарегистрирован: 06.09.2012

Залез тут в библиотеку TimerOne по аналогу сделал 2 такие функции:

void startTimer()
{
  //timer 1:
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  //set compare match register- 100khz to start
  OCR1A = 159; // = (16 000 000 / 100 000) - 1 = 159
  //turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS10 bit for 0 prescaler
  TCCR1B |= (1 << CS10);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
}

void stopTimer()
{
  TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
}

и теперь когда делаю так, то все работает:

stopTimer();
  if (Serial.available() > 0) 
  {
    
    // get incoming byte:
    char inChar = (char)Serial.read();
    if (inChar == 'n') Serial.println("generator");       
    
  }
  startTimer();

только частота меньше стала

beautiful mind
Offline
Зарегистрирован: 06.09.2012

пробовал не так, как у вас, а так:

  cli();
  if (Serial.available() > 0) 
  {
    
    // get incoming byte:
    char inChar = (char)Serial.read();
    if (inChar == 'n') Serial.println("generator");       
    
  }
  sei();

не помогло. и вообще мне кажется if (Serial.available() > 0) не выполняется, когда прерывания работают.

maksim
Offline
Зарегистрирован: 12.02.2012

а если так то не работает?


  if (Serial.available() > 0) 
  {
    stopTimer();
    // get incoming byte:
    char inChar = (char)Serial.read();
    if (inChar == 'n') Serial.println("generator");       
    startTimer();
  }
  

 

beautiful mind
Offline
Зарегистрирован: 06.09.2012

Да, не работает.

beautiful mind
Offline
Зарегистрирован: 06.09.2012

В общем на малых частотах можно работать так:

stopTimer();
  if (Serial.available() > 0) 
  {
    // get incoming byte:
    char inChar = (char)Serial.read();
    if (inChar == 'n') Serial.println("generator");
  }
  startTimer();

На больших уже картина ухудшается. Размазывается. Пока оставлю так. Если кто-то что-то подскажет, буду рад!

Дурачёк
Дурачёк аватар
Offline
Зарегистрирован: 25.11.2012

Картина размазывается всегда, просто на малых частотах это не очевидно.

Можно поиграться с RTS\DTS. Как минимум просто притащить RTS от FTDI на какой-нибудь пин, опрашиваемый в основном цикле, и открывать ком порт, только когда там что-то есть...

beautiful mind
Offline
Зарегистрирован: 06.09.2012

Вот эти ньюансы с RTS\DTS я как раз и не знаю) Еще одна проблема возникла. Если делать вот так:

String inString = "";
  stopTimer();
  while (Serial.available()) 
  {
    // get incoming byte:
    char inChar = (char)Serial.read();
    inString += inChar;
  }
  if (inString == "n") Serial.println("generator");
    else if (inString != "") Serial.println("Ser: " + inString);
  startTimer();

И отправить например: "f1234". То придет только f12, иногда f1, и очень редко больше доходит. если отправлять "f12345678901234567890". То один раз из десяти доходит 5 символов. В общем либо только первые 2-3 символа приходят только, а остальные пропадают, либо остальные на втором цикле читаются.

step962
Offline
Зарегистрирован: 23.05.2011

beautiful mind пишет:

В общем либо только первые 2-3 символа приходят только, а остальные пропадают, либо остальные на втором цикле читаются.

Так пропадают или на последующих циклах читаются?

Если первое, значит вы еще не умеете читать из последовательного порта. Дело поправимое.

Если второе, то что в этом такого удивительного? Процесс ведь асинхронный, передатчику нет дела до того, что вы там у себя в программе ожидаете - он строго по расписанию выдаст серию символов, а уж попадут они в один-единственный цикл или в несколько, зависит от сложности цикла (времени его прохождения) и метода чтения из порта.

beautiful mind
Offline
Зарегистрирован: 06.09.2012

Ну я говорю там когда как. Чаще проподают, чем во втором цикле доходят. Ну а читать может и не умею, в первый раз пробую что-то серьезное с Serial) Я как думал, если запустить цикл while (Serial.avalible()), то пока все данные не считаются с буфера, не выйдет программа из цикла. И следовательно думал, что в буфер если данных не сильно много, то все сразу записываются.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

По большому счету правильней ждать конца посылки, например, ждать символа перевода строки. Иначе Вы в цикле очищаете входной буфер inString, затем считываете часть строки. А в следующем loop снова очищаете inString и получаете остаток строки. Более того, Вы даже и не знаете, где кончится входная строка. Сначала определите протокол обмена.  Т.е. какие команды, какие у них параметры, как Вы определите на Ардуино, сколько символов в команде, сколько в ней параметров и какие они.

Еще, как вариант, со стороны компа посылать всю строку, а не посимвольно. Обычно это в настройках терминалов есть, но в ArduinoIDE мониторе - я не знаю, может это и есть.

Есть и другая сторона, если Вы не всю строку получаете, непонятно, где она теряется (возможно проблема пересечения интересов прерываний и Serial), но сначала лучше сделать правильное получение данных с компа по определенному протоколу.

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

 

Дурачёк
Дурачёк аватар
Offline
Зарегистрирован: 25.11.2012

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

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

RTS это третья нога FTDI, как правило не идущая куда-либо далее в схему.
Если оно слишком мелко для вас, можно зацепиться за DTR у дурины сброс по нему, так-что он как правило идёт на конденсатор не по далёку, к нему припаяться легоко, но у DTR есть своя специфика. На нём есть сигнал, вне зависимости от данных, а просто по факту открытия порта, программой, которая в свою очередь может делать это хоть на всю сессию. Но учитывая тот факт что абдурино использует DTR для сброса контроллера, а во время пользования дуриновским терминалом сброса не происходит, можно предположить, что DTR активен только на время посылки и вы можете слушать ком порт пока этот сигнал активен и глушить его, возвращая таймер, когда это не так...

dronsoft
Offline
Зарегистрирован: 02.06.2011

А отдельно можно реализовать сигнал пила? меня именно пила сигнал интересует

 

 

maksim
Offline
Зарегистрирован: 12.02.2012

Можно.

dronsoft
Offline
Зарегистрирован: 02.06.2011

А как я чет не догнал

 

dronsoft
Offline
Зарегистрирован: 02.06.2011

мне самое главное понять работу кода резистор какой взять

 

maksim
Offline
Зарегистрирован: 12.02.2012

В самом первом сообщении есть ссылка на проект, там есть схема, кнопочки со светодиодами можно убрать, оставив только потенциометры. В конце статьи есть ссылки на исходники, качаете их правите так что бы остался только режим "пила" и все.

dronsoft
Offline
Зарегистрирован: 02.06.2011

ок спасибо. будем пытаться