Перейти к содержанию
    

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

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

(17_02.202411-59-49).thumb.jpg.f1b27611d91e06db6cd8df859c897d90.jpg

Тут, кстати, "они там все" запросто получат двойной вход в одно и то же прерывание. Но об этом хомячкам не расскажут.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Тут, кстати, "они там все" запросто получат двойной вход в одно и то же прерывание. Но об этом хомячкам не расскажут.

Двойной вход - на самое страшное. При корректной обработке. А вот потеря других флажков в SR при таком коде - уже безвозвратна.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот это:

32 minutes ago, jcxz said:

После 1-го чтения, в регистре может появиться новый флаг (совсем другого прерывания по другому каналу). Который перед чтением был == 0. А значит дальнейшей операцией записи вы запишете в тот флаг старое значение ==0. Т.е. - сбросите этот флаг даже не заметив его.

Это ответ на это?

2 hours ago, LAS9891 said:

Теперь чуть-чуть понял. А что вот тут как:

2 hours ago, LAS9891 said:

Тут пишут, что флаг надо смотреть не прямо в регистре, а через переменную:

On 2/12/2024 at 5:54 PM, jcxz said:

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

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

Ну ок.

А тут пишут в п.9:

On 2/16/2024 at 3:36 PM, jcxz said:

проверяете наличие флага в INTF: если флаг есть - переход на п.7;

Тут получается напрямую в регистре смотреть? Или опять сначала прочитать в значение в переменную и уже в переменной смотреть значение?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Это ответ на это?

Это ответ на тему: 'почему нельзя использовать &= для сброса флагов в регистре INTF'. И вообще для операций с регистром INTF.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 minutes ago, jcxz said:

Это ответ на тему: 'почему нельзя использовать &= для сброса флагов в регистре INTF'.

Первый об этом написал Сергей Борщ. Потом это же написал я. Вы - третий. Это я уже понял, достаточно.

Что на счёт этого:

10 minutes ago, LAS9891 said:

 

2 hours ago, LAS9891 said:

Теперь чуть-чуть понял. А что вот тут как:

2 hours ago, LAS9891 said:

Тут пишут, что флаг надо смотреть не прямо в регистре, а через переменную:

On 2/12/2024 at 5:54 PM, jcxz said:

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

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

Ну ок.

А тут пишут в п.9:

On 2/16/2024 at 3:36 PM, jcxz said:

проверяете наличие флага в INTF: если флаг есть - переход на п.7;

Тут получается напрямую в регистре смотреть? Или опять сначала прочитать в значение в переменную и уже в переменной смотреть значение?

 

Изменено пользователем LAS9891

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 2/16/2024 at 3:36 PM, jcxz said:

Т.е. - после получения фронта - переход на п.1; после получения спада - переход на п.6.

  1. переключаете режим на регистрацию спада;
  2. чистите флаг события по данному каналу (в INTF);
  3. считываете реальное текущее значение на пине;
  4. проверяете наличие флага в INTF: если флаг есть - переход на п.2;
  5. Если с пина прочитан '0' - переход на п.6; если прочитан '1', то - переход на п.11 с результатом: pin=1;
  6. переключаете режим на регистрацию фронта;
  7. чистите флаг события по данному каналу (в INTF);
  8. считываете реальное текущее значение на пине;
  9. проверяете наличие флага в INTF: если флаг есть - переход на п.7;
  10. Если с пина прочитан '1' - переход на п.1; если прочитан '0', то - переход на п.11 с результатом: pin=0.
  11. Сравниваете текущий результат с предыдущим зафиксированным состоянием пина. Если они равны - выход из ISR без каких-либо действий.
  12. Если не равны: сохраняем новый результат как реальное зафиксированное значение пина. И выполняете все полезные действия, которые нужно сделать по фронту или спаду.

 

Хорош теории, вот свежий говнокод:

