Нужна помощь в понимании зеркального вывода таймера AVR ATMEGA/ATTINY.

Я пытаюсь использовать Timer1 микроконтроллера Atmel AVR, либо AtMega328, который используется в Arduino, либо ATTiny85, для вывода двух тактовых сигналов, которые являются зеркальными отображениями друг друга. Частота, которую я пытаюсь сгенерировать, представляет собой переменную от 1 МГц до 2 МГц или более, что слишком высоко, чтобы сделать это с помощью кода для переключения выходных контактов, если я не хочу делать почти ничего другого в контроллере. Поэтому я хочу использовать выход таймера непосредственно на связанных выводах. Я использую набор инструментов GCC, поэтому не ограничен библиотеками или языком arduino.

Timer1 в Atmega328 имеет два контакта, связанных с ним, и я могу получить от них два идентичных сигнала от 1 МГц до 2 МГц. Хотя в техническом описании, кажется, говорится, что я могу получить инвертированную форму волны, это сбивает меня с толку. Я также могу получить два сигнала с разными рабочими циклами на частоте 1 МГц, используя настройки ШИМ с Timer1, но оба сигнала становятся высокими одновременно, более короткий сигнал становится низким раньше. Это не служит моему проекту. Мне даже не нужно изменение ширины импульса ШИМ, мне просто нужны два одинаковых сигнала типа «часы» с противоположной фазой, вот и все.

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

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

У меня уже есть кристалл на 20 МГц и конденсаторы, подключенные к плате, и часы на 20 МГц надежно работают на ATMega328. На плате ATTiny85 у меня стоит кварц 8МГц и он тоже работает надежно.

Пожалуйста помоги. Спасибо.


ОБНОВЛЕНИЕ . Пока что в ответах и ​​комментариях есть несколько неверных предположений, поэтому, возможно, мне следует уточнить: обратите внимание, что в моем исходном сообщении я заявил, что использую тактовую частоту 20 МГц, а не 8 МГц , а также что мне не нужен ШИМ .

Единственным режимом, дающим достаточно высокую выходную частоту, является режим CTC, потому что режимы PWM не работают для выходной частоты 2 МГц. Есть ли способ инвертировать либо выход A таймера 1, либо выход B в режиме CTC?

Теперь я переключился на стандартный Arduino Uno (ATMega328, 16 МГц) вместо своей собственной платы на 20 МГц, чтобы проверить свой код, и это мой код для хороших стабильных часов 2 МГц в режиме CTC с контактов 9 и 10, таймер 1 выходной контакт:

#define tick 9
#define tock 10

void setup() {
  pinMode(tick, OUTPUT);  
  pinMode(tock, OUTPUT); 

  TCCR1A = _BV(COM1A0) | _BV(COM1B0) ;   // activate both output pins 
  TCCR1B = _BV(WGM12)| 1;                // set CTC mode, prescaler mode 1

  // various frustrating attempts to invert OC1B failed. What do I put here?

  OCR1A = 3;                             // set the counter max for 2 MHz

}

void loop() {
}

Следы осциллографа для обоих контактов идентичны и синхронизированы, как я могу инвертировать любой из двух сигналов? Режим инвертирования в таблице данных, по-видимому, ничего не делает в режиме CTC. Я неправильно читаю техническое описание, или мне все-таки придется использовать более низкую частоту и режим ШИМ?

Чтобы добавить конкретный вопрос «награды» к моему исходному запросу:
Итак, какие изменения мне нужно внести в мой код выше, чтобы он давал идеально инвертированные сигналы на контактах 9 и 11 на максимально возможной частоте для часов 16 МГц , будь то то есть 2 мгц или нет?

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

Глядя на страницу 97-98 даташита atmega8L , там есть таблица режимов работы. На странице 108 указано: «Биты COM21:0 определяют, должен ли генерируемый выходной сигнал ШИМ инвертироваться или нет (инвертированный или неинвертированный ШИМ)». Держите нас в курсе ваших успехов!
Почему бы не использовать простой транзисторный инвертор для зеркально-красных выходов?

Ответы (2)

Из таблицы данных ATtiny85:

Режим работы, т. е. поведение выводов таймера/счетчика и выходного сравнения, определяется комбинацией режима генерации сигнала (WGM0[2:0]) и режима сравнения выходного сигнала (COM0x[1:0]). биты. Биты режима сравнения выходных данных не влияют на последовательность подсчета, в то время как биты режима генерации сигналов влияют. Биты COM0x[1:0] определяют, должен ли генерируемый выходной сигнал ШИМ инвертироваться или нет (инвертированный или неинвертированный ШИМ ).

