Нельзя полюбить RTOS и избавиться от волнений. Любовь - это штука, волнующая кровь по определению, так что...
Стихли первые эмоции на основе эйфории, появилась тревога.
Отладка RTOS - та еще песня! Никогда не знаешь точно, что и как происходит не так, если оно не так. Когда одна задача посылает команды по USART в устройство, другая задача принимает от него ответы, а третья занимается управлением, понять, почему третья задача работает не правильно, очень не просто. Непросто потому, что запросы и ответы разделены во времени и в пространстве, и если управляющая задача ждет готовности, надо выяснить, из-за неотправленного запроса или же из-за не полученного ответа. А если на все это накладывается еще и не совсем разумное поведение устройства, то вообще все становится загадочно и страшно.
Пытаюсь совместить модуль плейера MP3-файлов с RTOS. Написал две функции-задачи для приема ответов и отправки команд. Вроде бы все правильно, все корректно.
/** * задача приема сообщений от других задач и выдачи команд в плейер * @param p не используется */ static void control_task(void *p){ static int16_t ver; static player_msg_t *msg; ver = get_current_mbox_version(&player_mailbox); while(1){ wait_for_increment_of(&tick, 10); // обработка сообщений if((msg = read_mbox_min_version(&player_mailbox, &ver)) != NULL){ // новое в ящике // разбор сообщения switch(msg->cmd){ case PCMD_RESET: // сброс status = P_NOT_READY; mp3_cmd(MP3_CMD_RESET,0,0); break; case PCMD_SET_VOL: // громкость mp3_cmd(MP3_CMD_SET_VOL, 0, msg->bparam > MP3_MAX_VOL ? MP3_MAX_VOL : msg->bparam); break; case PCMD_STOP: // остановка воспроизведения if(status != P_READY){ mp3_cmd(MP3_CMD_STOP, 0, 0); status = P_READY; } break; case PCMD_S_MSG_QUEUE: // воспроизвести сообщение с ожиданием while((status != P_READY)) yield(); //continue; case PCMD_S_MSG_NOW: // немедленно воспроизвести сообщение // если папка не указана - ищем трек в корне if(msg->bparam) mp3_cmd(MP3_CMD_PLAY_FOLDER, msg->bparam, msg->wparam); else mp3_cmd(MP3_CMD_PLAY, msg->wparam >> 8, msg->wparam & 0xFF); status = P_PLAY; break; case PCMD_L_MSG_QUEUE: // воспроизвести трек из "большой" папки с ожиданием while(status != P_READY) yield(); //continue; case PCMD_L_MSG_NOW: // воспроизвести из "большой" папки немедленно status = P_PLAY; mp3_cmd(MP3_CMD_PLAY_3000, ((msg->bparam & 0x0F) << 4) | ((msg->wparam>>8) & 0x0F),msg->wparam & 0xFF); break; case PCMD_USER: // любая иная команда mp3_cmd(msg->bparam, msg->wparam>>8, msg->wparam & 0xFF); break; } } release_mbox_read(); ver++; } } #include <util/delay.h> /** * Задача приема сообщений от модуля плейера. Осуществляет управление статусом * плейера в зависимости от принятых команд. * @param p не используется */ static void reseive_task(void *p){ static mp3_buf_t packet; static uint8_t old; static uint8_t d; while(1){ // обработка ответов модуля d = 0; // ждем время, достаточное для приема пакета (10 мс) wait_for_increment_of(&tick,10); // ищем стартовый байт if(data_reseived()) d = data_get(); if(d != MP3_START_BYTE) continue; // считываем пакет for(uint8_t i=0; i < (MP3_PACKET_SZ-1); i++){ packet.bytes[i] = d; while(!data_reseived()) to_os(); d = data_get(); } // обрабатываем пакет switch(packet.command){ case MP3_ERROR: // ошибка mprintf("\nError %02X st=%d", packet.param_lo, status); if(status == P_PLAY) status = P_READY; break; case MP3_STAY_USB:// конец воспроизведения case MP3_STAY_SD: // STAY приходит дважды!!!, один раз надо игнорировать //dbg_packet(&packet); if(old != packet.param_lo) status = P_READY; old = packet.param_lo; break; case MP3_DEV_STATUS: // инициализация закончена if((status == P_NOT_READY) && (packet.param_lo == DEV_SD)) status = P_READY; break; case MP3_PLUG_IN: // подключение источника status = P_READY; break; case MP3_PULL_OUT: // отключение источника status = P_NOT_READY; break; default: // все прочие пакеты break; } } }
Проверяю функционирование при помощи простой функции, "говорящей время":
void say_time(uint8_t h, uint8_t m){ player_send_msg(PCMD_S_MSG_QUEUE, FOLDER_MSG, SAY_TIME); if(h==0) h=24; if(m==0) m=60; player_send_msg(PCMD_S_MSG_QUEUE, FOLDER_HOUR, h); player_send_msg(PCMD_S_MSG_QUEUE, FOLDER_MIN, m); }
Вызываю эту функцию каждые 5 секунд, имитируя минуты, в отдельной задаче. В итоге система говорит время некоторое количество раз, после чего состояние модуля становится P_PLAY и не исчезает. Если посмотреть на код функций управления и приема ответов, то можно понять, что такая ситуация возможна, если модуль не ответил о том, что файл проигран до конца. НО ОН ОТВЕЧАЕТ! И отвечает 2 раза на каждый файл, о чем в документации нет ни слова!
Что происходит, как выяснить? Самое удивительное, что если снять ремарку с отладочного вывода содержимого принятого ответа, то все начинает работать! И в терминале я вижу, что на каждый файл приходит подтверждение окончания воспроизведения... И, значит, состояние P_PLAY обязано сбрасываться в P_READY! Но если отладочный вывод в терминал заремарить - рано или поздно все виснет.
А я-то думал, волноваться больше не придется...
Комментарии
Отправить комментарий