amaora 18 9 июля, 2016 Опубликовано 9 июля, 2016 · Жалоба Пытаюсь сделать драйвер или полегче говоря 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. Спасибо. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AlexandrY 2 9 июля, 2016 Опубликовано 9 июля, 2016 · Жалоба Спасибо. Функция с явным багом. xMSG.msg_data сразу после выхода из функции будет содержать невалидную ссылку на данные. И в целом работа с очередью неоправдано громоздкая. Ну допустим начало отправки сделано асинхронно, но потом то задача все равно застрянет на ожидании подтверждения. Функции IO можно просто завернуть в семафоры. Будет тот же эффект. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 18 10 июля, 2016 Опубликовано 10 июля, 2016 (изменено) · Жалоба Функция с явным багом. ... После выходы из функции эти данные уже никому не нужны (а когда нужны берется указатель из параметров функции), они обработаны и возвращен код результата. Во freertos семафоры это внутри те же очереди. Как завернуть, так? xSemaphoreTake(xSem1, ); // здесь передаем данные в конечный автомат (КА) и запускаем прерывание xSemaphoreTake(xSem2, ); // сюда попадаем когда КА сделает свои дела и вызовет xSemaphoreGive(xSem2); // забираем результат операции xSemaphoreGive(xSem1); Изменено 10 июля, 2016 пользователем amaora Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LightElf 0 11 июля, 2016 Опубликовано 11 июля, 2016 · Жалоба После выходы из функции эти данные уже никому не нужны (а когда нужны берется указатель из параметров функции), они обработаны и возвращен код результата. Во freertos семафоры это внутри те же очереди. Как завернуть, так? xSemaphoreTake(xSem1, ); // здесь передаем данные в конечный автомат (КА) и запускаем прерывание xSemaphoreTake(xSem2, ); // сюда попадаем когда КА сделает свои дела и вызовет xSemaphoreGive(xSem2); // забираем результат операции xSemaphoreGive(xSem1); пляски вокруг xSem2 можно заменить на пару vTaskSuspend/vTaskResume Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 18 12 июля, 2016 Опубликовано 12 июля, 2016 · Жалоба пляски вокруг xSem2 можно заменить на пару vTaskSuspend/vTaskResume А что будет если vTaskResume будет вызвана раньше vTaskSuspend? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
LightElf 0 15 июля, 2016 Опубликовано 15 июля, 2016 · Жалоба А что будет если vTaskResume будет вызвана раньше vTaskSuspend? Очевидно, что vTaskSuspend надо вызывать из критической секции. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 18 18 июля, 2016 Опубликовано 18 июля, 2016 · Жалоба Очевидно, что vTaskSuspend надо вызывать из критической секции. И по сравнению с этим одиночные вызовы xSemaphoreTake/xSemaphoreGive называются плясками? Пока сделал на семафорах. Другой вопрос близкий к теме. Хотел использовать Direct To Task Notifications, но получаю зависание всей системы на вызове ulTaskNotifyTake(pdTRUE, portMAX_DELAY). Включил assert, проверку переполнения стека, нехватку хипа, обложил отладочным выводом *Fault прерывания. Причину не нашел. Версия FreeRTOSv9.0.0, по коду мне показались эти Notify функции странными, в других местах тоже самое делается более аккуратно. До дна ковырять не стал, использовал семафоры. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
becopt 0 19 июля, 2016 Опубликовано 19 июля, 2016 · Жалоба Семафоры - хорошее решение. Правда я не понял зачем вам их два. Всё-таки наверное первый у вас - это мютекс? portMAX_DELAY - не лучший вариант, все-таки правильнее задать конкретное время и обработать ошибку, а то все зависнуть может. По поводу нотификейшнов проще было бы подсказать, если бы вы поделились кодом. Я бы начал с простого и проверил в конфиге configUSE_TASK_NOTIFICATIONS (: Я активно нотификейшнами пользуюсь как раз для такого типа синхронизации, по времени выходит быстрее. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 18 19 июля, 2016 Опубликовано 19 июля, 2016 · Жалоба Да первый это мьютекс. Время ожидания это понятно, обработку застрявшего обмена надо делать, согласен. Конфиг я первым делом и смотрел, то есть я его формировал читая документацию. Этот пункт включен, иначе компиляция не пройдет. Код очень простой, в одну задачу добавляю указанный мной вызов, повторю его, ulTaskNotifyTake(pdTRUE, portMAX_DELAY); более ничего относящегося к *Notify* нет. Время ожидания можно указать другое, ничего не меняет. Перед этим вызовом ставил вывод в usart, вывелся только первый символ. Вывод сделан через отправку в очередь каждого символа, и обработку в прерывании, без dma. Первый отправился немедленно, а когда пришло время для следующего то прерывания уже видимо были запрещены. Если в той задаче заменяю *Notify* на семафор, то начинает работать как должно. Иначе, не возвращается управление из *NotifyTake, останавливаются другие задачи. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
becopt 0 20 июля, 2016 Опубликовано 20 июля, 2016 · Жалоба Вы хотите использовать нотифаи в качестве бинарного семафора? Тогда может быть попробовать перед записью в очередь его точно сбросить (ulTaskNotifyTake(pdTRUE, 0);) ? И без кода прерывания сложно сказать, но я думаю у вас там корректно делаются vTaskNotifyGiveFromISR и portYIELD_FROM_ISR? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
amaora 18 20 июля, 2016 Опубликовано 20 июля, 2016 · Жалоба Проверил еще раз, вот весь код. Если убрать 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 наверно не стоит, если я их вот так снаружи уже использую. Даже если бы это работало. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
becopt 0 21 июля, 2016 Опубликовано 21 июля, 2016 · Жалоба Можно попробовать проинициализировать tIM как NULL и сделать assert перед vTaskNotifyGiveFromISR. А так все выглядит адекватно действительно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться