VladislavS 39 17 февраля Опубликовано 17 февраля · Жалоба 3 часа назад, LAS9891 сказал: Тут, кстати, "они там все" запросто получат двойной вход в одно и то же прерывание. Но об этом хомячкам не расскажут. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 17 февраля Опубликовано 17 февраля · Жалоба 6 минут назад, VladislavS сказал: Тут, кстати, "они там все" запросто получат двойной вход в одно и то же прерывание. Но об этом хомячкам не расскажут. Двойной вход - на самое страшное. При корректной обработке. А вот потеря других флажков в SR при таком коде - уже безвозвратна. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 17 февраля Опубликовано 17 февраля · Жалоба Вот это: 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; Тут получается напрямую в регистре смотреть? Или опять сначала прочитать в значение в переменную и уже в переменной смотреть значение? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 17 февраля Опубликовано 17 февраля · Жалоба 7 минут назад, LAS9891 сказал: Это ответ на это? Это ответ на тему: 'почему нельзя использовать &= для сброса флагов в регистре INTF'. И вообще для операций с регистром INTF. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 17 февраля Опубликовано 17 февраля (изменено) · Жалоба 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; Тут получается напрямую в регистре смотреть? Или опять сначала прочитать в значение в переменную и уже в переменной смотреть значение? Изменено 17 февраля пользователем LAS9891 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 17 февраля Опубликовано 17 февраля (изменено) · Жалоба On 2/16/2024 at 3:36 PM, jcxz said: Т.е. - после получения фронта - переход на п.1; после получения спада - переход на п.6. переключаете режим на регистрацию спада; чистите флаг события по данному каналу (в INTF); считываете реальное текущее значение на пине; проверяете наличие флага в INTF: если флаг есть - переход на п.2; Если с пина прочитан '0' - переход на п.6; если прочитан '1', то - переход на п.11 с результатом: pin=1; переключаете режим на регистрацию фронта; чистите флаг события по данному каналу (в INTF); считываете реальное текущее значение на пине; проверяете наличие флага в INTF: если флаг есть - переход на п.7; Если с пина прочитан '1' - переход на п.1; если прочитан '0', то - переход на п.11 с результатом: pin=0. Сравниваете текущий результат с предыдущим зафиксированным состоянием пина. Если они равны - выход из ISR без каких-либо действий. Если не равны: сохраняем новый результат как реальное зафиксированное значение пина. И выполняете все полезные действия, которые нужно сделать по фронту или спаду. Хорош теории, вот свежий говнокод: 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. } //------------------------------------------------------------------------------// Изменено 17 февраля пользователем LAS9891 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 17 февраля Опубликовано 17 февраля (изменено) · Жалоба 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. Вещи это все очевидные. Понятные априори даже без объяснений. Изменено 17 февраля пользователем LAS9891 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 17 февраля Опубликовано 17 февраля · Жалоба 1 час назад, LAS9891 сказал: Тут получается напрямую в регистре смотреть? Или опять сначала прочитать в значение в переменную и уже в переменной смотреть значение? Что значит "напрямую в регистре смотреть"? Вам уже ответили ранее: 3 часа назад, VladislavS сказал: Смотреть прямо в регистре периферии микроконтроллер не может. Он сначала считывает в регистр процессора, а затем смотрит. 11 минут назад, LAS9891 сказал: но при записи будет изменён только один нужный бит. Нет. PS: Уже по 10-му кругу одно и то же.... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 17 февраля Опубликовано 17 февраля (изменено) · Жалоба 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 работают дураки, да и во всех остальных производителях. Изменено 17 февраля пользователем LAS9891 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 234 17 февраля Опубликовано 17 февраля · Жалоба 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: Начинайте уже думать самостоятельно. Уже всё разжёвано донельзя. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 18 февраля Опубликовано 18 февраля · Жалоба 14 hours ago, jcxz said: Эта - один. А эта несколько? TIMER_INTF(TIMER1) &= ~TIMER_INTF_CH0IF; TIMER_INTF_CH0IF - это макрос одного бита! Согласен, что код будет не оптимальным по количеству операций, и так делать нерационально, и я не буду так делать. Но как эта запись изменит другие биты кроме одного? В язык Си внесли изменения, в которых операция "&" стала работать по другому? Пусть биты, отличные от TIMER_INTF_CH0IF, хоть обизменяются, макрос от этого не поменяется, и он установит в 0 только свой один бит! Скажите уже, что мне надо читать Страуструпа. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 178 18 февраля Опубликовано 18 февраля · Жалоба 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) { ... } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
VladislavS 39 18 февраля Опубликовано 18 февраля · Жалоба 3 часа назад, LAS9891 сказал: Скажите уже, что мне надо читать Страуструпа. Вообще-то, Кернигана и Ритчи 🙂 Это никому не повредит. Но что-то мне подсказывает, что там ни про работу с volatile сущностями, ни про оптимизирующий компилятор, ни про то как данные с задержками по шинам бегают, ни вообще про эмбэдд ни слова не будет. А до Страуструпа ещё надо дорасти. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 134 18 февраля Опубликовано 18 февраля · Жалоба 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. Теперь понятно, что этот макрос сбросит все биты и выделенный бит вы потеряли? Перед Струструпом надо двоичную арифметику освоить до вычислений в уме. С этого обычно начинают. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LAS9891 0 19 февраля Опубликовано 19 февраля (изменено) · Жалоба Переписал согласно ЦУ: On 2/16/2024 at 8:36 PM, jcxz said: Т.е. - после получения фронта - переход на п.1; после получения спада - переход на п.6. переключаете режим на регистрацию спада; чистите флаг события по данному каналу (в INTF); считываете реальное текущее значение на пине; проверяете наличие флага в INTF: если флаг есть - переход на п.2; Если с пина прочитан '0' - переход на п.6; если прочитан '1', то - переход на п.11 с результатом: pin=1; переключаете режим на регистрацию фронта; чистите флаг события по данному каналу (в INTF); считываете реальное текущее значение на пине; проверяете наличие флага в INTF: если флаг есть - переход на п.7; Если с пина прочитан '1' - переход на п.1; если прочитан '0', то - переход на п.11 с результатом: pin=0. Сравниваете текущий результат с предыдущим зафиксированным состоянием пина. Если они равны - выход из ISR без каких-либо действий. Если не равны: сохраняем новый результат как реальное зафиксированное значение пина. И выполняете все полезные действия, которые нужно сделать по фронту или спаду. Такая последовательность действий гарантирует, что в п.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: Розовый - дрыг ногой при каждом валидном прерывании на канале 2. Голубой - вид исходной посылки. НА НЕСИНХРОННОСТЬ СИГНАЛОВ ВО ВРЕМЕНИ НЕ СМОТРИМ. Это сигналы с разных каналов. На все каналы приходят одинаковые посылки. Голубой сигнал тут просто для того, чтобы видеть, как должна выглядеть правильная посылка. (Лень было провода перепаивать). Изменено 14 марта пользователем haker_fox Длинный код спрятал под спойлер. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться