Очень странное поведение DUE на прерываниях

Mulin.by
Offline
Зарегистрирован: 28.07.2016

Вообщем код запускает ШИМ на двух каналах:


#define BTN1 21
#define BTN2 20
#define BTN3 19

uint16_t duty = 21;
uint16_t freq = 42;
uint16_t n_impulse = 4;

void setup()
{
  Serial.begin(9600);
  pinMode(BTN1, INPUT);
  pinMode(BTN2, INPUT);
  pinMode(BTN3, INPUT);

  REG_PMC_PCER1 |= PMC_PCER1_PID33;
  REG_PIOC_ABSR |= PIO_ABSR_P26 | PIO_ABSR_P25;
  REG_PIOC_PDR |= PIO_PDR_P26 | PIO_PDR_P25;

  REG_TC2_CMR0 = TC_CMR_BCPC_CLEAR |
                 TC_CMR_ACPC_CLEAR |
                 TC_CMR_BCPB_SET |
                 TC_CMR_ACPA_SET |
                 TC_CMR_WAVE |
                 TC_CMR_WAVSEL_UP_RC |
                 TC_CMR_EEVT_XC0 |
                 TC_CMR_TCCLKS_TIMER_CLOCK1; 
                 
  REG_TC2_RC0 = freq;

  REG_TC2_IER0  = 0b00010000;
  REG_TC2_IDR0  = 0b11101111; 

  NVIC_EnableIRQ(TC6_IRQn);
  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;
}

void loop() {
  
  if (digitalRead(BTN1) == 0)
  {
    Serial.println("BTN1");
  }
  if (digitalRead(BTN2) == 0)
  {
    Serial.println("BTN2");
  }
  if (digitalRead(BTN3) == 0)
  {
    Serial.println("BTN3");
  }
}

void TC6_Handler(){
  static int i = 0;
  i++;
  uint16_t dummy = REG_TC2_SR0;
  REG_TC2_RB0 = freq;
  REG_TC2_RA0 = duty;
  if (i >= n_impulse)
  {
    REG_TC2_RA0 = freq;
    REG_TC2_RB0 = duty;
    
    i = 0;
  }
}

В обработчике события TC6_Handler() я формирую нужные мне формы сигнала на двух выводах. Частота freq и скважность импульса duty задаются отдельной функцие. В данном случае код максимально упрощен, но баг отлавливается. Значит первая страннойсть. Если в обработчике TC6_Handler() параметр REG_TC2_RA0 выглядит так как в коде выше, то есть:

REG_TC2_RA0 = duty;

То функция loop не работает от слова совсем. Если я параметр укажу прямо:

REG_TC2_RA0 = 21;

То функция loop работает условно замечательно. Почему условно. А здесь второй баг. Если я оставлю один обработчик кнопок, скажем так:

void loop() {
  
  if (digitalRead(BTN1) == 0)
  {
    Serial.println("BTN1");
  }
}

То функция не работает ни в случае прямой передачи переменной duty в REG_TC2_RA0=21, ни в случае  REG_TC2_RA0 = 21. 

Кто знает в чем дело? Хелп плиз

asam
asam аватар
Онлайн
Зарегистрирован: 12.12.2018

Глобальные переменные которые используются в прерывании должны быть объявлены как volatile иначе оптимизатор может напортачить.

Mulin.by
Offline
Зарегистрирован: 28.07.2016

Ах вот оно что! Спасибо огромное за помощь. Два дня потратил отлавливая баг)

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Есть ещё небольшая вероятность, что компилятор может строчку uint16_t dummy = REG_TC2_SR0  выкинуть (т.к. её результат нигде не используется) , тогда прерывание будет вечным, и до лупа очередь не дойдёт... :)
 

Mulin.by
Offline
Зарегистрирован: 28.07.2016

Вы тут один из самых гуристых гуру, может сможете подсказать. У меня в данном коде формируется сигнал на двух выходах с одной частотой, но с определенными пропусками. Примерно так:
Выход 1: _П_П_П___П_П_П___П_П_П_
Выход 2: _______П________П_______
То есть пропущенный импульс на выходе 1, формируется на выходе 2 с той же фазой, скважностью и частотой. Частоту, скважность и количество импульсов для пропуска я задаю сам. Но если я в функции loop занимаюсь опросом кнопок, у меня постоянно скачет количество пропущенных импульсов. Примерно так:

Выход 1: _П_П____П_П_П_П___П_П___П_П____П_П_П_П___П_П___
Выход 2: ______П__________П_____П______П__________П_____П_
 Если я ставлю прерывания, то ситуация чуть лучше. Вот пробую разные варианты типов встроенной переменной-счетчика отвечающей за количество импульсов:
static uint16_t i = 0;

volatile uint16_t i = 0;

volatile static uint16_t i = 0;

Так же ее выношу и вношу за пределы Слушателя TC6_Handler()
Во многом тыкаю пальцем в небо без особого понимания того, что делаю. Спасибо

Mulin.by
Offline
Зарегистрирован: 28.07.2016

dimax пишет:

Есть ещё небольшая вероятность, что компилятор может строчку uint16_t dummy = REG_TC2_SR0  выкинуть (т.к. её результат нигде не используется) , тогда прерывание будет вечным, и до лупа очередь не дойдёт... :)
 

 

Volatile же исправляет ситуацию?

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

Есть ли в DUE понятие приоритет прерываний? Дело в том, что такой стиль програмировария очень плох - смешивание ардуино и CMSYSа. Ардуина вешает свои прерывания, которые Вы не контролируете, и они могут вредить Вашим. И можно долго искать глюки. Поробуйте не использовать loop setup a main while. При этом не будет инициализироваться другая переферия и прерывания, кроме Ваших.

Mulin.by
Offline
Зарегистрирован: 28.07.2016

nik182 пишет:
Поробуйте не использовать loop setup a main while.

А как это должно выглядеть? можно простенький пример? Спасибо!

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

Первая ссылка из гугля https://ph0en1x.net/80-simple-program-for-avr-microcontroller-c-language...
И как только нажимается кнопка, начинается вывод в сериал. Если приоритет прерываний сериал выше чем таймера, будет та фигня, что Вы описали. Измените скорость вывода на 115200 и если дергаться будет меньше, значит так и есть. Надо приоритет своего прерывания ставить выше.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Mulin.by, для переменной i внутри прерывания ничего менять не нужно. Нужно только  глобальные freq duty  сделать volatile. Ещё мне не нравится содержание прерывания. Получается регистры RB0/RA0 вы сначала программируете одним, но если это был четвёртый вход -то тут же переписывате другим -так не делается.   

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

Mulin.by
Offline
Зарегистрирован: 28.07.2016

nik182 пишет:
Надо приоритет своего прерывания ставить выше.
Спасибо за ссылки. Нашел файл main и разобрался. Не подскажете как приоритеты прерываний изменить?

Mulin.by
Offline
Зарегистрирован: 28.07.2016

dimax пишет:

Получается регистры RB0/RA0 вы сначала программируете одним, но если это был четвёртый вход -то тут же переписываете другим -так не делается.   

А как быстрее с точки зрения процессорного времени? Операция записи, потом операция сравнения и снова операция записи, или Операция сравнения, затем запись, потом опять сравнение и запись? Вход в прерывание вроде соответствует частоте, ибо если он пропускает несколько фронтов в первом цикле, то эти же пропущенные фронты он добавляет во втором цикле. Еще интересно, что при низких частотах, он начинает пропускать единчный импульс на втором выходе. При чем начиная с определенной частоты

Mulin.by
Offline
Зарегистрирован: 28.07.2016

Такой код вроде правильнее, насколько я понял

volatile uint32_t dummy;
void TC6_Handler() {
  volatile static uint32_t i = 0;
  i++;
  dummy = REG_TC2_SR0;
  if (i < n_impulse)
  {
    REG_TC2_RB0 = freq;
    REG_TC2_RA0 = duty;
  }
  else
  {
    REG_TC2_RA0 = freq;
    REG_TC2_RB0 = duty;
    i = 0;
  }
}

 

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

Вы используете NVIC для поднятия прерывания. У NVIC есть другие функции, позволяющие установить приоритет прерывания до его поднятия. Посмотрите в описании и примените.

Mulin.by
Offline
Зарегистрирован: 28.07.2016

nik182 пишет:
Вы используете NVIC для поднятия прерывания. У NVIC есть другие функции, позволяющие установить приоритет прерывания до его поднятия. Посмотрите в описании и примените.
Я понял, спасибо, поищу

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Mulin.by, вы проверили работу после изменения переменных на volatile , какие флаги в dummy ?

Mulin.by
Offline
Зарегистрирован: 28.07.2016

dimax пишет:

Mulin.by, вы проверили работу после изменения переменных на volatile , какие флаги в dummy ?

Как буду за рабочим компом, проверю

Mulin.by
Offline
Зарегистрирован: 28.07.2016

Dummy я вывел в Serial. Плавает в трех значениях 6556 65556 65560. Как это интерпретировать не совсем понимаю. Вообщем по осциллографу пропуски импульсов происходят на определенной частоте +/-. Причем вот этот код работает условно нормально (пропуски на определенной частоте)
 

void TC6_Handler() {
  i++;
  dummy = REG_TC2_SR0;
  if (i >= n_impulse)
  {
    i = 0;   
    REG_TC2_RB0 = duty;
    REG_TC2_RA0 = freq;
  }
  else                    //C таким вариантом условия работает условно нормально
  {
    REG_TC2_RB0 = freq; 
    REG_TC2_RA0 = duty;
  }
}

А вот этот уже не отрабатывает второй if 

void TC6_Handler() {
  dummy = REG_TC2_SR0;
  if (i >= n_impulse)
  {
    i = 0;  
    REG_TC2_RB0 = duty;
    REG_TC2_RA0 = freq;
  }
  if (i < n_impulse)                //Вот так вроде не отрабатывет второй if
  {
    REG_TC2_RB0 = freq; 
    REG_TC2_RA0 = duty;
  }
}

Но если инкремент воткнуть во второй if как здесь:

void TC6_Handler() {

  dummy = REG_TC2_SR0;
  if (i >= n_impulse)
  {
    i = 0;   
    REG_TC2_RB0 = duty;
    REG_TC2_RA0 = freq;
  }
  if (i < n_impulse)
  {
    i++;                               //Инкремент переместил в условие
    REG_TC2_RB0 = freq; 
    REG_TC2_RA0 = duty;
  }
}

То опять начинаются пропуски, но уже во втором канале

Serial вообще отключал, все равно пропуски бывают. Такое ощущение, что не всегда при условии происходит присвоение значения REG_TC2_RA0 и REG_TC2_RB0. Еще замечено, что влияет на пропуски последовательность записи REG_TC2_RB0 и REG_TC2_RA0 то есть что стоит первое. 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Mulin.by, выложите полный скетч.

Mulin.by
Offline
Зарегистрирован: 28.07.2016

Убрал все лишнее оставил суть. Когда я докручиваю энкодером до частоты 525000 и до примерно 444000 начинается нестабильная работа например так:

Выход 1 _П_П_П___П_П_П_П__П_П_П
Выход 2 _______П_П______П______

 


#define ENA 17
#define ENB 18
#define ENSW 16

volatile char encoder_A; // Вывод энкодера 1
volatile char encoder_B; // Вывод энкодера 2
volatile char encoder_A_prev = 0;
volatile uint32_t i = 0; // Это счетчик внутри обработчика прерываний
volatile uint32_t dummy; // Это пустышка для очистки счетчика внутри обработчика

uint32_t freq = 42; // Количество тактов задающих частоту
uint32_t duty = 21; // Количество тактов задающих длительность
const uint16_t PWM_divider = 50; // Дискретизация ШИМ
const uint32_t clk_Freq = 21000000; // Частота контроллера /4

uint32_t freq2 = 635000;   //Частота в герцах
uint32_t duty2 = 50;       //Скважность в процентах
volatile uint32_t n_impulse = 4; //Количество импульсов для пропуска ниже как это выглядит
 // Выход 1 _П_П_П___П_П_П___П_П_П
 // Выход 2 _______П_______П______
 // Таким образом каждый четвертый импульс на втором выходе

void setup()
{
  attachInterrupt(ENA, listner, CHANGE);
  attachInterrupt(ENB, listner, CHANGE);
  attachInterrupt(ENSW, listner, CHANGE);

  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKDIS;      // Активирую таймер TC6
  REG_PMC_PCER1 |= PMC_PCER1_PID33;                 // Активирую переферию TC6 (TC2 Channel 0)
  REG_PIOC_ABSR |= PIO_ABSR_P26 | PIO_ABSR_P25;     // Подключаю выводы D4 и D5
  REG_PIOC_PDR |= PIO_PDR_P26 | PIO_PDR_P25;        // Отключаю иную переферию

  REG_TC2_CMR0 = TC_CMR_BCPC_CLEAR |                // Установливаю TIOB на совпадение с RC0
                 TC_CMR_ACPC_CLEAR |                // Установливаю TIOА на совпадение с RC0
                 TC_CMR_BCPB_SET |                  // Чет там делаю с B
                 TC_CMR_ACPA_SET |                  // Чет там делаю с А
                 TC_CMR_WAVE |                      // Реджим генератора
                 TC_CMR_WAVSEL_UP_RC |              // Автотригер RC
                 TC_CMR_EEVT_XC0 |                  // Set event selection to XC0 to make TIOB an output
                 TC_CMR_TCCLKS_TIMER_CLOCK1;        // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 48MHz)

  REG_TC2_RC0 = freq;
  REG_TC2_IER0  = 0b00010000;  // Включаю прерывания для = rc
  REG_TC2_IDR0  = 0b11101111;  // Отключаю все остальные прерывания
  // enable interrupt vector
  NVIC_EnableIRQ(TC6_IRQn);
  NVIC_SetPriority (TC6_IRQn, 0); // Задаю высший приоритет как выше писал nik182
  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Включаю и очищаю счетчик
}

