Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Вопросы по TNKernel
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы
Ruslan1
Здравствуйте!

Поставил, скомпилировал, подредактировал проект примера, идущий в комплекте, под себя, все компилируется и работает. Пока что экспериментирую на кошках (встроенный в MPLAB симулятор), железо через неделю только будет. Но уже есть некоторые вопросы.
Я не знаю пока вызвано ли это некорректным симулированием, попробую проверить на железе.

Вопрос первый, по работе системного таймера.
Проблема: от прерывания до следующего прерывания проходит меньше машинных циклов (МЦ), чем задано. Было бы больше- я б слова не сказал, но меньше....
В качестве таймера TNKernel пользует Core timer ядра MIPS32. Этот таймер инкрементируется каждые два МЦ, то есть в bsp.c вижу логичную конструкцию (это я уже под свои 40МГЦ переделал, в оригинале было под 80):

#define CORE_TIMER_PERIOD 20000UL /* 1 ms @ 40 MHz */

ну и есть прерывание:

void Sys_Tmr_Int_Handler (void)
{
INTClearFlag(INT_CT); // clear the interrupt flag
UpdateCoreTimer(CORE_TIMER_PERIOD); // update the period
}

результат по симулятору: 39991 МЦ. Хотя согласно таймеру должно быть 40000.

Изменив величину инициализации таймера на
#define CORE_TIMER_PERIOD (20000L + 5) /* 1 ms @ 40 MHz */

получил ровно 40000 МЦ, причем никакой погрешности не набегает при любом количестве прерываний.

То же самое с выдержками больше чем 1мс, скажем 1 секунда:
#define CORE_TIMER_PERIOD (20000000UL + 5) /* 1 s @ 40 MHz */

получаю ровно 40 миллионов МЦ. То есть ошибка аддитивная. Но почему таймер считает быстрее чем машинные циклы идут- не понимаю. Или лыжи не едут (симулятор врет) или реализация функции UpdateCoreTimer() хромает.

Вопрос второй.
Ну добился я что системный таймер в TNKernel идет как мне хочется (то есть с периодом 1мс), а не быстрее указанного. дальше перешел к проверке пауз в задачах. такая простенькая задача, чтобы светодиод поморгал

#define LED_PERIOD 500 //ms

//task
void TN_TASK LED_CPU_Task (void *par)
{
for (;;)
{
LED_CPU_ON;
tn_task_sleep(LED_PERIOD/2);
LED_CPU_OFF;
tn_task_sleep(LED_PERIOD/2);
}
}

Результат удивил: на строку включения светодиода (LED_CPU_ON) я должен попадать каждые 500мс или реже, если другие задачи работают. Реально время меньше требуемого примерно на 20 МЦ. Почему меньше?????????? Причем дельта явно меньше одного системного тика.
Я хочу быть уверенным, что прошел период не меньше указанного мной, это очень часто используется при выпиливании времянок: больше пожалуйста, а меньше нельзя.
Вероятно, разработчики считают таймаут от текущего системного тика который уже начался, тогда это понятно и нужно просто увеличивать нужные юзерские зазоры на единицу при вызове tn_task_sleep() (проблема юзера, а не кернела), но это где-то отображено?
В документации вижу обратное:
tn_task_sleep() : Функция переводит текущую задачу в ожидание на время не меньше чем timeout системных тиков.
Я согласен с этим и это нормально, но реально вижу "меньше чем timeout системных тиков." На малую долю этого тика, но ведь меньше.... И кто мне гарантирует что эта доля не увеличится до почти целого тика при каких-то условиях.

Оговорюсь, это все на основе симулятора, попробую позже на железе посмотреть.
yuri_t
В любой RTOS время Sleep() будет генерироваться с точностью 0..-1 системный тик.

Например, в TNKernel для ф-ции tn_task_sleep (100) с временем одного тика 1 ms,
реальная задержка может колебаться от 99 ms (+ несколько микросекунд)
до 100 ms (минус несколько микросекунд ).

Это связано с тем, что "фазовое соотношение" между вызовом ф-ции Sleep()
и прерыванием от системного таймера всегда случайно.
Ruslan1
Цитата(yuri_t @ Jan 27 2011, 18:33) *
В любой RTOS время Sleep() будет генерироваться с точностью 0..-1 системный тик.

Например, в TNKernel для ф-ции tn_task_sleep (100) с временем одного тика 1 ms,
реальная задержка может колебаться от 99 ms (+ несколько микросекунд)
до 100 ms (минус несколько микросекунд ).

Это связано с тем, что "фазовое соотношение" между вызовом ф-ции Sleep()
и прерыванием от системного таймера всегда случайно.


Ясно, спасибо!
Просто я ориентировался на рускоязычную документацию: "Функция переводит текущую задачу в ожидание на время не меньше чем timeout системных тиков.".
А нужно было оригинал смотреть: "This function puts the currently running task to the sleep for at most timeout system ticks"
sm.gif


