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

Вопросы по TNKernel

Здравствуйте!

 

Поставил, скомпилировал, подредактировал проект примера, идущий в комплекте, под себя, все компилируется и работает. Пока что экспериментирую на кошках (встроенный в 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 системных тиков." На малую долю этого тика, но ведь меньше.... И кто мне гарантирует что эта доля не увеличится до почти целого тика при каких-то условиях.

 

Оговорюсь, это все на основе симулятора, попробую позже на железе посмотреть.

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


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

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

 

Например, в TNKernel для ф-ции tn_task_sleep (100) с временем одного тика 1 ms,

реальная задержка может колебаться от 99 ms (+ несколько микросекунд)

до 100 ms (минус несколько микросекунд ).

 

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

и прерыванием от системного таймера всегда случайно.

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


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

В любой 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"

:)

 

 

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

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


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

А я вот на 24FJ64 запускаю

светодиодиками моргаю

 

и как я раньше без этого жил :biggrin:

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


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

Тоже есть вопрос по TN.

 

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

 

Я использую c++ обертку для мьютекса, там происходит автоосвобождение в деструкторе.

Будет там автоматом деструктор выполняться как при выходе из функции? Или лучше так не делать?

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


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

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

 

Я использую c++ обертку для мьютекса, там происходит автоосвобождение в деструкторе.

Будет там автоматом деструктор выполняться как при выходе из функции? Или лучше так не делать?

 

Правильность работы "внутрених" механизмов С++ можно проверить в embedded по-настоящему только

под отладчиком.

Рекомендую использовать симулятор и debugger Keil ( для АРМ/Cortex).

 

Кстати, наличие в С++ кода, написанного не напрямую (как в С) программистом, а добавляемого

компилятором самостоятельно, является основной причиной, по которой я лично не использую

С++ в embedded системах.

 

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


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

Будет там автоматом деструктор выполняться как при выходе из функции? Или лучше так не делать?

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

 

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

 

Кстати, наличие в С++ кода, написанного не напрямую (как в С) программистом, а добавляемого

компилятором самостоятельно, является основной причиной, по которой я лично не использую

С++ в embedded системах.

Какой именно код тут имеется в виду? Конкретно?

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


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

Какой именно код тут имеется в виду? Конкретно?

Например, default constructor/destructor в случае declaration типа MyClass x;

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


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

Локальный для функций.

 

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

 

В иаре это работает. Но нужно помнить о специфике ртос.

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


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

Например, 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. Где этот левый код?

 

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

 

Локальный для функций.

 

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

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

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


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

Сам мьютекс конечно обявлен как static и является сущностью ртос.

 

 

В конструктор даем указатель на мьютекс.

Потом вызываем захват мьютекса.

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

 

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);
   };
}

 

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

Изменено пользователем Rusoil

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


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

Rusoil

 

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

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


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

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

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

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

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

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

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

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

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

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