void loop() {}


void listner()// Это обработчик для энкодера. Кручу в одну сторону, частота увеличивается, в другую - уменьшается
{
  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKDIS; // Выключаю и очищаю счетчик
  encoder_A = digitalRead(ENA);
  encoder_B = digitalRead(ENB);
  if ((!encoder_A) && (encoder_A_prev)) {
    if (encoder_B) {
      freq2 += 1000; //увеличиваем частоту
    }
    else {
      freq2 -= 1000; //уменьшаем частоту
    }
  }
  encoder_A_prev = encoder_A;
  
  // Вычисляю новую частоту
  freq = (clk_Freq * 2) / freq2; // Вычисленная частота ШИМ
  duty = freq * (PWM_divider - duty2 / 2) / PWM_divider; // Вычисленная скважность ШИМ
  setParam();// Вызов функции которая присваивает новые значения частоты
}
 
void setParam() // Функция которая присваивает новые значения частоты
{
  REG_TC2_RC0 = freq;                             // Устанавливаю новую частоту
  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;      //   Включаю и очищаю счетчик
}

void TC6_Handler() {

  i++;
  dummy = REG_TC2_SR0;
  if (i >= n_impulse)
  {
    i = 0;
    REG_TC2_RB0 = duty;
    REG_TC2_RA0 = freq;
  }
  else
  {
    REG_TC2_RB0 = freq;
    REG_TC2_RA0 = duty;
  }
}

 

Mulin.by
Offline
Зарегистрирован: 28.07.2016

Вот фото с осциллографа

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

Приоритет лучше задавать до запуска, а так все в порядке. Нужно посчитать сколько тактов живёт прерывание, со всеми входами и выходами. Укладывается в 2 микросекунды?

Mulin.by
Offline
Зарегистрирован: 28.07.2016

nik182 пишет:
Приоритет лучше задавать до запуска, а так все в порядке. Нужно посчитать сколько тактов живёт прерывание, со всеми входами и выходами. Укладывается в 2 микросекунды?

Такты прописаны в регистре REG_TC2_SR0 ? Если так, то 6556 65556 65560. Значение плавает

Mulin.by
Offline
Зарегистрирован: 28.07.2016

Заметил, что проблемная частота лежит в пределах freq = 84/85 duty = 42

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Mulin.by, вам стоило сразу написать про частоту. МК не может выполнять прерывания с такой высокой частотой, как я понял по умолчанию прерывания у вас в программе строчат  с частотой 1млн раз в секунду. Вернее выполнить прерывание именно Due сможет, но ни на что другое времени просто не остаётся.  Думаю не стоит настраивать прерывания чаще, чем 100 000 раз в секунду.

Mulin.by
Offline
Зарегистрирован: 28.07.2016

dimax пишет:

Mulin.by, вам стоило сразу написать про частоту. МК не может выполнять прерывания с такой высокой частотой, как я понял по умолчанию прерывания у вас в программе строчат  с частотой 1млн раз в секунду. Вернее выполнить прерывание именно Due сможет, но ни на что другое времени просто не остаётся.  Думаю не стоит настраивать прерывания чаще, чем 100 000 раз в секунду.

Вы имеете ввиду что я записываю значения в  REG_TC2_RA0 и  REG_TC2_RB0 по каждому прерыванию? А как в этом случае лучше поступить, может вы сможете подсказать? 

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Mulin.by, если использовать такие высокие частоты нужно принципиально, то придётся отказаться от прерываний совсем. Есть другой способ управлять сигналами - модуляция/стробирование. В общем связка двух и более  таймеров, когда один управляет другим.   Не уверен, что это возможно на  Due,  может есть смысл сразу перейти на stm32, там такие вещи возможны.

Mulin.by
Offline
Зарегистрирован: 28.07.2016

dimax пишет:

Mulin.by, если использовать такие высокие частоты нужно принципиально, то придётся отказаться от прерываний совсем. Есть другой способ управлять сигналами - модуляция/стробирование. В общем связка двух и более  таймеров, когда один управляет другим.   Не уверен, что это возможно на  Due,  может есть смысл сразу перейти на stm32, там такие вещи возможны.

Да уже даже плата готова и распаяна (( Я вроде каких-то успехов добился запихиванием в цикле двух массивов с данными. Примерно так сие выглядит:
 

#define ENA 17
#define ENB 18
#define ENSW 16

volatile char encoder_A; // Вывод энкодера 1
volatile char encoder_B; // Вывод энкодера 2
volatile char encoder_A_prev = 0;


volatile uint32_t freq = 84; // Количество тактов задающих частоту
volatile uint32_t duty = 42; // Количество тактов задающих длительность
volatile uint32_t n_impulse = 4; //Количество импульсов для пропуска ниже как это выглядит
const uint16_t PWM_divider = 50; // Дискретизация ШИМ
const uint32_t clk_Freq = 21000000; // Частота контроллера /4
volatile uint32_t freq2 = 635000;   //Частота в герцах
volatile uint32_t duty2 = 50;       //Скважность в процентах

volatile uint32_t imp1[1000];
volatile uint32_t imp2[1000];

void setup()
{

  attachInterrupt(ENA, listner, CHANGE);
  attachInterrupt(ENB, listner, CHANGE);
  attachInterrupt(ENSW, listner, CHANGE);

  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKDIS;      // Активирую таймер TC6
  REG_PMC_PCER1 |= PMC_PCER1_PID33;                 // Активирую переферию TC6 (TC2 Channel 0)
  REG_PIOC_ABSR |= PIO_ABSR_P26 | PIO_ABSR_P25;     // Подключаю выводы D4 и D5
  REG_PIOC_PDR |= PIO_PDR_P26 | PIO_PDR_P25;        // Отключаю иную переферию

  REG_TC2_CMR0 = TC_CMR_BCPC_CLEAR |                // Установливаю TIOB на совпадение с RC0
                 TC_CMR_ACPC_CLEAR |                // Установливаю TIOА на совпадение с RC0
                 TC_CMR_BCPB_SET |                  // Чет там делаю с B
                 TC_CMR_ACPA_SET |                  // Чет там делаю с А
                 TC_CMR_WAVE |                      // Реджим генератора
                 TC_CMR_WAVSEL_UP_RC |              // Автотригер RC
                 TC_CMR_EEVT_XC0 |                  // Set event selection to XC0 to make TIOB an output
                 TC_CMR_TCCLKS_TIMER_CLOCK1;        // Set the timer clock to TCLK1 (MCK/2 = 84MHz/2 = 48MHz)

  REG_TC2_RC0 = freq;
  REG_TC2_IER0  = 0b00010000;  // Включаю прерывания для = rc
  REG_TC2_IDR0  = 0b11101111;  // Отключаю все остальные прерывания
  // enable interrupt vector
  NVIC_SetPriority (TC6_IRQn, 0); // Задаю высший приоритет как выше писал nik182
  NVIC_EnableIRQ(TC6_IRQn);
  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Включаю и очищаю счетчик
}

void loop() {}

volatile uint32_t i = 0; // Это счетчик внутри обработчика прерываний
volatile uint32_t dummy; // Это пустышка для очистки счетчика внутри обработчика


void TC6_Handler() {

  i++;
  dummy = REG_TC2_SR0;
  REG_TC2_RA0 = imp2[i];
  REG_TC2_RB0 = imp1[i];
  if (i == n_impulse + 1)
  {
    i = 0;
  }
}

void listner()// Это обработчик для энкодера. Кручу в одну сторону, частота увеличивается, в другую - уменьшается
{
  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKDIS; // Выключаю и очищаю счетчик
  encoder_A = digitalRead(ENA);
  encoder_B = digitalRead(ENB);
  if ((!encoder_A) && (encoder_A_prev)) {
    if (encoder_B) {
      freq2 += 1000; //увеличиваем частоту
    }
    else {
      freq2 -= 1000; //уменьшаем частоту
    }
  }
  encoder_A_prev = encoder_A;
  
  // Вычисляю новую частоту
  freq = (clk_Freq * 2) / freq2; // Вычисленная частота ШИМ
  duty = freq * (PWM_divider - duty2 / 2) / PWM_divider; // Вычисленная скважность ШИМ
  
  for (int i = 0; i < n_impulse + 1; i++)
  {
    imp1[i] = freq;
    imp2[i] = duty;
    if (i == n_impulse)
    {
      imp1[i] = duty;
      imp2[i] = freq;
    }
  }
  setParam();// Вызов функции которая присваивает новые значения частоты
}

void setParam() // Функция которая присваивает новые значения частоты
{
  REG_TC2_RC0 = freq;                             // Устанавливаю новую частоту
  REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;      //   Включаю и очищаю счетчик
}

Но и там есть некоторые проблемы

Mulin.by
Offline
Зарегистрирован: 28.07.2016

У меня есть еще один вариант. В котором я запускаю два таймера. Первый дает постоянный меандр, второй с пропуском:
Выход 1:  _П_П_П_П_П_П_П_П_П_П

Выход 2:  _____П______П_____П_

Фронты на самом деле совпадают идеально. Может есть какой способ принудительно, не останавливая первый таймер, устанавливать его выход на 0 при единице на выходе 2? Здесь у меня совсем все просто и топорно. Зато работает стабильно, при чем верчу кручу скважностью импульсами частотами в реалайме
 

freq = (clk_Freq * 2) / freq2; // Вычисленная частота ШИМ
    duty = freq * (PWM_divider - duty2/2) / PWM_divider;  // Вычисленная скважность ШИМ

    REG_TC2_CMR0 |= TC_CMR_ASWTRG_CLEAR;

    REG_PMC_PCER0 |= PMC_PCER0_PID27;                 // Enable peripheral TC6 (TC2 Channel 0)
    REG_PIOB_ABSR |= PIO_ABSR_P25;     // Switch the multiplexer to peripheral B for TIOA6 and TIOB6 D5
    REG_PIOB_PDR |= PIO_PDR_P25;        // Disable the GPIO on the corresponding pins
    REG_TC0_WPMR  = 0x54494D00;  // enable write to registers
    REG_TC0_CMR0  = 0b00000000000001101100010000000000; // alternative CMR for inverted output
    REG_TC0_IER0  = 0b00010000;  // enable interrupt on counter = rc
    REG_TC0_IDR0  = 0b11101111;  // disable other interrupts
    NVIC_EnableIRQ(TC0_IRQn);
    NVIC_SetPriority (TC0_IRQn, 0);

    REG_PMC_PCER1 |= PMC_PCER1_PID33;                 // Enable peripheral TC6 (TC2 Channel 0)
    REG_PIOC_ABSR |= PIO_ABSR_P25;     // Switch the multiplexer to peripheral B for TIOA6 and TIOB6 D5
    REG_PIOC_PDR |= PIO_PDR_P25;        // Disable the GPIO on the corresponding pins
    REG_TC2_WPMR  = 0x54494D00;  // enable write to registers
    REG_TC2_CMR0  = 0b000000000000001101100010000000000; // alternative CMR for inverted output
    REG_TC2_IER0  = 0b00010000;  // enable interrupt on counter = rc
    REG_TC2_IDR0  = 0b11101111;  // disable other interrupts
    //NVIC_EnableIRQ(TC6_IRQn);

    counter1[n_impulse + 1];
    for (int i = 0; i <n_impulse + 1; i++)
    {
      counter1[i] = freq;
      if (i == n_impulse)
      {
        counter1[i] = freq *2;
      }
    }
    
    REG_TC0_RC0 = freq * (n_impulse+2); // рассчёт частоты
    REG_TC0_RA0 = duty;  // PWM value

    REG_TC2_RC0 = freq;               // Низкий уровень
    REG_TC2_RA0 = duty;                               // Высокий уровень
    
    REG_TC0_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Enable the timer TC6
    REG_TC2_CCR0 = TC_CCR_SWTRG | TC_CCR_CLKEN;       // Enable the timer TC6

А далее какой-нибудь обработчик вроде:
 

void TC0_Handler()
{
  long dummy = REG_TC0_SR0;
  if (на этом таймере высоко)
  {
    вывод первого таймера сделать низким
  }
  else
  {
    выводом первого таймера управляет счетчик
  {

}

 

Mulin.by
Offline
Зарегистрирован: 28.07.2016

Вообщем кому интересно поставил на выходе логический  исключающее-или SN74HC86 с фронтами 17нс, что является эквивалентом ~5мГц. На выходе небольшие пульсации сгладил пикофарадным конденсатором.