Jump to content
    

GD32F103RBT6. Пропуск прерываний в режиме захвата

В GD32F103RBT6 имеется Timer1. В Timer1 имеется 4 канала. Каждый канал можно настроить в режим захвата по фронту/срезу на пине контроллера и настроить соответствующие прерывания. Собственно я так и настроил. Функция обработки прерывания от Timer1 выглядит так:

void TIMER1_IRQHandler (void)
{
uint32_t temp = 0;
uint8_t  ch   = 0; // Переменная определяет номер канала.

static uint16_t prev_tim_capture_reg_val [NUMBER_OF_DALI_CHANNELS] = {0}; // Предыдущее значение регистра захвата для каждого канала.

//---If channel 0 capture/compare interrupt flag is set---//
if (TIMER_INTF(TIMER1) & TIMER_INTF_CH0IF) // If channel 0 capture/compare interrupt flag is set.
  {
  //---Input capture event--------------------------------------------------------------//

  //-Clear capture/compare interrupt flag-//
  TIMER_INTF(TIMER1) &= (~TIMER_INTF_CH0IF);
//      NVIC_ClearPendingIRQ(TIMER1_IRQn);
  //--------------------------------------//

  ch   = 0; // Выбор номера канала.
  temp = TIMER_CH0CV(TIMER1) & 0xFFFF;

  if (wait_start_bit_flag[ch] == RESET)
    {
    if (temp > prev_tim_capture_reg_val[ch])
      PulseDuration[ch] = temp - prev_tim_capture_reg_val[ch];
    else
      PulseDuration[ch] = 0xFFFF - prev_tim_capture_reg_val[ch] + temp;
    }
  else
    PulseDuration[ch] = 0; // Сброс счётчика длительности импульса.

  prev_tim_capture_reg_val[ch] = temp;

  if (TIMER_CHCTL2(TIMER1) & TIMER_CHCTL2_CH0P) // Если было настроено прерывание на срез...
    {
    CallbackInputPulse_ch0(DALI_BUS_LOG0, PulseDuration[ch]);

    // Раз словили срез, то надо начинать отслеживать stop, отслеживаем его после каждого среза.
    StopDuration[ch] = 0; // Для старта отслеживания stop-а, сбрасываем длительность стоп-интервала. Другой счётчик воспримет этот сброс как старт к подсчёту длительности.
    }
  else // Если было настроено прерывание на фронт... Для микроконтроллера посылка начинается с фронта на его пине, поэтому при приёме посылки, сначала придём сюда!!!
    {
    if (wait_start_bit_flag[ch]) // Если словили стартовый фронт...
      wait_start_bit_flag[ch] = RESET;
    else
      CallbackInputPulse_ch0(DALI_BUS_LOG1, PulseDuration[ch]);
    }
  TIMER_CHCTL2(TIMER1) ^= TIMER_CHCTL2_CH0P; // Change channel x capture/compare function polarity.
  DaliStates[ch]        = (!DaliStates[ch]); // Присваиваем новое лог. значение установившееся на шине DALI.
  //------------------------------------------------------------------------------------//
  }
//--------------------------------------------------------//


//---If channel 1 capture/compare interrupt flag is set---//
if (TIMER_INTF(TIMER1) & TIMER_INTF_CH1IF) // If channel 1 capture/compare interrupt flag is set.
  {
  //---Input capture event--------------------------------------------------------------//
  TIMER_INTF(TIMER1) &= (~TIMER_INTF_CH1IF); // Clear capture/compare interrupt flag.

  ch   = 1; // Выбор номера канала.
  temp = TIMER_CH1CV(TIMER1) & 0xFFFF;

  if (wait_start_bit_flag[ch] == RESET)
    {
    if (temp > prev_tim_capture_reg_val[ch])
      PulseDuration[ch] = temp - prev_tim_capture_reg_val[ch];
    else
      PulseDuration[ch] = 0xFFFF - prev_tim_capture_reg_val[ch] + temp;
    }
  else
    PulseDuration[ch] = 0; // Сброс счётчика длительности импульса.
    
  prev_tim_capture_reg_val[ch] = temp;
  
//  i_ch1++;

  if (TIMER_CHCTL2(TIMER1) & TIMER_CHCTL2_CH1P) // Если было настроено прерывание на срез...
    {
    CallbackInputPulse_ch1(DALI_BUS_LOG0, PulseDuration[ch]);
    StopDuration[ch] = 0; // Для старта отслеживания stop-а, сбрасываем длительность стоп-интервала. Другой счётчик воспримет этот сброс как старт к подсчёту длительности.
    }
  else // Если было настроено прерывание на фронт...
    {
    if (wait_start_bit_flag[ch])
      wait_start_bit_flag[ch] = RESET;
    else
      CallbackInputPulse_ch1(DALI_BUS_LOG1, PulseDuration[ch]);
    }
  TIMER_CHCTL2(TIMER1) ^= TIMER_CHCTL2_CH1P; // Change channel x capture/compare function polarity.
  DaliStates[ch]        = (!DaliStates[ch]); // Присваиваем новое лог. значение установившееся на шине DALI.
  //------------------------------------------------------------------------------------//
  }
//--------------------------------------------------------//


//---If channel 2 capture/compare interrupt flag is set---//
if (TIMER_INTF(TIMER1) & TIMER_INTF_CH2IF) // If channel 2 capture/compare interrupt flag is set.
  {
  //---Input capture event--------------------------------------------------------------//
  TIMER_INTF(TIMER1) &= (~TIMER_INTF_CH2IF); // Clear update interrupt flag.

  ch   = 2; // Выбор номера канала.
  temp = TIMER_CH2CV(TIMER1) & 0xFFFF;

  if (wait_start_bit_flag[ch] == RESET)
    {
    if (temp > prev_tim_capture_reg_val[ch])
      PulseDuration[ch] = temp - prev_tim_capture_reg_val[ch];
    else
      PulseDuration[ch] = 0xFFFF - prev_tim_capture_reg_val[ch] + temp;
    }
  else
    PulseDuration[ch] = 0; // Сброс счётчика длительности импульса.
    
  prev_tim_capture_reg_val[ch] = temp;

  if (TIMER_CHCTL2(TIMER1) & TIMER_CHCTL2_CH2P) // Если было настроено прерывание на срез...
    {
    CallbackInputPulse_ch2(DALI_BUS_LOG0, PulseDuration[ch]);
    StopDuration[ch] = 0; // Для старта отслеживания stop-а, сбрасываем длительность стоп-интервала. Другой счётчик воспримет этот сброс как старт к подсчёту длительности.
    }
  else // Если было настроено прерывание на фронт...
    {
    if (wait_start_bit_flag[ch])
      wait_start_bit_flag[ch] = RESET;
    else
      CallbackInputPulse_ch2(DALI_BUS_LOG1, PulseDuration[ch]);
    }
  TIMER_CHCTL2(TIMER1) ^= TIMER_CHCTL2_CH2P; // Change channel x capture/compare function polarity.
  DaliStates[ch]        = (!DaliStates[ch]); // Присваиваем новое лог. значение установившееся на шине DALI.
  //------------------------------------------------------------------------------------//
  }
//--------------------------------------------------------//


//---If channel 3 capture/compare interrupt flag is set---//
if (TIMER_INTF(TIMER1) & TIMER_INTF_CH3IF) // If channel 3 capture/compare interrupt flag is set.
  {
  //---Input capture event--------------------------------------------------------------//
  TIMER_INTF(TIMER1) &= (~TIMER_INTF_CH3IF); // Clear capture/compare interrupt flag.

  ch   = 3; // Выбор номера канала.
  temp = TIMER_CH3CV(TIMER1) & 0xFFFF;

  if (wait_start_bit_flag[ch] == RESET)
    {
    if (temp > prev_tim_capture_reg_val[ch])
      PulseDuration[ch] = temp - prev_tim_capture_reg_val[ch];
    else
      PulseDuration[ch] = 0xFFFF - prev_tim_capture_reg_val[ch] + temp;
    }
  else
    PulseDuration[ch] = 0; // Сброс счётчика длительности импульса.
    
  prev_tim_capture_reg_val[ch] = temp;

  if (TIMER_CHCTL2(TIMER1) & TIMER_CHCTL2_CH3P) // Если было настроено прерывание на срез...
    {
    CallbackInputPulse_ch3(DALI_BUS_LOG0, PulseDuration[ch]);
    StopDuration[ch] = 0; // Для старта отслеживания stop-а, сбрасываем длительность стоп-интервала. Другой счётчик воспримет этот сброс как старт к подсчёту длительности.
    }
  else // Если было настроено прерывание на фронт...
    {
    if (wait_start_bit_flag[ch])
      wait_start_bit_flag[ch] = RESET;
    else
      CallbackInputPulse_ch3(DALI_BUS_LOG1, PulseDuration[ch]);
    }
  TIMER_CHCTL2(TIMER1) ^= TIMER_CHCTL2_CH3P; // Change channel x capture/compare function polarity.
  DaliStates[ch]        = (!DaliStates[ch]); // Присваиваем новое лог. значение установившееся на шине DALI.
  //------------------------------------------------------------------------------------//
  }
//--------------------------------------------------------//
}
//------------------------------------------------------------------------------//

