Jump to content

    
jenya7

Как лучше оформить таск?

Recommended Posts

У меня сейчас вся инициализация периферии в самом таске

void operation_task()
{
    static int mflag = 0;
	uint16_t message_status = 0;

    CFG_CONFIG_SetVal();

    /* PBIT process */
    power_init();
	WAIT1_Waitms(100);  

    /* SET config. value for FPGA */
    err_dat = init_configuration();
    /* detection of mode by discrete : Maintenance or Operational */
    g_crt_state = mode_detection_by_discrete();

    //includes import parameters from flash
    err_dat |= pbit_process();

    DrawTestDisplay();
    WAIT1_Waitms(500);
    ClearDisplay();

    MENU_Setup();

    ResetGlobalVariables();
    LIGHT_ImportDimParam();

	//////////////////LOGGER/////////////////////
    NFLASH_Reset();
    nand_flash_status = NFLASH_Init();

    LOG_ReadPartitionData(&logger_data);
    ImportBadBlocksData();
	
    Call_1553();
    mcp_init();
    incoming_mailboxes_init();

    while(1)
    {
        WDog1_Clear();

        if(mflag == 0)
        {
            operation_maint_process();
            message_status = MM_check_new_message();
            if (message_status > 1)
                err_count_1553++;
        }

        if(msgFlag == 1)
        {
           msgFlag = 0;

           mflag = dispecth_UART_cmd();
			    
        }
    }
}

int main(void)
{
   rtos_stat = FRTOS1_xTaskCreate((TaskFunction_t)operation_task, (signed char*) "oper_task", /*2048*/ 2048, NULL, 2, NULL);
  
   PEX_RTOS_START();
  
   for(;;){}
}
  

может вынести инициализацию из таска?

void operation_task()
{
    while(1)
    {
        WDog1_Clear();

        if(mflag == 0)
        {
            operation_maint_process();
            message_status = MM_check_new_message();
            if (message_status > 1)
                err_count_1553++;
        }

        if(msgFlag == 1)
        {
           msgFlag = 0;

           mflag = dispecth_UART_cmd();
			    
        }
    }
}

int main(void)
{
     static int mflag = 0;
	uint16_t message_status = 0;

    CFG_CONFIG_SetVal();

    power_init();
	WAIT1_Waitms(100);  

    err_dat = init_configuration();
    g_crt_state = mode_detection_by_discrete();

    err_dat |= pbit_process();

    DrawTestDisplay();
    WAIT1_Waitms(500);
    ClearDisplay();

    MENU_Setup();

    ResetGlobalVariables();
    LIGHT_ImportDimParam();

	//////////////////LOGGER/////////////////////
    NFLASH_Reset();
    nand_flash_status = NFLASH_Init();

    LOG_ReadPartitionData(&logger_data);
    ImportBadBlocksData();
	
    Call_1553();
    mcp_init();
    incoming_mailboxes_init();
  
   rtos_stat = FRTOS1_xTaskCreate((TaskFunction_t)operation_task, (signed char*) "oper_task", 2048, NULL, 2, NULL);
  
   PEX_RTOS_START();
  
   for(;;){}
}
  

 

Share this post


Link to post
Share on other sites
6 minutes ago, jenya7 said:

может вынести инициализацию из таска?

Лучше сделать отдельную задачу, которая:

1. Проинициализирует периферию

2. Запустит остальные задачи

3. Самоубьётся

 

Share this post


Link to post
Share on other sites

ИМХО, правильно можно так и так:

1. Если в задаче инициализируется только та периферия, которую обслуживает данная задача, тогда можно и в начале задачи это делать. Где-то читал, что в случае, если в инициализации используются системные вызовы, рекомендуется делать так:

void task1(void * param) {
  
  while (1) {
    
    periph_init();
    
    while (1) {
      periph_routine();
    }
      
  }
  
}

Мол компилятор без первого цикла (хоть он и не нужен) может криво оптимизировать и будет бо-бо.

2. Лично я предпочитаю все инициализации делать в одном месте, как в вашем втором случае.

Share this post


Link to post
Share on other sites
41 minutes ago, jenya7 said:

может вынести инициализацию из таска?

Да, лучше вынести и разбить на функции по назначению: инициализация дисплея, памяти, интерфейсов и т.п.. Ибо трудно читать такие огромные портянки) Инициализировать в одной задаче, которую затем удалить.

Впрочем, я также инициализирую периферию по "месту требования": т.е. в том файле, где эта периферия нужна. Драйвера периферии не позволяют инициализировать её повторно, поэтому коллизий не возникает.

 

 

23 minutes ago, SMaster said:

Мол компилятор без первого цикла (хоть он и не нужен) может криво оптимизировать и будет бо-бо.

А можно подробнее? Это же обыкновенная функция. Делаю без первого цикла, и всё работает.

Share this post


Link to post
Share on other sites
1 hour ago, aaarrr said:

Лучше сделать отдельную задачу, которая:

1. Проинициализирует периферию

2. Запустит остальные задачи

3. Самоубьётся

 

фига се. такого решения я не встречал.

Share this post


Link to post
Share on other sites
20 minutes ago, haker_fox said:

А можно подробнее? Это же обыкновенная функция. Делаю без первого цикла, и всё работает.

Да, действительно, для FreeRTOS вряд ли так надо делать. Встречал данную рекомендацию для старенькой jacOS (вот буквально только что нашел ее в доках от этой РТОС). Но там совсем другая история: это кооперативная ОС + IAR вытворял чудеса компиляции, возможно вызванные недоработками самой ОС. Сорри, ляпнул "рекомендацию" не подумав. :)

Share this post


Link to post
Share on other sites
3 minutes ago, SMaster said:

