30.06.2009

Кухонный таймер на Arduino (3)

Часть III

(окончание, см. Часть II и Часть I)

Итак, начнем перенос схемы с макетной платы в реальный мир. Поскольку на кухне нет стабилизированного источника +5В, будем полагаться на стандартную схему питания со стабилизатором 7805 (чтобы можно было питать, например, от такого БП на +9В).



Кроме того, надо окончательно определиться с номиналом токоогрничительных резисторов.

При выборе надо учитывать не только максимальный ток через вывод ATmega - 40 мА, но и общий ток на кристалл. Для ATmega8 он составляет 300 мА, но это значение поделено на две части - 100 мА может протекать через порты C0..С5 и 200 мА через все остальные. Пины отдельных сегментов я расположил как раз на выводах C0..C5, следовательно ток через сегмент не может превышать 100/6 = 16,7 мА (будем считать, что все сегменты засвечены - т.е. надо выводить восьмерку).

Из документации на семисегментный дисплей выясняем, что максимальный ток сегмента в нормальном режиме - 30 мА, падение напряжения - 2.0 В при токе 20 мА. Зная, что при последовательном включении сумма падений напряжений на диоде и резисторе равна питанию ( +5В ), получается, что на резисторе "упадет" 5-2=3 В, из чего можно вычислить минимальное сопротивление: R=U/I или 3/0.0167 = 180 Ом. Я решил не рисковать и поставил 220 (ток 13,6 мА).

Тут надо заметить, что есть еще пины, через которые протекает ток всего знакоместа, собирая токи всех сегментов. Простой подсчет показывает, что этот суммарный ток 13,6 x 8 = 109 мА более, чем в два раза первышает 40 мА.

Увы, тут сказывается грубость моих теоретических выкладок - на практике всё вполне надежно работает. Я не учел пару моментов: во-первых, у пина ATmega тоже есть свое внутреннее сопротивление, следовательно реально ток должен быть меньше. во-вторых - при мультиплексировании знакоместо светится только одну миллисекунду, со скважностью 4, что дает возможность скорректировать значение тока 109/4 = 27,25 мА.

Видимо, если вывести на дисплей четыре восьмерки и остановить мультиплексирование, один вывод у ATmega должен выйти из строя. Пока мне жалко губить МК ради такого эксперимента, но, думаю, рано или поздно это у меня получится, хотя бы и случайно ;)

Последнее дополнение, которого нет на схеме: я нашел место для уголовой вилки PLSR, для оперативного заливания новых версии скетчей (конечно, можно было бы установить и ICSP, но так мне показалось удобнее с точки зрения взамодействия с Arduino IDE).

Для размещения я выбрал такой корпус:



На верхней панели надо поместить дисплей и две кнопки, где-то сбоку - тумблер и гнездо питания 2.1/5.5 мм. Сопоставив между собой габариты всех элементов, я пришел к выводу, что схему надо разнести на две платы через вилки и гребенки PLS/PBS: вверху будут кнопки и дисплей, внизу - все остальное. По высоте как раз подходит - заодно не будет болтаться лишних проводов к кнопкам и дисплею.

Первым делом надо вырезать два кусочка макетной платы, под размер корпуса. Можно сразу вставить в них вилки и гребенки PLS/PBS, чтобы убедиться в правильной центровке собранной двуплатной конструкции. Для отрезания гетинакса удобно пользоваться ножницами по металлу, в крайнем случае электролобзиком или мелкой ножовкой по металлу. Надфилем можно аккуратно скруглить углы, чтобы лучше входило в корпус (да и порезаться шансов будет меньше).

Дальше, надо обратить внимание на крепление в корпусе - в данном случае 4 отверстия в нижней плате. Отверстия обычно намечаются маркером и сверлятся мини-дрелью. К сожалению, фирменного DREMEL у меня нет, обхожусь китайской CT-800, хотя - на самом деле, можно воспользоваться даже бытовой ударной дрелью/шуруповертом, главное - аккуратность и точность. Диаметр отверстий можно подбирать под размер саморезов (для этого корпуса подойдет 2,5 мм).

Теоретически, в макетной ничего сверлить не надо, но практически - у нас есть гнездо питания 2,1/5,5 мм с широкими выводами, которое надо еще и расположить на краю платы таким образом, чтобы оно получалось "заподлицо" с одетым внешним корпусом. Возьмите сверло нужного диаметра и просверлите еще три отверстия.

Теперь можно откладывать слесарный инструмент (но недалеко, он еще пригодится) и браться за паяльник. Вот что у меня получилось с нижней платой:



Голубая бочечка - это керамический резонатор 16 МГц. Как и любой керамический резонатор, он будет уступать кварцевому по стабильности и точности (у этого экземпляра - 0.5% погрешности). Для кухонного таймера это кажется несущественным; главный плюс такого резонатора - наличие встроенных конденсаторов нужных номиналов (поэтому у него три пина), что в итоге дает более компактное размещение на плате.



Электролиты надо использовать низкопрофильные - хотя бы 7мм в высоту. Обычные, высотой 11 мм, придется класть "на бок", потому что межплатное расстояние - меньше.



Как и резонатор, конденсатор-фильтр 100 нФ (маркировка 104 обозначает 10 x 10^4 пФ) надо располагать максимально близко к МК.



На этом фото показано подключение адаптера USB-TTL к вилке PLSR4, на которой, как нетрудно догадаться, Vcc (+5В), GND, Rx и Tx. Соединения удобно делать однопиновыми проводами с разъемами BLS:



Однако, если USB-TTL под рукой не оказалось, придется искать другие способы: например, адаптер COM-TTL, легко собираемый на одной микросхеме MAX232CPE. Или классический - установить вилку ICSP и прошивать HEX-файл, получающийся после компиляции из Arduino IDE (ищите в каталоге скетча, там создается подкаталог applet со всеми промежуточными данными), но для этого понадобится программатор, например - тот же Usbasp.



Пьезоэлемент пришлось расположить на нижней плате из-за толщины, но это все равно оказалось приемлемо: слышно его очень хорошо.

Теперь пора приниматься за верхнюю плату:





Часть соединительных проводов можно "пропустить" под корпусом индикатора, но это надо делать до его установки, а сам индикатор не опускать "до упора" на плату.



Самое время проверить правильность монтажа: соединить две платы и посмотреть, как оно заработает.





Как видите - все компоненты на нижней плате аккуратно уместились в межплатное расстояние.

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





Отверстия под разъем питания и тумблер вырезаются скальпелем:



Подаем питание на устройство, и... ждем 10 секунд, пока появятся мигающие нули. Почему так долго? Это происходит из-за бутлоадера, который был прошит в ATmega8, если вы дали хотя бы раз команду "Burn Bootloader" из Arduino IDE. Кстати, именно он может стать причиной активизации злых аппаратных духов.

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

На 3-4 итерации я решил внимательно разобраться, в чем же, собственно, дело. Для этого я добился повторения состояния "поломки" таймера, после чего прочитал флеш-память ATmega8: обнаружились отличия в 16 байтах по сравнению с тем, что зашивали изначально. Следовательно, память ATmega была чем-то испорчена. И, как вы уже догадались, это был опрометчиво оставленный в памяти Bootloader.

Рекомендую в готовом изделии выключить бутлоадер и запретить запись в память программ (не путать с возможностью программирования через ICSP) при помощи установки соответствующих fuse-и lock-битов. Если бутлоадер все-таки нужен - как правило, для отладки - советую обязательно включить BOD - Brown-out Detector, встроенный в ATmega и по умолчанию в Arduino выключенный. Корень проблемы в том, что бутлоадер может исполнять SPM-инструкции (Store Program Memory), которые модифицируют код программной секции. В момент включения питания происходит масса переходных процессов: возрастает напряжение питания, разгоняется резонатор. Необходимо дождаться их стабилизации, прежде чем начинать выполнение инструкций, иначе поведение МК может быть непредсказуемым - с чем я и столкнулся. Для нейтрализации этого эффекта применяют стандартные схемы - т.н. "супервизоры" питания, которые следят за уровнем входного напряжения и удерживают сигнал сброса контроллера до тех пор, пока оно не достигнет нормального уровня. Но поскольку в ATmega оно уже есть (BOD), остается просто активировать его через fuse-биты и выбрать напряжение триггера (в нашем случае Vcc = 5В, поэтому выбираем 4В).

Финальные значения fuse-битов: HFUSE = 0xCB, LFUSE = 0x1F.

Вот, пожалуй, и всё. С помощью доступных компонентов и минимальных навыков программирования/паяния получено имеющее практическую ценность изделие - простой кухонный таймер. Таким образом я завоевал для своего хобби "место под солнцем", очистив его в глазах родственников от ярлыка бесцельного и дорогостоящего времяпрепровождения.

Что может быть прекраснее? ;)

25.06.2009

Ethernet Shield по UDP

К сожалению, библиотека Ethernet для Arduino заточена под работу в режиме TCP. Иногда этого явно недостаточно, особенно при таких-то богатых возможностях чипа Wiznet W5100 как встроенная поддержка UDP, ICMP, IGMP, ARP и даже PPPoE.

Лично мне для работы с NTP понадобился именно UDP, поэтому пришлось быстро написать класс, аналогичный Client в библиотеке Ethernet, но работающий по UDP. Недолго думая, я нарек его ClientUDP и поместил в каталог Ethernet. Все, что мне потребовалось - переработать код существующего класса Client, о чем я и расскажу ниже.

Для тех, кого не интересуют тонкости: ClientUDP_1.0.zip (распаковать в каталог arduino-0016\hradware\libraries).

Итак, что меняется при переходе с TCP на UDP? Грубо говоря – почти всё.

Начнем с того, что получать данные из соединения TCP можно побайтно (или блоками - нововведение Arduino 0016), в то время как в UDP - только пакетами:



Когда сокет находится в режиме UDP ( Sn_MR_UDP ), в буфере Wiznet вместе с данными сохраняется и заголовок пакета, который тоже надо уметь читать и правильно обрабатывать.

Работа с Ethernet в Arduino целиком построена на библиотеках socket.c и w5100.c, являющихся уровнями работы с сокетами и чипом соответственно. И если при чтении сокета TCP надо вызывать recv, то при чтении UDP - уже recvfrom.

Тут кроется еще одна неприятность, о которой надо помнить: длина буфера len, передаваемая в recvfrom, с длиной принятого пакета (получаемого из заголовка, см. выше) не сравнивается (вносить изменения в socket.c я пока не решился).

Интерфейс стандартного Client выглядит так:

 
#include "Print.h"

class Client : public Print {
private:
static uint16_t _srcport;
uint8_t _sock;
uint8_t *_ip;
uint16_t _port;
public:
Client(uint8_t);
Client(uint8_t *, uint16_t);
uint8_t status();
uint8_t connect();
virtual void write(uint8_t);
virtual void write(const char *str);
virtual void write(const uint8_t *buf, size_t size);
int available();
int read();
void flush();
void stop();
uint8_t connected();
uint8_t operator==(int);
uint8_t operator!=(int);
operator bool();
friend class Server;
};




UDP - протокол без фазы установления соединения, поэтому часть функциональности connect выкидывается, а имя заменяется на open. Опциональный аргумент будет указывать нашему классу, как выбирать source-порт - на основе глобальной автоинкрементирующейся переменной (_srcport, объявлена в библиотеке), либо явно. В последнем случае, приложение полностью ответственно за то, чтобы у разных сокетов не случилось двух одинаковых source-портов.

Функция connected тоже смысла не имеет, но можно заменить opened.

Функция stop, закрывающая соединение, остается, но тоже упрощается.

Уходят в небытие операторы ==, !=, bool() - потому что в языке Processing аналогичного класса нет, равно как и нет объекта-сервера, который возвращает этот объект при подключении очередного Client-а (см. примеры в стандартной библиотеке Ethernet, чтобы понять, о чем речь).

Прототипы функций-членов записи write являются виртуальными и наследуются от класса Print. Соответственно, все операторы Print работают в конечном итоге через них, абстрагируясь таким образом от типа устройства вывода. В Arduino 0016 определили целых три варианта write:

1. вывод байта: virtual void write(uint8_t) = 0;
2. вывод строки: virtual void write(const char *str);
3. вывод буфера: virtual void write(const uint8_t *buffer, size_t size);

