К основному контенту

Новая любовь - новые тревоги

Нельзя полюбить 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! Но если отладочный вывод в терминал заремарить - рано или поздно все виснет.

А я-то думал, волноваться больше не придется...




Комментарии

Популярные сообщения из этого блога

Все ниже, и ниже, и ниже... стремим CLK AVR...

Как ни посмотришь, так все всегда в гонке... Выше, больше, быстрее, потом еще больше, еще выше, еще быстрее... Мегагерцы, Гигагерцы... А потом нервные срывы и - милости просим к нам в гнездо, в комнату с белым потолком, с правом на надежду! И это еще хорошо, если так повезет... А кому это надо? Мне, например, не надо. Свой последний проект на микроконтроллере AVR я сделал на тактовой частоте в 32768 Гц. Ни больше, ни меньше, а 32 килогерца. Само собой, это вышло не специально... Просто решил делать часы на микроконтроллере, в котором нет аппаратного таймера специально под организацию часов реального времени... Ну и самым простым оказалось перевести весь проект на тактирование от часового кварца.  А чего такого? Это самая низкая из доступных "по умолчанию" частот (даже тактирование от генератора WDT и то на большей частоте получается - порядка 100 кГц), при том стабильная, ибо кварц.  И вышло так, что практически никаких ограничений в процессе написания прошивки я не испытывал...

Раздвоение личности

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

Музей древностей

Был я молод, был я весел... Даже имел свой блог на сайте Паяльник (не к ночи будь сказано). Но, паяльник на то и паяльник, что неугодным его можно вставить в... в общем, вставить. А это неприятно, поверьте мне... В общем, забанили меня там. Поэтому новый личный блог я начал с переноса оттуда сюда буйной головы своих раздумий... Коллекция древностей будет пополняться, по мере сил. А потом пойдет пополнение уже свежачком...