Самый мощный органайзер и его SDK

Приветствую всех.
Продолжим тему разработки под ушедшие в историю платформы. Сегодня расскажем про органайзеры Casio серии Pocket Viewer, бывшие популярными в узких кругах в первой половине нулевых.

clux8kwl7tzptei05r4rowo4x2u.png

В ходе статьи узнаем, как и на чём под него писать, где взять софт, какие остались тематические сайты, а также много чего интересного.

Что это такое?


Casio Pocket Viewer позиционировался как электронный органайзер (при этом стоил он куда дешевле КПК «старших» моделей, который были основаны уже на Pocket PC и выпускались под линейкой Cassiopeia). Выпускалось их весьма большое количество моделей, но наибольшую популярность получили PV-S250 (PV-S450) и PV-S460 (PV-S660). Все они основаны на встраиваемой системе (на базе процессора NEC V30), имели монохромный экран разрешением 160×160, подключались к компьютеру по RS-232, работали на проприетарной ОС Pocket Viewer OS (или просто PVOS). Отличительной чертой был накопитель на флеш-памяти (первая цифра номера модели означает число её мегабайт), благодаря которому не требовалось постоянно следить за зарядом батареек, чтобы не потерять данные. Также поддерживалось и обновление ОС. Минусом этого варианта было низкое быстродействие — сохранение чего-либо занимало видимые секунды.
Таким образом, Casio PV был едва ли не последним массово производившимся КПК на базе процессора с архитектурой X86 (различные околопромышленные устройства вроде ТСД Symbol PDT или Psion Workabout в расчёт не берём). Последняя выпущенная модель, PV-S1600 имела подключение по USB и работала на процессоре SH-3, чем сильно отличалась от «классического» Pocket Viewer.
Главным конкурентом Pocket Viewer’а (в частности, модели PV-S450) был КПК Palm M100/M105, с которым чаще всего и сравнивался данный аппарат. PV имел ряд выгодных преимуществ: энергонезависимая память, колёсико прокрутки, удобное для чтения книг, большой экран, небывалое даже тогда время работы от батарей, стильный тонкий дизайн. Palm в ответ «брал» большим количеством приложений и лучшим, нежели PV, быстродействием.
В мои руки попал Casio PV-S450. Именно о нём и пойдёт речь дальше. Впрочем, всё, что будет сказано далее, справедливо для всех остальных моделей Casio Pocket Viewer на базе процессора x86. Устройства модели PV-S1600 у меня нет, так что тему разработки под него оставим до лучших времён. Также под управлением PVOS работали калькуляторы Casio ClassPad. За неимением у меня такого устройства, упоминание его тоже пока что скромно опустим.

Почему именно PV-S450?


На самом деле ставку на какую бы то ни было популярность этой модели я не делал. Всё дело в том, что PV-S450 был единственным экземпляром, который мне удалось достать в комплектном состоянии. На всем известном сайте объявлений можно найти устройства вместе с подставкой, но на момент написания статьи цена на все из них выходит далеко за рамки приличия.
На всякий случай напомню: единственно возможная связь с внешним миром обеспечивается через интерфейс RS-232. Casio PV не оснащён ни ИК-портом (за исключением одной-единственной модели, но установка приложений там не поддерживается, увы), ни каким-либо другим беспроводным интерфейсом, так что подключить его как-то иначе вряд ли выйдет. Кроме того, разъём подключения проприетарный (не думаю, что в обычном магазине радиодеталей вы его найдёте), так что покупка Casio PV без кабеля или подставки — крайне такое себе решение.
Также стоит учесть, что установка приложений возможна только на те КПК, в названии модели которых есть буква S (что, видимо, означает «Software»).

Обзор оборудования


Прежде чем переходить к средствам разработки, рассмотрим вначале сам органайзер.
У моего экземпляра интересное прошлое: ранее он принадлежал Дмитрию Newbilius Моисееву. Засветившись в его обзоре на YouTube, он был выставлен на вторичку и позже был приобретён мною. Ну что же, пришло его время снова оказаться продемонстрированным на просторах сети.

uk2b3ztycdq6cwsbedv9vc6ex6g.png

Экран КПК защищает пластиковая крышечка, которая у моего экземпляра даже умудрилась не отломаться и дожить до наших дней.

sf0xfx7ahikvyjezbtwrggyhmiu.png

