Jump to content

    

uOS и MultiCore

Пытаюсь запустить несколько различных задач на uOS которая поставляется совместно с пакетом MCStudio, но работает не так как ожидалось.

 

Собственно никакой многозадачности не наблюдаю. Работает только та задача у которой наивысший приоритет. (В данном случае task ).

 

Что я хотел получить: должны мигать три светодиода, и по приходу данных с UART должен заходить в обработчик uart_intr_hdl.

 

Вроде как такая конструкция на RTOS STM32 работает корректно, но тут, на uOS никак не желает.

 

Что я делаю не так ???

 

 

bool_t uart_intr_hdl(void *arg)
{
    ch = get_received_byte(1);
    LedTgl(1);
    return 1;
}

void task1 (void *arg)
{
    for(;;)
    {
        LedTgl(8);
        udelay(30000);
    }
}

void task2 (void *arg)
{
    for(;;)
    {
        LedTgl(4);
        udelay(40000);
    }
}



void task (void *arg)
{
    setup_baud_rate (1, KHZ, 115200);

    mutex_lock_irq (&receiver, RECEIVE_IRQ (1), &uart_intr_hdl, 0);
    enable_receiver (1);
    enable_receive_interrupt (1);

    transmit_byte(1, '*');

    for (;;)
    {
        udelay(50000);
        LedTgl(2);
    } // for(;;)
}




void uos_init (void)
{
    SYS_REG.CLK_EN.data = 0xFFFFFFFF; // включение тактовой частоты  _init_ включает только минимум частот

    LedInit();
    MFBSP2.DIR.data = 0x3ff;       // переключаем LDAT2[7:4] в режим выхода

    task_create (task1, 0, "task1", 1,  task1_space, sizeof (task1_space));
    task_create (task2, 0, "task2", 2,  task2_space, sizeof (task2_space));

    task_create (task, "task", "task", 3, task_space, sizeof (task_space));
}

Share this post


Link to post
Share on other sites

Для того чтобы переключались задачи надо, чтобы ядро получало управление и задачи с более высоким приоритетом засыпали.

Я лет 15 назад работал с этой операционкой и не помню: udelay() вызывает ядро?

 

Share this post


Link to post
Share on other sites

Добрый день!

 

 

В uOS реализован только приоритетный планировщик. Это значит, что никакой "карусели" ("round robin") нет. То есть всегда работает задача с наивысшим приоритетом. Если нет таких, то - фоновая задача.

Есть объяснение, почему именно так:

1) это сокращает время реакции на событие для приоритетных задач;

2) системный таймер не является обязательной частью прикладных программ под uOS; есть проекты, где он успешно не используется.

 

Теперь конкретно, что в Вашем примере не так. Функция udelay отмеряет время "в коротком цикле", то есть процессор при этом постоянно задействован. uOS считает, что у приоритетной задачи есть работа, и поэтому не переключается на другие задачи. Ещё другими словами напишу, udelay (так же, как и mdelay) не является "точкой ожидания".

Что делать - заменить на функцию задержки, которая является точкой ожидания - timer_delay(). Для этого нужно сначала проинициализировать системный таймер - см. код ниже.

 

#include <timer/timer.h>

timer_t timer;

void task1 (void *arg)
{
    for(;;)
    {
        LedTgl(8);
        timer_delay(&timer, 30);  // Вместо udelay(30000), для других задач - аналогично.
    }
}

void uos_init()
{
     timer_init(&timer, KHZ, 10);  // период тика - 10 мс.

     // ...
}

 

Второй параметр timer_init - константа KHZ, которая выражает частоту ядра в килогерцах и задаётся в настройках проекта. Практически всегда на месте второго параметра должна стоять именно эта константа. Ещё можно было бы выбросить из интерфейса функции, но исторически уже так сложилось.

Третий параметр - период тика таймера в миллисекундах. Это величина должна быть меньше используемых в коде задержек.

 

Функции задержки "в коротком цикле" (udelay, mdelay) нужны для отработки коротких задержек без переключения задачи. Надо понимать, что они не очень точные, особенно при малых временах задержки. И, наоборот, если величина в микросекундах сильно больше 1000, то очень желательно применять mdelay вместо udelay, поскольку на разных архитектурах не гарантируется, что большие величины поместятся в тип параметра функции и что корректно написан код udelay для больших значений параметра.

 

P.S. Скажу ещё, что на самом деле, можно реализовать "карусель", но на основе коллективной многозадачности задач с одинаковым приоритетом с помощью функции task_yield(). То есть задача, отработав некий код сама по своей инициативе с помощью этой функции отдаёт управление другой задаче с тем же приоритетом.

 