Первый метод надо перекрывать обязательно - он абстрактный; два остальных работают через него.

Выше я уже упоминал, что в UDP бесполезны байтовые операции ввода-вывода, но для совместимости, оставим на месте однобайтовый write - это будет посылка пакета из одного байта. Это надо обязательно учитывать при использовании print: печать byte и char будет порождать однобайтовые пакеты, println будет генерировать минимум два пакета, в каждом по одному символу '\r' и '\n'; та же ахинея будет происходить с распечатыванием целых и вещественных чисел - каждый символ будет передан отдельным пакетом.

Короче, если хотите сохранять ясность, используйте методы write напрямую, тем более, что они вполне публичные (public).

Процедуры чтения в классе Print нет, так что смело пишем свой собственный read.

Неизменной, пожалуй, останутся available, сообщающая размер приемного буфера, и status, читающая регистр состояния сокета (число состояний сокращается до двух: SOCK_CLOSED и SOCK_UDP).

Самая противная из всех - flush, которая очищает приемный буфер. Раньше она дергала однобайтовый read, пока available возвращала ненулевое значение. Теперь при вызове read надо забрать весь пакет целиком, не зная даже заранее его размер. Мое мнение – операцию сброса входного буфера надо выносить прямиком в socket.c, именно там ей и место. Поэтому, также исключаем ее из интерфейса.

Вот результат:

 
#include "Print.h"

class ClientUDP : public Print {
private:
static uint16_t _srcport;
uint8_t _sock;
uint8_t *_ip;
uint16_t _port;
public:
ClientUDP(uint8_t);
ClientUDP(uint8_t *, uint16_t);
uint8_t open(uint16_t);

uint8_t status();
virtual void write(uint8_t);
virtual void write(const uint8_t *, size_t);
uint16_t read(uint8_t *, uint16_t *, uint8_t *, uint16_t);
int available();
void stop();
uint8_t opened();
};



Если придираться, то, строго говоря, это не совсем Client, потому что сервер ему не нужен. Да и сам смысл этого класса нивелируется, если бы библиотека socket.c была доступна скетчам напрямую. Однако, на этом примере я наглядно демонстрирую, что при наличи исходного кода, здравой логики и желания, довольно просто можно получать необходимые вещи. В следующих статьях я обязательно покажу, как воспользоваться этой библиотекой.

23.06.2009

Измеряем температуру ATmega328P

Изучая последнюю версию документации на ATmega328 - даташит №8161 для предыдущей статьи, наткнулся на интересное упоминание о встроенном температурном датчике:

21.8 Temperature Measurement

The temperature measurement is based on an on-chip temperature sensor that is coupled to a single ended ADC8 channel. (...) The internal 1.1V voltage reference must also be selected for the ADC voltage reference source in the temperature sensor measurement.

When the temperature sensor is enabled, the ADC converter can be used in single conversion mode to measure the voltage over the temperature sensor. The measured voltage has a linear relationship to the temperature (...) is approximately 1 mV/°C and the accuracy of the temperature measurement is +/- 10°C.


То есть, получается, что в ATmega328P/168P есть некий восьмой канал ADC, на котором висит температурный сенсор. Правда, крайне неточный: плюс-минус целых 10°C.

Градуировка:
  • 242 mv = -45°C
  • 314 mV = +25°C
  • 380 mV = +85°C

Ради интереса набросал скетч:

uint16_t temperature = 0;

void setup(){
Serial.begin(57600);
analogReference(INTERNAL);
temperature = analogRead(8);
}

void loop(){
temperature = analogRead(8);
Serial.print(temperature);
Serial.println(" temp");
delay(1000);
}



(нам понадобятся модификации wiring_analog.c из предыдущей статьи, чтобы читать ADC8).

Опытным путем выяснил, что если заливать его в старые ATmega без температурного сенсора, на выходе будем практически стабильно получать константу. Однако, мое удивление удвоилось, когда в ATmega328P-PU он показал... 360. При величине опорного напряжения в 1.1 В разрядность будет равна 1,07 мВ (1.1/1024), следовательно получается 385,2 мВ: чуть больше максимума по документации.

Еще раз перепроверив документацию, скетч и даже ядро Arduino, я пошел за феном и погрел работающий МК: действительно, реакция на изменение температуры наблюдалась весьма однозначная. Действительно, температурный сенсор, блин.

Даже с поправкой на погрешность, имеем довольно солидное смещение. Правда, в том же даташите прямо говорят о разбросе показаний в зависимости от экземпляра МК и необходимости калибровки с записью вычисленного смещения в EEPROM. Ради интереса вставил другой ATmega328P - увидел значение 350.

В результате экспериментов выяснилось, что процессоры ATmega168-20PU/MU такого сенсора не имеют (из ADC8 читается 1023), равно как и ATmega168V (читается 217). К сожалению, ATmega168P под рукой не оказалось.

Можно ли использовать температурный датчик ATmega328P? Мне кажется, ценность такого "термометра" крайне низкая - разве что фиксировать относительное повышение температуры кристалла - например, чтобы избегать перегрузок. Даже самый простой аналоговый датчик мне кажется предпочтительнее, постараюсь рассказать про это подробнее в следующих статьях.

21.06.2009

Как измерить Vcc?

Недавно наткнулся на интересную статью о том, как измерить собственное напряжение питания в Arduino.

Как известно, ATmega может питаться от широкого диапазона напряжений, поэтому может оставаться "в строю" даже в случае постепенного разряда батареи, выражающегося в уменьшении напряжения. Ситуация эта в робототехнике более чем стандартная, за примером далеко ходить не надо: возьмем проект Voyager. Это робот-космический аппарат, запущенный за пределы солнечной системы и потому лишенный способности питаться от солнечных батарей. На него установлены радиоизотопные термические генераторы (ядерные батарейки), которые на момент старта выдавали 30В / 470Вт, но каждый год они теряют 0.78% своей мощности. Соответственно, в настоящий момент осталось около 60% от первоначальной, и приходится включать исследовательские подсистемы поочередно, чтобы не перегрузить генераторы.

В Arduino нельзя просто взять и подключить Vcc к аналоговому пину напрямую - по умолчанию AREF связан с Vcc и вы всегда будете получать максимальное значение 1023, от какого бы напряжения вы не питались. Спасает подключение к AREF источника напряжения с заранее известным, стабильным напряжением, но это - лишний элемент в схеме.

Еще можно соединить Vcc с AREF через диод: падение напряжение на диоде заранее известно, поэтому вычислить Vcc не составит труда. Однако, при такой схеме через диод постоянно протекает ток, сокращая жизнь батареи, что тоже не очень удачно.

Как же выкрутиться из положения, не добавляя новых элементов в схему и не сокращая время работы батареи? Оказывается, выход - есть, и поможет нам внутренний источник опорного напряжения 1.1В в ATmega (в документации он проходит как bandgap reference), которое не зависит от Vcc. Получается такая формула:

V_BAT=(1.1*1024)/analogRead(14);

где V_BAT - это напряжение Vcc в вольтах, а analogRead(14) - результат прямого чтения АЦП из канала 14.

В Arduino разрешены чтения только из каналов 0-7 (не удивляйтесь, если нашли на своем Diecimila или Duemilanove только 0-5, возьмите, к примеру, Seeeduino ;)

Чтобы появилась возможность отправлять данные в другие каналы, в том числе в 14, надо изменить маску в библиотеке-ядре Arduino. Для этого откройте файл hardware\cores\arduino\wiring_analog.c и найдите там строку:

ADMUX = (analog_reference << 6) | (pin & 0x07);

замените ее на:

ADMUX = (analog_reference << 6) | (pin & 0x0f);

После этого можно написать вот такой скетч:
 
uint16_t raw_bandgap = 0; // значение внутреннего bandgap
float volt_battery = 0.0;

void setup(){
Serial.begin(57600);
}

void loop(){
// Чтение напряжения батареи
analogReference(DEFAULT); // использовать Vcc как AREF
raw_bandgap = analogRead(14); // холостое чтение после смены AREF (см. 23.5.2 в руководстве)
raw_bandgap = analogRead(14); // измерить значение внутреннего bandgap
volt_battery = (1.1 * 1024) / raw_bandgap; // вычислить Vcc
Serial.print(volt_battery);
Serial.println(" v_bat");
delay(1000);
}



Тем, кто хочет знать напряжение на батарее, стоит иметь ввиду, что при наличии регулятора (типа L7805CV), мы будем измерять напряжение Vout регулятора, а в случае наличия входного диода - надо учесть напряжение, которое на нем падает.

Вот так выглядят вычисляемые значения в Seeeduino, в момент переключения с питания 5В на 3,3В:



... при этом мой вольтметр показывает меньшие значения. Но, тем не менее - скетч работает ;)

UPD: Нашел примеры, в которых вместо константы 1.1 используется 1.05. Результат получается гораздо ближе к показаниям вольтметра, ищу теоретическую базу, способную объяснить этот факт...

18.06.2009

Кухонный таймер на Arduino (2)

Часть II

(продолжение, см. начало в Части I)

Пришло время сосредоточиться на программной части.

Мне пришло в голову вполне тривиальное деление по функциям:
  1. поддержка отсчета времени
  2. индикация
  3. сигнализация
  4. опрос и чтение кнопок

1.Ура! С поддержкой времени нам повезло: в некотором смысле, поддерживать отсчет времени Arduino умеет самостоятельно.

В его ядре определена процедура прерывания по таймеру 0, которая прозрачно для нашего скетча инкрементирует счетчик "тиков". Пользовательская программа в любой момент может получить значение этого счетчика в виде количества миллисекунд с момента запуска через вызов millis(). Остается только зафиксировать в переменной начальное значение, а потом ожидать конечное, которое вычисляется путем прибавления задержки к зафиксированному начальному.

Правда, есть маленькая неприятность: как и любой другой счетчик, он подвержен переполнению. На наше счастье, это случится через 49 дней после старта скетча, поэтому попробуем пока этим пренебречь ;)

2. Алгоритм индикации можно с небольшими изменениями позаимствовать из моей статьи про Shield с семисегментным индикатором на шине I2C.

3. Включать и выключать пищалку надо записью HIGH или LOW в соответствующий порт. Так что вся премудрость сводится к вызову digitalWrite.

4. Чуть больше внимания надо уделить кнопкам. Во-первых, оставлять висеть в воздухе цифровые входы нежелательно, иначе мы получим непредсказуемые результаты. Обычно, в таких случаях вывод притягивают к Vcc, чтобы в разомкнутом состоянии поддерживать на входе МК логическую единицу. Во-вторых, надо бороться с дребезгом.

На схеме в первой части нет никаких подтягивающих резисторов. Не верьте своим глазам, на самом деле они есть! Просто внутри ATmega. Ну зачем ставить внешние, если можно использовать готовые внутренние? Вот так по datasheet-у выглядит логическая схема универсального Pin-а ATmega:



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

pinMode(PINNO,INPUT);
digitalWrite(PINNO,HIGH);


Функцию digitalWrite надо вызывать строго после перевода пина режим INPUT, в противном случае у нее будет другой смысл.

Теперь - самое сложное - надо спроектировать интерфейс с человеком. Всем известно, что инструкции не читают, предпочитая полагаться на интуицию и законы подобия. Даже от сверхпедантичного пользователя, вдумчиво штудирующего руководства по эксплуатации нового телевизора или холодильника, нельзя ожидать аналогичного подхода к таймеру с двумя кнопками и четырьмя цифрами - чего там можно напридумывать-то?!

Действительно, придумывать особо нечего. Но чтобы описать и запрограммировать это самое ничего, воспользуемся концепцией машины состояний. Для тех, кто хочет подробно разобраться, рекомендую статью в русской википедии. В двух словах: считается, что система может находиться в одном из нескольких состояний. Переход в другое состояние осуществляется по наступлению некоего события (нажали или отпустили кнопку, достигнуто время срабатывания таймера, и т.д). Таким образом, определив все состояния системы и все события, надо написать маленькие фрагменты алгоритма: как реагировать на такой-то сигнал в таком-то состоянии. После обработки события можно изменить состояние системы или оставить ее в текущем.

Вот как это будет выглядеть на практике, в нашем случае:



1. Исходное - после включения питания. Таймер бесконечно долго ждет, когда ему определят время срабатывания. В этом состоянии дисплей мигает целиком.