Помимо сенсорного экрана с сенсорными же подэкранными кнопками никаких элементов управления на передней панели нет.

t9k0zdgfq4ovcslxvgnipma1u5s.png

На обратной стороне переключатель блокировки крышки отсека батареек (а заодно и выключатель питания), кнопка перезагрузки (у данного экземпляра она запала и не нажимается. Это особенность конкретного устройства: Pocket Viewer’ов у меня два, и на втором всё отлично работает), этикетка с моделью.

vgcaw-0xvmpk2d2rrctnvk8m0gm.png

Слева джойстик прокрутки. Именно за этот элемент управления многие любили PV — читать на этом устройстве было очень удобно. Это не колёсико, аналогичное JogDial на КПК Sony, это именно джойстик, который можно нажимать или наклонять в две стороны.
На фото для сравнения джойстик у PV и у Sony Clié.

wqq86pwnzetzcy-ntckpboehi5k.png

Снизу тот самый проприетарный разъём для подключения к компьютеру.

cmbz6ntd-vmrqpec3rqvqs5njka.png

А вот и подставка. Весьма удобная, кстати.

t-djuwtto52ryjgig_pl4uipbhe.png

Разбирать КПК я не стал: побоялся сломать защёлки. Но на просторах обнаружились фото внутренностей.
Отчётливо видны процессор системы «китайская капля», микросхема памяти (на фото представлена модель PV-S250, рядом видно место для ещё одной, которая установлена в модели PV-S450), ОЗУ, шлейфы, катушка преобразователя для ЭЛИ-подсветки.

Pocket Viewer SDK


Casio приняла правильное решение: средства разработки под данный КПК находились полностью в открытом доступе. В лучшие времена SDK можно было скачать с официального сайта.
Хотя для древнего софта правило «интернет ничего не забывает» традиционно не работает, SDK удалось найти вообще без проблем — находился он тут. Internet Archive продолжает радовать: помимо SDK обнаружились и архивы некоторых ныне ушедших в историю тематических сайтов.
Итак, для начала разработки под Casio PV понадобится примерно следующее:

  • Компьютер с ОС Windows 98/NT/2000 или виртуальная машина с таковой. На современные ОС установить SDK не выходит: инсталлятор банально не запускается. Впрочем, ничто не мешает поставить его на старой ОС, а затем перенести папки на новую, все компоненты запускаются даже на Windows 10×64.
  • Собственно, сам Pocket Viewer SDK. Где его взять, уже было сказано чуть выше. Для вашего удобства все ссылки будут продублированы в конце поста.
  • Любой удобный для вас редактор кода, например, тот же Notepad++. Впрочем, при желании можно использовать и банальный «Блокнот».
  • Casio PVOS Application Manager. Позволяет устанавливать приложения в формате *.bin на реальный КПК. Также есть версия на русском языке.
  • КПК с подставкой для подключения к компьютеру. На нём будем запускать протестированные в эмуляторе приложения. Также следует отметить, что компьютер должен быть оснащён COM-портом. Различные переходники USB2COM работают, но крайне плохо.

Ставим софт


Установка SDK каких-любо нюансов не имеет, ставится он как и любое другое Windows-приложение. Так что документировать данный процесс смысла не вижу. После установки в корне системного диска появятся папки LSIJ и CASIO. В первой находится компилятор LSIC86PV, позволяющий собрать приложение для Pocket Viewer OS. Во второй будет находиться вложенная папка (название зависит от версии установленного вами SDK), где лежат все остальные средства разработки: библиотеки, примеры программ (впрочем, для введения в разработку под PVOS они сложноваты), документация, эмулятор, Application Manager.
Больше никаких манипуляций типа установки PATH или чего-то вроде этого не требуется.

Симулятор


В папке SIM находится симулятор данного КПК. Это не полный его эмулятор, то есть вполне возможно, что поведение программы в нём и на реальном устройстве будет отличаться. С этим ничего не поделать.

xhaiun_vtftteqd65in_ih9_aru.png

Для запуска открываем имеющийся в папке рядом с ним проект PV-S450 и всё, можно запускать.
Для того, чтобы поменять установленное приложение, жмём на панели меню «Project», далее «Configuration», в открывшемся окне переходим на вкладку «Chips».

tt8pa_cvik_ccovcuqjbsr7fnrc.png

Далее ищем в списке «SAMPLE», жмякаем правой кнопкой мыши, выбираем «Proparties» (угу, так и написано), выбираем нужный нам бинарник вместо sample.bin.

xe4hvyzturuqzitbtnskd_rsw8e.png

Не забываем после каждого изменения (например, после очередной компиляции) перезагрузить наш виртуальный КПК.

Разумеется, можно использовать данную прогу не только для разработки, а ещё и для того, чтобы запустить софт для PVOS при отсутствии самого КПК.

Application Manager


Для установки и удаления приложений на реальном КПК существует Application Manager. Пользоваться им предельно просто: запускаем программу, насаживаем КПК на подставку, инициируем загрузку (через кнопку «Menu bar» из главного экрана, а не кнопкой на подставке, иначе будет ошибка связи!), запускаем обмен данными крайней левой кнопочкой на панели меню Application Manager.

1jo4bpybvdnljtx61qrqx3q0h4e.png

Установив связь и получив список программ, добавляем или удаляем нужные, а затем производим обмен данными (той же самой кнопкой или «Execute\Update PV»). Всё, приложение установлено (ну, или снесено).

Структура проекта


Итак, заглянем в папку C и посмотрим, что же у нас там лежит. В папке Bin хранятся скомпилированные бинарники, пригодные для загрузки в КПК или запуска в симуляторе. Также есть несколько папок с библиотеками, а также разделы Sample и Sample1. Это и есть проекты. Откроем какой-нибудь из них. В каждой из этих двух папок лежит Makefile, батник для сборки, каталоги с исходниками. Также есть папка MENUICON — там хранится иконка приложения для отображения в списке в главном меню.
Запустим батник и убедимся, что проект успешно компилируется.

Пишем первую программу


Ну что, со сборкой разобрались. Пришло время написать что-то своё. Итак, создаём в папке C ещё какую-нибудь папку, скажем, Test. А в ней — такую же структуру папок, какую видели в других проектах. Копируем также содержимое MENUICON.
Берём MAKEFILE из уже имеющегося проекта и слегка модифицируем его под наши задачи. Примерно так:

#Makefile for PocketViewer2 Sample Program

include …\COM_LNK\MakeSDK.1

### -------- Define Make Application -------- ###

#== TargetName ==
TARGET = test

#== Program Name ==
PROGNAME = «test»

#== ProgramVersion (EX. 0100→Ver1.00) ==
VERSION = 0100

#== MenuIcon (Xsize=45dot, Ysize=28dot) ==
MICON = menuicon\icon.bmp

#== ListMenuIcon (Xsize=27dot, Ysize=20dot) ==
LICON = menuicon\Licon.bmp

#== CompileObjectFile ==
APLOBJS = $(ODIR)\test.obj

### ----------------------------------------- ###

include …\COM_LNK\MakeSDK.2

В папке C создаём какой-нибудь файл, скажем, test.c. И пишем там следующее:

#include	
#include	"define.h"
#include	"libc.h"

TCHTBL TchList[1] = 
{
		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

void main()
{
	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]);


	LibClrDisp();
	LibPutDisp();
	LibTchInit();

	while(1)
	{
		LibTchWait(&tsts);           
	}

	LibTchStackClr();
	LibJumpMenu();

}

С батником для сборки заморачиваться не будем — позаимствуем его у того же предыдущего проекта. Запускаем его. Если всё было сделано правильно, компиляция должна пройти успешно, а в папке Bin появится наш бинарник test.bin. Загружаем его в симулятор и пробуем запускать. И получаем совершенно пустой экран. Да, всё так и должно быть. Что важно, КПК при этом не завис, не перезагрузился и никак иначе не заглючил. Нажмём «Menu», и программа закроется.

Что тут происходит?


Теперь разберёмся, как оно вообще работает.
TCHSTS tsts; — создание структуры для работы с сенсорным экраном. Именно из неё мы будем получать все данные о касаниях.
Следующие четыре строки — инициализация стека сенсорного экрана. В ходе данной процедуры мы очищаем стек и загружаем туда две таблицы — TchHardIcon и TchList. Таблица касаний — это сведения о каждом объекте на сенсорном экране, доступном для касания. Первая из них — это подэкранные кнопки (Menu, Esc и все остальные), вторая — пользовательские объекты. Их определяет созданный в начале текста программы массив:

TCHTBL TchList[1] = 
{
		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};