void TIMER1_Exception_Handling (void)
{
//---Variables---//
uint8_t  ch       = 0; // Переменная определяет номер канала.
uint32_t regval   = TIMER_INTF(TIMER1);
uint32_t polarity = TIMER_CHCTL2(TIMER1);

EdgeEnum edge                        = FALLING;
         GPIO_PinState NewPinState   = GPIO_PIN_RESET;
static   GPIO_PinState OldPinState   = GPIO_PIN_RESET;
         GPIO_PinState LoopCondition = GPIO_PIN_RESET;
//---------------//

//---If channel 0 capture/compare interrupt flag is set---//
if (regval & TIMER_INTF_CH0IF) // If channel 0 capture/compare interrupt flag is set.
  {
  ch       = 0; // Выбор номера канала.
  polarity = (polarity & TIMER_CHCTL2_CH0P);

  //---Input capture event--------------------------------------------------------------//
  if (polarity) // Если было настроено прерывание на срез...
    {
    edge          = RISING;
    LoopCondition = GPIO_PIN_RESET;
    }
  else // Если было настроено прерывание на фронт...
    {
    edge          = FALLING;
    LoopCondition = GPIO_PIN_SET;
    }

  do
    {
    EdgeChanging(TIMER1, ch, edge);
    edge          = !edge;
    LoopCondition = !LoopCondition;
    do
      {
      TIMER_INTF(TIMER1) = ~TIMER_INTF_CH0IF; // Clear capture/compare interrupt flag.
      NewPinState        = GPIO_GetPinState(DALI_RX_Connect[0].port, DALI_RX_Connect[0].pin);
      }
    while(TIMER_INTF(TIMER1) & TIMER_INTF_CH0IF);
    }
  while(NewPinState == LoopCondition);
    
  if (NewPinState == OldPinState)
    return;
  else
    {
    OldPinState = NewPinState;
    //И выполняете все полезные действия, которые нужно сделать по фронту или спаду
    }
  }
}


//...//


void EdgeChanging (uint32_t timer, uint8_t channel, EdgeEnum edge)
{
uint32_t TIMER_CHCTL2_CHxP = (TIMER_CHCTL2_CH0P << (channel * 4));
uint32_t temp              = TIMER_CHCTL2(timer);

if (edge == FALLING)
  temp = (temp | TIMER_CHCTL2_CHxP);
else
  temp = (temp & TIMER_CHCTL2_CHxP);
  
TIMER_CHCTL2(timer) = temp;    // Change channel x capture/compare function polarity.
}
//------------------------------------------------------------------------------//

 

Изменено пользователем LAS9891

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, LAS9891 said:

А значит дальнейшей операцией записи вы запишете в тот флаг старое значение ==0.

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

Например ~TIMER_INTF_CH0IF, это в двоичной системе число 11111111111111111111111111111101, и когда будет запись в TIMER_INTF(TIMER1) запишется только 0 в бит номер 1, потому что "Software can read as well as clear this bit by writing 0. Writing 1 has no effect on the bit value". Чтобы изменить два бита нужно, например, записать 11111111111111111111111101111101. Вещи это все очевидные. Понятные априори даже без объяснений.

Изменено пользователем LAS9891

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Тут получается напрямую в регистре смотреть? Или опять сначала прочитать в значение в переменную и уже в переменной смотреть значение?

Что значит "напрямую в регистре смотреть"?

Вам уже ответили ранее:

3 часа назад, VladislavS сказал:

Смотреть прямо в регистре периферии микроконтроллер не может. Он сначала считывает в регистр процессора, а затем смотрит.

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

но при записи будет изменён только один нужный бит.

Нет.

 

PS: Уже по 10-му кругу одно и то же.... :dash2:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 minutes ago, jcxz said:

Что значит "напрямую в регистре смотреть"?

Надо делать так:

uint32_t regval = TIMER_INTF(TIMER1);

//---If channel 0 capture/compare interrupt flag is set---//
if (regval & TIMER_INTF_CH0IF) // If channel 0 capture/compare interrupt flag is set
  {;}

Или так:

//---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
  {;}

 

10 minutes ago, jcxz said:

Нет.

Т.е. вот эта конструкция:

TIMER_INTF(TIMER1) = ~TIMER_INTF_CH0IF;

Установит в 0 не один бит?

P.S.: как-то всё ещё не верится что в STM работают дураки, да и во всех остальных производителях.

Изменено пользователем LAS9891

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Надо делать так:

uint32_t regval = TIMER_INTF(TIMER1);

//---If channel 0 capture/compare interrupt flag is set---//
if (regval & TIMER_INTF_CH0IF) // If channel 0 capture/compare interrupt flag is set
  {;}

Или так:

//---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
  {;}

Если анализируется один флаг - без разницы, если много - первое.

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

Т.е. вот эта конструкция:

TIMER_INTF(TIMER1) = ~TIMER_INTF_CH0IF;

Установит в 0 не один бит?

Эта - один.

 

PS: Начинайте уже думать самостоятельно. Уже всё разжёвано донельзя.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 hours ago, jcxz said:

Эта - один.

А эта несколько?

TIMER_INTF(TIMER1) &= ~TIMER_INTF_CH0IF;

TIMER_INTF_CH0IF - это макрос одного бита! Согласен, что код будет не оптимальным по количеству операций, и так делать нерационально, и я не буду так делать. Но как эта запись изменит другие биты кроме одного? В язык Си внесли изменения, в которых операция "&" стала работать по другому? Пусть биты, отличные от TIMER_INTF_CH0IF, хоть обизменяются, макрос от этого не поменяется, и он установит в 0 только свой один бит!