2. Выбор времени - нажимая кнопки, пользователь устанавливает желаемое время до срабатывания, в минутах. Чтобы войти в это состояние, достаточно нажать одну из кнопок. Одна кнопка увеличивает время, вторая - уменьшает. И поскольку клавиши Enter не предусмотрено, будем считать, что вы закончили ввод, если кнопки не трогали 5 секунд подряд и установлено время, отличное от нуля. Пока происходит установка времени, дисплей по-прежнему мигает целиком, отображая текущее установленное число минут.

3. Отсчет времени. В этом состоянии дисплей не мигает, за исключением точек по центру - раз в секунду. Естественно, отображается время, оставшееся до срабатывания - чтобы пользователь всегда знал, сколько еще осталось ждать. Для простоты, кнопки в этом режиме - игнорируем.

4. Сигнализация. Включается пьезоизлучатель - желательно прерывисто, так его лучше слышно (чтобы человеческое ухо не адаптировалось к раздражителю). Дисплей вновь мигает (как в исходном состоянии), но при этом показывает время, на которое был установлен (как бы напоминая "а ставили-то меня вот на сколько минут, вы не забыли?").

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

Перейдем к рассмотрению скетча. Основной цикл будет выглядеть так:


void loop() {
indicate();
update_time();
update_alarm();
update_buttons();
}

Чтобы обеспечить стабильное отображение дисплея, каждая процедура должна работать фиксированное количество времени ( в таком простом устройстве даже не потребовалось изощряться - все именно так ).

indicate() - поочередно отображает все четыре цифры на дисплее (включая точки), показывая нам каждую по 1 мс (таким образом, в сумме выполняясь не менее 4 мс).

update_time() - отвечает за правильные параметры отображения дисплея ( обновляет высвечиваемую величину, управляет миганием ) и дополнительно следит, когда систему надо переводить в состояние "Сигнализация".

update_alarm() - управляет пьезоизлучателем, в том числе его прерывистой трелью.

update_buttons() - опрашивает кнопки и, в зависимости от состояния системы, предпринимает соответствующие действия.

Состояние системы хранится в глобальной переменной state, имеющий характерный для C тип enum:


typedef enum { E_WAIT=0, E_SETUP, E_STARTED, E_ALARMED} fsm_type;
fsm_type state = E_WAIT;


На самом деле, в state хранится целое число от 0 до 3, но применение enum позволяет безо всяких дополнительных #define придать этим значениям смысл, помогая в конечном итоге разбираться с текстом программы. Ведь куда проще смотрится if (state != E_STARTED), чем if (state!=2).

По мере необходимости, все функции в скетче сверяются со значением state: например, update_alarm() не будет пытаться включать пьезоизлучатель, если состояние state не равно E_ALARMED, а update_time() переведет систему в состояние E_ALARMED только из состояния E_STARTED и только по истечению установленного времени.

Кстати, я забыл упомянуть по борьбу с дребезгом. Так вот - при такой идеологии построения скетча, этой проблемы - нет. Ведь в чем суть дребезга? В момент нажатия кнопки случается переходной процесс, когда контакт еще не до конца нажат. В итоге происходит серия последовательных разрывов-замыканий с высокой скоростью (дребезг). Чтобы программа не реагировала на быстрые смены состояний кнопки, вводятся задержки (иногда их называют умным словом "гистерезис"). Однако, в моем случае на каждое однократное чтение кнопок в процедуре update_buttons() приходится один вызов indicate(), который и обеспечивает ту самую необходимую задержку.

Периодически по тексту можно встретить в операторах if такую конструкцию:

if (millis() % 1000UL > 500)

или

(millis() % 1000UL > 500) ? HIGH:LOW

Это - простой способ определить фазу в мигании или звуковом сигнале. Берем текущее время в миллисекундах, выделяем остаток от деления на 1000 и получаем таким образом число от 0 до 999 - это число миллисекунд в текущем времени. Далее сравниваем с границей ( 500 ) и решаем - включить или выключить наше устройство. Увеличивая или уменьшая остаток от деления, можно ускорить или замедлить мигание, а изменяя соотношение с границей - скважность, т.е. преобладание одного состояния над другим.

Вот полный текст скетча для скачивания: cook_timer_1.0.zip.

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

В последней части я добавлю схему внешнего питания, спаяю и упакую таймер в корпус, а затем буду бороться со злыми духами аппаратной части (не путать с багами в скетче ;). Так что, окончание обязательно следует.

16.06.2009

Наклейка Arduino на ATmega

Эта идея в какой-то степени перекликается с PAPERduino и BBAC и сведена к абсолютному минимуму. По размеру корпуса PDIP-28 делается стикер:


... и приклеивается на корпус ATmega каким-либо образом. Ура, теперь запутаться невозможно:



Оригинальный пост автора этой идеи, там же можно взять стикер в виде PDF, EPS, SVG... ;)

13.06.2009

Seeeduino

Название: Seeeduino
Процессор: ATmega168
Тактовая частота: 16МГц
Совместимость с Shield-платами: есть
Совместимость с Arduino IDE: есть
Страница проекта: http://www.seeedstudio.com/blog/?page_id=110



В каком-то смысле это - идеальный Arduino. Изначально просматривая спецификации платы, я не обратил внимание и на половину возможностей - зато теперь могу с уверенностью утверждать, что Альберт Миао потрудился на славу. 

1. Миниатюризация

Избавившись от МК в PDIP-корпусе, автор решил максимально опустить элементы на плате - чтобы не было никаких проблем с установкой Shield-плат. Даже SMD-резонатор выбран миниатюрный, не говоря уже о разъемах и кнопке.

2. Поддержка 3.3В

При помощи одного из переключателей на плате, можно выбрать напряжение питания - +5В или +3,3В. Удобно, если надо работать с какой-то трехвольтовой периферией. Также актуально и для экономии энергии - при таком напряжении ток потребления Atmega понижается в два раза. Думаю, у кого-то уже созрел вопрос "а получится ли у ATmega при понижении до 3,3В по-прежнему работать на тактовой 16 МГц"? Ответ положительный, авторы заявляют, что тестируют каждый изготавливаемый экземпляр на предмет работы от 3,3В.

3. Дополнительный ряд для цифровых входов с шагом 0.1"