Share this post


Link to post
Share on other sites
Что я делаю не так ???

for(;;)

{

LedTgl(8);

udelay(30000);

}

Так писать под ОС нельзя. Ни под какую ОС. Задача должна отдавать неиспользуемое время системе.

Share this post


Link to post
Share on other sites

С этим разобрался. Заработало. Спасибо всем ответившим. Отдельное спасибо vatilin за подробные объяснения.

По поводу задержки, я ее ставил просто как задержку. В реальной программе так не пишу. Ну и посмотрел я реализацию udelay в uOS, оказалось что она просто тупо ждет, и к сожалению она не аналог Sleep в RTOS.

Теперь разбираюсь с прерываниями.... вроде как работают, но пока еще не привычно для меня.

Share this post


Link to post
Share on other sites
к сожалению она не аналог Sleep в RTOS.

OSTimeDly()

 

Теперь разбираюсь с прерываниями.... вроде как работают, но пока еще не привычно для меня.

К прерываниям uCOS вообще никакого отношения не имеет. Кроме PendSV и немножко SysTick.

Share this post


Link to post
Share on other sites
К прерываниям uCOS вообще никакого отношения не имеет. Кроме PendSV и немножко SysTick.

 

У меня uOS а не uCOS, они немного разные как мне кажется.

 

В uOS за обработку прерываний отвечают функции:

mutex_lock_irq () Захват прерывания

mutex_unlock_irq () Освобождение прерывания

mutex_wait () Ожидание прерывания

Share this post


Link to post
Share on other sites

Что бы не плодить сообщения, продолжу в этой теме. 

Продолжаю безуспешную войну с uOS. Сейчас вычищаю баги в проекте. 

Ситуация такая: Использую аппаратный UART в Multicore. Всё работает, но если поток данных по UART идёт сплошняком, есть предположения, что прерывание приходит чаще, чем проц успевает его обработать, то он уходит в функцию void 
mutex_activate (mutex_t *m, void *message) 
и остаётся там навсегда (крутится внутри while() ).  Кто с этим сталкивался ? Как побороть и куда ещё посмотреть ?

Ниже привожу инициализацию порта


/*
 * Receive interrupt task.
 */
void  uart422_receiver (void *arg)
{
	uartpkt_t *u = arg;

	// Enable receiver & transmitter
	mutex_lock_irq (&u->receiver, RECEIVE_IRQ(u->port), 0, 0);
	enable_receiver (u->port);
	enable_receive_interrupt (u->port);

    debug_printf("Uart422 started.\n");

	for (;;) {
		mutex_wait (&u->receiver);				// Ожидаем событие по приему или по принудительному пинку для передачи

#ifdef clear_receive_errors
		clear_receive_errors (u->port);
#else
		if (test_frame_error (u->port)) {
			//debug_printf ("FRAME ERROR\n");
			clear_frame_error (u->port);
		}
		if (test_parity_error (u->port)) {
			//debug_printf ("PARITY ERROR\n");
			clear_parity_error (u->port);
		}
		if (test_overrun_error (u->port)) {
			//debug_printf ("RECEIVE OVERRUN\n");
			clear_overrun_error (u->port);
		}
		if (test_break_error (u->port)) {
			 // debug_printf ("BREAK DETECTED\n");
			clear_break_error (u->port);
		}
#endif
		uart422_rtx_proc(u);					// Вызываем функцию обработчика события с USART

	//	task_yield();
	}
}



void uart422_init (uartpkt_t *u, small_uint_t port, int prio, unsigned int khz,
	unsigned long baud)
{
	u->khz = khz;
	u->port = (port) ? (volatile unsigned*)(0xb82f3800) : (volatile unsigned*)(0xb82f3000);

	mutex_init(&u->receiver);

	/* Setup baud rate generator. */
	setup_baud_rate (u->port, u->khz, baud);

	/* Create uart receive task. */
	uart422_task_s = task_create (uart422_receiver, u, "uart422", prio, u->rstack, sizeof (u->rstack));

}

 

Функция работы с портом


