У меня всегда было желание иметь подобное устройство. При этом я сформулировал ряд собственных требований, которым оно должно удовлетворять. Прежде всего, мне очень хотелось, чтобы по мере движения на дисплее пририсовывался график изменения скорости в зависимости от расстояния или времени, накопленный за небольшой период. А также, фиксировать (логировать) измерения на запоминающее устройство для дальнейшей передачи статистических данных на компьютер, их более детального просмотра. Дешёвые модели не полностью соответствуют моим требованиям, а за дорогие модели переплачивать не хочется.
Исходя из вышесказанного, я принял решение о создании собственного велоспидометра на базе микроконтроллера ATmega8. Было множество вопросов, в частности о применяемой периферии. Случайно наткнулся на статьи про использование дисплея от некогда популярного мобильного телефона Nokia 3310. Прочитав на него даташит и убедившись в простоте его управления, у меня не оставалось и сомнений, что велоспидометр будет изготовлен в корпусе вышеупомянутого телефона с его же родным дисплеем. Корпус довольно неплохой, да и сам аппарат найти не составляет труда.
В качестве ПЗУ для записи статистики измерения я решил поставить классическое ПЗУ 24XX512 (на 512 Кбит), которая управляется по интерфейсу I2C. Заморачиваться на применении карты памяти SD/MMC я не стал. Ещё одной немаловажной функцией в устройстве являются часы. Они служат для привязки некоторых измеряемых определённых параметров (например, максимальная скорость) к дате и времени, а также, необходимы для записи временных отметок в статистике. В качестве часов я применил отдельную от микроконтроллера микросхему часов реального времени (RTC), которая имеет независимое питание от батарейки и также общается с контроллером по I2C.
Дополнительные второстепенные требования я реализовывал в функционале устройства на этапе написания программы. Сюда относятся всякие организационные вопросы: количество задействованных кнопок, расположения на дисплее тех или иных элементов, навигация по интерфейсу и так далее. В плане навигации я заранее решил не усложнять программу, например, не реализовывать меню настроек, в частности, настройки даты и времени. Настройка часов осуществляется один раз. Часы «тикают» независимо в самой микросхеме RTC благодаря кварцу 32.768 КГц и батареи питания, которой хватает на длительное время. Настройка даты и времени осуществляется через UART интерфейс велоспидометра, подключающийся в COM-порт компьютера, одним нажатием. Через этот же интерфейс предполагалось считывание данных статистики из ПЗУ в компьютер. Для всего этого необходимо написать соответствующую программу для компьютера. Однако, как показала дальнейшая практика, от последнего пришлось отказаться. Во-первых, была проблема реализации приёма данных из контроллера в компьютер на этапе написания компьютерной программы. И, что ещё существеннее, увеличивался объём программы для контроллера. Гораздо было интереснее разместить ПЗУ (в SMD корпусе SOIC-8) на съёмной площадке, соразмерной с SIM-картой и использовать соответствующий свободный слот мобильного телефона. Для этого необходимо изготовить устройство считывания ПЗУ на основе SIM ридера по одной из известных схем программатора I2C ПЗУ. Как оказалось в дальнейшем, это решение не вызвало лишних неудобств.
Ещё один из немаловажных вопросов – вывод символьной (в том числе и цифровой) информации на графический дисплей. Для этого необходима графическая информация о том или ином символе. Данная информация тесно связана с таким параметром, как размер отображаемого шрифта. Для отображения основного параметра, скорости движения, для хорошей наглядности желательно применять крупный шрифт. Однако, как будет показано в дальнейшем, подобная графическая информация о десяти цифрах не уместится в память МК, а применение того же внешнего более ёмкого ПЗУ замедлит скорость прорисовки шрифта. Мной было принято решение в качестве самого крупного шрифта использовать шрифт высотой 8 точек. Графическую информацию для этого шрифта я стянул из файла «8X8.FNT» какой-то программы от MS DOS, предварительно разгадав его структуру и сделав дальнейшую обработку.
Как в дальнейшем оказалось на практике, этого размера вполне достаточно для наглядности скорости. В качестве размера для дополнительного шрифта я выбрал размер 3x5 и самостоятельно нарисовал графику для цифр такого размера. Этими мелкими цифрами отображаются дополнительные параметры: дата/время, средняя и максимальная скорость, путь.
Графическая информация обоих шрифтов хранится в определённых двумерных массивах. Каждый элемент массива, размером в 1 байт, обозначает распределение пикселей определённого столбца определённой цифры. Для крупного шрифта на каждую цифру выделено 8 столбцов, а для мелкого – 3. Для этого же мелкого шрифта, размером 3X5, формальная высота составляет не 5, а 8 точек (округление до одного байта). Это позволяет заранее предусмотреть расположение 5-позиционного шрифта в пределах 8-позиционной зоны в вертикальном направлении одним из 4-х возможных способов. Данные факты хорошо отображены на рисунке ниже, где продемонстрировано моделирование графики для первых двух цифр этого шрифта. Для моделирования отлично подходит всем известная программа Excel. Исходными данными являются расстановки «единичек» в соответствующие поля для требуемой графики. Из них по формулам рассчитываются значения массивов, вплоть до кода языка Си, который в дальнейшем может быть скопирован в текст программы для микроконтроллера.
Теперь речь пойдёт об особенностях управления применяемого дисплея. Данный дисплей монохромный и его размеры – 84 на 48 пикселей. Управление дисплеем от МК осуществляется по SPI интерфейсу. Переданные байты по SPI интерпретируются в дисплее в двух режимах: байты для вывода на дисплей и байты команд конфигурации. Эти режимы задаются самим МК по определённому пину дисплея (D/C). Полный перечень команд приведён в даташите на дисплей. Часть этих команд используется в моём устройстве, и служат для первоначальной инициализации дисплея при подаче питания на устройство: температурный коэффициент, контрастность, режим последовательной прорисовки (горизонтальный или вертикальный) и т.д. Сразу отмечу, что применяется горизонтальный режим прорисовки. Это значит, что при передаче байта в режиме вывода на дисплей, адрес автоматически увеличивается на единицу по строкам вправо. Когда заканчивается строка, адрес позиции переходит на начало следующей строки. Достаточно только предварительно отправить в дисплей специальную команду позиционирования на определённый адрес строки и столбца (начальная позиция), и затем отправлять байты данных последовательно один за другим для вывода графики. Стоит отметить особенность адресного пространства и интерпретации графики в зависимости от принимаемых дисплеем байтов. Замечу, что для монохромной графики один байт содержит информацию сразу о восьми пикселях. Рассматриваемый дисплей разделён в вертикальном направлении на 6 горизонтальных зон по 8 строк в каждой (6*8=48). Каждый столбец каждой зоны и будет соответствовать определённому байту, который отправляется с адресом соответствующего столбца (0…83) и номера зоны (0…5). Адрес считается не с единицы, с нуля. Например, если позиционироваться по адресу (34; 2) и отправить байт данных 255 (в бинарном виде «11111111»), то загорятся все 8 пикселей с 16 по 23 по вертикали и в 35-ом столбце по горизонтали. Из этой особенности следует, на мой взгляд, один из недостатков: невозможность управлять состоянием каждого пикселя по отдельности на аппаратном уровне. Байт является минимальной порцией данных для графики. При передаче байта по текущему адресу обновляются все 8 соответствующих пикселей в текущей зоне. Дисплей не поддерживает считывание текущей отображаемой графической информации в направление к микроконтроллеру. Поэтому, при необходимости, нужно заранее в МК хранить выводимую информацию в специально отведённом буфере, а для изменения состояния каких-либо пикселей (битов) применять битовые маски для байтов из этого буфера и передавать их в дисплей заново.
Моделирование и продумывание расположения той или графической информации на дисплее производилось с учётом вышесказанной особенности. Так было предпринято для упрощения кода при написании программы. И совершенно не случайно размер шрифта рассматривался из разряда 8, 16, 24, т. е. кратным 8. Графическую информацию, по аналогии с дисплеем, я также разделил на 6 горизонтальных зон. В первой зоне мелким шрифтом отображаются абсолютное и текущее (с момента включения устройства) значения оборотов. Во второй зоне — абсолютное и текущее значения пути (в километрах с округлением до сотых). В третьей зоне – средняя скорость. В четвёртой – максимальная скорость и крупным шрифтом – текущая скорость. В пятой зоне отображаются два прогресс бара для индикаций заполнения ПЗУ и количества перезаписи. В шестой, последней зоне, – дата и время. Именно пятая зона является тем самым исключением, когда в вертикальном направлении любого взятого столбца присутствуют пиксели, относящиеся к разной информации. Поэтому данная информация с применением битовых масок собирается в буфер, содержимое которого затем выводится на дисплей в эту пятую зону. Ещё, в 3-5 зонах присутствует информация для прорисовки рамки вокруг отображаемого значения скорости. В последней зоне каждый первый (самый младший) бит на всех столбца установлен в «1» для прорисовки линии сепаратора (40-ая строка). Для данного моделирования и наглядности адресов я изобразил всё вышесказанное в ячейках Excel.
Именно таким образом выглядит первое окно отображения. Всего два окна. Второе окно – вывод графика (гистограммы) движения. Для неё отведено 5 зон (40 строк) по вертикали и все 84 столбца по горизонтали. Шестая зона с часами для обоих окон одинакова.
При программировании мной было принято решение не прибегать к использованию какой-либо библиотеки для работы с данным дисплеем. Лично мне проще разобраться в даташите, реализовать часть функций самостоятельно, чем разбираться в библиотеке. Тем более, в этом нашлись определённые достоинства. Недавно, скачав одну из библиотек, я всё же разобрался в её функциональных особенностях. Она является универсальной, с её помощью можно управлять пикселями по отдельности и позиционироваться на реальный адрес пикселя. Но библиотека использует буфер, размером 84*6 Байт, и данный буфер по таймеру периодически отправляется в дисплей, производя обновление графики. Тем самым, занят таймер и часть памяти МК. В моём конкретном случае нет необходимости использования библиотеки, так как при моделировании я заранее позаботился о максимальном разделении информации по отображаемым зонам, поставленным в полное соответствие зонам дисплея. И нет необходимости периодически обновлять информацию на дисплее: информация обновляется только тогда и только в том месте, когда она изменяется (с каждым оборотом колеса, с каждым нажатием какой-либо кнопки и т.д.). Тем самым я ещё раз подчёркиваю: в зависимости от поставленной задачи можно избежать использования каких-либо библиотек.
Для работы с часовой микросхемой и ПЗУ я также не прибегал к использованию библиотек: все функции довольно простые и реализованы мной после изучения даташитов на данные компоненты.
Теперь рассмотрим электрическую схему устройства.
Схема велоспидометра относительно простая. Кроме всего перечисленного выше, схема содержит элемент IC5 MAX756, служащий преобразователем питания из 3 в 5 Вольт для надёжного питания от оригинальной батареи мобильно телефона Nokia 3310. Реализовывать схему под 3-вольтовое питание я не стал за неимением соответствующего МК и периферии. На текущий момент времени MAX756 я пока не приобрёл, и вся схема пока питается от выносной батареи «Крона» с применением регулятора LM7805 (не совсем оптимальный вариант). Она подключается к разъёму наушников с нижней стороны телефона. К порту прерывания INT0 МК (пин 32) подключен геркон SF1, являющийся датчиком оборота колеса. Он надёжно подключается с нижней стороны телефона к разъёму зарядки. К произвольным портам (пины 23, 27, 28) подключены функциональные кнопки S1-S3, совпадающие с кнопками «1», «2», «3» мобильного телефона. К пину 29 сброса МК подключена кнопка S4, совпадающая с верхней торцевой кнопкой включения питания мобильного телефона. Это я сделал просто так. Само же устройство не имеет дежурного режима и включается с подачи питания. К SPI порту контроллера (пины 15-17) подключен дисплей IC2 и разъём для перепрошивки X1. С разъёмом, который я хотел сделать на основе уже имеющихся «пятачков» на оригинальной плате для сопряжения с ПК (в этом же месте), получилась небольшая загвоздка, и в дальнейшем я перенесу его на другое место. На этот же разъём зацеплен UART интерфейс для пользовательского подключения к компьютеру, через который настраиваются дата и время на устройстве (пины 30-31, RX/TX). Дисплей к контроллеру подключен через делители на резисторах, служащие для снижения напряжения, ибо дисплей работает на напряжении 3.3 В. В дополнение, пины дисплея D/C (данные/команда), SCE (строб) и RES (сброс дисплея) подключены к произвольным портам МК PB0, PB1 и PB2 соответственно (пины 12-14). Питание дисплея осуществляется через диоды D1-D3 и резистор R6, которые служат для снижения напряжения с 5 до 3.3 В, избегая применения линейного регулятора. Кварц Cr1, тактирующий МК, номиналом 4.5 МГц выбран случайно, но обдуманно. Просто он мне попался под руку, и я решил применить именно его. К портам МК PD4 и PD5 (пины 2 и 9) подключены транзисторы Q1 и Q2, на которые нагружены светодиоды подсветки дисплея и клавиатуры. Контроллер предусматривает возможность управлять подсветками по отдельности, как это предусматривает оригинальная схема мобильного телефона (это было на аппаратном уровне, а не на пользовательском), хотя на практике этого не нужно. Шина I2C подключена к портам PC2-PC3 (пины 25-26) и для простоты реализована программно с применением соответствующей библиотеки (хотя и подключена к портам аппаратного TWI). На шине подвешены ПЗУ IC3 и часы реального времени (RTC) IC4. Сразу оговорюсь, чтобы не было критики в комментариях: я знаю, что DS1307 не лучшее решение, но на момент разработки схемы я не знал про существование DS3231. ПЗУ находится на съёмном разъёме, по аналогии с SIM-картой. Дополнительный порт контроллера PC1 (пин 24) служит для приёма импульсов с частотой 1 Гц с RTC, по которым обновляется время на дисплее. Все пассивные компоненты обвесов – согласно даташитам на каждый активный компонент.
Рассмотрим математические соображения для расчёта тех или иных параметров. Как уже в начале было сказано, принцип измерения скорости основывается на подсчёте периода оборота колеса, а расстояние вычисляется на основе измерения количества таких оборотов. Контроллер измеряет время между предыдущим и вновь приходящим импульсом с геркона. Результат измерения преобразуется в значение скорости путём деления значения периметра колеса на период оборота, и это значение обновляется на дисплее с каждым импульсом (оборотом колеса). Здесь стоит отметить, что, с точки зрения физики, вычисляется средняя скорость движения велосипеда на участке пути, соответствующем периметру колеса. Отдельно подсчитывается количество импульсов, преобразующееся затем в значение расстояния. Для измерения периода оборота колеса контроллер использует собственный таймер. В ATmega8 имеется 8-битный и 16-битный таймер. От разрядности таймера зависит динамический диапазон измерения. В моём случае применяется 16-битный таймер, так как 8 бит (256 градаций счёта) категорически недостаточно. Максимальный период измерения (до переполнения таймера) будет соответствовать минимально измеряемой скорости. Можно ввести так называемый программный таймер, который позволит измерять большие периоды. Однако для упрощения программы я этого делать не стал. При применяемом кварце 4.5 МГц и максимальном значении делителя 1024 в конфигурации таймера имеем: (1/(4500000/1024)) = 0.000227556 сек. Этому значению соответствует минимальный период счёта. А максимальный период счёта составит 0.000227556*65536 = 14.913 сек. Максимально измеряемая скорость, соответствующая минимально измеряемому периоду, составит порядка 30000 км/ч. Этого даже и не стоило оговаривать, «запас сверху» просто огромный. А вот минимально измеряемая скорость, соответствующая максимально измеряемому периоду, составит 2.26/14.913/1000*3600 = 0.54 км/ч. Здесь 2.26 – периметр велосипедного колеса (в метрах) в моём случае. Меня вполне устраивает данное минимально измеряемое значение. Если велосипед будет двигаться со скоростью меньше, чем 0.54 км/ч, велоспидометр будет фиксировать отсутствие движения (и переполнение таймера). При данном кварце 4.5 МГц UART интерфейс отлично работает на скорости 2400 бод с допустимой приемлемой погрешностью. Этой скорости также вполне хватает, тем более, UART используется у меня для разовой настройки часов с компьютера (для копирования даты и времени с компьютера в устройство). Если брать кварц по частоте выше, минимально измеряемая скорость будет увеличиваться, что будет для меня недопустимо, и появится необходимость использовать программный таймер. А если брать ниже, то снижается быстродействие устройства в целом. Поэтому я решил оставить именно этот кварц.
Замечу, что величины период и скорость лежат в обратно пропорциональной зависимости, а таймер микроконтроллера измеряет период дискретно. В нашем случае на диапазон измерения (0.000227556…14.913) равномерно нанесены точки в количестве 65535, разделяя его на множество равных интервалов. И эти точки соответствуют всевозможным измеряемым значениям. Применяя преобразование интервалов из времени в скорость, данная система интервалов преобразуется из равномерной в обратно пропорциональную. Следовательно, диапазон всевозможно измеряемых скоростей разбивается на неравномерные интервалы. Длина этих интервалов возрастает с возрастанием самого значения скорости. Учитывая этот факт, огромный «запас сверху», о котором я писал чуть выше, будет не лишним. На практике будет вполне достаточно за максимально измеряемую скорость велосипеда взять значение 100 км/ч. Это хотя бы для того, чтобы не вводить новый разряд (сотни) и не увеличивать ширину отображаемого параметра на дисплее. Посчитаем, чему равна длинна интервала между соседними возможными значениями на скорости в окрестности, например, 90 км/ч. По обратным формулам или подбором несложно подсчитать, что для значения таймера 397 (из 65536 возможных) соответствует измеренная скорость 90.06 км/ч. А при соседнем значении таймера 398 – 89.83 км/ч. А разность между скоростями – 0.23 км/ч, что уже более чем допустимо. А на более низких скоростях данная разность будет ещё меньше. Дисплей отображает значение скорости с точностью до сотых. Однако на практике обычно хватает округления до целого или до десятых. Из всего сказанного можно сделать вывод: неравномерностью «сетки» скоростей можно пренебречь, так как вызванная ею погрешность измерения не превосходит допустимую погрешность.
Для подсчёта расстояния достаточно умножить количество импульсов (оборотов) на периметр колеса. При этом, разумеется, расстояние рассчитывается с точностью до периметра колеса, что вполне допустимо. Текущая средняя скорость рассчитывается, как отношение текущего пройденного пути к значению времени с момента включения. Это самое время контроллер считает путём подсчёта количества импульсов, приходящих раз в секунду с RTC. Средняя скорость на дисплее обновляется вместе с обновлением времени (раз в секунду). Все остальные параметры обновляются с каждым оборотом колеса.
Теперь по поводу мелких особенностей интерфейса. Первая кнопка служит для переключения режимов (графический режим или режим отображения значений). Вторая кнопка – для вывода абсолютной (за всё время) максимальной скорости взамен относительной при её удерживании. Также, выводится дата и время достижения этой скорости на место текущей даты и времени. А ещё, выводится значение текущего адреса ПЗУ на место значения относительного числа оборотов (для контроля). Оценить это значение можно по горизонтальному прогресс бару на 38-ой строке дисплея. На данное ПЗУ, ёмкостью 65536 байт (512 кбит), осуществляется запись измеряемых параметров. Как будет сказано позже, достаточно записывать первоначально измеряемый параметр (период оборота колеса) с меткой начального времени. Все остальные параметры легко рассчитываются компьютерной программой на этапе сканирования ПЗУ. Третья кнопка предназначена для управления подсветкой. В отличие от эскиза экрана выше, я в дальнейшем убрал незначащие нули на второстепенных параметрах для более наглядного их отображения. В графическом режиме слева направо прорисовывается гистограмма скорости движения, которая наглядно демонстрирует процесс изменения скорости за небольшой участок расстояния в 84 оборота колеса. Значение гистограммы – скорость в масштабе 1 пиксель на 1 км/ч. Если скорость движения превышает 40 км/ч, масштаб изображения сокращается по вертикали в 2 раза во избежание зашкаливания. Доскональные особенности поведения устройства описывать здесь не нужно.
Стоит отметить одно из характерных отличий моего велоспидометра от дешёвых покупных. Оно заключается в быстродействии обновления показания скорости на дисплее. В моём устройстве оно обновляется сразу же, как рассчиталось, с каждым оборотом колеса. В покупных устройствах оно обновляется с некой задержкой. Возможно, данная задержка происходит из-за попытки фильтрации шума измерения (например, методом скользящей средней) для того, чтобы стабилизировать показание скорости на дисплее для более детальной наглядности. А возможно, дисплей обновляется полностью через равные промежутки времени (например, два раза в секунду). Может быть это и удобно, но мне хотелось реализовать обновление скорости именно с каждым оборотом колеса.
Печатная плата изготовлена методом ЛУТ по форме оригинальной платы применяемого мобильного телефона. При изготовлении платы я использовал программу «SLayout». При этом я заранее на сканере сделал снимок оригинальной платы с двух сторон и подложил изображения в SLayout как шаблон. Это необходимо для того, чтобы нарисовать площадки для коннекта дисплея, кнопок и разъёмов в исключительно нужных местах. При изготовлении платы возникала погрешность около 0.5 мм. Данная погрешность оказалось приемлемая в плане совмещения контактных площадок и элементов. Однако эта погрешность сказалась на качестве подстветки: запаянные светодиоды оказались смещены на доли миллиметров и не попали в фокус светорассеивающих оправок. Из-за этого яркость подстветки уменьшилась, снизив КПД. Ниже на рисунках показан вид печатной платы в SLayout вместе с тремя мелкими печатными платами для ПЗУ в форме SIM карты. Также, приведены сканы оригинальной печатной платы с двух сторон.
Часть элементов (кнопки, разъёмы) сообщаются между собой тонкими проводными перемычками за неимением возможности проложить дорожки. Предусмотрен запас по всем имеющимся кнопкам, т.е., есть возможность задействовать любую имеющуюся кнопку. Возможно, удобно будет большую кнопку по центру сделать кнопкой переключения режимов отображения. В верхнем левом углу на плате размещена 3-вольтовая батарея питания RTC. Вообще, все элементы на плате размещены грамотно с согласованием их габаритов с габаритами корпуса. В отличие от позолоченного оригинала, плата собственного изготовления покрыта обычным припоем. Как показала первоначальная практика, контакт с дисплеем и прочей периферией не теряется.
Программа для МК получилась довольно большой и занимает значительную часть его памяти. Кроме этого, программа предусматривает использование собственной энергонезависимой памяти контроллера (EEPROM) для записи и сохранения необходимой информации. Ниже представлена таблица распределения этой информации по адресам EEPROM.
Address | Size | Data |
0 | 4 | n (for S) |
4 | 2 | t_min (for v_max) |
6 | 6 | Date of t_min |
12 | 2 | Address EEPROM |
14 | 1 | EEPROM RW Count |
128 | 80 | Digits 8X8 |
208 | 30 | Digits 3X5 |
Первыми четырьмя байтами сохраняется пройденный путь, как количество оборотов колеса. Я специально выбрал 32-битный целочисленный тип для данной переменной, поскольку на практике значения пройденного пути сравнительно велики. К примеру, целочисленная 16-битная переменная смогла бы сохранить максимум 65536 оборотов (около 148 км), что естественно мало. Далее следуют два байта для сохранения абсолютной максимальной скорости. По факту сохраняется минимальное время оборота колеса. Переменная занимает два байта, поскольку её значение является результатом измерения 16-битного таймера. Следующие 6 байт – дата и время достижения вышесказанной максимальной скорости. Данные представлены ровно в том формате, в каком они считываются из микросхемы RTC (исключая номер дня недели). Далее – два байта, сохраняющие значение текущего адреса внешнего ПЗУ. Это своего рода указатель, который необходим для возможности продолжения записи статистики на ПЗУ после очередного включения устройства. МК должен знать, на какой позиции адресного пространства внешнего ПЗУ он остановился в последний раз. С этой позиции МК продолжит запись. Этому значению отведено 2 байта, так как адресное пространство внешнего ПЗУ 16-битное. Это следует из размера ПЗУ в 64 кБ. Далее – однобайтовая переменная, сохраняющая значение количества перезаписей ПЗУ. Перезаписью считается тот случай, когда вышеописанный указатель достигнет максимального значения и обратится в ноль. При этом вновь поступающая информация на ПЗУ начнёт записываться с самого начала, стирая имеющуюся на ней старую информацию. Целочисленная однобайтовая переменная способна сохранять максимум 256 значений. Напоминаю, что значения указателя адреса ПЗУ и количества перезаписей визуально индицируются двумя прогресс барами на дисплее. Далее, после большого резервного пробела EEPROM МК, начиная с адреса 128, хранится графическая информация о цифрах размера 8x8. Для неё отведено 80 Байт (8 Байт на каждую цифру, как уже говорилось ранее). И, наконец, начиная с адреса 208, хранятся 30 Байт для графической информации о мелких цифрах 3x5 (по три байта на цифру).
Кроме основной программы для микроконтроллера, мной были написаны ещё три вспомогательные программы для компьютера, о которых речь пойдёт ниже. Все программы не имеют графического интерфейса и работают из командной строки Windows XP.
Первая программа позволяет скопировать дату и время с компьютера в велоспидометр через COM порт. Подключение велоспидометра к компьютеру осуществляется через микросхему MAX232. Средствами WinAPI программа получает текущую дату и время в специальную структурную переменную типа SYSTEMTIME. Из этой переменной извлекаются текущие день, месяц, год, номер дня недели, часы, минуты, секунды в десятичном формате. Все эти числа, за исключением года, не превышают двух десятичных разрядов (меньше 100) и лежат в пределах одного байта. Значение года преобразуется в двузначное число вычитанием из него числа 2000, значения текущего тысячелетия. Каждое из таких двузначных десятичных чисел преобразуется в двоично-десятичный формат, характерный для микросхемы RTC. В этом формате двузначное число также занимает объём в один байт. Старшими 4-мя битами кодируется цифра десятков, а младшими – цифра единиц. В дальнейшем из этих чисел формируется посылка, размером 13 Байт, согласно заранее определённому мной протоколу. Первые пять байт представляют собой слово «TIME=», согласно стандартной кодировке ASCII. Потом следуют секунды, минуты, часы, день недели, день, месяц, год. Последний байт представляет собой символ «#», как символ конца сообщения. Данная посылка отправляется с компьютера в устройство по COM порту. Программа микроконтроллера принимает посылку и проверяет её на корректность, согласно вышесказанному формату. Если первые пять байтов «TIME=», а последний «#», посылка считается корректной, и внутри стоящие байты интерпретируются в соответствующем порядке. Не изменяя эту цепочку байт, контроллер отправляет её в микросхему RTC по шине I2C, конфигурируя её на текущую дату и время. Отмечу, что эта микросхема поддерживает счёт дней недели от 1 до 7, хотя как таковым календарём, определяющим соответствие даты и дня недели, она не является. Вывод информации на дисплей о дне недели в своём устройстве я не предусматривал.
Вторая программа предназначена для обработки данных из содержимого внешнего ПЗУ. Изначально предполагалась, что данное содержимое должно переписаться из ПЗУ в файл-образ с помощью какой-либо известной программы, работающей с известными программаторами МК и ПЗУ (например, «icprog»). Однако, детальнее изучив принцип работы I2C, я сумел реализовать данный функционал и включил его в свою программу. Схема программатора ПЗУ такой серии, которую я применил в устройстве, представлена на рисунке ниже.
ПЗУ подключается к COM порту компьютера, который используется не как средство обмена информацией по RS-232 (где достаточно задействовать выводы TX, RX, GND), а как средство произвольного ввода-вывода логических сигналов. Через вывод TX осуществляется питание ПЗУ, которое стабилизируется до 5В регулятором 78L05. Управляя с компьютера выводом TX, мы можем включить или выключить микросхему ПЗУ. Однонаправленная линия тактирования SCL сосредоточена на пине RTS розетки COM порта, а двунаправленная линия данных SDA – на двух пинах: CTS (приём данных) и DTR (передача данных). Резисторы и стабилитроны D1 и D2 служат для ограничения уровня сигнала до TTL, на котором работает ПЗУ.
Данный стандартный программатор я изготовил для моего особого случая, где вместо панельки для ПЗУ применён SIM-ридер от сломанного мобильного телефона.
Средствами WinAPI программа обращается к пинам COM порта компьютера, устанавливает необходимые на них значения (0 или 1), а также снимает с пина CTS приходящее двоичное значение с ПЗУ. На базе этого инструментария реализован функционал для работы с I2C, согласно соответствующей спецификации, в подробности которой я вдаваться не буду. Программа может считывать содержимое ПЗУ в файл-образ (как и штатный программатор), а также обрабатывать такой файл, или обрабатывать информацию с ПЗУ напрямую. Обработка информации заключается в получении выходных файлов статистики в заранее определённом табличном формате на основе входной информации с ПЗУ. Каждый такой файл соответствует одной поездке (с момента текущего до момента следующего включения устройства). Сначала я кратко опишу формат входных данных, который я заранее определил. При каждом включении устройства записываются два байта нулей по текущему адресу, который считывается из EEPROM микроконтроллера. При начале вращения колеса (при первом импульсе) после таймаута или после включения устройства записывается текущая дата и время в двоично-десятичном формате (как она хранится в регистрах микросхемы RTC). А следом записывается два байта «единиц» 0xFF. Во время вращения колеса при каждом k-ом импульсе (k=2,3,...) записывается время оборота колеса между (k-1)-ым и k-ым импульсом двумя байтами (старшим и младшим). Очевидно, что данной информации достаточно для привязки текущего (не абсолютного) пройденного пути и скорости к дате и времени. Выходной формат является текстовым и представляет собой табулированную таблицу в файлах *.csv, которые открываются в программе Excel по «двойному щелчку» мыши. Строки этой таблицы соответствуют оборотам колеса, а значения столбцов приведены ниже.
ADR | Значение адреса ПЗУ |
DATE/TIME | Дата и время начала движения |
DEC | Значение таймера в десятичном формате |
time | Текущее время |
t | Время в пути с момента включения |
v | Скорость |
n | Число оборотов |
S | Путь |
an | Абсолютное число оборотов (только в пределах текущего ПЗУ) |
aS | Абсолютный путь (только в пределах текущего ПЗУ) |
n_day | Число оборотов за текущие сутки |
S_day | Путь за текущие сутки |
v_max | Максимальная скорость за текущую поездку |
av_max | Абсолютная максимальная скорость (только в пределах текущего ПЗУ) |
v_mid | Средняя скорость за текущую поездку |
Скриншот содержимого такого файла в Excel представлен на рисунке ниже. Также, изображены графики изменения текущей, средней и максимальной скорости различными цветами в одной системе координат. Аргумент (ось X) – значения числа оборотов, как исходные данные. На рисунке представлены изменения параметров за первые 730 оборотов. С данной переменной линейной зависимостью связан пройденный путь (730 оборотов соответствует примерно 1650 м). Поэтому, можно сказать, что графики отражают зависимости скоростей от расстояния (с точностью до масштаба по горизонтали), в отличие от традиционной зависимости скорости от времени, на что следует обращать внимание. Как уже говорилось, данная особенность обусловлена идеологией и принципом измерения скорости по оборотам колеса. Но ведь каждому обороту колеса (моменту сближения магнита и геркона) приписан определённый момент времени. Естественно, данная последовательность временных меток не является равномерной. Однако, для формальности и удобства, в Excel есть возможность в качестве аргумента для графиков указать массив значений времени в пути или текущего времени. Но всё равно следует помнить, что реальная зависимость скорости от времени (на равномерных интервалах времени для дискретного случая) выглядела бы по-другому, с переменным масштабом по горизонтали.
На рисунке ниже изображена та же зависимость скорости от оборотов, но уже с применением фильтра методом скользящей средней с шириной окна в 11 оборотов. Все графики построены в программе Excel известными методами.
Сравнивая два графика изменения скорости, очевидно, что в отфильтрованном графике отсутствует высокочастотная компонента, т.е. удалён шум. Ширина окна скользящего среднего в 11 оборотов (около 25 м пути), на мой взгляд, великовата. Если действительно ставить вопрос о фильтрации показаний от шума, то достаточно взять небольшую ширину окна, например, равной трём. Этот алгоритм можно заложить в программу велоспидометра, т. к. он может применяться не только для анализа извлечённых показаний, но и для отображения этих показаний в реальном времени. Несмотря на простоту этого алгоритма, я не буду вдаваться в подробности его описания, так как эта тема освещается в курсе математики и выходит за рамки данной статьи. И здесь ещё одно уточнение по поводу средней скорости. Как я уже писал, средняя скорость это единственный параметр, который обновляется не с каждым оборотом колеса, а каждую секунду. Это я сделал для того, чтобы на дисплее наблюдалось изменение средней скорости даже при очень медленном движении. Поэтому, значения показаний на дисплее в реальном времени будут немного отличаться от значений, рассчитываемых в дальнейшем компьютерной программой на этапе сканирования ПЗУ. Также будут отличаться показания абсолютного числа оборотов, абсолютного пути и абсолютной максимальной скорости. На дисплее отображаются действительно абсолютные значения (за всё время существования устройства), а в выходных таблицах – только в пределах текущего считываемого ПЗУ.
Третья программа, по своей сути, является той же программой для прошивки микроконтроллера. Я работаю с простейшим программатором «STK 200», подключенным к LPT порту компьютера, а точнее, с его аналогом, если его так можно назвать, ибо в простейшем случае программатор вовсе не содержит активных элементов. Фактически, МК по SPI интерфейсу подключается к определённым пинам LPT порта напрямую и работает, как ведомое устройство. Программа реализует протокол обмена данными с контроллером ATmega8, согласно его даташиту (стр. 237). Физический уровень SPI реализован путём управления регистрами LPT порта с применением известной динамической библиотеки «inpout32.dll». Библиотека у меня подключается не как к проекту (так как я в «Dev-cpp» избежал создания как такового проекта, создав один простейший «сишный» файл), а с помощью функции LoadLibrary с применением структурного типа HINSTANCE. Переменной данного типа ставится в соответствие библиотека «inpout32.dll», и в дальнейшем в отдельные переменные извлекаются указатели на функции из данной библиотеки. В inpout32.dll имеются только две функции, которые отвечают за ввод и вывод данных. Обращение к этим функциям происходит по извлечённым заранее указателям. Управление пинами LPT порта по отдельности осуществляется при помощи битовых масок. В моём конкретном случае, написанная мной программа работает с EEPROM областью контроллера и предназначена для считывания, резервирования, записи, корректировки и восстановления из резервной копии хранящихся в ней данных, которые я расписал ранее. Как и все остальные программы, программа работает из командной строки. В таких случаях, для реализации многофункциональности программы применяются функции «switch-case» и текстовый пользовательский диалог, например, «введите ‘1’ для операции №1, …, введите ‘0’ для выхода из программы». Данные отображаются в различных удобных для меня форматах. В дополнение к сказанному, программа может выводить полный дамп EEPROM контроллера в 512 Байт на экран. Также, программа умеет записывать в память контроллера графическую информацию о применяемых шрифтах. В случае с мелким шрифтом, размером 3X5, программа берёт информацию из текстового файла «Fonts 3X5.txt», который находится с ней в одном каталоге. Файл содержит табулированную таблицу из 30 байтов (3 на 10), записанных в 16-ричном формате. При желании его можно легко отредактировать в текстовом редакторе, изменив, таким образом, графику данного шрифта. Как уже говорилось, этот мелкий шрифт настолько простой, что менять его графику не имеет смысла. Единственное, может только потребоваться его вертикальный сдвиг, так как имеется запас пространства по высоте в 8 пикселей, а шрифт имеет высоту, равную 5. В случае с крупным шрифтом, размером 8X8, которым отображается текущая скорость, я предусмотрел функционал куда интереснее. Графическая информация о данном шрифте представлена не в текстовом файле в виде таблицы байтов, а в графических наглядных файлах BMP. Каждой цифре соответствует один такой файл. Его параметры – размер 8X8, монохромный с чёрно-белой палитрой. Ниже показан скриншот известного графического редактора «MS Paint» с открытым в нём файлом «8.bmp».
Эмпирическим путём я изучил структуру монохромных BMP файлов, полученных из MS Paint», и на этой основе мне удалось научиться читать каждый пиксель монохромной BMP картинки (исключая использование структур и вспомогательных библиотек). На этапе построчного горизонтального чтения снизу вверх (именно так и организована структура BMP файла), программа преобразует информацию в вертикальный формат, свойственный применяемому дисплею. Эта операция осуществляется в один проход, где применяются битовые маски и накопление значения переменных. Ниже я покажу этот участок кода для i-ой цифры, обратив внимание на простоту процесса.
for(k=0; k<8; k++){
fnt[i][k] = 0;
}
for(j=0; j<8; j++){
fseek(f, 62+4*j, SEEK_SET);
byte = ~fgetc(f);
for(k=0; k<8; k++){
if(byte & pow2(7-k)){
fnt[i][k] += pow2(7-j);
}
}
}
В первом цикле происходит инициализация элементов массива fnt нулями. В дальнейшем, каждый k-ый элемент этого массива (k=0…7) для i-ой цифры (i=0…9) будет нести графическую информацию о каждом соответствующем столбце каждой соответствующей цифры. Следующий цикл – пробег по строкам картинки BMP файла. Оператором fseek мы позиционируемся на байт по смещению 62+4*j заранее определённого BMP файла f. Специфика формулы, по которой рассчитывается смещение в зависимости от номера строки j, обусловлена структурой BMP файла. В промежуточную переменную byte мы получаем значение байта по вышесказанному смещению. Этот байт хранит информацию обо всех восьми пикселях монохромной картинки в текущей строке j. Оператором ‘~’ происходит побитовая инверсия байта, что приводит к цветовой инверсии каждого пикселя. Это связано с тем, что чёрный пиксель в палитре монохромного BMP файла соответствует логическому «0», а белый – «1». В применяемом же дисплее – всё наоборот. Во вложенном цикле происходит побитовый анализ байта и в тоже время накопление информации в выходной массив fnt. Функция pow2 – возведение двойки в неотрицательную целочисленную степень, написанная самостоятельно. Вместо этой функции можно применить более эффективный оператор побитового сдвига «<<», но на момент написания этой программы я им не пользовался.
Кроме этого, программа предусматривает возможность записать в память МК одну из нескольких графических вариантов этого шрифта на мой выбор. Эти варианты реализованы с помощью директорий (папок) с именем вида «v1», «v2», «v3» и т. д., которые располагаются в папке «Fonts 8X8» в одном каталоге с программой. А уже в этих папках лежат необходимые BMP файлы. Благодаря вышеописанному функционалу, имеется возможность в графическом редакторе поправить или нарисовать цифры с «чистого листа», сохранив и распределив их по директориям. У меня имеется три варианта шрифта. Первый вариант – оригинальный. Второй – как и оригинал, но с зачёркнутым нулём и видоизменённой единицей (без нижнего подчёркивания). Третий – шрифт с прямоугольной окантовкой.
На фотографиях ниже изображены: изготовленная печатная плата устройства с задней стороны; устройство на столе с подключенным к нему питанием (с неокончательной версией прошивки); устройство в работе, установленное на велосипед, с отображающимся на нём графиком изменения скорости.
В процессе эксплуатации устройства всё-таки были выявлены небольшие недостатки, связанные с особенностями изготовления. Прежде всего – плохой контакт дисплея с контактными площадками печатной платы. В оригинальном мобильном телефоне контакты на плате позолоченные, и окисление отсутствует. В моём случае они просто лужёные.
Исходя из вышесказанного, было принято решение переделать устройство в другой корпус, а также переделать печатную плату, на которую дисплей будет припаиваться внаглую. Этим процессом я занялся совсем недавно. В результате получилась более устойчивая к работе конструкция.
Корпус для устройства я изготовил из куска оргстекла, толщиной 17 мм, на фрезерном станке ЧПУ. Для этого я предварительно набросал эскизы корпуса в программе SPlan, почти полностью не владея тематикой чертежей, САПР и т.д.
Данные наброски нужны для общего представления и получения координат опорных точек. На их основании пишется программа на станок ЧПУ, принимая во внимание общие принципы и последовательности фрезеровки. Программу ЧПУ я писал вручную в Excel, применяя функции автозаполнения на повторяющиеся операции.
Схему устройства я также немного подкорректировал, она представлена на рисунке ниже.
Вместо ненужной подсветки стоит для красоты один светодиод, который вспыхивает с каждым оборотом колеса. Также перерисованы разъёмы, и отсутствуют другие элементы, которые не понадобились в обновлённой версии конструкции. дальнейшем я нашёл и поставил кварц 4.433619 МГц, незначительно подкорректировав некоторые константы в исходном коде собственной программы. Было также сделано ещё несколько незначительных изменений в программе.
Фотография готового изделия представлена ниже. Устройство питается от АКБ, которая стоит на борту велосипеда. От неё же запитано освещение на случай поездок в тёмное время суток.
Именно в таком исполнении устройство заработало полностью без каких-либо сбоев. Единственный недостаток — применение не очень качественного чипа RTC: зимой при низких температурах время заметно спешит, приходится раз в месяц корректировать.
Комментариев нет:
Отправить комментарий