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

Блокирование задач на время i/o операции

Пытаюсь сделать драйвер или полегче говоря hal модуль i2c. Надо как-то реализовать такие функции как запись/чтение в/из i2c slave. Есть конечный автомат вызываемый из прерывания, обрабатывающий состояния i2c, передача стартов, стопов, адреса, данных и т.п.. В начале надо дождаться того, что этот автомат будет свободен, это первая точка блокирования. Отдать задание и затем дождаться его выполнения, это вторая точка блокирования.

 

В голову приходит, только конструкция из одной очереди запросов и пула очередей для ответов на запросы. Как-то громоздко, нельзя ли попроще? Использовать "короткие" очереди, чтобы сократить тот пул до одной очереди ответов, зная, что только одна задача может находится в состоянии ожидания ответа? Как еще?

 

int i2c_write_reg(int addr, int reg, int val)
{
    halI2C_MSG_t        xMSG, *pMSG;
    unsigned char        msg_data[1];

    pMSG = &xMSG;
    xMSG.xTask = xTaskGetCurrentTaskHandle();
    xMSG.msg_addr = addr << 1;
    xMSG.msg_sreg = reg;
    xMSG.len_data = 1;
    xMSG.msg_data = msg_data;

    msg_data[0] = val;

    xQueueSendToBack(pI2C->xQueueReq, &pMSG, portMAX_DELAY);

    NVIC_SetPendingIRQ(I2C1_EV_IRQn);
    //ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

    return xMSG.errno;
}

 

Вариант с TaskNotify отбросил, по той причине, что этот механизм будет использован на уровне выше, для запуска задачи которая пользуется i2c.

 

Спасибо.

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


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

Спасибо.

 

Функция с явным багом.

xMSG.msg_data сразу после выхода из функции будет содержать невалидную ссылку на данные.

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

 

Ну допустим начало отправки сделано асинхронно, но потом то задача все равно застрянет на ожидании подтверждения.

Функции IO можно просто завернуть в семафоры. Будет тот же эффект.

 

 

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


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

Функция с явным багом.

...

 

После выходы из функции эти данные уже никому не нужны (а когда нужны берется указатель из параметров функции), они обработаны и возвращен код результата.

 

Во freertos семафоры это внутри те же очереди. Как завернуть, так?

 

xSemaphoreTake(xSem1, );

// здесь передаем данные в конечный автомат (КА) и запускаем прерывание

xSemaphoreTake(xSem2, );

// сюда попадаем когда КА сделает свои дела и вызовет xSemaphoreGive(xSem2);
// забираем результат операции

xSemaphoreGive(xSem1);

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

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


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

После выходы из функции эти данные уже никому не нужны (а когда нужны берется указатель из параметров функции), они обработаны и возвращен код результата.

 

Во freertos семафоры это внутри те же очереди. Как завернуть, так?

 

xSemaphoreTake(xSem1, );

// здесь передаем данные в конечный автомат (КА) и запускаем прерывание

xSemaphoreTake(xSem2, );

// сюда попадаем когда КА сделает свои дела и вызовет xSemaphoreGive(xSem2);
// забираем результат операции

xSemaphoreGive(xSem1);

пляски вокруг xSem2 можно заменить на пару vTaskSuspend/vTaskResume

 

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


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

пляски вокруг xSem2 можно заменить на пару vTaskSuspend/vTaskResume

 

А что будет если vTaskResume будет вызвана раньше vTaskSuspend?

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


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

А что будет если vTaskResume будет вызвана раньше vTaskSuspend?

Очевидно, что vTaskSuspend надо вызывать из критической секции.

 

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


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

Очевидно, что vTaskSuspend надо вызывать из критической секции.

 

И по сравнению с этим одиночные вызовы xSemaphoreTake/xSemaphoreGive называются плясками?

 

Пока сделал на семафорах. Другой вопрос близкий к теме. Хотел использовать Direct To Task Notifications, но получаю зависание всей системы на вызове ulTaskNotifyTake(pdTRUE, portMAX_DELAY). Включил assert, проверку переполнения стека, нехватку хипа, обложил отладочным выводом *Fault прерывания. Причину не нашел. Версия FreeRTOSv9.0.0, по коду мне показались эти Notify функции странными, в других местах тоже самое делается более аккуратно. До дна ковырять не стал, использовал семафоры.

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


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

Семафоры - хорошее решение. Правда я не понял зачем вам их два. Всё-таки наверное первый у вас - это мютекс?

portMAX_DELAY - не лучший вариант, все-таки правильнее задать конкретное время и обработать ошибку, а то все зависнуть может.