Если вы пробовали сделать Shield-плату на стандартной макетке, то практически наверняка споткнулись о нестандартное расположение гребенки цифровых выводов в оригинальном Arduino, которая зачем-то "ломает" стандартную сетку 2,54 мм (или 0.1 дюйма).

В Seeeduino есть ряд дополнительных отверстий - туда можно запаять либо дублирующие гнезда PBS, либо вилку PLS - на ваше усмотрение:



4. Кнопка сброса

Мало того, что она миниатюрная (как на LilyPad), так и еще и вынесена поближе к краю платы. То есть, даже если сверху пристыкована Shield-плата, кнопка сброса остается доступной. Рядом "прописался" стандартный светодиод PWR и дополнительный - RST. Ощущать визуально, что плата сбросилась - наверное, излишество, но приятное ;)



5. Место для колодки I2C

К аналоговым выходам Arduino подключаются аналоговые сенсоры и устройства I2C. Чтобы сделать их подключение проще, аналоговые пины 0-3, питание и земля выведены на отдельную колодку (точнее, под эту колодку оставлено место). Полная свобода выбора - можно впаять туда не только вертикальную, но и угловую вилку/колодку, имея таким образом и подключенный Shield, и подключенную плату сенсоров. Пример установки прямой колодки:



6. Дополнительные аналоговые входы

Не секрет, что у ATmega в TQFP-корпусе больше ножек, чем у аналога в PDIP-28. Чтобы добро не пропадало зря ;) инженеры ATMEL повесили на них два дополнительных входа АЦП. На Seeeduino они выведены на колодку питания, предоставляя в наше распоряжение два лишних аналоговых входа. Как знать, может невзначай и пригодиться... Жаль, что они не универсальные - использовать их в качестве выходов - нельзя.



7. Место для разъема UART

На последних Arduino стали ставить разъем X3, который можно использовать для программирования через bit-bang режим USB-чипа FT232RL. На Seeduino разведены вообще все сигналы, считайте, что у вас есть полноценный переходник USB-TTL/UART. Чтобы осуществлять через него самопрограммирование, надо установить вилку и соединить контакты, например, при помощи одногнездового кабеля:


Приятная мелочь: дырочки подсадочных мест под все дополнительные разъемы на этой плате не залиты припоем, как на оригинальных Arduino, а следовательно - перед установкой не надо возиться с их прочисткой (не забывайте, что в Италии все далается по бессвинцовой технологии).

Пустое место под электрлит C8: авторы утверждают, что его наличие будет благотворно влиять на режим работы FT232RL, когда с ее внутреннего стабилизатора снимается 3,3В, позволяя увеличить ток до 100 мА. В более поздних версиях Seeeduino на этом месте располагается дополнительный вход для подачи стабилизированного питания.

Резюме: на фоне многочисленных плюсов, минусов гораздо меньше - нельзя поменять процессор, миниатюрные разъемы USB и питания могут создать некоторые проблемы в процессе поиска ответных частей. Однако, если нужна универсальность - лучше не придумать, тем более, что цена будет ниже, чем у оригинального Arduino.

10.06.2009

Кухонный таймер на Arduino (1)

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

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

Итак, прежде всего - подберем компоненты:
  • дисплей для отображения времени
  • пара кнопок для управления
  • зуммер для привлечения внимания хозяйки
  • корпус, в который все это поместится
  • источник питания

Ну и конечно же, Arduino.

Наиболее хорошо для этой цели подходит Arduino Pro: основные разъемы не установлены, можно поставить ровно столько, сколько надо (а также именно то, что требуется - вилки или колодки, нужный разъем питания):



За неимением такого замечательного девайса, придется собирать на макетке. Как обычно - ATmega8-16PU, кварц 16 МГц + 2 конденсатора 22пФ, притянуть сброс через 10К к Vcc, схема птиания на L7805. Поскольку я постоянно собираю Arduino / Freeduino, все это у меня имеется в избыточном количестве.

С эстетической точки зрения, лучше устанавливать часовой дисплей с "двоеточием" по центру, которое мигает раз в секунду при отсчете времени. В минимальном варианте достаточно просто двух цифр с числом оставшихся минут. Поглядывать на таймер надо издалека, поэтому размер и яркость цифр имеют значение - лучше всего подойдет светодиодный дисплей. Удобно использовать индикатор, у которого одноименные выводы уже соединены - все равно мы будем отображать методом мультепликсирования. У меня был красный семисегментный CA56-21EWA (Kingbright), с общим анодом:



В качестве зуммера можно использовать пьезоизлучатель. Часто на материнских платах можно встретить такой вариант:



Это пятивольтовый KPX-G1205B (Kepo Electronics).

В него встроен генератор, позволяющий издавать звуковое колебание заданной частоты простой подачей разности потенциалов на его выводы. Очень удобно, да и ток всего 30 мА (напоминаю, что максимальный ток через выход ATmega - 40 мА). Если не закрывать отверстие, пищит достаточно громко - простой бесчеловечный эксперимент показал, что хозяйка слышит его сквозь какафонию из льющей из водопроводного крана воды и орущего телевизора, причем еще и из другой комнаты. Те, кому повезло меньше, могут использовать схему с транзистором ;)

Кнопки могут быть как тактовые, так и с креплением на корпус. Идеально подойдут влагозащищенные, но это влетит в копеечку. Я выбрал компромиссные SWT-9 с разноцветными колпачками:





Для включения питания идеально подходит классический тумблер MTS-102:



Среди корпусов для РЭА, которые я мог заказать у поставщиков, мне приглянулись черные пластиковые, на защелках. Брать размер 70 х 40 мм я побоялся (как потом выяснилось, абсолютно верно), но вот 90 х 56 мм уже выглядело для такой схемы вполне комфортно, тем более, что в высоту он 30 мм, а значит - дисплей с кнопками и контроллер можно разместить на двух разных платах, соединив их при помощи PLS/PBS:



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

Настало время рисовать схему в Eagle, но прежде подсчитаем, сколько потребуется портов ввода/вывода.

Семисегментная шина требует 7+1=8 выходов: восьмой в данном случае управляет двумя точками по центру. Один сигнал на каждое знакоместо: +4, итого уже 12. Один выход потребуется для пьезоизлучателя, два - для подключения кнопок. В сумме надо 15, а у Arduino всего 13 цифровых выходов, из которых категорически не хотелось бы занимать 0 и 1 - там UART, через которую заливается скетч.

