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

Здравствуйте. Помогите пожалуйста разобраться. Собрал для примера тестовый проект, МК 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 
 


Заранее спасибо за помощь.

 

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


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

25 minutes ago, abi said:

В чем может быть причина, что прерывания не работают во время выполнения кода процесса?

Может быть вы их выключаете в функциях работы с последовательным портом? Ну или не вы, а либа, если используете таковую...

 

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


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

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

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


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

В 17.02.2020 в 21:06, abi сказал:

В логе видно, что флаги устанавливаются, прерывания разрешены, вроде все условия для выполнения прерывания есть, но их нет до тех пор пока процесс не уходит в sleep

Процесс уходит в слип - означает, что процесс просто отдал управление ядру, а оно, соответственно, передало его (управление) другому процессу. Когда работает ядро, прерывания заблокированы (это короткое время), и если они начинают работать "во время слип", это значит, что они работают, когда активен другой процесс. У другого процесса другой контекст и значения регистров тоже другие. Похоже, что у интересующего вас процесса почему-то залочены прерывания. А у другого не залочены.

 

Что блокирует прерывания во время работы процесса, с ходу сказать сложно. Попробуйте в коде процесса сграбить значение регистров, которые рулят прерываниями - статусный  регистр, где глобальный флаг I (если правильно помню, с AVR работал в позапрошлой жизни), например, скопировать его в глобальную переменную и потом вывести её. Если есть эмулятор, то можно остановиться по брейкпоинту и посмотреть просто значения всех интересующих регистров.

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


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

Кстати, да, хороший вопрос. Правда, я не в курсе, какие там решающие в этом контексте отличия. Вроде ядро-то МК там такое же.

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


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

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) который просто переключает состояние пина.

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


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

45 минут назад, abi сказал:

когда какой-либо процесс активный, прерывания процессор не выполняет.

Вот это не очень понятно. Ведь всегда есть какой-то активный процесс. Если это не рабочие процессы, то процесс Idle. С точки зрения оси нет разницы.

А вообще эти прерывания когда-нибудь выполняются? Может, просто вектор неверно задан? Или нужен ещё какой-то флажок разрешения этого прерывания?

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


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

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 в котором происходит перепланировка прерывания начинают работать.

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


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

То есть, если в проекте оставить один процесс TProc1, то тестовый пин будет дёргаться 500 мс, потом 50 мс не дёргаться, потом снова 500 дёргаться и так далее?

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


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

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

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


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

Очень интересно. Не могу представить, как такое может быть.

Попробуйте задать scmRTOS_IDLE_HOOK_ENABLE 1

и определить функцию idle_process_user_hook(), в которой тоже выдавайте на печать всю эту информацию (STATUS, SREG и проч).

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


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

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 и прерывания работают.

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


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

Сдаётся мне, что вы неверно заполняете начальную структуру контекста проекта.

Смотрите реализацию функции init_stack_frame(). Возможно, что для xmega в контекст нужно включить PMIC?

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


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

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 начнет блокировать прерывания.

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


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

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

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

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

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

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

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

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

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

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