В Таблице 11-5 показано, как установить режим.

Mode   WGM  WGM  WGM  Timer/Counter Mode    TOP      Update of    TOV Flag
c0     02   01   00   of Operation                   OCRx at      Set on
==========================================================================
0      0    0    0    Normal                0xFF     Immediate    MAX(1)
1      0    0    1    PWM, Phase Correct    0xFF     TOP          BOTTOM
2      0    1    0    CTC                   OCRA     Immediate    MAX
3      0    1    1    Fast PWM              0xFF     BOTTOM       MAX
4      1    0    0    Reserved              –        –            –
5      1    0    1    PWM, Phase Correct    OCRA     TOP          BOTTOM
6      1    1    0    Reserved              –        –            –
7      1    1    1    Fast PWM              OCRA     BOTTOM       TOP

Вам нужен режим Fast PWM (то есть либо режим 3, либо режим 7). Если вы хотите изменить рабочий цикл, и похоже, что вы это делаете, вам нужен режим 7 и варьируйте рабочий цикл, установив OCRA.

В Таблице 11-3 показано, как установить режим сравнения вывода для режима Fast PWM.

COM0A1/   COM0A0/
COM0B1    COM0B0     Description
===============================================================================
0         0          Normal port operation, OC0A/OC0B disconnected.
0         1          Reserved
1         0          Clear OC0A/OC0B on Compare Match, set OC0A/OC0B at BOTTOM
                     (non-inverting mode)
1         1          Set OC0A/OC0B on Compare Match, clear OC0A/OC0B at BOTTOM
                     (inverting mode)

Другими словами, вы можете установить низкий уровень выхода OC0A, когда значение таймера == OCR0A, и высокий уровень, когда значение таймера == 0x00, установив COM0A1:COM0A0 = 0b10. Или наоборот, установив COM0A1:COM0A0 = 0b11. И аналогично для OC0B, OCR0B, COM0B0, COM0B1.

Частота ШИМ определяется тактовой частотой ввода-вывода (для вас это звучит как 8 МГц) и настройкой предварительного делителя таймера. И уравнение задается как f_clk_IO / (N * 256) для режима Fast PWM.

Таким образом, вы можете использовать OC0A для «нормальной» полярности и OC0B для «инвертированной» полярности, установив OCR0A и OCR0B на одно и то же значение и установив COM0A1:COM0A0 = 0b10 и COM0B1:COM0B0 на 0b11.

ОБНОВИТЬ

Учитывая, что вы хотите переключать выход как можно быстрее и используете Mega328, работающую на частоте 16 МГц, рабочий режим CTC позволит вам получить частоту переключения:

f_OCnA = f_clk_IO / (2 * N * [1 + OCRnA) = 16e6 / (2 * 1 * [1 + 1]) = 4 МГц

Режим Fast PWM позволит вам переключать вывод в:

f_OCnxPWM = f_clk_IO / (N * [1 + TOP]) = 16e6 / (1 * [1 + 1]) = 8 МГц

Так что я все еще думаю, что вам нужен режим Fast PWM. В частности, режим 3 с OCR0A = OCR0B = 0x80 для рабочего цикла 50%. И установите биты COM0A в 0x3, а биты COM0B в 0x2, чтобы две формы волны на OC0A и OC0B инвертировали друг друга.

Обновление № 2 Больше Mega328 Попробуйте этот код Arduino:

#define tick 9
#define tock 10

void setup(){

  pinMode(tick, OUTPUT);  
  pinMode(tock, OUTPUT); 

  // Setup Waveform Generation Mode 15
  // OC1A Compare Output Mode = inverting mode
  // OC1B Compare Output Mode = non-inverting mode
  // Timer Prescaler = 1
  // TOP = OCR1A = 1

  //COM1A[1:0] = 0b11, COM1B[1:0] = 0b10, WGM1[1:0] = 0b11
  TCCR1A = _BV(COM1A1) | _BV(COM1A0) | _BV(COM1B1) | _BV(WGM11) | _BV(WGM10);

  //WGM1[3:2] = 0b11, CS1[2:0] = 0b001
  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);

  OCR1A = 0x0001;
  OCR1B = 0x0001;
}