По поводу нотификейшнов проще было бы подсказать, если бы вы поделились кодом. Я бы начал с простого и проверил в конфиге configUSE_TASK_NOTIFICATIONS (: Я активно нотификейшнами пользуюсь как раз для такого типа синхронизации, по времени выходит быстрее.

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


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

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

 

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

 

ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

 

более ничего относящегося к *Notify* нет. Время ожидания можно указать другое, ничего не меняет. Перед этим вызовом ставил вывод в usart, вывелся только первый символ. Вывод сделан через отправку в очередь каждого символа, и обработку в прерывании, без dma. Первый отправился немедленно, а когда пришло время для следующего то прерывания уже видимо были запрещены.

 

Если в той задаче заменяю *Notify* на семафор, то начинает работать как должно. Иначе, не возвращается управление из *NotifyTake, останавливаются другие задачи.

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


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

Вы хотите использовать нотифаи в качестве бинарного семафора? Тогда может быть попробовать перед записью в очередь его точно сбросить (ulTaskNotifyTake(pdTRUE, 0);) ?

И без кода прерывания сложно сказать, но я думаю у вас там корректно делаются vTaskNotifyGiveFromISR и portYIELD_FROM_ISR?

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


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

Проверил еще раз, вот весь код. Если убрать Notify и раскомментировать семафоры, то работает. А так останавливается все, не только эти задачи, но и не относящаяся к этому коду задача обработки команд по usart.

 

#include <stddef.h>

#include "hal/hal.h"
#include "hal/i2c.h"
#include "hal/mpu6050.h"
#include "hal/timebase.h"
#include "hal/usart.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"

#include "fp/im.h"

#include "main.h"
#include "lib.h"
#include "sh.h"

i2c_mpu6050_t			i2c_mpu6050;

fp_im_t __CCM__			fp_im;

TaskHandle_t			tIM;

void timebaseIRQ()
{
BaseType_t		xWoken = pdFALSE;

//xSemaphoreGiveFromISR(glob.xSemIM, &xWoken);
vTaskNotifyGiveFromISR(tIM, &xWoken);

portYIELD_FROM_ISR(xWoken);
}

void taskFP_IM(void *pvParameters)
{
int			rc = 0;

rc = i2c_mpu6050_enable(&i2c_mpu6050, 1, 1);

do {
	//xSemaphoreTake(glob.xSemIM, portMAX_DELAY);
	ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

	rc = i2c_mpu6050_read(&i2c_mpu6050);

	if (rc == 0) {

		fp_im_main(	&fp_im,
				i2c_mpu6050.accel,
				i2c_mpu6050.gyro,
				i2c_mpu6050.temp);

		if (fp_im.refresh_flag) {

			fp_im.refresh_flag = 0;
			xSemaphoreGive(glob.xSem100HZ);
		}
	}
	else {
	}
}
while (1);
}

void taskFP_100HZ(void *pvParameters)
{
int		i = 0;

do {
	xSemaphoreTake(glob.xSem100HZ, portMAX_DELAY);

	i++;

	if (i >= 10) {

		i = 0;
		xSemaphoreGive(glob.xSem10HZ);
	}

}
while (1);
}

void taskFP_10HZ(void *pvParameters)
{
do {
	xSemaphoreTake(glob.xSem10HZ, portMAX_DELAY);

	printf("%f %f %f (%f %f %f) %f" EOL,
		&fp_im.sv[0],
		&fp_im.sv[1],
		&fp_im.sv[2],
		&fp_im.sq[1],
		&fp_im.sq[2],
		&fp_im.sq[3],
		&fp_im.temp);

}
while (1);
}

void taskFP_INIT(void *pvParameters)
{
i2cEnable();

glob.xSemIM = xSemaphoreCreateBinary();
glob.xSem100HZ = xSemaphoreCreateBinary();
glob.xSem10HZ = xSemaphoreCreateBinary();

xTaskCreate(taskFP_IM, "tFP_IM", 1024, NULL, 4, &tIM);
xTaskCreate(taskFP_100HZ, "tFP_100HZ", 4 * 1024, NULL, 3, NULL);
xTaskCreate(taskFP_10HZ, "tFP_10HZ", 4 * 1024, NULL, 2, NULL);

timebaseEnable(IM_FREQ_HZ);

vTaskDelete(NULL);
}

 

Внутри i2c_* функций использовать Notify наверно не стоит, если я их вот так снаружи уже использую. Даже если бы это работало.

 

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


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

Можно попробовать проинициализировать tIM как NULL и сделать assert перед vTaskNotifyGiveFromISR.

А так все выглядит адекватно действительно.

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


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

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

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

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

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

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

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

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

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

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