static bool_t uart422_rtx_proc (uartpkt_t *u)
{
unsigned char tx_char=0;
unsigned char c=0;

	if (test_get_receive_data(u->port, &c))
		{
		mutex_lock(&u->receiver);

			switch (c)
			{
			case 0xff:
				break;

				......
                  
			default:
				if (u->rx_packet.data_in_process==1)
				{
					if (u->rx_packet.addcode!=0)
					{
						c = c + 0xfd;				// дополняем принятое значение
						u->rx_packet.addcode= 0;
					}
					u->rx_packet.pkt_buffer.p_buffer[u->rx_packet.ptr] = c;
					u->rx_packet.ptr = (u->rx_packet.ptr < MAX_CTL_SIZE) ? (u->rx_packet.ptr + 1) : u->rx_packet.ptr;	// На всякий случай ограничим прием при переполнении
					u->rx_packet.crc= (u->rx_packet.crc + c) & 0xff;
				}
				break;
			}


			mutex_unlock(&u->receiver);
		}


// ------------    Transmit section   ------------------
	//  Check that transmitter buffer is busy.
	if (! test_transmitter_empty (u->port)) {
		return 1;
	}

	if (u->tx_packet.data_in_process==2)
	{		// Функция для завершения передачи и перевода трансивера на приём
//		delay(25);								// !!! возможно будет отжирать системное время по завершини передачи
//		RS485_SetRxMode();
		u->tx_packet.data_in_process=0;
		disable_transmit_interrupt (u->port);
		u->tx_packet.total_packets++;
		mutex_unlock(&u->transmitter);				// Снимаем блокировку
		return 0;
	}

			......	}
}

	return 1;
}

 

И сама функция в которой проц застревает (из uOS/main.c)

(пробовал ставить UOS_SIGNAL_SMART =0, результат такой же)

void 
mutex_activate (mutex_t *m, void *message)
{
	task_t *t;
	mutex_slot_t *s;


	assert (m != 0);
	if (! m->item.next)
		mutex_init (m);

	while (! list_is_empty (&m->waiters)) {
		t = (task_t*) list_first (&m->waiters);
		assert2 (t->wait == m
		        , "assert task %s(0x%x) wait %x activate from %x($%x)\n"
		        , t->name, (unsigned)t
		        , t->wait, m, (unsigned)message
		        );//uos_assert_task_name_msg
		t->wait = 0;
        t->message = message;
#if UOS_SIGNAL_SMART > 0
        //получили сигнал, и жду захвата мутеха, потому пермещу нитку из ожидания сигнала в 
        //  захватчик мутеха
	    if (t->MUTEX_WANT != 0)
		if (mutex_wanted_task(t))
		    continue;
#endif
		task_activate (t);
	}
 ........

 

Share this post


Link to post
Share on other sites
1 час назад, Digi сказал:

Ситуация такая: Использую аппаратный UART в Multicore. Всё работает, но если поток данных по UART идёт сплошняком, есть предположения, что прерывание приходит чаще, чем проц успевает его обработать, то он уходит в функцию void 
mutex_activate (mutex_t *m, void *message) 
и остаётся там навсегда (крутится внутри while() ).  Кто с этим сталкивался ? Как побороть и куда ещё посмотреть ?

Искать причину почему у Вас прерывания обрабатываются так медленно. Не знаю какая у Вас скорость и какой процессор и его частота, но приём из UART должен быть рассчитан так, чтобы успевал принять всё. Если скорость порта достаточно большая, а скорость CPU - маленькая, то лучше использовать DMA. Опять-же - если в UART-е отсутствует аппаратное FIFO (какой МК? - сиё тайна великая) и есть DMA, то скорей всего DMA почти обязательно при скоростях >=115200.

Share this post


Link to post
Share on other sites
Цитата

Ситуация такая: Использую аппаратный UART в Multicore. Всё работает, но если поток данных по UART идёт сплошняком, есть предположения, что прерывание приходит чаще, чем проц успевает его обработать, то он уходит в функцию void 
mutex_activate (mutex_t *m, void *message) 
и остаётся там навсегда (крутится внутри while() ).  Кто с этим сталкивался ? Как побороть и куда ещё посмотреть ?

А ваше прерывание не реентантно?

текущий обработчик мутехов mutex_activate  не реентантен - он не должен прерываться и вызываться внутри другого прерывания. операции с очередью задач task_active должны быть атомарны.

если есть сомнения, можно вставить асерт в mutex_activate  на вложенный вызов и убедиться срабатывает ли он.

	if (test_get_receive_data(u->port, &c))
		{
		mutex_lock(&u->receiver);
			switch (c)
			{
			case 0xff:

Вот этот код както опасно выглядит: сначала получаются данные, а потом ожидается прерывание? и потом полученные данные обрабатываются?

Edited by AlexRayne

Share this post


Link to post
Share on other sites

Насколько я понял, Вы используете мою ветку ядра.

Специально для ожидания данных из прерываний  я ввел 

bool_t mutex_wait_until (mutex_t *m
        , scheduless_condition waitfor, void* waitarg
        )

она позволяет не уходить в ожидание сигнала от прерывания, если по факту есть данные. Это позволит избежать пропуска прерываний. Функция waitfor выполняется атомарно, в ней можно проверить - есть ли в уарте данные. 

Edited by AlexRayne

Share this post


Link to post
Share on other sites

По-моему, проблема в следующем:

1) Сначала мьютекс прикрепляется к прерыванию. Поскольку используется функция mutex_lock_irq, а не mutex_attach_irq, то сразу после выхода из этой функции мьютекс u->receiver захвачен.

mutex_lock_irq (&u->receiver, RECEIVE_IRQ(u->port), 0, 0);

2) Поскольку мьютекс захвачен, то после выхода из функции mutex_wait он тоже остаётся захваченным (пока поток находится внутри функции mutex_wait и ожидает события, мьютекс, конечно, освобождается, но обратно автоматически захватывается при выходе из mutex_wait).

3)  Далее поток попадает в функцию uart422_rtx_proc, и если выполняется условие:

	if (test_get_receive_data(u->port, &c))
		{
		mutex_lock(&u->receiver);

мьютекс опять захватывается - попытка захватить захваченный мьютекс! Смысла в этой операции нет никакого: мьютекс и так уже захвачен нашей задачей.

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

 

В общем, рекомендация такая: удалить mutex_lock(&u->receiver), mutex_unlock(&u->receiver) из функции uart422_rtx_proc. Мьютекса u->transmitter это не касается, хотя я вижу, что есть вызов mutex_unlock, но не вижу вызова mutex_lock - тоже как-то странно...

Share this post


Link to post
Share on other sites

"Это ошибка выполнения при использовании нерекурсивных мьютексов, на этом месте мы должны вылететь по ассерту внутри mutex_lock. "

по идее нерекурсивный мутех должен выдать хальт/останов нитки при попытке повторного захвата, а не полагаться на ассерт?

"опять захватывается - попытка захватить захваченный мьютекс! Смысла в этой операции нет никакого: мьютекс и так уже захвачен нашей задачей."

видимо он попытался использовать один мутех и для блокировки доступа к буфферу и для сигнала от прерывания?

это могло бы проканать как раз еслиб он использовал mutex_attach_irq, или разлочил мутех перед ожиданием сигнала.

Edited by AlexRayne

Share this post


Link to post
Share on other sites
17 hours ago, jcxz said:

Искать причину почему у Вас прерывания обрабатываются так медленно. Не знаю какая у Вас скорость и какой процессор и его частота, но приём из UART должен быть рассчитан так, чтобы успевал принять всё. Если скорость порта достаточно большая, а скорость CPU - маленькая, то лучше использовать DMA. Опять-же - если в UART-е отсутствует аппаратное FIFO (какой МК? - сиё тайна великая) и есть DMA, то скорей всего DMA почти обязательно при скоростях >=115200.

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

 

12 hours ago, AlexRayne said:

Вот этот код както опасно выглядит: сначала получаются данные, а потом ожидается прерывание? и потом полученные данные обрабатываются?

в uart244_receiever ожидается прерывание, затем в uart422_rtx_proc я проверяю, есть ли данные для приёма, а вот то что после mutex_lock_irq (&u->receiver, RECEIVE_IRQ(u->port), 0, 0)  он остаётся захвачен, не знал. 

10 hours ago, vatilin said:

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

 

В общем, рекомендация такая: удалить mutex_lock(&u->receiver), mutex_unlock(&u->receiver) из функции uart422_rtx_proc. Мьютекса u->transmitter это не касается, хотя я вижу, что есть вызов mutex_unlock, но не вижу вызова mutex_lock - тоже как-то странно...

Да, ассерты отключены. Про кривое использование мьютексов понял. Попробую исправить.

Share this post


Link to post
Share on other sites
4 часа назад, Digi сказал:

Я же указал, МК - Multicore. Только дело тут не в скорости обработки (это уже следующий момент оптимизации, а в том что я криво написал код).

И что? Причём тут ОС? У меня тоже есть RTOS. Она никак не относится к обработке прерываний.

Символы от UART-а складываются в FIFO, а потом только через какой-нить механизм ОС отправляется уведомление задаче о пополнении FIFO.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now