Генерация высокочастных импульсов с помощью ардуино Леонардо ATmega32u4

maclay
Offline
Зарегистрирован: 01.01.2013
leshak
Offline
Зарегистрирован: 29.09.2011

>Дергать цифровые выходы командой digitalWrite() не получается

И в чем это "не получается", проявляется?

>Можно дергать через регистры но боюсь испортить контроллер.

Зря. Собственно "много кода" digitalWrite это просто выяснение какой порт и какой бит нужно установить на разных платах. А, в итоге, это все равно будет запись в порт. Просто digitalWrite долго до него добирается.

Испортить контроллер вы можете только большоим током через ножку. Например ногу подключенную напрямую к земле, включить на выход и дать в него HIGH. А чем вы это сделаете - digitalWrite или прямой записью - не важно.

Так чтоhttp://arduino.ru/Tutorial/Upravlenie_portami_cherez_registry.  Но  вначале неплохо-бы убедится что digitalWrite действительно не справляется с задачей. 

>Я понял как работать с таймером на прерываниях а вот как быстро дёргать цифровой выход проблема.

Погуглите "arduino секреты PWM" (вот к примеру http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM), еще может помочь книга "arduino cookbook". Ну либо вкуриватся в даташиты в раздел работы с таймером самостоятельно. Cмотреть как сконфигурировать таймер, что-бы он по переполнению самостоятельно переключал ногу, без отвелечение проца на это. 

Кстатит искать это можно не только "в ардуино" сайтах, но и про про AVR микроконтроллеры.

А ишо можно попробовать воспользоватся библиотекой Tone. Не знаю правда какую частоту из нее можно выжать.

 

 

 

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

Задача поставлена слишком неконкретно, потому сказать что либо нереально. Многие вопросы уже leshak объяснил

maclay
Offline
Зарегистрирован: 01.01.2013

Согласен задачу нужно уточнить. Сейчас нужно заставить генерировать серию импульсов длительностью 0, 5 сек. с четырьмя частотами (1МГц, 500КГц, 100 КГц и 50 КГц). Запуск генерации начинается по команде с ком порта. До 100 КГц как я понял более или менее нормально а вот с высокими частотами все трудно, чего не получается.

Прошу помощи сам пока новичок

 

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

Как я понял, ком порт дает конфигурацию и старт. Если так, то возможно получится. Хотя бы один близкий пример последовательности можете привести?
Конкретно сколько импульсов, какой частоты, время между переключениями частот и т.п.

maclay
Offline
Зарегистрирован: 01.01.2013

Пакет конфига такой <A B> 

где А - частота импульсов а В - длительность серии импульсов.

 

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

 

 

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

Попробуйте так - идёте в arduino\hardware\arduino\cores\arduino находите файл Tone.CPP в нем находите функцию void tone(uint8_t _pin, unsigned int frequency, unsigned long duration)... меняете int(выделен жирным) на long. Сохраняете файл, запускаете ArduinoIDE и пользуетесь функцией tone. Теоретически должна работать, если есть осцилограф, то проверьте посмотрите так ли оно или хотябы частотомером мерьте.

maclay
Offline
Зарегистрирован: 01.01.2013

Так и сделал, при компиляции выдала ошибку undefined reference to `tone(unsigned char, unsigned int, unsigned long)'

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

В тойже папке есть файл Arduino.h в нем в конце void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); тоже int на long исправьте.

maclay
Offline
Зарегистрирован: 01.01.2013

Нашел обьявление функции в Arduino.h подправил и его буду проверять)

maclay
Offline
Зарегистрирован: 01.01.2013

Заработало но больше 60 КГц не повышается если выставить 300 КГц и период 5 секунд идет сигнал в 60 КГц но не 5 сек а почти 25 секунд

maclay
Offline
Зарегистрирован: 01.01.2013

Тоесть видимо количество импульсов правильно считает а вот интервал непрвильно считает видимо внутренние переменные тоже надо менять. Давно не програмировал но вроде вспоминаю быстро)

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

В Tone.cpp в функции tone во всех строках с расчетом ocr = F_CPU / frequency / 2 / 8 - 1; добавьте ко всем делителям букву L :

ocr = F_CPU / frequency / 2L  - 1; 
.....

ocr = F_CPU / frequency / 2L / 8L - 1; 
.....
и т.д.

но мне кажется, что дело уже не в этом...

maclay
Offline
Зарегистрирован: 01.01.2013

Да не помогло(

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Судя по всему, функция софтовая, хоть и использует таймер0. Больше не сможет. Остается таймер, но там можно будет получить тоько фиксированную сетку частот. Если не устроит - то только внешний DSP

maclay
Offline
Зарегистрирован: 01.01.2013

Да устроит, частоты фиксированые 50КГц, 100КГц, 500КГц и 1МГц. Но у меня опыта работы с прерываниями 0. ( В плане программирования на с++ более или менее нормально но С на компе преподавали а с железом мало работал. Курю мануалы уже 3и сутки а каменный цветок не выходит. Результат надо завтра послезавтра показывать а у меня максимум 60 КГц. На одном буржуйском сайте создатель tone.cpp писал что до 1 мегагерца реально дотянуть, мне в принципе и более маленькая частота устроит но не менее 500 КГц

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

Имелось ввиду, что они фиксированы не так как вы захотите, а как может их генерить МК - в зависимости от предделителя и тактовой частоты.

maclay
Offline
Зарегистрирован: 01.01.2013

Хм. по идее тактовая частота атмеги 16МГц поделив на 8 получим 2МГц. Этого должно хватить для генерации импульсов частотой 1МГц? а поделив на 16 можем сгенерировать 500 КГц, а на 64 125КГц и использовав предделитель 128 получаем 62,5 КГц этого более чем достаточно.

 

maclay
Offline
Зарегистрирован: 01.01.2013

Вопрос как мне безопасно дергать цифровой выход? И какой? Как я понял 1 и 2 нельзя 8 вроде безопасно, но дергать их надо через регистры, я боюсь получить мертвый камень, програматора нет, есть более старая ардуина на атмеге 328 её пока колупаю. Но за неё тоже переживаю не хочется трупов)

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

Что вы за них переживаете? ничего с ними не случится, и что вы так боитесь регистров? Какая у вас дуина на 328 атмеге?

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

Проверьте, будет ли 1 мегагерц на 9 цифровом выводе? Обратите внимание на первую строку.

#define OC1A 1 // для ATmega32U4 - 5, а для для ATmega328 - 1

void setup()
{
  cli();
  DDRB |= 1<<OC1A;         
  PORTB &= ~(1<<OC1A);
  TCCR1A = 0b01000000;   //при совпадении уровень OC1A меняется на противоположный
  TCCR1B = 0b00000010;   //CLK/8
  uint16_t c = 1;
  OCR1AH = highByte(c);  
  OCR1AL = lowByte(c);
  TIMSK1 = 0b00000010;   //разрешаем прерывание по совпадению
  sei();                 
}


ISR(TIMER1_COMPA_vect)   //обработчик прерывания по совпадению 
{
  TCNT1H = 0;            
  TCNT1L = 0;
}

void loop() {}

 

maclay
Offline
Зарегистрирован: 01.01.2013

Проверил, измерил осцилографом получилось 256 МГц сейчас попробую делитель уменьшить.

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

В функции

#define OC1A 1 // для ATmega32U4 - 5, а для для ATmega328 - 1

void setup()
{
  cli();
  DDRB |= 1<<OC1A;         
  PORTB &= ~(1<<OC1A);
  TCCR1A = 0b01000000;   //при совпадении уровень OC1A меняется на противоположный
  TCCR1B = 0b00000010;   //CLK/8
  uint16_t c = 1;
  OCR1AH = highByte(c);  
  OCR1AL = lowByte(c);
  TIMSK1 = 0b00000010;   //разрешаем прерывание по совпадению
  sei();                 
}

не удалось разглядеть включения режима CTC (set bit WG12/WGM12 to 1; WG13/WGM13 - факультативно). Для этого необходимо дополнительно установить бит 3 регистра TCCR1B:

TCCR1B = 0b00001010;   //CTC and CLK/8

В противном случае счетчик будет находиться в обычном режиме и генерировать прерывание TIMER1_OVF при достижении значения TOP=0xFFFF, т.е. при переполнении счетчика. Прерывания по совпадению (TIMER1_COMPA) не произойдет. Ибо даташит гласит:

"The simplest mode of operation is the Normal mode (WGMn3:0 = 0). In this mode the counting direction is always up (incrementing), and no counter clear is performed. The counter simply overruns when it passes its maximum 16-bit value (MAX = 0xFFFF) and then restarts from the BOTTOM (0x0000)."

И таки да - здесь настоятельно необходимо уходить от магических чисел и использовать макроопределения. Т.е. вместо

TCCR1A = 0b01000000;   //при совпадении уровень OC1A меняется на противоположный
TCCR1B = 0b00001010;   //CTC and CLK/8

писать (вариант для ATMega328)

TCCR1A = _BV(COM1A0);   //при совпадении уровень OC1A меняется на противоположный
TCCR1B = _BV(WG12) | _BV(CS11);   //CTC and CLK/8

для ATMega32u4, соответственно, не WGnX а WGMnX:

TCCR1A = _BV(COM1A0);   //при совпадении уровень OC1A меняется на противоположный
TCCR1B = _BV(WGM12) | _BV(CS11);   //CTC and CLK/8

и так далее.

Меньше шансов запутаться. Проще сверяться с даташитом. Дидактичнее.

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

maclay пишет:

Проверил, измерил осцилографом получилось 256 МГц сейчас попробую делитель уменьшить.

Что-то у вас не в порядке с осциллографом. Может быть Гц? ATMega, затактированная на 16 МГц никак не может выдавать на гора частоту больше своей тактовой.

maclay
Offline
Зарегистрирован: 01.01.2013

Опечатался 250 килогерц

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

 

step962 пишет:

В функции

#define OC1A 1 // для ATmega32U4 - 5, а для для ATmega328 - 1

void setup()
{
  cli();
  DDRB |= 1<<OC1A;         
  PORTB &= ~(1<<OC1A);
  TCCR1A = 0b01000000;   //при совпадении уровень OC1A меняется на противоположный
  TCCR1B = 0b00000010;   //CLK/8
  uint16_t c = 1;
  OCR1AH = highByte(c);  
  OCR1AL = lowByte(c);
  TIMSK1 = 0b00000010;   //разрешаем прерывание по совпадению
  sei();                 
}

не удалось разглядеть включения режима CTC (set bit WG12/WGM12 to 1; WG13/WGM13 - факультативно). Для этого необходимо дополнительно установить бит 3 регистра TCCR1B:

TCCR1B = 0b00001010;   //CTC and CLK/8

В противном случае счетчик будет находиться в обычном режиме и генерировать прерывание TIMER1_OVF при достижении значения TOP=0xFFFF, т.е. при переполнении счетчика. Прерывания по совпадению (TIMER1_COMPA) не произойдет. Ибо даташит гласит:

"The simplest mode of operation is the Normal mode (WGMn3:0 = 0). In this mode the counting direction is always up (incrementing), and no counter clear is performed. The counter simply overruns when it passes its maximum 16-bit value (MAX = 0xFFFF) and then restarts from the BOTTOM (0x0000)."

Да, точно.

step962 пишет:

И таки да - здесь настоятельно необходимо уходить от магических чисел и использовать макроопределения. Т.е. вместо

TCCR1A = 0b01000000;   //при совпадении уровень OC1A меняется на противоположный
TCCR1B = 0b00001010;   //CTC and CLK/8

писать (вариант для ATMega328)

TCCR1A = _BV(COM1A0);   //при совпадении уровень OC1A меняется на противоположный
TCCR1B = _BV(WG12) | _BV(CS11);   //CTC and CLK/8

для ATMega32u4, соответственно, не WGnX а WGMnX:

TCCR1A = _BV(COM1A0);   //при совпадении уровень OC1A меняется на противоположный
TCCR1B = _BV(WGM12) | _BV(CS11);   //CTC and CLK/8

и так далее.

Меньше шансов запутаться. Проще сверяться с даташитом. Дидактичнее.

А вот с этим... не знаю по какой причине, НО уже не первый раз наблюдаю у людей проблему со стандартными хэдэрами для МК, то есть IDE на имена битов тупо ругается, поэтому и "магические числа".

maclay
Offline
Зарегистрирован: 01.01.2013

Проверил, на буквы ide действительно заругался, использовал магические числа. осцилограф dso nano показал 270 КГц. но у него частота дискретизации 1 МГц, может импульсы просто сливаются? форма сигналов стала более неравномерная но это опятьже может быть косяком дискретизации

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

А если так

#define OC1A 1 // для ATmega32U4 - 5, а для для ATmega328 - 1

void setup()
{
  cli();
  DDRB |= 1<<OC1A;         
  PORTB &= ~(1<<OC1A);
  TCCR1A = 0b01000000;   //при совпадении уровень OC1A меняется на противоположный
  TCCR1B = 0b00001001;   //CLK
  uint16_t c = 8;
  OCR1AH = highByte(c);  
  OCR1AL = lowByte(c);
  TIMSK1 = 0b00000010;   //разрешаем прерывание по совпадению
  sei();                 
}

void loop() {}

И вы так и не ответили, на какой дуине тестируете?

maclay
Offline
Зарегистрирован: 01.01.2013

Сейчас тестирую на 328

maclay
Offline
Зарегистрирован: 01.01.2013

сейчас показывает  113 КГц форма сигнала стала неравномаерная

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

У вас что то с осцилографом, не на чем больше померить? А попробуйте в 10 строке 8 заменить на 1.

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

Лучше 8 заменить на 49999 - должны получить частоту 16000000/2/50000=160 Гц. Легче контролировать частоту - даже плохоньким осциллографом или тем же скетчем (завести выходную ногу на любой цифровой пин и, считывая его состояние в цикле loop(), определить время между фронтами/рассчитать частоту).

maclay
Offline
Зарегистрирован: 01.01.2013

Возможно осцилограф гонит, другого нет пока другой обещали дать послезавтра. 32u4 нет в списке ide, я под убунтой сижу. пока леонардо в её списке нет(

 

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

Леонардо нет, потому что у вас версия IDE ниже 1.0.1, да и не надо леонардо. Так что у вас осцил показывает с 1 и с 49999 ?

maclay
Offline
Зарегистрирован: 01.01.2013

 

Нашел хороший осцилограф без последних изменений показывает 750 КГц, это тоже неплохо. Завтра изменю программу и посмотрю что покажет с последними правками. Машину с иде настраиваю

maclay
Offline
Зарегистрирован: 01.01.2013

Долго разбирался с аппаратной частью не касающейся генератора но недавно вернулся к генератору. После экспериментов с параметром uint16_t c = 8 получил искомый мегагерц) и даже больше. Теперь встал вопрос об изменении частоты генерации а также об её остановке.

Как вынести запуск генерации в отдельную функцию?

И можно ли добавить прерывание которое будет запускать и останавливать генерацию?

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

Вот ряд частот, которого можно добиться 

8.000 МHz
4.000 МHz 
2.666 МHz
2.000 МHz
1.600 МHz
1.333 МHz
1.143 МHz
1.000 МHz
888 kHz
800 kHz
727 kHz
666 kHz
615 kHz
571 kHz
533 kHz
500 kHz
470 kHz
444 kHz
421 kHz
400 kHz
380 kHz
363 kHz
347 kHz
333 kHz
320 kHz
307 kHz
296 kHz
285 kHz
275 kHz
266 kHz
258 kHz
250 kHz
242 kHz
235 kHz
228 kHz
222 kHz
216 kHz
210 kHz
205 kHz
200 kHz
195 kHz
190 kHz
186 kHz
181 kHz
177 kHz
173 kHz
170 kHz
166 kHz
163 kHz
160 kHz
156 kHz
153 kHz
150 kHz
148 kHz
145 kHz
142 kHz
140 kHz
137 kHz
135 kHz
133 kHz
131 kHz
129 kHz
126 kHz
и т.д.

 

maclay
Offline
Зарегистрирован: 01.01.2013

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

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

Вы вроде хотели частоту задавать из сериал-монитора?

maclay
Offline
Зарегистрирован: 01.01.2013

Да именно так, с ком портом разобрался а вот как менять частоту и запускать-останавливать генерацию понять не могу. По идее надо это вынести в отдельную функцию как tone. Начал её разбирать но понял что только сильнее запутался.

Да и еще один момент при попытке сгенерировать 50 кГц ардуина начала выдавать два быстрых импульса и длинный промежуток между ними.

 

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

В общем методом проб удалось выяснить следующее - на частотах свеше 320 кГц перестает работать сериал, хотя не знаю как себя будет вести сериал на ATmega32u4, потому как проверяю на ATmega328.

#define OUT 9          // вывод выходного сигнала

unsigned long frequency = 0;  // частота в Гц 

void setup() 
{
  Serial.begin(9600); 
  pinMode(OUT, OUTPUT);
  Serial.println("START");
}

void loop()
{
  if(Serial.available()) Set_frequency(Serial.parseInt());
}



void Set_frequency(uint32_t freq)
{
  uint16_t ocr; 
  cli();
  if(freq)
  {
    TCCR1A = 0b01000000; 
    if(freq < 134) 
    {
      TCCR1B = 0b00001010; 
      ocr = 1000000/freq - 1;
      freq = 1000000/(ocr+1);
    }
    else 
    {
      TCCR1B = 0b00001001;
      ocr = 8000000/freq - 1;
      freq = 8000000/(ocr+1);    
    }
    
    OCR1AH = highByte(ocr);  
    OCR1AL = lowByte(ocr);
    TIMSK1 = 0b00000010;
  }
  else
  {
    TCCR1A = 0;
    TCCR1B = 0; 
    TIMSK1 = 0;
    freq = 0;
  }
  sei();
  Serial.println(freq); 
}

ISR(TIMER1_COMPA_vect){
}

Заливаете скейтч, запускаете сериал-монитор, пишите в него нужную частоту в Гц, нажимаете Send, в ответ приходит значение реальной частоты и на 9 ноге генерится сигнал.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

В леонардо можно задействовать PLL для получения высокочастотного ШИМ. Сам, правда, не пробовал.

maclay
Offline
Зарегистрирован: 01.01.2013

Вести с полей)) Делал усилитель сигнала, много времени ушло.

Но есть проблемма решения которой я не нашел. После выставления частоты выше 500 кГц ардуина переставала отвечать на команды. После отключения сериал монитора ардуина перестаёт генерировать сигнал.

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

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

Походу просто не хватает процессорного времени при генерации такой частоты, не работает ни millis() ни delay() ни _delay_ms () ни while(), так что походу только ресет.

#define OUT 9          // вывод выходного сигнала

unsigned long frequency = 0;  // частота в Гц 

void setup() 
{
  Serial.begin(9600);
  pinMode(OUT, OUTPUT); 
  Serial.setTimeout(10);
}

void loop()
{
  delay(5);
  Serial.print("Frequency: ");
  while(!Serial.available()); 
  Set_frequency(Serial.parseInt());
  Serial.println(" Hz");
  delay(10);  
  TIMSK1 = 0b00000010;
  TCCR1A = 0b01000000;
}

void Set_frequency(uint32_t freq)
{
  uint16_t ocr; 
  cli();
  if(freq > 15)
  {
    if(freq < 134) 
    {
      ocr = 1000000/freq - 1;
      freq = 1000000/(ocr+1);
      TCCR1B = 0b00001010; 
    }
    else 
    {
      ocr = 8000000/freq - 1;
      freq = 8000000/(ocr+1);  
      TCCR1B = 0b00001001;       
    }
    OCR1AH = highByte(ocr);  
    OCR1AL = lowByte(ocr);
  }
  else
  {
    TCCR1B = 0; 
    freq = 0;
  }
  Serial.print(freq);
  sei();

}

ISR(TIMER1_COMPA_vect){}

 

maclay
Offline
Зарегистрирован: 01.01.2013

Тоесть если сбрасывается уарт происходит ресет ардуины? Но ведь прерывания забиты и опросить порт она не может

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

Если UART завис, а нужно выбрать другую частоту, то нажимаете RESET.

maclay
Offline
Зарегистрирован: 01.01.2013

Закрытие окна сериал монитора приводит к прекращению генерации даже мегагерца. Как я пония при этом посылается "волшебный" пакет на ардуину как я понял это ат команда установки обрыва связи.

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

Так а зачем вы его закрываете, чем вам это окно мешает? У вас просто дуина ресетится и все.

maclay
Offline
Зарегистрирован: 01.01.2013

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

leshak
Offline
Зарегистрирован: 29.09.2011

maksim пишет:

Так а зачем вы его закрываете, чем вам это окно мешает? У вас просто дуина ресетится и все.

А чего она ресетится на ЗАКРЫТИЕ? Или это именно леонардовский прикол? Обычно же вроде на открытие ресет идет?

UPD: глянул в описалово леонардо. Он и на открытие не должен ресетится. У него ребут идет при открытии/закрытии порта на 1200

UPD2: попроуйте сменить на Serail.begin на другую скорость. Возможно из за муток с таймерами у него сносит башню и он опознает текущиую скорость как 1200, что в свою очередь, выглядит как "нужно ребутнутся при закрытии".

maclay
Offline
Зарегистрирован: 01.01.2013

Все опыты пока ставлю на 328 атмеге но в понедельник проверю на леонардо.

На открытие вроде не ресетится при  бросе пишет start в ком порт но если переподключаешься при генерации частоты ниже 500 кГц start не приходит, соответственно ребута нет.