LAS9891 0 12 февраля Опубликовано 12 февраля (изменено) · Жалоба В GD32F103RBT6 имеется Timer1. В Timer1 имеется 4 канала. Каждый канал можно настроить в режим захвата по фронту/срезу на пине контроллера и настроить соответствующие прерывания. Собственно я так и настроил. Функция обработки прерывания от Timer1 выглядит так: Spoiler 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). Изменено 14 марта пользователем haker_fox Длинный код спрятал под спойлер. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dimka76 59 12 февраля Опубликовано 12 февраля · Жалоба On 2/12/2024 at 3:33 PM, LAS9891 said: Если есть у кого-нибудь другие комментарии, очень прошу подсказать. Как устранить или минимизировать пропуски? Сам пробовал увеличить стек - не помогло. Повысил приоритет прерываний от Timer1 с 0 до 7. Вроде как стало лучше. Какая частота на вход захвата подается и какая частота тактирования таймера ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 12 февраля Опубликовано 12 февраля · Жалоба 6 minutes ago, dimka76 said: Какая частота на вход захвата подается и какая частота тактирования таймера ? На вход приходит такое: Если я не обсчитался, то это что-то около 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. Вроде как стало лучше. Это я херню написал. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dimka76 59 12 февраля Опубликовано 12 февраля · Жалоба 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. //---------------------------------// У вас случайно здесь переполнения не происходит ? Может отладчиком посмотреть. Или выход таймера на ножку вывести и осциллографом посмотреть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 12 февраля Опубликовано 12 февраля · Жалоба 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, как и должно быть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dimka76 59 12 февраля Опубликовано 12 февраля · Жалоба On 2/12/2024 at 4:27 PM, LAS9891 said: Смотрели, ничего криминального не увидели А время работы обработчика прерывания не измеряли ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 12 февраля Опубликовано 12 февраля · Жалоба 1 minute ago, dimka76 said: А время работы обработчика прерывания не измеряли ? Как и зачем? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dimka76 59 12 февраля Опубликовано 12 февраля · Жалоба On 2/12/2024 at 5:05 PM, LAS9891 said: Как и зачем? Как: Хотя бы так - в начале прерывания какую-нибудь свободную ножку в единичку поставить, а перед выходом из обработчика в нолик ее сбросить. И осциллографом наблюдать длительность получившегося импульса. Зачем: Чтобы развеять все сомнения. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 224 12 февраля Опубликовано 12 февраля · Жалоба 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 раз здесь на форуме писали, почему так не надо делать. И почему нужно читать документацию на контроллер... Так нет же - всё равно продолжаете наступать на те же самые грабли. Вот этот быдлокод и приводит к потере прерываний. Открывайте мануал на МК, читайте описание регистра 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. ... } ... Это первое, что бросилось в глаза. Дальше не смотрел. Возможно там ещё баги есть. 1 2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 12 февраля Опубликовано 12 февраля · Жалоба А я бы вообще вот так сделал u32 i = TIMER_INTF(TIMER1); TIMER_INTF(TIMER1) = 0; if (i & TIMER_INTF_CH0IF) { ... } if (i & TIMER_INTF_CH1IF) { ... } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 224 12 февраля Опубликовано 12 февраля · Жалоба 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) { ... } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 12 февраля Опубликовано 12 февраля · Жалоба Теоретически да. Несколько тактов "под угрозой". Безопаснее будет TIMER_INTF(TIMER1) = ~i; 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 13 февраля Опубликовано 13 февраля (изменено) · Жалоба Переписал согласно: 18 hours ago, jcxz said: Корректный код должен быть примерно таким: Вроде бы стало лучше, но полностью проблема не ушла. 18 hours ago, jcxz said: то следует принять меры против случаев многократных пересечений порога из-за дребезга Полностью согласен с Вами, мастер. По своему убогому скудоумию не знаю как поступить с этим. Подскажите, мастер. Изменено 13 февраля пользователем LAS9891 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 133 13 февраля Опубликовано 13 февраля · Жалоба 41 минуту назад, LAS9891 сказал: В этой строке всё верно? Прям "=" должно быть? Может "&=" ? Такие вопросы как раз и возникают из-за того, что 41 минуту назад, LAS9891 сказал: Поскольку про регистр INTF в документации не читал, А ведь прочитать про него в документации займет даже меньше времени, чем писать на форум. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 13 февраля Опубликовано 13 февраля · Жалоба 3 minutes ago, Сергей Борщ said: займет даже меньше времени Согласен. Вот всё что есть в UserManual о регистре INTF в Timer1: Что я тут должен понять? Между каких строк я не вижу текста? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться