|
|
  |
Блокирование задач на время i/o операции |
|
|
|
Jul 9 2016, 20:19
|
Местный
  
Группа: Участник
Сообщений: 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. Спасибо.
|
|
|
|
|
Jul 9 2016, 20:47
|

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

|
Цитата(amaora @ Jul 9 2016, 23:19)  Спасибо. Функция с явным багом. xMSG.msg_data сразу после выхода из функции будет содержать невалидную ссылку на данные. И в целом работа с очередью неоправдано громоздкая. Ну допустим начало отправки сделано асинхронно, но потом то задача все равно застрянет на ожидании подтверждения. Функции IO можно просто завернуть в семафоры. Будет тот же эффект.
|
|
|
|
|
Jul 10 2016, 10:24
|
Местный
  
Группа: Участник
Сообщений: 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
|
|
|
|
|
Jul 11 2016, 12:31
|
Частый гость
 
Группа: Участник
Сообщений: 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
|
|
|
|
|
Jul 12 2016, 14:43
|
Местный
  
Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778

|
Цитата(LightElf @ Jul 11 2016, 15:31)  пляски вокруг xSem2 можно заменить на пару vTaskSuspend/vTaskResume А что будет если vTaskResume будет вызвана раньше vTaskSuspend?
|
|
|
|
|
Jul 15 2016, 11:55
|
Частый гость
 
Группа: Участник
Сообщений: 180
Регистрация: 5-04-09
Пользователь №: 47 205

|
QUOTE (amaora @ Jul 12 2016, 17:43)  А что будет если vTaskResume будет вызвана раньше vTaskSuspend? Очевидно, что vTaskSuspend надо вызывать из критической секции.
|
|
|
|
|
Jul 18 2016, 18:39
|
Местный
  
Группа: Участник
Сообщений: 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 функции странными, в других местах тоже самое делается более аккуратно. До дна ковырять не стал, использовал семафоры.
|
|
|
|
|
Jul 19 2016, 17:41
|
Местный
  
Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778

|
Да первый это мьютекс. Время ожидания это понятно, обработку застрявшего обмена надо делать, согласен. Конфиг я первым делом и смотрел, то есть я его формировал читая документацию. Этот пункт включен, иначе компиляция не пройдет. Код очень простой, в одну задачу добавляю указанный мной вызов, повторю его, Код ulTaskNotifyTake(pdTRUE, portMAX_DELAY); более ничего относящегося к *Notify* нет. Время ожидания можно указать другое, ничего не меняет. Перед этим вызовом ставил вывод в usart, вывелся только первый символ. Вывод сделан через отправку в очередь каждого символа, и обработку в прерывании, без dma. Первый отправился немедленно, а когда пришло время для следующего то прерывания уже видимо были запрещены. Если в той задаче заменяю *Notify* на семафор, то начинает работать как должно. Иначе, не возвращается управление из *NotifyTake, останавливаются другие задачи.
|
|
|
|
|
Jul 20 2016, 09:00
|
Частый гость
 
Группа: Участник
Сообщений: 78
Регистрация: 7-04-10
Из: Пушкино
Пользователь №: 56 462

|
Вы хотите использовать нотифаи в качестве бинарного семафора? Тогда может быть попробовать перед записью в очередь его точно сбросить (ulTaskNotifyTake(pdTRUE, 0)  ? И без кода прерывания сложно сказать, но я думаю у вас там корректно делаются vTaskNotifyGiveFromISR и portYIELD_FROM_ISR?
|
|
|
|
|
Jul 20 2016, 17:03
|
Местный
  
Группа: Участник
Сообщений: 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 наверно не стоит, если я их вот так снаружи уже использую. Даже если бы это работало.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|