Когда фронты/срезы поступают на один из 4 каналов, всё работает как надо и вопросов не возникает. Но если фронты/срезы будут поступать одновременно на два и более каналов, то вопросы возникают.

Если слать фронты/срезы на канал 0 и канал 1, то на канале 1 будут пропуски в обработке прерываний. Выяснил это просто инкрементируя счётчики фронтов/срезов при обработке прерываний от каналов. Причём, если код как в посте, то пропуски будут на канале 1. 

Если в коде поменяю местами очерёдность обработки каналов (сначала канал 1, а потом канал 0), то пропуски всё равно будут на канале 1. Если уберу обработку канала 0, и буду слать фронты/срезы на каналы 1 и 2. То пропуски будут на канале 2.

Вангую, что мне ответят:

1) читай Джозефа Ю.;

2) не нужно совать всю обработку события в функцию обработки прерывания. Нужно установить флаг события, а уж в основном цикле разбираться;

3) использовать другие таймеры - не могу, остальные все заняты;

4) в UserManualе ответа не нашёл, Erratы не нашёл на сайте производителя.

Если есть у кого-нибудь другие комментарии, очень прошу подсказать. Как устранить или минимизировать пропуски?

Сам пробовал увеличить стек - не помогло. Приоритет прерываний от Timer1 равен 7. Приоритеты остальных прерываний ниже (значение больше 7).

 

Edited by LAS9891

Share this post


Link to post
Share on other sites

On 2/12/2024 at 3:33 PM, LAS9891 said:

Если есть у кого-нибудь другие комментарии, очень прошу подсказать. Как устранить или минимизировать пропуски?

Сам пробовал увеличить стек - не помогло. Повысил приоритет прерываний от Timer1 с 0 до 7. Вроде как стало лучше.

 

Какая частота на вход захвата подается и какая частота тактирования таймера ?

Share this post


Link to post
Share on other sites

6 minutes ago, dimka76 said:

Какая частота на вход захвата подается и какая частота тактирования таймера ?

На вход приходит такое:

image.thumb.jpeg.4f859b2e0406cfe443e3958b6c2551f3.jpeg

Если я не обсчитался, то это что-то около 1,2 кГц.

На счёт частоты тактирования таймера.  На вход таймера приходят 108 MHz (частота тактирования шины APB1 54 MHz умноженная на 2), затем импульсы проходят через prescaler, который понижает частоту до 1 MHz. На всякий случай код:

//---Настройка TIMER1---//
// SYSTEM_CLOCK = 108 MHz
RCU_APB1RST &= (~RCU_APB1RST_TIMER1RST);     // TIMER1 Unreset.
RCU_APB1EN  |= RCU_APB1EN_TIMER1EN;          // TIMER1 clock enable.
temp         = RCU_ClockFreq_Get(CK_APB1)*2; // Умножаем на 2 потому что в нашем случае APB1 prescale != 1, поэтому CK_TIMERx = CK_APB1 x2 = 54MHz x2 = 108MHz.
//temp = 108_000_000