На наше счастье, аналоговые выходы можно перепрограммировать в цифровые оператором DigitalMode. Аналоговый вход 0 будет соответствовать 14-му цифровому, а 5й - 19-му. АЦП нашей схеме не требуется, так что смело можно переводить все шесть выходов в цифровой режим.

Для начала соберем такую схему на мактной плате:



Продолжение следует...

08.06.2009

USBasp

Когда писал статью про Metaboard, обратил внимание на программатор USBasp, по набору деталей и возможностям сильно напоминающий AVR910:



Ради интереса решил повторить на макетке:



По моим впечатлениям, обмен идет гораздо быстрее, чем с AVR910 от prottoss-а.

По причине прямой поддержки со стороны avrdude ( >= 5.2.0), программатор можно использовать с Arduino IDE. Для этого надо дописать несколько строк в файл arduino-0016\hardware\programmers.txt:
usbasp.name=USBasp
usbasp.communication=usb
usbasp.protocol=usbasp

После этого появляется соответствующее меню в ArduinoIDE:



Работает, что характерно ;)

Firmware с исходниками и чертежи печатных плат можно найти на сайте автора (Thomas Fischl).

Традиционные минусы дешевого программатора:
  • не умеет питаться от программируемой схемы
  • нет предохранителя на линии питания USB (ну, это легко добавить)
Как обычно, выложил комплект деталей на "молоток".

05.06.2009

ArduinoIDE 0016

Вчера вышла новая ArduinoIDE версии 0016. Можно качать.

Что новенького?

Новые примеры в разделе Communications (с соответствующими частями для Processing и MAX/MSP) by Tom Igoe.
  • SerialCallResponse - посылает через Serial-порт данные от сенсоров + код для отрисовки на PC
  • SerialCallResponseASCII - тоже самое, но данные передаются в десятичном виде через запятую.

Это для тех, кто пишет обработку поступающих от Arduino данных на Processing.

Поддержка Arduino Pro, Pro Mini 3.3V/8 MHz, а также LilyPad Arduino на ATmega328.

Все прогрессивное человечество уже перешло на ATmega328! А вы? ;) Кстати, одно время в DIP-корпусе этот МК отсутствовал на Маузере и Дижикее - не понимаю, неужели раскупили любители Arduino?...

Добавлены члены write(str) и write(str,buf) в классы Print, Serial и Ethernet Client/Server.

Ну наконец-то! Я испытал это неудобство на собственной "шкуре", когда модифицировал Ethernet Library под UDP. Действительно, базовый родительский класс для вывода - Print, поддерживающий богатое разнообразие входных форматов данных через функцию-член print - число, строка, массив, разные длины целых, разные системы счисления. Но в конечном-то итоге вызывается виртуальный write - именно его и должна перекрывать библиотека-потомок, реализующая вывод данных в определенное устройство. При этом write всегда принимал один-единственный символ - то есть, вывод шел побайтный. Приемлемо в LiquidCrystal, но в Ethernet эта концепция вписалась только по причине исключительной поддержки TCP. Для UDP мне пришлось заводить write_block, но теперь этого делать не надо! Bravissimo!

Улучшено подключение и отключение в классах Ethernet, что позволило сократить число неуспешных соединений и длительных таймаутов.

Например, в Client.connect(): более аккуратная работа с номером сокета, проверка состояния полузакрытия сокета SOCK_FIN_WAIT, дополнительная задержка 1 мс между окончанием установления соединения и проверкой, что оно еще живое... Я-то думал, что там уже нечего вычищать - но нет, работа по повышению стабильности продолжается!

Оптимизировано переполнение timer0, который используется в функциях millis() и micros()

Код стал чуть короче, в сумме это дало экономию 80 байт! Поскольку этот фрагмент кода включается в каждый скетч, "похудение" отразится на результатах компиляции всех скетчей. И - как знать, может кто-то не может втиснуться в размер памяти программ своей ATmega8/168 и не хватает ему для счастья 30-40 байт... Вы же знаете, как на C быстро память расходуется... ;)

Пофикшен макрос bits(), работавший не более, чем для 15 бит.

Бывает.

Добавлена константа ARDUINO с номером версии.

Я считаю - давно пора. Как бы не старалось сообщество, все равно от версии к версии происходят порой жесткие изменения (pulsein в версии 0013), которые хотелось бы контроллировать при написании кода.

Посортировали пункты в меню Tools > Boards.

Да они же целую кучу плат добавили (см. выше) - вот наверное оно распухло... Ну-ка, взглянем:


Ну просто красота. Нет предела совершенству!

Добавили "Copy to HTML".

Вот это - по-настоящему круто! Раньше для публикации я пользовался разными приблудами, в которые сначала заряжался кусок кода, отформатированный под форум Arduino. Теперь можно напрямую! Выглядит вот так:

/*
* Blink
*
* The basic Arduino example. Turns on an LED on for one second,
* then off for one second, and so on... We use pin 13 because,
* depending on your Arduino board, it has either a built-in LED
* or a built-in resistor so that you need only an LED.
*
* http://www.arduino.cc/en/Tutorial/Blink
*/

int ledPin = 13; // LED connected to digital pin 13

void setup() // run once, when the sketch starts
{
pinMode(ledPin, OUTPUT); // sets the digital pin as output
}

void loop() // run over and over again
{
digitalWrite(ledPin, HIGH); // sets the LED on
delay(1000); // waits for a second
digitalWrite(ledPin, LOW); // sets the LED off
delay(1000); // waits for a second
}

(напрямую вставил в блог из ArduinoIDE, ничего не менял).

Пофикшено (вроде бы) "Couldn't determine program size".

У меня такое бывало, периодически. С чем связано - так и не смог установить со 100%-ой точностью.

Автоматическая генерация зависимостей для Makefile.

Этого тоже недоставало, когда шла работа над библиотеками... Когда один скетч - не актуально, но стоит добавить файлов - и Arduino могла просто не компильнуть нужный исходник. Приходилось сносить объектный файл вручную.

03.06.2009

Metaboard