Скажите уже, что мне надо читать Страуструпа.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

А эта несколько?

TIMER_INTF(TIMER1) &= ~TIMER_INTF_CH0IF;

Если бы регистр TIMER_INTF был не volatile, то тогда строчка чистила бы 1 бит, тот самый TIMER_INTF_CH0IF.

Но регистр как раз периферийный, т.е. volatile, соответственно, программист должен предпринять все действия по устранению побочных эффектов, и производители МК этому способствуют, приделывая битам таких регистров атрибут r_w0 или r_w1. Соответственно, код нужно строить так, чтобы не было потенциальных "гонок" между абстрактной машиной Си и реальным МК, а это достижимо как раз операцией write-only, а не read-modify-write. Соответственно, корректный код по обработке флажков таймера будет иметь такой скелет:

ISR_Handler() {
  u32 intf = TIMER_INTF(TIMER1);
  
  TIMER_INTF(TIMER1) = ~intf;
  
  if(intf & TIMER_INTF_CH0IF) {
    ...
  }
  
  if(intf & какой_то_другой_флаг) {
    ...
  }
  
  ...
}


В некоторых случаях, когда логика программы может запретить некоторые прерывания, при этом оставляя в работе другие (я про один и тот же таймер говорю), может понадобиться конструкция чуть сложнее, которую чуть выше jcxz написал:

В 12.02.2024 в 18:20, jcxz сказал:

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

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) {
  ...
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Скажите уже, что мне надо читать Страуструпа.

Вообще-то, Кернигана и Ритчи 🙂  Это никому не повредит. Но что-то мне подсказывает, что там ни про работу с volatile сущностями, ни про оптимизирующий компилятор, ни про то как данные с задержками по шинам бегают, ни вообще про эмбэдд ни слова не будет. А до Страуструпа ещё надо дорасти.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Пусть биты, отличные от TIMER_INTF_CH0IF, хоть обизменяются, макрос от этого не поменяется, и он установит в 0 только свой один бит!

Следите за руками:

Пусть регистр для простоты будет 4-битным и этот ваш бит для простоты будет нулевым, то есть TIMER_INTF_CH0IF будет равно 0b0001.

Вот программа добралась до вашего &= и INTF в этот момент равен 0b0001.

read считает из него куда-то внутрь процессора 0b0001

в этот момент взвелся еще один флаг и INTF стал равен 0b0101

~TIMER_INTF_CH0IF будет 0b1110

в процессе modify считанное куда-то внутрь процессора 0b0001 & 0b1110 дадут 0b0000.

далее write это полученный 0 запишет в INTF и это сбросит все биты в INTF, то есть в нем получится 0b0000.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

On 2/16/2024 at 8:36 PM, jcxz said:

Т.е. - после получения фронта - переход на п.1; после получения спада - переход на п.6.

  1. переключаете режим на регистрацию спада;
  2. чистите флаг события по данному каналу (в INTF);
  3. считываете реальное текущее значение на пине;
  4. проверяете наличие флага в INTF: если флаг есть - переход на п.2;
  5. Если с пина прочитан '0' - переход на п.6; если прочитан '1', то - переход на п.11 с результатом: pin=1;
  6. переключаете режим на регистрацию фронта;
  7. чистите флаг события по данному каналу (в INTF);
  8. считываете реальное текущее значение на пине;
  9. проверяете наличие флага в INTF: если флаг есть - переход на п.7;
  10. Если с пина прочитан '1' - переход на п.1; если прочитан '0', то - переход на п.11 с результатом: pin=0.
  11. Сравниваете текущий результат с предыдущим зафиксированным состоянием пина. Если они равны - выход из ISR без каких-либо действий.
  12. Если не равны: сохраняем новый результат как реальное зафиксированное значение пина. И выполняете все полезные действия, которые нужно сделать по фронту или спаду.

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

Получилось так:

Spoiler
void TIMER1_IRQHandler (void)
{
//---Variables---//
uint32_t temp     = 0;
uint8_t  ch       = 0; // Переменная определяет номер канала.
  
uint32_t INTF_regval   = TIMER_INTF(TIMER1);
uint32_t CHCTL2_regval = TIMER_CHCTL2(TIMER1);
uint32_t  polarity     = 0x00;

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

EdgeEnum edge                                                = FALLING;
         GPIO_PinState NewPinState [NUMBER_OF_DALI_CHANNELS] = {GPIO_PIN_RESET};
static   GPIO_PinState OldPinState [NUMBER_OF_DALI_CHANNELS] = {GPIO_PIN_RESET};
         GPIO_PinState LoopCondition = GPIO_PIN_RESET;

static GPIO_PinState PinState = GPIO_PIN_RESET; // Для отладки.
//---------------//

//---If channel 0 capture/compare interrupt flag is set---//
if (INTF_regval & TIMER_INTF_CH0IF) // If channel 0 capture/compare interrupt flag is set.
  {
  ch       = 0; // Выбор номера канала.
  polarity = (CHCTL2_regval & TIMER_CHCTL2_CH0P);

  //---Input capture event--------------------------------------------------------------//
  if (polarity) // Если было настроено прерывание на срез...
    {
    edge          = RISING;
    LoopCondition = GPIO_PIN_RESET;
    }
  else // Если было настроено прерывание на фронт...
    {
    edge          = FALLING;
    LoopCondition = GPIO_PIN_SET;
    }

  do
    {
    EdgeChanging(TIMER1, ch, edge);
    edge          = (EdgeEnum)!edge;
    LoopCondition = (GPIO_PinState)!LoopCondition;
    do
      {
      TIMER_INTF(TIMER1) = ~TIMER_INTF_CH0IF; // Clear capture/compare interrupt flag.
      NewPinState[ch]    = GPIO_GetPinState(DALI_RX_Connect[ch].port, DALI_RX_Connect[ch].pin);
      }
    while(TIMER_INTF(TIMER1) & TIMER_INTF_CH0IF);
    }
  while(NewPinState[ch] == LoopCondition);
    
  if (NewPinState[ch] == OldPinState[ch])
    {;}//return;
  else
    {
    i_ch0++;
      
    OldPinState[ch] = NewPinState[ch];
      
    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 (polarity) // Если было настроено прерывание на срез...
      {
      DaliStates[ch] = DALI_BUS_LOG1; // Присваиваем новое лог. значение установившееся на шине DALI.
      CallbackInputPulse_ch0(DALI_BUS_LOG0, PulseDuration[ch]);
      StopDuration[ch] = 0;
      }
    else // Если было настроено прерывание на фронт...
      {
      DaliStates[ch] = DALI_BUS_LOG0; // Присваиваем новое лог. значение установившееся на шине DALI.
      if (wait_start_bit_flag[ch]) // Если словили стартовый фронт...
        wait_start_bit_flag[ch] = RESET;
      else
        CallbackInputPulse_ch0(DALI_BUS_LOG1, PulseDuration[ch]);
      }
    }
  }
//--------------------------------------------------------//
  
//---If channel 1 capture/compare interrupt flag is set---//
if (INTF_regval & TIMER_INTF_CH1IF) // If channel 1 capture/compare interrupt flag is set.
  {;// Аналогично channel 0 //}
//--------------------------------------------------------//

//---If channel 2 capture/compare interrupt flag is set---//
if (INTF_regval & TIMER_INTF_CH2IF) // If channel 2 capture/compare interrupt flag is set.
  {;// Аналогично channel 0 //}
//--------------------------------------------------------//
   
//---If channel 3 capture/compare interrupt flag is set---//
if (INTF_regval & TIMER_INTF_CH3IF) // If channel 3 capture/compare interrupt flag is set.
  {;// Аналогично channel 0 //}
//--------------------------------------------------------//

}

//...//

void EdgeChanging (uint32_t timer, uint8_t channel, EdgeEnum edge)
{
uint32_t temp              = TIMER_CHCTL2(timer);
uint32_t TIMER_CHCTL2_CHxP = (TIMER_CHCTL2_CH0P << (channel * 4));

if (edge == FALLING)
  temp |= TIMER_CHCTL2_CHxP;
else
  temp &= (~TIMER_CHCTL2_CHxP);

TIMER_CHCTL2(timer) = temp;    // Change channel x capture/compare function polarity.
}
//------------------------------------------------------------------------------//

Работает плюс/минус также. Заметная разница только в том, что раньше чаще пропуски были на канале 3. Сейчас ещё не удалось поймать пропуск на канале 3.

Вот так случилось на канале 2:

ererererer.thumb.jpg.9d5bdeda5cfdb960db794fa8607ac7c3.jpg

Розовый - дрыг ногой при каждом валидном прерывании на канале 2.

Голубой - вид исходной посылки. НА НЕСИНХРОННОСТЬ СИГНАЛОВ ВО ВРЕМЕНИ НЕ СМОТРИМ. Это сигналы с разных каналов. На все каналы приходят одинаковые посылки. Голубой сигнал тут просто для того, чтобы видеть, как должна выглядеть правильная посылка. (Лень было провода перепаивать).

Изменено пользователем haker_fox
Длинный код спрятал под спойлер.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...