abi 0 Posted February 17, 2020 · Report post Здравствуйте. Помогите пожалуйста разобраться. Собрал для примера тестовый проект, МК ATxmega256A3U, scmRTOS 5.1.0. Во время выполнения процесса перестают работать прерывания, как только процесс уходит в sleep прерывания начинают работать. В чем может быть причина, что прерывания не работают во время выполнения кода процесса? Причем прерываний нет и от системного таймера OS TCDO, когда работает процесс. int main() { BspClockInit(); BspPin2Init(); BspComUartConnect(); ComSetFrameFormat(); ComSetBaudRate(Baud_19200BPS); TCD0.CTRLA = TC_CLKSEL_DIV1_gc; TCD0.CTRLB = TC_WGMODE_NORMAL_gc; TCD0.CTRLC = 0x00; TCD0.CTRLD = 0x00; TCD0.PER = 24000 - 1; TCD0.INTCTRLA = TC_OVFINTLVL_LO_gc; TCD0.CTRLFSET = TC_CMD_UPDATE_gc; TCD0.CTRLFCLR = TC_CMD_UPDATE_gc; TCD0.CTRLFSET = TC_CMD_RESTART_gc; TCD0.CTRLFCLR = TC_CMD_RESTART_gc; TCF0.CTRLA = TC_CLKSEL_DIV1024_gc; TCF0.CTRLB = TC_WGMODE_NORMAL_gc; TCF0.CTRLC = 0x00; TCF0.CTRLD = 0x00; TCF0.PER = 234 - 1; TCF0.INTCTRLA = TC_OVFINTLVL_LO_gc; TCF0.CTRLFSET = TC_CMD_UPDATE_gc; TCF0.CTRLFCLR = TC_CMD_UPDATE_gc; TCF0.CTRLFSET = TC_CMD_RESTART_gc; TCF0.CTRLFCLR = TC_CMD_RESTART_gc; PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_RREN_bm; OS::run(); } template<> void TProc1::exec() { unsigned char temp; for(;;) { sleep(1000); for (unsigned char i = 0; i < 50; i++) { unsigned int cnt = TCF0.CNT; ComPutchar(cnt >> 8); ComPutchar(cnt & 0xFF); temp = TCF0.INTFLAGS; ComPutchar(temp); temp = SREG; ComPutchar(temp); } } } #pragma vector=TCF0_OVF_vect OS_INTERRUPT void TimerF0_period_ISR() { BspPinTgl2(); } Во время выполнения процесса в цикле в UART вывожу состояния регистров таймера TCF0 и SREG CNT FLAGS SREG 0058 f0 82 В TCF0.INTFLAGS флаг переполнения сброшен, прерывания работали. 0065 f0 80 0095 f0 80 00c6 f0 80 000d f1 80 Произошло переполнение таймера, выставлен флаг переполнения в TCF0.INTFLAGS, 003e f1 80 в SREG I=1 прерывания разрешены 006e f1 80 009f f1 80 00d0 f1 80 Произошло повторное переполнение таймера, прерывания нет, флаг не сброшен. 0017 f1 80 0047 f1 80 0078 f1 80 Заранее спасибо за помощь. Quote Ответить с цитированием Share this post Link to post Share on other sites
haker_fox 0 Posted February 17, 2020 · Report post 25 minutes ago, abi said: В чем может быть причина, что прерывания не работают во время выполнения кода процесса? Может быть вы их выключаете в функциях работы с последовательным портом? Ну или не вы, а либа, если используете таковую... Quote Ответить с цитированием Share this post Link to post Share on other sites
abi 0 Posted February 17, 2020 · Report post 6 minutes ago, haker_fox said: Может быть вы их выключаете в функциях работы с последовательным портом? Ну или не вы, а либа, если используете таковую... В цикле только одна функция используется, просто в UART передает данные void ComPutchar(char ch) { while ((USARTD1.STATUS & USART_DREIF_bm) == 0); USARTD1.DATA = ch; } Пробовал даже вместо цикла с выводом данных, вставлял простую функцию задержки, результат одинаковый. #define delay_ms(ms) ( __delay_cycles((F_CPU / 1000UL) * (ms)) ) for(;;) { sleep(1000); delay_ms(10); } В логе видно, что флаги устанавливаются, прерывания разрешены, вроде все условия для выполнения прерывания есть, но их нет до тех пор пока процесс не уходит в sleep Quote Ответить с цитированием Share this post Link to post Share on other sites
dxp 0 Posted February 19, 2020 · Report post В 17.02.2020 в 21:06, abi сказал: В логе видно, что флаги устанавливаются, прерывания разрешены, вроде все условия для выполнения прерывания есть, но их нет до тех пор пока процесс не уходит в sleep Процесс уходит в слип - означает, что процесс просто отдал управление ядру, а оно, соответственно, передало его (управление) другому процессу. Когда работает ядро, прерывания заблокированы (это короткое время), и если они начинают работать "во время слип", это значит, что они работают, когда активен другой процесс. У другого процесса другой контекст и значения регистров тоже другие. Похоже, что у интересующего вас процесса почему-то залочены прерывания. А у другого не залочены. Что блокирует прерывания во время работы процесса, с ходу сказать сложно. Попробуйте в коде процесса сграбить значение регистров, которые рулят прерываниями - статусный регистр, где глобальный флаг I (если правильно помню, с AVR работал в позапрошлой жизни), например, скопировать его в глобальную переменную и потом вывести её. Если есть эмулятор, то можно остановиться по брейкпоинту и посмотреть просто значения всех интересующих регистров. Quote Ответить с цитированием Share this post Link to post Share on other sites
AHTOXA 0 Posted February 19, 2020 · Report post А разве scmRTOS поддерживает XMEGA? Quote Ответить с цитированием Share this post Link to post Share on other sites
dxp 0 Posted February 19, 2020 · Report post Кстати, да, хороший вопрос. Правда, я не в курсе, какие там решающие в этом контексте отличия. Вроде ядро-то МК там такое же. Quote Ответить с цитированием Share this post Link to post Share on other sites
abi 0 Posted February 19, 2020 · Report post 6 hours ago, AHTOXA said: А разве scmRTOS поддерживает XMEGA? Сделал несколько небольших изменений для работы с xmega, если кому интересно позже открою проект. Все прерывания установил на один уровень. Перепланирование процессов производится прямой передачи управления #define scmRTOS_CONTEXT_SWITCH_SCHEME 0. Изначально основной проект был сделан под mega128, затем решил адаптировать его под xmega. Проект заработал, но заметил при приеме данных по UART, временами пакеты принимаются с ошибкой. Позже выяснил причину, оказалось, что когда какой-либо процесс активный, прерывания процессор не выполняет. Прерывание откладывается на время выполнения кода процесса, т.е. до тех пор пока не произойдет перепланировка. Если процесс занимает короткое время данные по UART успевают приниматься, иначе несколько байт теряются. Для поиска причины сделал тестовый проект, в нем 3 процесса, в каждом процессе через UART вывожу состояния нескольких регистров. Для примера код одного процесса. template<> void TProc1::exec() { unsigned char temp; unsigned int val; for(;;) { sleep(2500); for (unsigned char i = 0; i < 10; i++) { temp = TCF0.INTFLAGS; ComPutchar(temp); val = SP; ComPutchar(val >> 8); ComPutchar(val & 0xFF); temp = PMIC.INTPRI; ComPutchar(temp); temp = PMIC.STATUS; ComPutchar(temp); temp = SREG; ComPutchar(temp); } } } Полученные данные Proc1 INTFLAGS SP INTPRI STATUS SREG f0 21b1 00 01 82 f0 21b1 00 01 80 f0 21b1 00 01 80 f1 21b1 00 01 80 f1 21b1 00 01 80 f1 21b1 00 01 80 f1 21b1 00 01 80 f1 21b1 00 01 80 f1 21b1 00 01 80 f1 21b1 00 01 80 Proc3 INTFLAGS SP INTPRI STATUS SREG f0 2337 00 01 82 f0 2337 00 01 80 f0 2337 00 01 80 f1 2337 00 01 80 f1 2337 00 01 80 f1 2337 00 01 80 f1 2337 00 01 80 f1 2337 00 01 80 f1 2337 00 01 80 f1 2337 00 01 80 Proc2 INTFLAGS SP INTPRI STATUS SREG f0 2274 00 01 82 f0 2274 00 01 80 f0 2274 00 01 80 f1 2274 00 01 80 f1 2274 00 01 80 f1 2274 00 01 80 f1 2274 00 01 80 f1 2274 00 01 80 f1 2274 00 01 80 f1 2274 00 01 80 Из лога видно, что прерывания всегда разрешены SREG = 0x80, I = 1. Таймер TCF0 после переполнения выставляет флаг (0x01), INTFLAGS = 0xf1. Непонятно почему в регистре PMIC.STATUS всегда 1, насколько я понял статус показывает какого уровня прерывание на данный момент выполняется и этот флаг должен быть сброшен после выполнения. В проекте всего два разрешенных прерывания, системный таймер OS - TCD0 (1 ms) и от таймера TCF0 (2 ms) который просто переключает состояние пина. Quote Ответить с цитированием Share this post Link to post Share on other sites
AHTOXA 0 Posted February 19, 2020 · Report post 45 минут назад, abi сказал: когда какой-либо процесс активный, прерывания процессор не выполняет. Вот это не очень понятно. Ведь всегда есть какой-то активный процесс. Если это не рабочие процессы, то процесс Idle. С точки зрения оси нет разницы. А вообще эти прерывания когда-нибудь выполняются? Может, просто вектор неверно задан? Или нужен ещё какой-то флажок разрешения этого прерывания? Quote Ответить с цитированием Share this post Link to post Share on other sites
abi 0 Posted February 19, 2020 · Report post 8 minutes ago, AHTOXA said: Вот это не очень понятно. Для примера запущен таймер TCF0, который каждые 2 ms генерирует прерывания, в обработчике переключаю состояние пина, осциллографом видно что каждые 2 ms пин меняет состояние, т.е. работает правильно. Но когда работают процессы, прерывания от таймера не происходят, например #define delay_ms(ms) ( __delay_cycles((F_CPU / 1000UL) * (ms)) ) template<> void TProc1::exec() { unsigned char temp; unsigned int val; for(;;) { sleep(500); delay_ms(50); } } В течении 500 ms (когда процесс в sleep(500)) прерывания есть, как только процесс становится активным попадает в delay_ms(50) прерываний нет, и по осциллографу видно, что в течении 50 ms, пин не меняет состояние. Далее снова в течении 500 ms прерывания есть и т.д. В посту выше приводил пример с выводом данных через UART, пока в цикле выводятся данные прерываний нет, как только в процессе вызывается любой сервис OS в котором происходит перепланировка прерывания начинают работать. Quote Ответить с цитированием Share this post Link to post Share on other sites
AHTOXA 0 Posted February 19, 2020 · Report post То есть, если в проекте оставить один процесс TProc1, то тестовый пин будет дёргаться 500 мс, потом 50 мс не дёргаться, потом снова 500 дёргаться и так далее? Quote Ответить с цитированием Share this post Link to post Share on other sites
abi 0 Posted February 19, 2020 · Report post Да, как будто код в процессе находится в критической секции, но это не так, как показывал на примере выше прерывания разрешены и вроде все условия для выполнения прерывания есть. Если запустить несколько процессов, то каждый процесс будет откладывать прерывание на время его выполнения. Quote Ответить с цитированием Share this post Link to post Share on other sites
AHTOXA 0 Posted February 19, 2020 · Report post Очень интересно. Не могу представить, как такое может быть. Попробуйте задать scmRTOS_IDLE_HOOK_ENABLE 1 и определить функцию idle_process_user_hook(), в которой тоже выдавайте на печать всю эту информацию (STATUS, SREG и проч). Quote Ответить с цитированием Share this post Link to post Share on other sites
abi 0 Posted February 20, 2020 · Report post 13 hours ago, AHTOXA said: Попробуйте задать scmRTOS_IDLE_HOOK_ENABLE 1 Добавил в idle_process_user_hook() вывод состояния регистров. При активном процессе idle прерывания работают, это видно осциллографом и по состоянию регистров. Флаг регистра TCF0.INTFLAGS сброшен, состояние PMIC.STATUS всегда = 0. INTFLAGS SP INTPRI STATUS SREG f0 23a7 00 00 82 f0 23a7 00 00 80 f0 23a7 00 00 80 f0 23a7 00 00 80 f0 23a7 00 00 80 Не могу понять, почему в рабочих процессах всегда PMIC.STATUS = 1, а в процессе idle PMIC.STATUS = 0 и прерывания работают. Quote Ответить с цитированием Share this post Link to post Share on other sites
AHTOXA 0 Posted February 20, 2020 · Report post Сдаётся мне, что вы неверно заполняете начальную структуру контекста проекта. Смотрите реализацию функции init_stack_frame(). Возможно, что для xmega в контекст нужно включить PMIC? Quote Ответить с цитированием Share this post Link to post Share on other sites
abi 0 Posted February 20, 2020 · Report post 16 minutes ago, AHTOXA said: Возможно, что для xmega в контекст нужно включить PMIC? У PMIC 3 регистра. PMIC.STATUS только для чтения. PMIC.INTPRI сохраняет вектор последнего подтвержденного низкоуровневого прерывания, если разрешен режим Round-Robin, но он отключен. PMIC.CTRL для настройки уровня разрешенных прерываний, т.к. у всех прерываний установлен Low Level, регистр настроен на LOLVLEN и всегда равен 1. Выводил состояние этого регистра в процессах, он не меняется и всегда равен 1. Поэтому думаю сохранять регистры PMIC в контекст смысла нет. А вот интересно при создании объекта IdleProc используется функция init_stack_frame () так же как и при создании рабочих процессов, т.е. все одинаково, а результат разный. Позже попробую добавить в IdleProc функцию sleep и скорее всего и IdleProc начнет блокировать прерывания. Quote Ответить с цитированием Share this post Link to post Share on other sites