Название: Metaboard
Процессор: ATmega8/88/168/328P
Тактовая частота: 16МГц
Совместимость с Shield-платами: нет
Совместимость с Arduino IDE: есть, неполная
Страница проекта: http://metalab.at/wiki/Metaboard



Metaboard - Arduino-совместимая плата с USB, но без USB-чипа. USB реализовано в микропрограмме ATmega, код основан на драйвере V-USB от Objective Development ( я касался этого вопроса, когда писал про AVR910 на USB ). Принципиальная схема:



Специально для этой платы был разработан bootloader, который принимает по USB скомпилированный скетч от Arduino IDE, а потом запускает его. Получается весьма симпатичное и экстремально дешевое решение для Arduino с USB, которое можно собрать своими руками. Вот что у меня получилось на беспаечной макетной плате за четверть часа:



На странице проекта можно скачать схему и чертеж односторонней печатной платы в формте Eagle: обратите внимание, что практически все простарнство между колодками входов и выходов Arduino пустое - там насверлили дырок и устроили макетное поле. Кстати, не ищите ICSP-разъем - автор предлагает желающим припаять его на макетное поле, если уж им так его недостает ;)

Однако, не спешите радоваться :( В этой жизни за все приходится расплачиваться.

1. Размер bootloader-а для ATmega8 вырос до 2К. Оригинальный занимал 1К и оставлял для скетча 7168 байт, теперь же будет доступно 6144. Это все еще весьма много, но будет сильно ограничивать при подключении библиотек. Так что надо брать сразу ATmega168 - тем более, что в этом случае размер доступной памяти не изменится. Правда, придется забыть и про фокус с +1К к свободной памяти, про который я как-то уже писал.

2. Аппаратный последовательный порт, обитающий на пинах Rx/Tx ATmega, остается "как бы" незадействованным. То есть, использовать-то его можно, но, в отличие от Diecimila / Duemilanova, он никак не связан с USB. Код Objective Development работает по прерывыаниям и обязательным условием является заведение сигнала D- на пин ATmega, поддерживающий прерывания. Таким образом, вместо обычных PD0 и PD1 для загрузки скетчей используются PD2 и PD4.

3. Автосброса - нет. Обычно, Arduino IDE "опускает" перед загрузкой скетча DTR, чтобы сформировать сигнал сброса на МК. Для приложения (в данном случае - ArduinoIDE) это выглядит довольно просто - надо вызвать соответствующую функцию Win32 API. После этого управление получит драйвер FT232RL, который сформирует соответствующий служебный пакет данных и передаст его по USB, далее его примет FT232 и переведет свою специально выделенную ножку "DTR" в состояние логического нуля, что, в свою очередь, вызовет сброс на МК.

Но в Metaboard нет отдельного внешнего чипа, который играл бы роль внешней цепи сброса. Сброс возможен только по нажатию кнопки на плате. По сбросу запускается bootloader, который опрашивает PD7 ( digital 7 по обозначениям Arduino ). Если там логический ноль, то bootloader начинает ожидать ( бесконечно долго! ) заливание скетча, а по окончании передает ему управление. В противном случае управление сразу передается по нулевому адресу.

На первый взгляд кажется, что вы теряете digital 7, но его заземление требуется только на время заливки скетча ( вы еще не успели забыть, что здесь по USB нельзя общаться через Hardware Serial? ). В любом случае, придется стараться собирать схему так, чтобы digital 7 не использовался на вход и был по крайней мере притянут к Vcc.

Кстати, чтобы код Objective Development работал, надо обязательно ставить кварц, никаких керамических или внутренних RC-цепочек использовать нельзя.

Для тех, у кого еще не успело пропасть желание собрать из комплекта деталей и макетки практически полноценный Arduino, для достижения этой цели надо еще чуть-чуть поработать. Разберем на примере ATmega8.

Скачайте и распакуйте bootloader: http://www.obdev.at/products/vusb/usbasploader.html.

Нам потребуется hex-файл firmware\hexfiles\mega8_16mhz.hex, поместим его в дерево файлов Arduino IDE, в каталог arduino-0015\hardware\bootloaders\atmega8\.

Поскольку этот bootloader работает не по протоколу stk500, то придется заводить отдельную секцию в boards.txt:

meta8.name=Metaboard w/ ATmega8

meta8.upload.protocol=usbasp
meta8.upload.maximum_size=6144
meta8.upload.speed=19200

meta8.bootloader.low_fuses=0xdf
meta8.bootloader.high_fuses=0xc8
meta8.bootloader.path=atmega8
meta8.bootloader.file=mega8_16mhz.hex
meta8.bootloader.unlock_bits=0x3F
meta8.bootloader.lock_bits=0x0F

meta8.build.mcu=atmega8
meta8.build.f_cpu=16000000L
meta8.build.core=arduino


Как видите, кое-что изменилось: название протокола - usbasp, уменьшился на 1К upload.maximum_size, bootloader.file указывает на скачанный на предыдущем шаге бутлоадер, а bootloader.high-fuse задает размер бутлоадера, равный . Теперь можно перестартовать Arduino IDE, выбрать в списке плат "Metaboard w/ ATmega8". Если МК не прошит, то теперь это можно сделать через стандартное Tools->Burn Bootloader->w/ Parallel Programmer ( разумеется, вилка ICSP должна быть установлена ;).

Теперь надо скачать драйвер USBasp, иначе устройство будет обнаружено, но использовать его не получится. Идем на страницу http://www.fischl.de/usbasp/, качаем и распаковываем самый последний архив. При подключении Metaboard, на запрос об обновлении драйверов, подсовываем каталог bin\win-driver\libusb_0.1.12.1.

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

UPD: совместимый с Metaboard вариант MKBoard R1 можно купить здесь (в виде набора деталей или в сборе). 

Резюме: самый дешевый способ собрать Arduino с USB самостоятельно. Особенно актуально, если скетчу не надо обмениваться с PC информацией через библиотеку Serial.

01.06.2009

Калькулятор fuse-битов

Случайно наткнулся на прикольный калькулятор fuse-битов, позволяет наглядно выбрать fuse-биты для устройств AVR - по смыслу. В какой-то степени даже может заменить datasheet.

Хотя, в datasheet все-таки тоже смотреть надо, мало ли что ;)