Проблема пользовательского ШИМ таймера Arduino 4

Я сделал хороший код, который генерирует быструю ШИМ с рабочим циклом 50%, и я могу изменить частоту с помощью потенциометра. Он выводит прямые и инвертированные каналы с некоторым мертвым временем. Я использую Arduino Micro, также известную как ATmega32U4. Код на самом деле является кодом "Atmel". Код работает нормально, пока я не выключу Arduino Micro, а затем снова включу его.

Я запрограммировал код и регистры так, чтобы частота изменялась от 10 кГц до 100 кГц. Но после включения/выключения питания частота меняется с 5кГц на 50кГц. После того, как это произошло, мне нужно снова запрограммировать плату с помощью Arduino IDE, чтобы она работала правильно. Опять же после включения/выключения питания он изменился. Я совершенно уверен, что один из регистров перезаписан «уровнем аппаратной абстракции Arduino» или как бы мы его ни называли. Я еще не прочитал все регистры, поэтому не знаю, какой из них перезаписан. Я предполагаю, что это прескалер. Как предотвратить это? Должен ли я записать содержимое регистра в другое место? Или я должен написать это несколько раз, чтобы быть уверенным? Почему или как это вообще происходит?

Вот код:

#define OSC1  5
#define OSC2  13

uint8_t read_reg1;
uint8_t read_reg2;
int pot, freq; 

void setup() {
  pinMode(OSC1, OUTPUT); 
  pinMode(OSC2, OUTPUT); 
  Serial.begin(9600);

  cli();  // disable global interrupts

  TCCR4A=0; // clear register
  TCCR4B=0x06; // configure prescaler to 64 (CK = CLK / 64 = 1.5 MHz)
  TCCR4C=0;
  TCCR4D=0; // select Fast PWM operation (0 << WGM41)|(0 << WGM40)
  PLLFRQ=(PLLFRQ&0xCF)|0x30; // select clock source and frequency
  OCR4C=150; // select PWM frequency
  OCR4A=150/2; // set duty cycle
  DT4 = 0x55; // set dead times. DT = (1 / 48Mhz) * 0...15

  // enable interrupt on timer4 overflow
  TIMSK4|=(1 << TOIE4);

  // This register write has to be after others. Otherwise the PWM generation will not work. I do not know why. 
  TCCR4A=0x42; // COM4A1..0 = 01, OC4A and !OC4A connected. PWM4A = 1 (activate channel A PWM output)

  sei(); // enable global interrupts
}

void loop() {
  //cli();
  pot = analogRead(A0);
  freq = map(pot, 0, 1023, 14, 166);
  //sei();
  /*
  Serial.print("Pot value: ");
  Serial.print(pot);
  Serial.print("\tFreq value: ");  
  Serial.println(1500000/freq);
  */

}

ISR(TIMER4_OVF_vect){
  OCR4C = freq;
  OCR4A = freq / 2; 
}

person Jüri Bogatkin    schedule 21.02.2019    source источник


Ответы (1)


Я точно не знаю, почему вы сразу после программирования ведете себя иначе, но загрузчик, который использует Arduino Micro (Caterina), не выполняет полный сброс после запуска, поэтому изменения, внесенные загрузчиком в регистры AVR, часто видны для эскиз пользователя.

Мне удалось решить проблему, удалив строку, изменяющую PLLFRQ. Вот упрощенная версия вашего кода, который всегда производит ШИМ 3,31 кГц:

void setup()
{
  pinMode(5, OUTPUT); 
  pinMode(13, OUTPUT);

  TCCR4A = 0;
  TCCR4B = 0x06; // configure prescaler to 64 (CK = CLK / 64 = 1.5 MHz)
  TCCR4C = 0;
  TCCR4D = 0; // select Fast PWM operation (0 << WGM41)|(0 << WGM40)
  OCR4C = 150; // select PWM frequency
  OCR4A = 150 / 2; // set duty cycle
  DT4 = 0x55; // set dead times. DT = (1 / 48Mhz) * 0...15

  // This register write has to be after others.
  // Otherwise the PWM generation will not work. I do not know why. 
  // COM4A1..0 = 01, OC4A and !OC4A connected.
  // PWM4A = 1 (activate channel A PWM output)
  TCCR4A = 0x42;
}

void loop()
{
}

Не стоит возиться с постскейлером PLL, так как это, вероятно, повлияет на любую другую библиотеку Arduino, использующую таймеры, включая стек USB.

person David Grayson    schedule 22.02.2019