Т. Ташпулатов
Новости Электроники 19, 2007
Некоторые практические советы по работе с открытым пакетом Open AT. Этот пакет бесплатно поставляется компанией Wavecom разработчикам беспроводных систем.
В предлагаемой статье мы попытаемся рассмотреть практическую реализацию пользовательского интерфейса на приложении Open AT.
Рис. 1. Реализация игры «Тетрис» на платформе Wavecom OpenAT
Командный режим
Самым простым и наиболее распространенным способом взаимодействия приложения OpenAT с пользователем является общение через последовательный порт с использованием эмулятора терминала. Последовательный порт модема при этом находится в так называемом командном режиме (COMMAND MODE). Пользователь вводит команды с классическим префиксом AT, и получает один или несколько ответов модема:
ATZ
OK
AT+WIND?
+WIND: 32767
OK
AT+START
ERROR
Команда из последнего примера приводит к выдаче сообщения об ошибке, так как не является стандартной AT-командой. Тем не менее, мы можем сделать ее понятной модему, воспользовавшись возможностью создавать собственные команды в API OpenAT (здесь и далее подразумевается использование библиотеки ADL из комплекта поставки OpenAT).
adl_atCmdSubscribe ("at+start", (adl_atCmdHandler_t)AtStartHandler, ADL_CMD_TYPE_ACT);
Обработчик нашей команды:
void AtStartHandler (adl_atCmdPreParser_t *p)
{
adl_atSendResponse (ADL_AT_RSP, " Hello, world! ");
};
Скомпилируем, загрузим (AT+WDWL) и запустим наше приложение (AT+WOPEN=1). Убеждаемся, что набор стандартных команд пополнился еще одной:
ATZ
OK
AT+START
Hello, world!
Прозрачный режим
Необходимость предварять собственные команды префиксом AT и завершать ввод нажатием Enter может быть не всегда удобна. Часто от пользователя требуется нажатие одной-двух клавиш, например, стрелки управления курсором для перемещения по пунктам экранного меню, или «горячей клавиши» для выполнения определенных действий. В этом случае альтернативой стандартному командному режиму является режим прозрачной передачи данных (DATA MODE).
Для работы в DATA MODE воспользуемся соответствующими вызовами группы Flow Control Manager из API OpenAT. Зарегистрируем собственные обработчики событий управления и передачи данных для работы с последовательным портом UART1:
UARTHandle = adl_fcmSubscribe (ADL_PORT_UART1, (adl_fcmCtrlHdlr_f)UARTCtrlHandler, (adl_fcmDataHdlr_f)UARTDataHandler);
Переведем последовательный порт в режим передачи данных:
adl_fcmSwitchV24State (UARTHandle, ADL_FCM_V24_STATE_DATA);
Теперь все данные, вводимые в последовательный порт, будут попадать в наш обработчик:
bool UARTDataHandler (u16 DataLen, u8 *Data)
{
return (TRUE);
}
Вывод в последовательный порт тоже несложен:
adl_fcmSendData (UARTHandle, data, len);
Остается только реализовать собственный протокол обмена. При большом желании и в прозрачном режиме можно имитировать работу с AT-командами, построив соответствующим образом логику обработки входного потока и пользуясь вызовами уже знакомой нам группы Commands из API OpenAT.
Организация выводимой информации
Вывод сообщений приложения с использованием стандартных сервисов adl_atSendResponse () или adl_fcmSendData () удобен до тех пор, пока этих сообщений немного. В случае, когда поток отладочной информации велик, ориентироваться в нем становится затруднительно. Ситуация становится еще хуже, если выводятся потоки сообщений от различных частей приложения, работающих независимо друг от друга - например, сообщения о состоянии GSM-сети или GPRS-соединения, о процессе приема или передачи данных и так далее.
Выйти из положения можно, переведя терминал в режим эмуляции ANSI и воспользовавшись управляющими последовательностями ANSI. Это даст нам возможность раскрасить выводимые символы в 16 цветов, а фон - в 8.
ascii ansiSetGreenColor [] = {0x1b, ‘[‘, ‘4', ‘2', ‘m', 0};
wm_sprintf (buf, "%sЭто строчка зеленого цвета", ansiSetGreenColor);
adl_fcmSendData (UARTHandle, buf, wm_strlen (buf));
Теперь ворох выводимых сообщений радует глаз пестротой, но по-прежнему не очень удобен для работы - появляющиеся новые сообщения заставляют экран прокручиваться, скрывая более старые. Устраним неудобство, воспользовавшись другой разновидностью последовательностей ANSI, предназначенных для управления экраном и позиционирования курсора.
Очистим экран:
ascii ansiClearScreen [] = {0x1b, ‘[‘, ‘2', ‘J', 0};
adl_fcmSendData (UARTHandle, ansiClearScreen, wm_strlen (ansiClearScreen));
Выведем приветствие в правом верхнем углу (практичней было бы вывести там, к примеру, уровень сигнала RSSI, получаемый командой AT+CSQ):
wm_sprintf (buf, "%c[%d;%dHHello, world!", 0x1b, x, y);
adl_fcmSendData (UARTHandle, buf, wm_strlen (buf));
Можно разбить экран на зоны, куда будут выводиться сообщения из разных блоков или обработчиков программы, со своими цветами и прокруткой. Гораздо удобней унылого нечитабельного потока, не правда ли?
Работа с таймерами
Теперь нам осталось разобраться только с периодичностью вывода информации. Библиотека ADL OpenAT предлагает разработчику возможность работы с двумя типами таймеров - однократными и циклическими.
Однократный таймер, как следует из названия, срабатывает только один раз, вызывая по истечении таймаута задаваемый пользователем обработчик.
void MyTimeout (u8 id)
{
wm_sprintf (buf, "Сработал таймер %d", id)
adl_fcmSendData (UARTHandle, buf, wm_strlen (buf));
}
В качестве минимальных отрезков времени доступны два: 18,5 и 100 миллисекунд. Указанное количество отсчетов (100) и выбранный тип (18,5 мс) даст в результате интервал срабатывания таймера в 1.85 секунд.
adl_tmrSubscribe (FALSE, 100, ADL_TMR_TYPE_TICK, (adl_tmrHandler_t) MyTimeout);
Циклический таймер, напротив, будет вызывать обработчик пользователя с заданным интервалом до тех пор, пока не будет принудительно остановлен.
tmrHandle = adl_tmrSubscribe (TRUE, 10, ADL_TMR_TYPE_100MS, (adl_tmrHandler_t) ScreenRefresh);
Мы будем использовать циклический таймер для периодического обновления экрана. Со временем можно ускорить темп вывода, остановив таймер и перезапустив его с новым значением длительности задержки.
adl_tmrUnSubscribe (tmrHandle, (adl_tmrHandler_t)ScreenRefresh, ADL_TMR_TYPE_100MS);
tmrHandle = adl_tmrSubscribe (TRUE, 9, ADL_TMR_TYPE_100MS, (adl_tmrHandler_t) ScreenRefresh);
Примечание: Таймеры в OpenAT являются практически единственным способом организовать в пользовательском приложении длительные паузы или ожидание каких-либо событий, так как приложение OpenAT выполняется в режиме кооперативной многозадачности, и блокировка процессора приложением может привести к срабатыванию сторожевого таймера и полной перезагрузке системы.
Тетрис для фортепиано с оркестром
Вооружившись полученными знаниями, можно реализовать известную игру на платформе Wavecom OpenAT (см. рис. 1). Запуск игры будет производиться командой AT+START, управление игрой - нажатиями клавиш J, K, L в режиме прозрачной передачи данных.
Отрисовка изменений на экране выполняется периодически, вместе с обновлением показаний часов (время, прошедшее с момента запуска приложения).