//---Вариант для SYSCLK = 108 MHz---//
TIMER_PSC(TIMER1)    = (uint16_t)((temp/1000000)-1); // CK_TIMERx = CK_APB1 x2 = 54MHz x2 = 108MHz -> if TIMER_PSC = 107 -> 108MHz/(107+1) = 1000000 -> PSC_CK = 1 MHz.
//---------------------------------//

 

25 minutes ago, dimka76 said:

Повысил приоритет прерываний от Timer1 с 0 до 7. Вроде как стало лучше.

Это я херню написал.

Share this post


Link to post
Share on other sites

On 2/12/2024 at 4:13 PM, LAS9891 said:

На вход приходит такое:

А вы осциллографом не смотрели, что на входе микроконтроллера твориться ?

On 2/12/2024 at 4:13 PM, LAS9891 said:
//---Вариант для SYSCLK = 108 MHz---//
TIMER_PSC(TIMER1)    = (uint16_t)((temp/1000000)-1); // CK_TIMERx = CK_APB1 x2 = 54MHz x2 = 108MHz -> if TIMER_PSC = 107 -> 108MHz/(107+1) = 1000000 -> PSC_CK = 1 MHz.
//---------------------------------//

У вас случайно здесь переполнения не происходит ?

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

Share this post


Link to post
Share on other sites

1 minute ago, dimka76 said:

А вы осциллографом не смотрели

Смотрели, ничего криминального не увидели.

 

2 minutes ago, dimka76 said:

У вас случайно здесь переполнения не происходит ?

Я в комментариях к коду написал, что temp = 108000000, значение из отладчика. Соответственно ((108_000_000 / 1_000_000) - 1) = 107, и это всё в uint16_t, где переполнение?

 

4 minutes ago, dimka76 said:

ножку вывести и осциллографом посмотреть

При переполнении и прерывании на выходе было 1 МHz, как и должно быть.

Share this post


Link to post
Share on other sites

On 2/12/2024 at 4:27 PM, LAS9891 said:

Смотрели, ничего криминального не увидели

А время работы обработчика прерывания не измеряли ?

Share this post


Link to post
Share on other sites

1 minute ago, dimka76 said:

А время работы обработчика прерывания не измеряли ?

Как и зачем?

Share this post


Link to post
Share on other sites

On 2/12/2024 at 5:05 PM, LAS9891 said:

Как и зачем?

Как:

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

И осциллографом наблюдать длительность  получившегося импульса.


Зачем:
Чтобы развеять все сомнения.

Share this post


Link to post
Share on other sites

2 часа назад, LAS9891 сказал:
if (TIMER_INTF(TIMER1) & TIMER_INTF_CH0IF) // If channel 0 capture/compare interrupt flag is set.
  {
  //---Input capture event--------------------------------------------------------------//

  //-Clear capture/compare interrupt flag-//
  TIMER_INTF(TIMER1) &= (~TIMER_INTF_CH0IF);

Ну ведь уже 100500 раз здесь на форуме писали, почему так не надо делать. И почему нужно читать документацию на контроллер...

Так нет же - всё равно продолжаете наступать на те же самые грабли.  :unknw:

Вот этот быдлокод и приводит к потере прерываний. Открывайте мануал на МК, читайте описание регистра INTF таймера и начинайте думать - как правильно с ним работать.

2 часа назад, LAS9891 сказал:

4) в UserManualе ответа не нашёл

Да вы его даже не читали. Иначе бы не написали такое как выше.

Корректный код должен быть примерно таким:

u32 i = TIMER_INTF(TIMER1);
if (i & TIMER_INTF_CH0IF) {
  TIMER_INTF(TIMER1) = ~TIMER_INTF_CH0IF; // Clear capture/compare interrupt flag.
  ...
}
if (i & TIMER_INTF_CH1IF) {
  TIMER_INTF(TIMER1) = ~TIMER_INTF_CH1IF; // Clear capture/compare interrupt flag.
  ...
}
if (i & TIMER_INTF_CH2IF) {
  TIMER_INTF(TIMER1) = ~TIMER_INTF_CH2IF; // Clear capture/compare interrupt flag.
  ...
}
...

Это первое, что бросилось в глаза. Дальше не смотрел. Возможно там ещё баги есть.

Share this post


Link to post
Share on other sites

А я бы вообще вот так сделал 

u32 i = TIMER_INTF(TIMER1);
TIMER_INTF(TIMER1) = 0;

if (i & TIMER_INTF_CH0IF) {
  ...
}
if (i & TIMER_INTF_CH1IF) {
  ...
}

 

Share this post


Link to post
Share on other sites

3 часа назад, LAS9891 сказал:
if (TIMER_CHCTL2(TIMER1) & TIMER_CHCTL2_CH0P) { // Если было настроено прерывание на срез...
...
} else { // Если было настроено прерывание на фронт... Для микроконтроллера посылка начинается с фронта на его пине, поэтому при приёме посылки, сначала придём сюда!!!
...
}
TIMER_CHCTL2(TIMER1) ^= TIMER_CHCTL2_CH0P; // Change channel x capture/compare function polarity.

Здесь тоже потенциальный баг. Плохая идея так делать - менять полярность на ходу. И даже не предпринять никаких мер коррекции случаев дребезга.

Подумайте - что будет если будет дребезг сигнала возле уровня переключения "0"/"1" и до строчки:

TIMER_CHCTL2(TIMER1) ^= TIMER_CHCTL2_CH0P;

сигнал успеет несколько раз пересечь уровень переключения "0"/"1" туда-сюда?

 

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

42 минуты назад, VladislavS сказал:

А я бы вообще вот так сделал 

u32 i = TIMER_INTF(TIMER1);
TIMER_INTF(TIMER1) = 0;

Так делать нельзя. Иначе будут теряться прерывания. Подумайте почему.

(сначала написал неправильно)

Если уж хочется сразу сбросить все флажки, то нужно так:

u32 i = TIMER_INTF(TIMER1);
TIMER_INTF(TIMER1) = ~(i & (TIMER_INTF_CH0IF | TIMER_INTF_CH1IF | ...));
if (i & TIMER_INTF_CH0IF) {
  ...
}
if (i & TIMER_INTF_CH1IF) {
  ...
}

Share this post


Link to post
Share on other sites

Теоретически да. Несколько тактов "под угрозой". Безопаснее будет

TIMER_INTF(TIMER1) = ~i;

Share this post


Link to post
Share on other sites

Переписал согласно:

18 hours ago, jcxz said:

Корректный код должен быть примерно таким:

Вроде бы стало лучше, но полностью проблема не ушла.

 

18 hours ago, jcxz said:

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

Полностью согласен с Вами, мастер. По своему убогому скудоумию не знаю как поступить с этим. Подскажите, мастер.

Edited by LAS9891

Share this post


Link to post
Share on other sites

41 минуту назад, LAS9891 сказал:

В этой строке всё верно? Прям "=" должно быть? Может "&=" ?

Такие вопросы как раз и возникают из-за того, что

41 минуту назад, LAS9891 сказал:

Поскольку про регистр INTF в документации не читал,

А ведь прочитать про него в документации займет даже меньше времени, чем писать на форум.

Share this post


Link to post
Share on other sites

3 minutes ago, Сергей Борщ said:

займет даже меньше времени

Согласен. Вот всё что есть в UserManual о регистре INTF в Timer1:

(13_02.202409-47-22).thumb.jpg.2a105047453a479291bb2d942546e06d.jpg(13_02.202409-47-53).thumb.jpg.8d6731466a91d019a91d498b1272b602.jpg

Что я тут должен понять? Между каких строк я не вижу текста?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...