Sleep, который тормозит программу, мы использовали таймер. Чтобы это сработало, надо в обработчике события OnTimer: все время увеличивать переменную tall. Полностью процедура по таймеру приводится далее, т. к. tall нам понадобится не только для этого.
Как только мы обратились к процедуре AfComPort1.open, у нас немедленно будет создан параллельный поток и весь прием пойдет через него. Поэтому, чтобы при определении модема принятые байты не обрабатывались, нужно не забыть добавить в процедуру приема выход по условию FiagCOM=Faise.
Для создания этой процедуры обычным способом — через инспектор объектов — создадим обработчик события AfComPort1DataRecived[17] (листинг 18.5).
Листинг 18.5
procedure TForm1.AfComPort1DataRecived(Sender:TObject; Count:Integer);
{чтение очередного байта по сообщению wmCOMPORT}
var i: integer;
begin
if FlagCOM=False then exit; {если модем еще не опрошен}
if count<>0 then {если что-то принято}
begin
AfComPort1.ReadData(ab,count); {читаем буфер в массив}
хn:=xn+count; {число принятых байт}
tall:=0; {обнуляем время}
end;
end;
На самом деле условие count<>0 не требуется, оно введено просто ради порядка (иначе бы процедура просто не была бы вызвана). По выходу из процедуры в переменной хn будет накапливаться количество принятых байтов. Осталось только дописать остальные процедуры (листинг 18.6).
Листинг 18.6
procedure TForm2.Button2Click(Sender:TObject);
begin {запрос}
if FlagCOM=False then exit;
{если порт еще не инициализирован — выход}
AfComPortl.PurgeRX; {очищаем буфер порта на всякий случай}
xb:=$А2;
AfComPort1.WriteData(xb,1); {посылаем команду}
FlagSend:=$А2; {обозначаем посылку запроса времени}
tall:=0; {обнуляем время}
хn:=0; {счетчик принятых байтов}
Timer1.Enabled:=True; {запускаем таймер}
end;
procedure TForm1.FormCreate(Sender:TObject);
begin
{инициализация COM1 при запуске}
stcom:='COM1';
IniCOM;
end;
procedure TForm1.ComboBox1Select(Sender: TObject);
begin
stcom:=ComboBox1.Text; {устанавливаем порт COM1,2,3,4}
IniCOM;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
AfComPort1.Close; {закрытие порта}
end;
Теперь нам осталось разобраться с тем, что мы там напринимали. Это позволит сделать установленное нами значение FiagSend и таймер. В таймере переменную tall мы будем увеличивать на единицу, а в процедуре приема мы все время ее обнуляем, так что пока она равна нулю, можно полагать, что прием еще не закончился. Как только она станет больше единицы (прошло более секунды с момента последнего принятого байта или прием вообще не происходил), мы начинаем что-то делать, но только в том случае, если флаг FlagCOM установлен (True), иначе это вообще был не прием, а опрос модема. Сказанное иллюстрирует листинг 18.7.
Листинг 18.7
procedure TForm1.Timer1Timer(Sender:TObject);
var i: integer;
begin {таймер}
inc tall
if FlagCOM=False then exit;
if tall>1 then
begin
Timer1.Enabled:=False; {выключаем таймер}
if xn=0 then {если счетчик = 0, то ничего не принято}
begin
Application.MessageBox('Устройство не обнаружено 1,'Error',МВ_ОК);
exit {выход из процедуры — неудача}
end else
begin {иначе обрабатываем данные}
if FlagSend=$A2 then {если был запрос времени}
begin
if xn<>6 then
begin
Application.MessageBox('Неправильный формат данных','Error',MB_OK);
exit;
end;
StaticText1.Caption:=IntToHex(ab[1],2); // часы
StaticText2.Caption:=IntToHex(ab[2],2); // минуты
StaticText3.Caption:=IntToHex(ab[3],2); // секунды
StaticText4.Caption:=IntToHex(ab[4],2); // дата
StaticText5.Caption:=IntToHex(ab[5],2); //месяц
StaticText6.Caption:=IntToHex(ab[6],2); // год
end;
end;
end;
end; {конец таймера)
По аналогии вы легко добавите процедуры, соответствующие всем остальным командам, предусмотренным в программе нашего измерителя. Пользуясь функцией DateTime, легко создать процедуру, которая будет загружать из компьютера точное время (только С форматом TDateTime придется немного попотеть, см. по этому поводу [15] и [16]). Не забывайте принимать и анализировать возвращаемые байты для процедур записи. При длинной процедуре приема данных из flash, когда число байтов заранее неизвестно, суммарное значение счетчика хn покажет, сколько именно байтов принято. Причем если это число не кратно четырем, то можно смело утверждать, что целостность данных была нарушена. И не забудьте увеличить размер массива ab, если у вас энергонезависимая память большей емкости!
Наряду с такой программой для обслуживания прибора имеет смысл также написать отдельное приложение, которое обрабатывает скачанные данные по температуре и давлению, переводит их в физические величины и представляет их в вид масштабируемого графика, отфильтровывая и записывая отдельно для просмотра байты сбоев с соответствующими датами.
Глава 19Практические схемы на AVR
… и телефонный аппарат (клубок катодов, спаек, клемм, сопротивлений) безмолвствует.
И. Бродский «Посвящается Ялте»
Возможности МК серии AVR весьма велики, вплоть до того, что на старших моделях можно выстраивать полноценные системы управления, например, жидкокристаллическими дисплеями. Среди продукции Atmel есть контроллер USB на AVR-ядре, и с его помощью можно делать настоящие USB-устройства, не прибегая к эмуляции СОМ-порта. Но и младшие модели AVR вполне пригодны для серьезных вещей, достаточно упомянуть, что такое устройство, как компьютерная клавиатура, изначально была спроектирована на куда менее мощном, чем почти любой современный AVR, контроллере 8048.
Здесь мы рассмотрим способы построения некоторых типовых узлов с помощью AVR, не задаваясь вопросом конструирования законченных приборов. Довести до ума и встроить в необходимые вам устройства эти узлы вы сможете сами, я лишь покажу, как это делается в принципе. И начнем мы с самого, пожалуй, интересного — попробуем заставить МК воспроизводить цифровой звук.
На самом деле способность воспроизводить звук встроена во все МК AVR изначально. Для этого надо лишь иметь «исходник»— ранее записанный звуковой файл с определенными параметрами. Такое устройство можно использовать, как узел голосовой сигнализации — например, если укомплектовать им наш измеритель с часами, и он сможет вслух сообщать время и температуру. Для этого придется сделать немного— разделить в памяти звуковые сэмплы, произносящие различные слова, и комбинировать их по ходу дела в нужном порядке. Именно так работают системы автоматического оповещения, например, о задолженности по телефонным счетам. Здесь мы в подробности влезать не будем, а покажем, как вообще организовать на AVR режим воспроизведения цифрового звука.
Что такое цифровой звук, вы уже знаете из главы 10— это последовательность отсчетов сигнала с определенной частотой (называемой частотой дискретизации или частотой оцифровки, битрейтом) и с определенным разрешением по напряжению (квантованием). Сначала давайте поймем, как в принципе можно воспроизводить такой звук.
Предположим, что мы имеем в качестве источника набор байтов, напрямую представляющий исходную оцифрованную последовательность (сжатые форматы мы не рассматриваем, поскольку это увело бы нас далеко за рамки темы книги). «Лобовой» метод понятен из той же главы 10: взять ЦАП, подать ему на вход оцифрованный звук с той же частотой, с которой его оцифровывали, а к его выходу подключить фильтр, усилитель и динамик. Но вот, например, производители сотовых телефонов просто замучили своей «полифонией»: сидя в маршрутке, невольно вздрагиваешь, когда у соседки в сумочке вдруг то ребенок заплачет, то котенок замяукает. Не многие, однако, задавались вопросом — а как это делается? Неужели в мобильник встраивают настоящий цифровой тракт, со всеми этими ЦАПами, фильтрами и усилителями? Вовсе нет. Главная идея, которая заложена в простой реализации воспроизведения цифрового звука, называется «усилитель в режиме D», от слова digital — цифровой.
В усилителях класса D цифровой звук вообще не переводится в аналоговую форму какими-то специальными устройствами. Наоборот, там обычный звук представляется в виде последовательности прямоугольных импульсов, пропорциональных по длительности интенсивности сигнала. Для усиления таких импульсов не нужно никаких ухищрений — чаще всего используют комплементарную пару транзисторов, подобно тому, как усиливается сигнал на выходе логических КМОП-микросхем. Выгода заключается в том, что теоретически КПД такого импульсного усилителя может быть равен 100 %, в