PS. Eventflags и индивидуальные условия для каждой ожидающей задачи это вообще праздник какой-то! Я не припомню ничего подобного в юкосе.
dENIM
А я вот на 24FJ64 запускаю
светодиодиками моргаю

и как я раньше без этого жил biggrin.gif
Rusoil
Тоже есть вопрос по TN.

Будет ли происходить раскрутка стека при вызове tn_task_exit(0)?

Я использую c++ обертку для мьютекса, там происходит автоосвобождение в деструкторе.
Будет там автоматом деструктор выполняться как при выходе из функции? Или лучше так не делать?
yuri_t
Цитата(Rusoil @ Feb 14 2011, 14:52) *
Будет ли происходить раскрутка стека при вызове tn_task_exit(0)?

Я использую c++ обертку для мьютекса, там происходит автоосвобождение в деструкторе.
Будет там автоматом деструктор выполняться как при выходе из функции? Или лучше так не делать?


Правильность работы "внутрених" механизмов С++ можно проверить в embedded по-настоящему только
под отладчиком.
Рекомендую использовать симулятор и debugger Keil ( для АРМ/Cortex).

Кстати, наличие в С++ кода, написанного не напрямую (как в С) программистом, а добавляемого
компилятором самостоятельно, является основной причиной, по которой я лично не использую
С++ в embedded системах.
Rusoil
Спасибо, учту.
dxp
Цитата(Rusoil @ Feb 14 2011, 16:52) *
Будет там автоматом деструктор выполняться как при выходе из функции? Или лучше так не делать?

А деструктор объекта вообще когда вызывается?

Upd. Перечитал свой вопрос и понял, что нужно уточнение. У вас этот мутекс - это глобальный объект или локальный по отношению к этой фукнции?

Цитата(yuri_t @ Feb 14 2011, 23:55) *
Кстати, наличие в С++ кода, написанного не напрямую (как в С) программистом, а добавляемого
компилятором самостоятельно, является основной причиной, по которой я лично не использую
С++ в embedded системах.

Какой именно код тут имеется в виду? Конкретно?
yuri_t
Цитата(dxp @ Feb 15 2011, 08:18) *
Какой именно код тут имеется в виду? Конкретно?

Например, default constructor/destructor в случае declaration типа MyClass x;
Rusoil
Локальный для функций.

Обявляется локальный объект, мютекс захватывается и дальше о нем забываем. Если есть десяток проверок после которых нужен выход из функции, то это сильно упрощает код.

В иаре это работает. Но нужно помнить о специфике ртос.
dxp
Цитата(yuri_t @ Feb 15 2011, 14:43) *
Например, default constructor/destructor в случае declaration типа MyClass x;

Код
class TSlon
{
public:
    int get_a() { return a; }

private:
    int  a;
};

int main()
{
    TSlon Slon;
    
    return Slon.get_a();
}


Код
int main()                      
         main:                    
{                                
     TSlon Slon;                  
                                  
     return Slon.get_a();        
0C43         MOV.W   #0x0, R12    
3041         RET                  
}


Это для MSP430. Где этот левый код?

Кстати. Вы считаете, что использовать неинициализированные данные - это хорошо?

Цитата(Rusoil @ Feb 15 2011, 15:07) *
Локальный для функций.

Обявляется локальный объект, мютекс захватывается и дальше о нем забываем. Если есть десяток проверок после которых нужен выход из функции, то это сильно упрощает код.

Я, простите, не очень понимаю. Мутекс (пусть в плюсовой обёртке) - это сущность от RTOS? Она является средством межзадачного взаимодействия? Если так, то как она может быть локальной? Точнее, как к ней получает доступ код из других задач?
Rusoil
Сам мьютекс конечно обявлен как static и является сущностью ртос.


В конструктор даем указатель на мьютекс.
Потом вызываем захват мьютекса.
При выходе из функции все локальные объекты уничтожаются - автоматом вызывается деструктор.

CODE

AUTOMUTEX::AUTOMUTEX(void * const _mutex) : m_isMutexLock(-1), m_mutex(_mutex) {}

sint32_t AUTOMUTEX::Lock() {
if (m_isMutexLock != TERR_NO_ERR) {
m_isMutexLock = tn_mutex_lock_polling((TN_MUTEX *)m_mutex);
};

return m_isMutexLock;
}

AUTOMUTEX::~AUTOMUTEX() {
if (m_isMutexLock == TERR_NO_ERR) {
tn_mutex_unlock((TN_MUTEX *)m_mutex);
};
}


Из других задач получить доступ к мьютексу не является проблемой.
dxp
Rusoil

Понятно. Ответ на ваш исходный вопрос: если функция tn_task_exit возвращает управление в вызывающую функцию, то деструктор будет вызван. Если нет, то не будет. Какое именно у tn_task_exit поведение, надо смотреть в документации.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.