Jump to content
  • entries
    21
  • comments
    135
  • views
    2219

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

ARV

555 views

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

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



0 Comments


Recommended Comments

There are no comments to display.

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Add a comment...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...