реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> Блокирование задач на время i/o операции
amaora
сообщение Jul 9 2016, 20:19
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Пытаюсь сделать драйвер или полегче говоря 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.

Спасибо.
Go to the top of the page
 
+Quote Post
AlexandrY
сообщение Jul 9 2016, 20:47
Сообщение #2


Ally
******

Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050



Цитата(amaora @ Jul 9 2016, 23:19) *
Спасибо.


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

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

Go to the top of the page
 
+Quote Post
amaora
сообщение Jul 10 2016, 10:24
Сообщение #3


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Цитата(AlexandrY @ Jul 9 2016, 23:47) *
Функция с явным багом.
...


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

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

Код
xSemaphoreTake(xSem1, );

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

xSemaphoreTake(xSem2, );

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

xSemaphoreGive(xSem1);


Сообщение отредактировал amaora - Jul 10 2016, 10:25
Go to the top of the page
 
+Quote Post
LightElf
сообщение Jul 11 2016, 12:31
Сообщение #4


Частый гость
**

Группа: Участник
Сообщений: 180
Регистрация: 5-04-09
Пользователь №: 47 205



QUOTE (amaora @ Jul 10 2016, 13:24) *
После выходы из функции эти данные уже никому не нужны (а когда нужны берется указатель из параметров функции), они обработаны и возвращен код результата.

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

CODE
xSemaphoreTake(xSem1, );

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

xSemaphoreTake(xSem2, );

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

xSemaphoreGive(xSem1);

пляски вокруг xSem2 можно заменить на пару vTaskSuspend/vTaskResume
Go to the top of the page
 
+Quote Post
amaora
сообщение Jul 12 2016, 14:43
Сообщение #5


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Цитата(LightElf @ Jul 11 2016, 15:31) *
пляски вокруг xSem2 можно заменить на пару vTaskSuspend/vTaskResume


А что будет если vTaskResume будет вызвана раньше vTaskSuspend?
Go to the top of the page
 
+Quote Post
LightElf
сообщение Jul 15 2016, 11:55
Сообщение #6


Частый гость
**

Группа: Участник
Сообщений: 180
Регистрация: 5-04-09
Пользователь №: 47 205



QUOTE (amaora @ Jul 12 2016, 17:43) *
А что будет если vTaskResume будет вызвана раньше vTaskSuspend?

Очевидно, что vTaskSuspend надо вызывать из критической секции.
Go to the top of the page
 
+Quote Post
amaora
сообщение Jul 18 2016, 18:39
Сообщение #7


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



Цитата(LightElf @ Jul 15 2016, 14:55) *
Очевидно, что vTaskSuspend надо вызывать из критической секции.


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

Пока сделал на семафорах. Другой вопрос близкий к теме. Хотел использовать Direct To Task Notifications, но получаю зависание всей системы на вызове ulTaskNotifyTake(pdTRUE, portMAX_DELAY). Включил assert, проверку переполнения стека, нехватку хипа, обложил отладочным выводом *Fault прерывания. Причину не нашел. Версия FreeRTOSv9.0.0, по коду мне показались эти Notify функции странными, в других местах тоже самое делается более аккуратно. До дна ковырять не стал, использовал семафоры.
Go to the top of the page
 
+Quote Post
Valentine Logino...
сообщение Jul 19 2016, 07:20
Сообщение #8


Частый гость
**

Группа: Участник
Сообщений: 78
Регистрация: 7-04-10
Из: Пушкино
Пользователь №: 56 462



Семафоры - хорошее решение. Правда я не понял зачем вам их два. Всё-таки наверное первый у вас - это мютекс?
portMAX_DELAY - не лучший вариант, все-таки правильнее задать конкретное время и обработать ошибку, а то все зависнуть может.
По поводу нотификейшнов проще было бы подсказать, если бы вы поделились кодом. Я бы начал с простого и проверил в конфиге configUSE_TASK_NOTIFICATIONS (: Я активно нотификейшнами пользуюсь как раз для такого типа синхронизации, по времени выходит быстрее.
Go to the top of the page
 
+Quote Post
amaora
сообщение Jul 19 2016, 17:41
Сообщение #9


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



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

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

Код
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);


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

Если в той задаче заменяю *Notify* на семафор, то начинает работать как должно. Иначе, не возвращается управление из *NotifyTake, останавливаются другие задачи.
Go to the top of the page
 
+Quote Post
Valentine Logino...
сообщение Jul 20 2016, 09:00
Сообщение #10


Частый гость
**

Группа: Участник
Сообщений: 78
Регистрация: 7-04-10
Из: Пушкино
Пользователь №: 56 462



Вы хотите использовать нотифаи в качестве бинарного семафора? Тогда может быть попробовать перед записью в очередь его точно сбросить (ulTaskNotifyTake(pdTRUE, 0)wink.gif ?
И без кода прерывания сложно сказать, но я думаю у вас там корректно делаются vTaskNotifyGiveFromISR и portYIELD_FROM_ISR?
Go to the top of the page
 
+Quote Post
amaora
сообщение Jul 20 2016, 17:03
Сообщение #11


Местный
***

Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778



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

CODE
#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 наверно не стоит, если я их вот так снаружи уже использую. Даже если бы это работало.
Go to the top of the page
 
+Quote Post
Valentine Logino...
сообщение Jul 21 2016, 09:41
Сообщение #12


Частый гость
**

Группа: Участник
Сообщений: 78
Регистрация: 7-04-10
Из: Пушкино
Пользователь №: 56 462



Можно попробовать проинициализировать tIM как NULL и сделать assert перед vTaskNotifyGiveFromISR.
А так все выглядит адекватно действительно.
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 6th July 2025 - 16:16
Рейтинг@Mail.ru


Страница сгенерированна за 0.01452 секунд с 7
ELECTRONIX ©2004-2016