для старенькой jacOS

Оооо, так это же начало 2000-х, как помню) Тогда и компиляторы могли "глючить". Тот же IAR.

Share this post


Link to post
Share on other sites

У меня периферия, требующая первостепенной инициализации (отладочный UART, например), настраивается перед запуском всех задач.
Т.е. в main() есть некая функция InitDevDrv(), которая настраивает периферию первой необходимости и периферию общего назначения.
Периферия общего назначения это, например, какие-нибудь GPIO для реле, дискретных входов/выходов и т.д.
Их драйверы не обслуживают какую-то конкретную задачу, они лишь предоставляют некий сервисный API любому желающему процессу.

А вот уже конкретная периферия под конкретный программный модуль настраивается в самой задаче перед while(1) или около того.

Вот, к примеру, main()

Скрытый текст

#include "main.h"

#include "pcdbg.h"
#include "gbctl.h"
#include "canctl.h"
#include "sysctl.h"

#include "eds.h"

#include "hw.h"
#include "wdt.h"
#include "dbg.h"
#include "led.h"
#include "pwr.h"
#include "nvm.h"


static void InitDevDrv(void)
{
  wdt_InitExt();
  wdt_UpdAll();
  
  dbg_Init();
  led_Init();
  pwr_Init();
  nvm_Init();
}

static void ActAftSysRst(void)
{
  if(hw_isRstOnPwr())
    SysCtlInf.rstonpwr = 1,
    dbg_PrintWarn("Reset on 'PWR'");
  else if(hw_isRstOnSoft())
    dbg_PrintWarn("Reset on 'SOFT'");
  else if(hw_isRstOnIWDT())
    dbg_PrintCrit("Reset on 'Int. WDT'");
  else if(hw_isRstOnExtWDT())
    dbg_PrintCrit("Reset on 'Ext. WDT'");
  hw_ClrRstInf();
  
  dbg_PrintNorm("System start");
}

static void RunUsrApp(void)
{
  pcdbg_Init();
  gbctl_Init();
  canctl_Init();
  sysctl_Init();
}

int main(void)
{
  InitDevDrv();
  ActAftSysRst();
  
  RunUsrApp();
  eds_RunTskManager();
  
  return 1;
}

P.S. Вот тут движок форума создает пустую строку, которую невозможно удалить и которая режет глаза...

а это, например, функция инициализации модуля взаимодействия по CAN

Скрытый текст

...
void canctl_Init(void)
{
  can_Init();
  
  eds_CreateTsk(EDS_TSKID_CANCTL, CANCtlTsk, 256);
}

 

У Xilinx, например, был небольшой костыль в исходниках FreeRTOS, которую они поставляли с Vivado SDK.
Они в xPortStartSheduler() вставляли вызов пользовательской функции vApplicationSetupHardware(), в которой можно было инициализировать железо.
Но, ИМХО, это не всегда удобно.

Share this post


Link to post
Share on other sites
25 minutes ago, haker_fox said:

Оооо, так это же начало 2000-х, как помню) Тогда и компиляторы могли "глючить". Тот же IAR.

Для меня это была вторая половина 2000х :) Пару-тройку лет назад надо было подновить проект тех времен. И тут я удивился: в АТМега16 1 кБ RAM. Рили? Казалось, что больше. И как-то ведь работает. :)

Сорри за офтоп, нахлынули воспоминания. :)

4 minutes ago, Arlleex said:

Они в xPortStartSheduler() вставляли вызов пользовательской функции vApplicationSetupHardware(), в которой можно было инициализировать железо.

Такой костыль несложно и самому добавить. :) В общем-то не страшно и последовательно вызвать vApplicationSetupHardware(), а затем xPortStartSheduler(). :)

Share this post


Link to post
Share on other sites
1 hour ago, jenya7 said:

такого решения я не встречал.

Между тем, это правильное решение: так появляется возможность пользоваться средствами ОС при инициализации периферии.

Share this post


Link to post
Share on other sites
2 минуты назад, SMaster сказал:

В общем-то не страшно и последовательно вызвать vApplicationSetupHardware(), а затем xPortStartSheduler().

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

Share this post


Link to post
Share on other sites
40 minutes ago, aaarrr said:

Между тем, это правильное решение

Делал раньше точно так, как вы указали выше. Но консольку, хотя бы на вывод, иногда полезно настроить и до старта ОС. По-крайней мере иметь возможность заставить драйвер последовательного порта поработать без окружения ОС.

42 minutes ago, Arlleex said:

так это запрещенные прерывания на момент входа в нее.

О как. А заче запрещать прерывания для конфигурации периферии?

Share this post


Link to post
Share on other sites
5 минут назад, haker_fox сказал:

О как. А заче запрещать прерывания для конфигурации периферии?

Чтобы какой-нибудь GiveFromISR() не ввел еще не запущенный диспетчер в ступор.
Или, например, в обработчике прерывания вызывается API RTOS выдачи семафора.
А этот семафор еще не выделился в памяти (не был вызван CreateSemaphore()).

Share this post


Link to post
Share on other sites
6 часов назад, aaarrr сказал:

3. Самоубьётся

А это то зачем?  :shok:

3 часа назад, Arlleex сказал:

А этот семафор еще не выделился в памяти (не был вызван CreateSemaphore()).

Так вообще то, по уму, сначала делается инит всех переменных драйвера(-ов) в ОЗУ, и только затем - инит периферии, использующей эти переменные.

Если эти переменные (в том числе и средства синхронизации ОС) используются как семафоры разделяемой периферии, то ининтить их можно сразу в состояние "занято". А уже код инициализации периферии, по завершении своей работы, переведёт их в "свободно".

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.