void loop(){

}
Позвольте мне немного пожевать и посмотреть, работает ли это. Спасибо.
Перечитав ваш ответ, чтобы попробовать его сегодня, я вижу пару неверных предположений: я указал тактовую частоту 20 МГц (и теперь я переключился на 16 МГц), а не «(8 МГц, похоже, для вас)» . Также я указал, что мне не нужно изменение ширины импульса ШИМ, поэтому не знаю, где вы предположили: «Если вы хотите изменить рабочий цикл, и это звучит так, как будто вы это делаете» .
@ExcitingProjects Я отталкивался от вашего утверждения: «На печатной плате ATTiny85 у меня есть кварц 8 МГц, и он также надежно работает». и мой ответ относится к ATtiny85. Я постараюсь обновить свой ответ в ответ на ваш обновленный вопрос.
@vicateu Спасибо. Я обновил вопрос, поскольку режим инвертирования, похоже, не влияет на режим CTC, если только я не пропустил какой-то шаг.
@ExcitingProjects из таблицы данных ATmega328: «Для режимов без ШИМ биты COM0x1: 0 определяют, должен ли вывод быть установлен, очищен или переключен при совпадении сравнения»
@vicatcu Вопрос: Вы пробовали это? Я только что сделал это, изменив все на Timer1 в соответствии с требованием OP, и это не работает в моей сфере - оба сигнала становятся высокими одновременно, несмотря ни на что. Что может быть не так?
@AnindoGhosh нет, я лично не пробовал, но я просто опубликовал код Arduino, чтобы попробовать, и я уверен, что он должен работать
@vicatcu Странно ... Это не работает, и хоть убей, я не могу понять, что с ним не так: теперь у прицела только один штифт высокий, а другой низкий, даже не те часы, которые у нас были раньше. . Странный.
@AnindoGhosh попробуйте сделать OCR1A = OCR1B = 0x8000 для развлечения?
@vicatcu То же самое. Один высокий, один низкий, часов нет.
хм... почему бы ему не тикать.... я озадачен
@vicatcu Извините, не работает.

Семейство ATtinyX5 имеет PLL внутри, используйте его, большой мальчик.

Я также использую внутреннюю PLL для питания тактовой частоты процессора и имею 16 МГц без XTAL. Это ценно, так как у вас всего 5 булавок. (я не считаю пин-код сброса). Также один ШИМ с ФАПЧ (OCR1B) работает на контактах XTAL с дополнительным дополнительным выходом. Вам просто нужно отрегулировать предохранители для 16Mhz Xtalless ATtiny... Или просто позволить процессору работать на частоте 8Mhz, но запустить PWM с тактовой частотой 64Mhz без замены предохранителей..

Вы можете иметь ШИМ с тактовой частотой до 64 МГц (но разрешение 1 бит). Или 125 кГц при 8-битном разрешении. Вы можете уменьшить разрешение ШИМ и увеличить скорость, уменьшив значение регистра OCR1C.

Для 1 МГц вам нужно установить OCR1C на 63. Для 2 МГц вам нужно установить OCR1C на 31. Для 4 МГц вам нужно установить OCR1C на 15. ...

Просто включите PLL с помощью этого кода:

PLLCSR |= (1 << PLLE);           //Start PLL
while( !(PLLCSR & (1<<PLOCK)) ); //Wait for PLL lock
//PLLCSR |= (1<<LSM );           //Low Speed PLL that clocks 32Mhz, not 64Mhz
PLLCSR |= (1 << PCKE);           //Enable PLL

Теперь у вас есть тактовая частота 64 МГц на ШИМ "OCR1B0/OCR1A0".

Кроме того, вы можете настроить OCR1[A/B]0 и XOCR1[A/B]0 для зеркального вывода.

if(0){ //Synch mode
     //OCR1A & XOCR1A enable for Synch operation but not allow odd PWM values!
     TCCR1 |= (1 << PWM1A) | (0 << COM1A1) | (1 << COM1A0); 
     //Also ATtinyX5 has "Dead Time Generator", use it ;)
     DTPS1 = 3;   //8x Prescaler for dead time generator (maximum)
     DT1A = 0xff; //Clk dead on both channels (maximum)
     }
   else
     TCCR1 |= (1 << PWM1A) | (1 << COM1A1) | (0 << COM1A0);  //ONLY OCR1A enabled

Вы должны знать, что генератор мертвого времени будет потреблять выход PWM, если вы установите OCR1A = 1. Вам нужны более высокие значения, чем мертвое время.

С уважением,

Эрдем