Именно в него должны будут записываться данные обо всех размещённых нами на экране элементах управления. Для каждого элемента необходимо указать координаты, в пределах которых на него можно нажать, а также ряд параметров вроде ID этого объекта. Подробнее об этом поговорим чуть позже.
Следом идут ещё две команды — LibClrDisp () и LibPutDisp (). Первая из них производит очистку, вторая — выводит на дисплей содержимое экранного буфера. Без вызова LibPutDisp () изображение на экране останется без изменений.
Далее идёт бесконечный цикл, в котором мы вызываем функцию LibTchWait (&tsts). Дело в том, что для получения данных сенсорного экрана его необходимо постоянно опрашивать. Если мы уберём эту функцию, при запуске программы она зависнет, не в силах считать даже нажатие кнопки «Menu». После опроса мы можем получить данные о том, что было нажато на экране.
И, напоследок, очистка стека тачскрина и выход в меню.

Hello, world!


Попробуем вывести что-то на экран. Для этого предусмотрена функция LibStringDisp ().
Добавим её в нашу первую программу:

#include	
#include	"define.h"
#include	"libc.h"

TCHTBL TchList[1] = 
{
		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

void main()
{
	TCHSTS tsts;
	char * str = "Hello, Habrahabr!";

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibPutDisp();
	LibTchInit();

	LibStringDsp(str,0,0,100,IB_PFONT2);
	LibPutDisp();
	
	while(1)
	{
		LibTchWait(&tsts);           
	}

	LibTchStackClr();
	LibJumpMenu();

}

Первый аргумент данной функции — указатель на строку, далее идут координаты, максимальный размер (в пикселях) и шрифт.
Компилируем, загружаем в симулятор:

6sl0nivdgtkdsdail0z9npnqvno.png

Работает. Кто бы сомневался. Время загрузить в «железный» девайс. Открываем Application Manager, соединяемся…

gvkd9ivsfbraaljdlgmfdrkt1gc.png

Жмём на иконку нашего приложения, и на экране появляется примерно следующее:

570dqtopivbujmttitznymxcypu.png

Отлично. Работает.

BMP


Вывели текст — попробуем вывести и картинку. Такую, например:

fg-cvnbnmo3mb3rwgpgslqyjv7g.jpeg

Разумеется, нельзя просто так взять и загрузить её в память PV. Для этого необходимо сделать ряд нехитрых манипуляций.
Перво-наперво, откроем Photoshop и сделаем её такой:

xu-orzylcgntf8l9odvvfiuz8ag.png

Обесцветим её, а заодно и подгоним под размер экрана.
Теперь откроем папку Tools из комплекта SDK, где лежит утилита для преобразования BMPшек в массив. Запускаем её, открываем в ней нашу картинку, конвертируем:

mpfg4ut3kqik3qohbloj2hn7gsc.png

На выходе получаем файл с расширением *.BMT. Не буду копировать всё его содержимое, покажу только первые несколько строк:

	GSIZE(152, 160),
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x83, 0x8F, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFC, 0x01, 0x0F, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xDF, 0xFE, 0x82, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xD9, 0xFF, 0x80, 0xF7, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xD9, 0x8F, 0x80, 0xD7, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xBE, 0xCF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFC, 0x60, 0x0F, 0x80, 0x27, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x3B, 0xCF, 0xFF, 0xFC, 0xFF, 0xFD, 0xFC, 0x00, 0x0F, 0x00, 0x3F, 0xFF, 0xFF, 0xFF,

Первые байты массива отражают размер картинки, далее идёт стандартный bitmap.
Программа в итоге получается такая:

#include	
#include	"define.h"
#include	"libc.h"
#include 	"l_define.h"
#include 	"l_libc.h"

#define OBJ_ICN1 0x9000

TCHTBL TchList[2] = 
{
		0, 0, 
		152, 160,
		ACT_ICON,
		OBJ_ICN1,
		0x0000,
		
		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

byte bitmap[] = {
/*Вот здесь должно быть полное содержимое *.BMT-файла*/
};

T_ICON newIcon = {&TchList[0], bitmap, NULL, 0x01};

void main()
{

	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibIconPrint(&newIcon);
	LibPutDisp();
	LibTchInit();
	
	while(1)
	{
		LibTchWait(&tsts);
	}

	LibTchStackClr();
	LibJumpMenu();

}

OBJ_ICN1 — это ID элемента, находящегося на экране. В качестве него можно взять любое значение в диапазоне 0×8000–0xFFFF. Далее пропишем нашу картинку в TchList. Укажем координаты (x1, y1, x2, y2), а также то, что этот объект — иконка. Создадим массив байт, куда скопируем наш файл *.BMT, а также объект newIcon — он связывает элемент из TchList и изображение. В функции main () добавится строка LibIconPrint (&newIcon); — вывод картинки на экран.
Компилируем, и всё, можно запускать!

Кнопки


Рассмотрим теперь работу с кнопками. Вообще, в PVOS нет понятия кнопок, есть просто область на экране, нажатия на которую мы отслеживаем. Но тыкать в пустое место не хочется, поэтому обозначим место кнопки соответствующей BMPшкой.
Откроем папку DOC\GRAPHICS. Там лежит целая куча картинок, что называется, на все случаи жизни. Берём какую-нибудь из них и перегоняем в BMT:

xa80l3nfsaupr5stndu2dgmyk8w.png

И пишем вот такую программу:

#include	
#include	"define.h"
#include	"libc.h"
#include 	"l_define.h"
#include 	"l_libc.h"

#define OBJ_BTN1 0x9000

TCHTBL TchList[2] = 
{
		25, 25, 
		25 + 45, 25 + 28,
		ACT_ICON,
		OBJ_BTN1,
		0x0000,

		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

byte bitmap[] = {

	GSIZE(45, 28),
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x1C, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x27, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x20, 0xE0, 0x30, 0x07,
	0x00, 0x40, 0x58, 0x38, 0x30, 0x07,
	0x00, 0x40, 0x5E, 0x14, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0x94, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xD4, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xAC, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0xA8, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x28, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x58, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x50, 0x30, 0x07,
	0x00, 0x42, 0xFE, 0x50, 0x30, 0x07,
	0x00, 0x42, 0x7E, 0xB0, 0x30, 0x07,
	0x00, 0x42, 0x1C, 0xA0, 0x30, 0x07,
	0x00, 0x42, 0x05, 0x60, 0x30, 0x07,
	0x00, 0x43, 0x01, 0x40, 0x30, 0x07,
	0x00, 0x41, 0xC2, 0xC0, 0x30, 0x07,
	0x00, 0x40, 0x73, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x1F, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x07,
	0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x07,

};

T_ICON IconButton1 = {&TchList[0], bitmap, NULL, 0x01};

void buttonPressed() {
	char * str = "Button pressed";
	LibStringDsp(str, 0, 0, 100, IB_PFONT2); 
	LibPutDisp();
}

void main()
{

	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibIconPrint(&IconButton1);
	LibPutDisp();
	LibTchInit();
	
	while(1)
	{
		LibTchWait(&tsts);
		if(tsts.obj == OBJ_BTN1) {
		buttonPressed();
		}
	}

	LibTchStackClr();
	LibJumpMenu();

}

Создаём функции обработчика кнопок. Думаю, в пояснении они не нуждаются.
Собственно, всё происходит так же, как и с картинками, за исключением функции main (). Помимо постоянного опроса тачскрина мы проверяем, были ли нажаты кнопки (точнее говоря, был ли тык в отчерченную координатами область). Для этого используются всё те же заданные нами ID.
Ну что, компилируем и запускаем? Работает? Отлично.

_zklne1s-nc_8wkbkgucx50ibto.png

Ну, где одна кнопка, там и две. И программа, в общем-то, похожа… Bitmap оставил один на двоих, если что.
#include	
#include	"define.h"
#include	"libc.h"
#include 	"l_define.h"
#include 	"l_libc.h"

#define OBJ_BTN1 0x9000

#define OBJ_BTN2 0x9001

TCHTBL TchList[3] = 
{
		25, 25, 
		25 + 45, 25 + 28,
		ACT_ICON,
		OBJ_BTN1,
		0x0000,
		
		25, 80, 
		25 + 45, 80 + 28,
		ACT_ICON,
		OBJ_BTN2,
		0x0000,

		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

byte bitmap[] = {

	GSIZE(45, 28),
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x1C, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x27, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x20, 0xE0, 0x30, 0x07,
	0x00, 0x40, 0x58, 0x38, 0x30, 0x07,
	0x00, 0x40, 0x5E, 0x14, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0x94, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xD4, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xAC, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0xA8, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x28, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x58, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x50, 0x30, 0x07,
	0x00, 0x42, 0xFE, 0x50, 0x30, 0x07,
	0x00, 0x42, 0x7E, 0xB0, 0x30, 0x07,
	0x00, 0x42, 0x1C, 0xA0, 0x30, 0x07,
	0x00, 0x42, 0x05, 0x60, 0x30, 0x07,
	0x00, 0x43, 0x01, 0x40, 0x30, 0x07,
	0x00, 0x41, 0xC2, 0xC0, 0x30, 0x07,
	0x00, 0x40, 0x73, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x1F, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x07,
	0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x07,

};

T_ICON IconButton1 = {&TchList[0], bitmap, NULL, 0x01};
T_ICON IconButton2 = {&TchList[1], bitmap, NULL, 0x01};

void button1Pressed() {
	char * str = "Button 1 pressed";
	LibStringDsp(str, 0, 0, 100, IB_PFONT2); 
	LibPutDisp();
}

void button2Pressed() {
	char * str = "Button 2 pressed";
	LibStringDsp(str, 0, 0, 100, IB_PFONT2); 
	LibPutDisp();
}

void main()
{

	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibIconPrint(&IconButton1);
	LibIconPrint(&IconButton2);
	LibPutDisp();
	LibTchInit();
	
	while(1)
	{
		LibTchWait(&tsts);
		if(tsts.obj == OBJ_BTN1) {
		button1Pressed();
		}
		if(tsts.obj == OBJ_BTN2) {
		button2Pressed();
		}
	}

	LibTchStackClr();
	LibJumpMenu();

}

RS-232


Несколько неожиданно, но имеющийся у КПК COM-порт можно использовать и в своих целях, а не только лишь для связи с ПК.
Итак, для начала порт надо открыть:

byte openPort()
{
	SRL_STAT srl;

	srl.port = IB_SRL_COM2;
	srl.speed = IB_SRL_9600BPS; 
	srl.parit = IX_SRL_NONE;
	srl.datab = IX_SRL_8DATA;
	srl.stopb = IX_SRL_1STOP;
	srl.fctrl = IX_SRL_NOFLOW;

	if (LibSrlPortOpen(&srl) != IW_SRL_NOERR) 
	{
		LibPutMsgDlg("LibSrlPortOpen error");
		return 1;
	}

	return 0;
}

Здесь мы задаём параметры порта и записываем их.
Пропишем в функции main ():

if(!openPort()) {
		LibStringDsp("Port opened", 0, 0, 100, IB_PFONT2); 
		LibPutDisp();
	}
	else LibJumpMenu();

Теперь разберёмся с тем, как производить сам обмен данными. Для этого существуют функции LibSrlRecvByte () и LibSrlSendByte (). Используются они примерно так:

byte ReadCOM(byte * data)
{
	byte sReceivedByte;
	if (LibSrlRecvByte(&sReceivedByte) == IW_SRL_NODATA) return 1;
	*data = sReceivedByte;
	return 0;
}

byte WriteCOM(byte data)
{
	if(LibSrlSendByte(IB_FOLLOW_BUSY, data) == IW_SRL_NOERR) return 0;
	else return 1;	
}

Ну, а теперь посмотрим, как это применять на практике. Возьмём нашу программу с двумя кнопками и добавим ранее упомянутые функции:

#include	
#include	"define.h"
#include	"libc.h"
#include 	"l_define.h"
#include 	"l_libc.h"

#define OBJ_BTN1 0x9000

#define OBJ_BTN2 0x9001

TCHTBL TchList[3] = 
{
		25, 25, 
		25 + 45, 25 + 28,
		ACT_ICON,
		OBJ_BTN1,
		0x0000,
		
		25, 80, 
		25 + 45, 80 + 28,
		ACT_ICON,
		OBJ_BTN2,
		0x0000,

		0, 0, 0, 0,
		ACT_NONE,
		OBJ_END,
		0x0000
};

byte bitmap[] = {

	GSIZE(45, 28),
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xE0, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x1C, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x27, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x20, 0xE0, 0x30, 0x07,
	0x00, 0x40, 0x58, 0x38, 0x30, 0x07,
	0x00, 0x40, 0x5E, 0x14, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0x94, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xD4, 0x30, 0x07,
	0x00, 0x40, 0xBF, 0xAC, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0xA8, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x28, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x58, 0x30, 0x07,
	0x00, 0x41, 0x7F, 0x50, 0x30, 0x07,
	0x00, 0x42, 0xFE, 0x50, 0x30, 0x07,
	0x00, 0x42, 0x7E, 0xB0, 0x30, 0x07,
	0x00, 0x42, 0x1C, 0xA0, 0x30, 0x07,
	0x00, 0x42, 0x05, 0x60, 0x30, 0x07,
	0x00, 0x43, 0x01, 0x40, 0x30, 0x07,
	0x00, 0x41, 0xC2, 0xC0, 0x30, 0x07,
	0x00, 0x40, 0x73, 0x80, 0x30, 0x07,
	0x00, 0x40, 0x1F, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x40, 0x00, 0x00, 0x30, 0x07,
	0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x07,
	0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x07,

};

T_ICON IconButton1 = {&TchList[0], bitmap, NULL, 0x01};
T_ICON IconButton2 = {&TchList[1], bitmap, NULL, 0x01};

byte openPort()
{
	SRL_STAT srl;

	srl.port = IB_SRL_COM2;
	srl.speed = IB_SRL_9600BPS; 
	srl.parit = IX_SRL_NONE;
	srl.datab = IX_SRL_8DATA;
	srl.stopb = IX_SRL_1STOP;
	srl.fctrl = IX_SRL_NOFLOW;

	if (LibSrlPortOpen(&srl) != IW_SRL_NOERR) 
	{
		LibPutMsgDlg("LibSrlPortOpen error");
		return 1;
	}

	return 0;
}

byte ReadCOM(byte * data)
{
	byte sReceivedByte;
	if (LibSrlRecvByte(&sReceivedByte) == IW_SRL_NODATA) return 1;
	*data = sReceivedByte;
	return 0;
}

byte WriteCOM(byte data)
{
	if(LibSrlSendByte(IB_FOLLOW_BUSY, data) == IW_SRL_NOERR) return 0;
	else return 1;	
}

void button1Pressed() {
	LibStringDsp("Serial data sent", 0, 0, 100, IB_PFONT2); 
	WriteCOM('1');
	LibPutDisp();
}

void button2Pressed() {
	byte serialData = 0;
	if(!ReadCOM(&serialData)) {
		LibStringDsp("New serial data!", 0, 0, 100, IB_PFONT2);
		WriteCOM(serialData);
		}
	LibPutDisp();
}

void main()
{

	TCHSTS tsts;

	LibTchStackClr();
	LibTchStackPush(NULL);

	LibTchStackPush(TchHardIcon);
	LibTchStackPush(&TchList[0]); 

	LibClrDisp();
	LibIconPrint(&IconButton1);
	LibIconPrint(&IconButton2);
	LibPutDisp();
	LibTchInit();
	
	if(!openPort()) {
		LibStringDsp("Port opened", 0, 0, 100, IB_PFONT2); 
		LibPutDisp();
	}
	else LibJumpMenu();
	
	while(1)
	{
		LibTchWait(&tsts);
		if(tsts.obj == OBJ_BTN1) {
		button1Pressed();
		}
		if(tsts.obj == OBJ_BTN2) {
		button2Pressed();
		}
	}

	LibTchStackClr();
	LibJumpMenu();

}

Теперь при нажатии первой кнопки в порт будет отправляться »1», а при нажатии второй — только что считанный байт (если в буфере он есть).
Проверим:

w1pdokmbaceauvmldzl9_p0ra0c.png

Кто бы сомневался.

Вот как-то так…


Увы, материалов по программированию под PVOS в сети исчезающе мало. Раньше их было больше, но теперь большая часть ссылок не работает.
Понятное дело, вряд ли это может быть хоть как-то полезно на практике. Но я совершенно уверен, что кого-то это заинтересует, и где-то станет одной программой для PVOS больше. Такие дела.

Ссылки


Увы, большинство тематических сайтов на данный момент уже невозможно открыть. Какого-либо крупного и ещё живого, наподобие palmdb.com, но для PV, просто нет. Даже на ещё живых сайтах большая часть ссылок уже не открывается. Если вы знаете какие-то ресурсы, которые не отражены здесь — пишите, я их обязательно добавлю.

Живые сайты (те, которые на момент написания статьи всё ещё нормально работают и содержат минимум битых ссылок):

Мёртвые сайты (представлены для ознакомительных целей, доступны для изучения в Internet Archive):

© Habrahabr.ru