понедельник, 1 февраля 2010 г.

USB-программатор для контроллеров Atmel

Наконец-то собрал девайсину, нормальный человеческий программатор. До этого для прошивки использовался самый простейший вариант на четырёх резисторах работающий через LPT порт. Штука конечно была простая и нехитрая, но неудобная тем что это был LPT порт(почти R.I.P. в наше время) и не безопасная. Так я им однажды спалил одну из ножек atmega168 отвечающую за SPI, так что тот теперь не прошивается..
В связи с тем, назревшая необходимость стала толчком к делу. Нашёл нормальный и относительно недорогой по себестоимости вариант программатора USB-ASP. После довольно продолжительного шаманства в программе SprintLayout, рисунок был перенесён на печатную плату используя технологию лазерного утюга. Затем было лужение, паяние, потом танцы с бубном.... потом опять паяние....... потом опять танцы... и бац! заработало, прошивает!!!
По части дизайна я считаю у меня получилось очень даже ничего:
А вот в пайке я ламер, из-за этого и были танцы сначала.... не пропой. Я пожалел что не увеличил диаметр контактов для размещения DIP панельки, очень не легко было паять в этих местах.
Прошивка в программе avrdude проста и выполняется из консоли командой вроде этой:
$avrdude -v -c usbasp -p atmega644 -U flash:w:file.hex
Скорость выполнения составляет примерно 5 кбайт/сек, что тоже вполне неплохо. Бывают ошибки при попытке прошивки (пишет Expected Signature), причём судя по всему напрямую зависит от компьютера. Так на моём стационарном 6 из 10 попыток успешны, а на нетбуке все 10/10 оказались успешными.
Мой вариант разводки печатной платы в программе SprintLayout можно взять здесь (доработанный вариант).

вторник, 26 января 2010 г.

Библиотека для цифрового термометра DS18B20

Захотелось мне тут порезвиться с этой замечательной микросхемой. И так как я страдаю фигнёй манией писать библиотеки для разного вида подобных устройств, библиотека была написана! Писал долго, потому как было мало свободного времени. К тому же очень хотелось чтобы она поддерживала весь функционал доступный программисту. А функционал у неё неплохой. Микруха такая стоит порядка 150-300руб. (как повезёт), и имеет следующие основные возможности:
  • Работа по интерфейсу 1-wire, который требует всего одной линии для связи с МК;
  • Возможность питания от одной линии данных;
  • Уникальный 64-битный ID для каждого датчика;
  • Возможность развёртывать целые микросети таких датчиков связанных одной шиной;
  • Неплохая точность, ±0.5°C в диапазоне -10°C до 85°C;
  • Оповещение о выходе температуры из заданных пределов;
  • Совместимость с DS1822;
Варианты подключения
Может питаться как от внешнего источника, так и от линии данных. На следующих рисунках показаны варианты подключения (слева-направо) от внешнего источника, от шины данных.
 

Особенности библиотеки
Разработанная библиотека поддерживает все возможности данного цифрового термометра, и занимает в памяти МК от 254 до 918 байт, в зависимости от выбранных настроек конфигурации. В минимальной конфигурации поддерживается:
  • Измерение температуры (convert);
  • Чтение температуры (readTemp);
  • Выбор датчика по его уникальному коду (selectSensor);
Полный вариант конфигурации поддерживает к тому же:
  • Дополнительные функции (такие как recall, readScratchpad, copyScratchpad, writeScratchpad, и др.);
  • Автоматическая проверка контрольной суммы при приёме данных;
  • Поддержка датчиков питающихся паразитным током;
  • Поиск устройств на шине а также датчиков чья температура выходит из заданных пределов (searchROM, searchAlarm);
  • Дополнительные функции для обработки значения температуры, с целью дальнейшего вывода на дисплей (extractInt, extractFract);
Примеры использования
Простой пример демонстрирующий вывод текущего значения температуры на дисплей:
#include "wh1602b/wh1602b.h"
#include "ds18b20/ds18b20.h"
#include <avr/pgmspace.h>

void conf_my_lcd();
void loop();

int main(void)
{
  conf_my_lcd(); // настроить вывод на жк-дисплей
  // выбрать текущий датчик (0 т.к. единственный у меня)
  if (!ds18b20_selectSensor(0)) {
    wh1602b_putsP(PSTR("Ошибка на линии!"));
    for (;;)
      ;
  }
  for (;;)
    loop(); // цикл обработки
}

void loop()
{
  ds18b20_convert();  // попросить у датчика измерить температуру
  wh1602b_move(0,0);
  wh1602b_putsP(PSTR("t="));
  int16_t t = ds18b20_readTemp(); // прочитать значение
  if (t < 0) // вывести минус если отрицательно
    wh1602b_putc('-');
  wh1602b_puth(1, ds18b20_extractInt(t)); // вывести целую часть на дисплей
  wh1602b_putc('.');
  wh1602b_puth(1, ds18b20_extractFract(t, 1)); // вывести дробную часть на дисплей
  wh1602b_putsP(PSTR("\1C"));
}

// <...>
Вот что получилось:

Следующий пример демонстрирует вывод уникального кода микросхемы на дисплей:
#include "wh1602b/wh1602b.h"
#include "ds18b20/ds18b20.h"
#include <avr/pgmspace.h>

void conf_my_lcd();

int main(void)
{
  conf_my_lcd(); // настроить вывод на жк-дисплей
  uint8_t id[8];
  if (!ds18b20_readROM(id)) { // прочитать уникальный код датчика в id
    wh1602b_putsP(PSTR("Ошибка CRC!")); // если ошибка
    for (;;)
      ;
  }
  wh1602b_putsP(PSTR("ROM Code ="));
  wh1602b_move(1,0);
  uint8_t i;
  for (i = 0; i < 8; ++i)
    wh1602b_puth(2, id[i]);
  for (;;)
    ;
}

// <...>
И результат:

Следующий пример демонстрирует поиск устройств на шине:
#define F_CPU 20000000ULL /* частота мк, гц (для delay) */
#include <avr/pgmspace.h>
#include <util/delay.h>

#include "wh1602b/wh1602b.h"
#include "ds18b20/ds18b20.h"

void conf_my_lcd();
void loop();

int main(void)
{
  conf_my_lcd(); // настроить вывод на жк-дисплей
  for (;;)
    loop();  // цикл обработки
}

void loop()
{
  uint8_t i, dc = 0, id[8];
  int8_t r;
  // поиск устройств на шине 1-wire
  while ((r = ow_searchROM(id))) {
    if (r < 0)
     continue; // повтор если ошибка crc
    wh1602b_move(0,0);
    // вывести уникальный код датчика на дисплей
    for (i = 0; i < 8; ++i)
      wh1602b_puth(2, id[i]);
    _delay_ms(2000); // пауза 2с.
    ++dc;
  }
  wh1602b_move(1,0);
  wh1602b_putn(dc); // вывести число датчиков
  wh1602b_putsP(PSTR(" devices."));
}

// <...>
И результат в моём случае:


Больше примеров можно будет найти в архиве с библиотекой, ссылка на который указана в разделе "скачать".

Настройка конфигурации
Перед компиляцией библиотеки необходимо задать порты к которым подключён дисплей, а также включаемые возможности. Для этого необходимо отредактировав файл ds18b20/conf.h:
#define F_CPU 20000000 /* частота мк, Гц */

#define USE_EXTRACTINT    /* поддержка ds18b20_extractInt() */
#define USE_EXTRACTFRACT  /* поддержка ds18b20_extractFract() */

// поддержка автоматической проверки данных контрольной суммой
#define SUPPORT_CHECKSUM
// поддержка возможности поиска устройств на шине
#define SUPPORT_ROMSEARCH
// поддержка датчиков питающихся паразитным током
#define SUPPORT_PARASITE_MODE
// поддержка некоторых дополнительных функций
#define SUPPORT_COMPLETE

/* порт к которому подключена линия данных */
#define DDR_SENSOR   _SFR_IO_ADDR(DDRD)
#define PIN_SENSOR   _SFR_IO_ADDR(PIND)
#define PORT_SENSOR  _SFR_IO_ADDR(PORTD)
/* ножка к которой подключена линия данных */
#define NPIN_SENSOR  PD2
Убрать лишние возможности тем самым уменьшив размер занимаемый библиотекой в памяти МК, можно закомментировав некоторые из строчек начинающихся с #define.

Полный перечень поддерживаемых функций
  • uint8_t ds18b20_selectSensor(const uint8_t id[8])
    Выбирает текущий датчик по его уникальному коду id. Если id = 0, то выбирается первое попавшееся устройство, которое должно быть единственным в сети. Если возвращаемое значение = 0, то вероятно линия данных не правильно настроена. В случае успеха возвращается значение > 0.
  • void ds18b20_convert()
    Отправляет команду текущему датчику чтобы тот начал измерение температуры.
  • uint8_t ds18b20_readROM(uint8_t id[8])
    Загружает уникальный код датчика в массив указанный аргументом id. В сети должно находиться только одно устройство, иначе возникнут коллизии. Если возвращаемое значение = 0, то вероятно линия данных не правильно настроена. В случае успеха возвращается значение > 0.
  • int16_t ds18b20_readTemp()
    Возвращает измеренное значение температуры текущего датчика в градусах. Младшие 4 бита определяют дробную часть, остальная часть - целое значение.
    Например, если возвращаемое значение = 0x0191 = 0000000110010001 = 2^4+2^3+2^0+2^-4 = +25.0625°C
  • uint8_t ds18b20_readPowerSupply()
    Возвращает 1 если текущий датчик использует внешнее питание, 0 - питание от линии данных (паразитное).
  • uint8_t ds18b20_readScratchpad(sspad* p)
    Загружает состояние внутренней памяти текущего датчика в *p. Проверяется контрольная сумма, и возвращается значение > 0 в случае успеха. Если 0, то вероятно контрольная сумма не совпала, либо линия данных не правильно настроена.
    Структура sspad определена в файле ds18b20/ds18b20.h и имеет следующее представление:
    typedef struct {
      uint8_t t_lsb;       // temperature LSB
      uint8_t t_msb;       // temperature MSB
      int8_t t_h;          // Th register
      int8_t t_l;          // Tl register
      uint8_t config;      // configuration register
      uint8_t reserved[3]; // reserved
    } sspad;
    
    Подробней о назначении каждого параметра можно почитать в официальной документации к ds18b20.
  • void ds18b20_writeScratchpad(const sspad* p)
    Записывает данные в *p в память текущего датчика.
  • void ds18b20_copyScratchpad()
    Копирует состояние памяти текущего датчика в его eeprom. EEPROM определяется как энергонезависимая память, и при каждом включении питания, данные из eeprom датчика будут загружаться в его оперативную память.
  • void ds18b20_recall()
    Восстанавливает состояние памяти текущего датчика из его eeprom.
  • int8_t ow_searchROM(uint8_t id[8])
    Выполняет поиск следующего из устройств на шине 1-wire, и передаёт его уникальный код в параметр на который указывает id. Если возвращаемое значение = 0, то поиск завершён. Если возвращаемое значение = -1, то это означает что контрольная сумма не совпала, и данные были повреждены при приёме. В случае успеха возвращается значение > 0.
  • int8_t ds18b20_searchAlarm(uint8_t id[8])
    Выполняет поиск следующего датчика у которого пороговое значение температуры вышло из заданных пределов, и передаёт его уникальный код в параметр на который указывает id. Если возвращаемое значение = 0, то поиск завершён. Если возвращаемое значение = -1, то это означает что контрольная сумма не совпала, и данные были повреждены при приёме. В случае успеха возвращается значение > 0.
  • uint16_t ds18b20_extractInt(int16_t t)
    Возвращает целую часть значения температуры из двухбайтовой величины t, получаемой при помощи вызова функции readTemp. Возвращаемое значение является абсолютным и представлено в двоично-десятичном формате (bcd).
  • uint16_t ds18b20_extractFract(int16_t t, uint8_t r)
    Возвращает дробную часть значения температуры из двухбайтовой величины t, получаемой при помощи вызова функции readTemp, округляя его до 1/10^r. Возвращаемое значение является абсолютным и представлено в двоично-десятичном формате (bcd).
Совместимость
Думаю что библиотека будет совместима с большинством микроконтроллеров AVR серии Mega (в моём случае был использован Atmega644).

Скачать
  • Исходный код библиотеки вместе с примером можно скачать здесь (или здесь). Лицензия GNU GPL v3+;
  • Официальная документация по ds18b20 от производителя;

воскресенье, 10 января 2010 г.

Встречайте - Maxduino!))

На днях собрал себе вот такой девайсик:



Данное устройство представляет собой что-то на подобии отладочной платы на базе контроллера AtMega644, имеет подключаемый ЖК-дисплей, а также содержит внешний часовой кварц. Основной причиной по которой я решил использовать именно AtMega644 была поддержка JTAG, а также большое количество линий ввода/вывода - 32 по сравнению с моим прошлым AtMega168, и большой объём памяти программ - 64кб, вместо 16.
Кварц и дисплей могут быть отключены если снять перемычки соответственно с "Ext. Clk" и "LCD En". Контрастность дисплея здесь может быть настроена с помощью переменного резистора с маркировкой "Contrast", есть кнопка сброса.
Встраивание ЖК-дисплея в состав платы я нашёл для себя довольно нужным, потому как он часто бывает необходим для отображения каких-то промежуточных данных. Также очень хочется поиграться с JTAG, но сначала надо будет собрать устройство для сопряжения JTAG-интерфейса и AVRStudio..
Плата была разведена при помощи Лазерно-Утюжной технологии, о которой я впервые прочитал здесь, а позже углубился здесь=) Лужение платы производилось сплавом Розе..


PS. Название платы конечно шутка и пародие на Arduino и её многочисленные клоны=)

воскресенье, 27 декабря 2009 г.

Библиотека для семисегментного дисплея BQ-M322RD

В связи с возникшей потребностью, была написана библиотека для работы с 4-х разрядным семисегментным дисплеем BQ-M322RD разработанным компанией Bright Led Electronics. Я также уверен что данная библиотека сможет работать и с другими аналогичными 4х-разрядными дисплеями, с общим анодом.
Внешний вид дисплея


Свечение сегментов тут практически не видно, так как сьёмка проходила на ярком дневном освещении. А так, не мешало бы приложить какое-нибудь тёмное стёклышко:)
Особенности данной библиотеки
При написании использовался ассемблер. Кроме этого, пытаясь максимально сократить размер кода, использованные алгоритмы были приближены к идеалу совершенства=) Для отображения цифр на сегментах дисплея был использован способ называемый динамической индикацией. Библиотека способна реализовывать этот режим двумя способами:
  1. Используя ресурсы таймера 0
  2. Используя функцию m322_loop() которую программа должна будет непрерывно вызывать в процессе своего выполнения
Библиотека занимает 264 байт памяти программ и 13 байт памяти данных при использовании первого способа, и соответственно 240 и 13 байт при использовании второго.
Выбрать один из способов можно отредактировав файл m322/conf.h

Примеры использования

Простой пример:
/*
 программа использует библиотеку откомпилированную
 с опцией #define USE_TIMER0
*/
#include "m322/m322.h"

int main(void)
{
  m322_init(); // инициализировать семисегментный дисплей
  m322_putn(1234); // вывести число 1234
  m322_setdot(0, TRUE); // зажечь точку в разряде 0
  for (;;)
    ;
}
Если библиотека откомпилирована вторым способом, то:
#include "m322/m322.h"

int main(void)
{
  m322_init(); // инициализировать семисегментный дисплей
  m322_putn(1234); // вывести число 1234 на семисегментный дисплей
  m322_setdot(0, TRUE); // зажечь точку в разряде 0
  for (;;)
    m322_loop(80); // цикл динамической индикации
}
В данном случае добавлен вызов функции m322_loop() в бесконечный цикл, которая реализует динамическую индикацию. В качестве аргумента ей надо указать некоторое число. Частота мерцания дисплея будет зависеть от этого значения. Чем выше значение - тем меньше частота мерцания. Для моего 16мГц МК на котором я тестировал данный пример, оказалось оптимальным значение 80. Если брать больше - будет заметно мерцание, если меньше - разряды могут "поплыть".
Более интересный пример:
/*
 программа использует библиотеку откомпилированную
 с опцией #define USE_TIMER0
*/
#define F_CPU 16000000ULL /* частота работы мк (для _delay) */
#include <util/delay.h>
#include "m322/m322.h"

int main(void)
{
  m322_init(); // инициализировать семисегментный дисплей

  uint16_t number = 0;
  uint8_t n_dot = 0;
  for (;;) {
    m322_setdot(n_dot, TRUE); // зажечь точку с заданным номером
    m322_putn(number); // вывести десятичное число на индикатор
    _delay_ms(100); // пауза 100мс
    m322_setdot(n_dot, FALSE); // погасить точку с заданным номером

    if (++n_dot == 5)
      n_dot = 0;
    if (++number == 10000)
      number = 0;
  }
}
Если попробовать выполнить программу на МК, то получим - на дисплее будет отображаться число, которое будет увеличиваться каждые 100 миллисекунд, а точка "зажигаясь" будет перемещаться циклически слева-направо.
Как настроить библиотеку под себя
Перед компиляцией библиотеки необходимо задать порты к которым подключён дисплей, отредактировав файл m322/conf.h:
//
// Порты используемые для доступа к отдельным сегментам
//
#define PORT_SEGDATA  _SFR_IO_ADDR(PORTD)
#define DDR_SEGDATA   _SFR_IO_ADDR(DDRD)
/* номера пинов для каждого отдельного сегмента */
#define NPIN_DP PD7 /* точка */
#define NPIN_G  PD6
#define NPIN_F  PD5
#define NPIN_E  PD4
#define NPIN_D  PD3
#define NPIN_C  PD2
#define NPIN_B  PD1
#define NPIN_A  PD0
//
// Порты используемые для доступа к отдельным разрядам
//
#define DDR_SEGCTL    _SFR_IO_ADDR(DDRB)
#define PORT_SEGCTL   _SFR_IO_ADDR(PORTB)
/* номера пинов для каждого отдельного разряда */
#define NPIN_SEGCTL0  PB4 /* D1 */
#define NPIN_SEGCTL1  PB1 /* D2 */
#define NPIN_SEGCTL2  PB2 /* D3 */
#define NPIN_SEGCTL3  PB3 /* D4 */
//
// Дополнительные настройки
//
// заккоментируйте следующую опцию если не хотите использовать
//  ресурсы системного таймера0 для организации динамической
//  индикации семисегментного дисплея:
/* использовать таймер */
#define USE_TIMER0

#ifdef USE_TIMER0
// частота мерцания семисегментного индикатора зависит
//  от коэффициента предделителя таймера0,
// и вычисляется по формуле f = F_CPU/(P*256*4), где:
//  F_CPU - тактовая частота мк, Гц
//  P - коэффициент предделителя
/* значение предделителя для таймера */
#define TIMER0_PRESCALER ((1<<CS00)|(1<<CS01)) /* предделитель 64 */
#endif // USE_TIMER0
Я думаю здесь комментарии излишни=)

Схематическое изображение дисплея:



Полный перечень поддерживаемых функций
Библиотека поддерживает все самые необходимые функции, а именно:
  • void m322_init()
    Выполняет инициализацию семисегментного дисплея (настройка портов, таймера).
  • void m322_puthd(uint8_t c, uint8_t n)
    Выводит шестнадцатеричную цифру n в разряд с номером c индикатора.
  • void m322_puthh(uint8_t n)
    Выводит шестнадцатеричное число n в старшую часть индикатора (разряды 0 и 1)
  • void m322_puthl(uint8_t n)
    Выводит шестнадцатеричное число n в младшую часть индикатора (разряды 2 и 3)
  • void m322_setdot(uint8_t c, uint8_t b)
    Задаёт точку в разряд с номером c индикатора. Если b = TRUE - зажечь, иначе погасить.
  • void m322_putn(uint16_t n)
    Выводит на индикатор беззнаковое десятичное число n
  • void m322_loop(uint16_t n)
    Цикл для организации динамической индикации без использования таймера0
Совместимость
Думаю что библиотека будет совместима с большинством микроконтроллеров AVR серии Mega (в моём случае был использован Atmega8).

Скачать
Исходный код библиотеки вместе с примером можно скачать здесь (или здесь).

воскресенье, 6 декабря 2009 г.

Библиотека для ЖК-дисплея WH1602b

После долгой кропотливой работы изучения документации и написания кода, родилась полезная и нужная для меня библиотека для работы с этим дисплеем. Возможно она окажется полезной кому-нибудь ещё :)
О дисплее
Дисплей этот я приобрёл на молотке около года тому назад, за почти астрономическую сумму в 450рэ. Ну тем не менее он устраивает меня во всём, и в быту неприхотлив=)
Особенности этой библиотеки
Код написан на ассемблере, и очень компактен. Занимает от 298 до 466 байт в памяти программ МК. В файле конфигурации wh1602b/conf.h можно дополнительно установить поддержку следующих функций:
  • Поддержка знакогенератора (298+52 байт)
  • Поддержка функции вывода десятичных чисел (298+66 байт)
  • Поддержка функции вывода шестнадцатеричных чисел (298+72 байт)
Основные функциональные возможности
  • Вывод строк, символов
  • Вывод чисел
  • Очистка дисплея
  • Управление курсором
  • Поддержка знакогенератора (до 8-ми знаков, согласно спецификации)
  • Настройка различных параметров работы
Не поддерживается:
  • Чтение памяти дисплея
  • Текущее положение курсора нельзя определить

Библиотека использует 4-битную шину для передачи данных.

Примеры использования

Простой пример:
#include "wh1602b/wh1602b.h"

int main(void)
{
  wh1602b_init(); // инициализировать модуль, порты
  // настроить параметры управления дисплеем
  wh1602b_displayctl(DC_DISPLAY_ON); // дисплей включён
  // задать число строк
  wh1602b_funcset(FS_LINES_2); // 2 строки
  // очистить область
  wh1602b_clear();
  // вывести строку на дисплей
  wh1602b_puts("Hello, World!");

  for (;;)
    ;
}
Результат:

Следующий пример демонстрирует возможность загрузки собственного знака:
#include "wh1602b/wh1602b.h"

// собственно массив представляющий собой
//  знак который надо загрузить
static const uint8_t window_char[8] = {
  0x1f, // 1 1 1 1 1
  0x11, // 1 0 0 0 1
  0x0e, // 0 1 1 1 0
  0x04, // 0 0 1 0 0
  0x0e, // 0 1 1 1 0
  0x15, // 1 0 1 0 1
  0x15, // 1 0 1 0 1
  0x00  // 0 0 0 0 0
};

int main(void)
{
  wh1602b_init(); // инициализировать модуль, порты
  // настроить параметры управления дисплеем
  wh1602b_displayctl(DC_DISPLAY_ON); // дисплей включён
  // задать число строк
  wh1602b_funcset(FS_LINES_2); // 2 строки
  // очистить область
  wh1602b_clear();
  // записать символ ассоциировав его с кодом 5
  wh1602b_genc(5, window_char);
  // вернуться в начало (команда аналогичная move).
  //   этот вызов является обязательным после вызова genc()
  wh1602b_home();
  // вывести строку на дисплей вместе с новым знаком :)
  wh1602b_puts("Крякозябла \5");

  for (;;)
    ;
}
Результат:

В качестве аргументов для wh1602b_puts и wh1602b_genc выступает указатель на строку расположенную в области RAM. Для того чтобы иметь возможность выводить строки из памяти программ, необходимо соответственно использовать wh1602b_putsP или wh1602b_gencP. Например:
#include "wh1602b/wh1602b.h"
#include <avr/pgmspace.h>

int main(void)
{
  <...>
  // вывести строку1 из памяти программ
  wh1602b_putsP(PSTR("Здравствуй"));
  // переместить курсор на заданную позицию (нумерация с нуля)
  wh1602b_move(1,4); // 2 строка, 5 столбец
  // вывести строку2 из памяти программ
  wh1602b_putsP(PSTR("Мир!"));
  <...>
}
Результат:


Как настроить библиотеку под себя
Для начала необходимо задать порты к которым подключён дисплей. Для этого необходимо отредактировать файл wh1602b/conf.h:
#define F_CPU 16000000 /* частота мк, Гц */

#define USE_PUTN /* включить поддержку wh1602b_putn() */
#define USE_PUTH /* включить поддержку wh1602b_puth() */
#define USE_GENC /* включить поддержку wh1602b_genc() */

/* ножки к которым подключены выводы RS, RW, EN */
#define NPIN_RS PB4
#define NPIN_RW PB3
#define NPIN_EN PB2

/* порты к которым подключены выводы RS, RW, EN */
#define PORT_RS _SFR_IO_ADDR(PORTB)
#define PORT_RW _SFR_IO_ADDR(PORTB)
#define PORT_EN _SFR_IO_ADDR(PORTB)
#define DDR_RS  _SFR_IO_ADDR(DDRB)
#define DDR_RW  _SFR_IO_ADDR(DDRB)
#define DDR_EN  _SFR_IO_ADDR(DDRB)

/* ножки к которым подключены выводы DB4-DB7 */
#define NPIN_DB7  PD7
#define NPIN_DB6  PD6
#define NPIN_DB5  PD5
#define NPIN_DB4  PD4

/* порты к которым подключены выводы DB4-DB7 */
#define PORT_DB7  _SFR_IO_ADDR(PORTD)
#define PORT_DB6  _SFR_IO_ADDR(PORTD)
#define PORT_DB5  _SFR_IO_ADDR(PORTD)
#define PORT_DB4  _SFR_IO_ADDR(PORTD)
#define DDR_DB7   _SFR_IO_ADDR(DDRD)
#define DDR_DB6   _SFR_IO_ADDR(DDRD)
#define DDR_DB5   _SFR_IO_ADDR(DDRD)
#define DDR_DB4   _SFR_IO_ADDR(DDRD)

Соответствующие ножки дисплея показаны на следующем рисунке:

Как компилировать программы написанные с использованием этой библиотеки
Существует ряд особенностей при компиляции программ использующих данную библиотеку. Дело в том что ЖК-дисплей понимает только строки написанные в своём собственном варианте кодировки. Поэтому компилировать надо с ключом -fexec-charset=wh1602b. При этом, в системе должен быть установлен специальный модуль для iconv - wh1602b. Готовые скомпилированный модуль можно скачать здесь (x86_64 и i686), или собрать его самостоятельно. Для установки необходимо скопировать файл WH1602B.so в папку с модулями gconv (у меня это /usr/lib/gconv), а также отредактировать файл /usr/lib/gconv/gconv-modules, добавив строчки:
# from      to      module    cost
alias WH-1602B//     WH1602B//
alias 1602B//       WH1602B//
module  WH1602B//    INTERNAL    WH1602B    1
module  INTERNAL    WH1602B//    WH1602B    1
Самостоятельная сборка такого модуля довольно кропотливая процедура, и будет описана в следующий раз.

Полный перечень поддерживаемых функций
  • void wh1602b_init()
    Выполняет инициализацию модуля дисплея.
  • void wh1602b_funcset(uint8_t t)
    Устанавливает число строк и размера шрифта. В качестве аргументов может служить комбинация одного или нескольких параметров:
    FS_LINES_2 - использовать две строки дисплея (по умолчанию одна строка, FS_LINES_1)
    FS_FONT_5x11 - использовать шрифт размером 5х11 (по умолчанию 5х8)
  • void wh1602b_entrymode(uint8_t t)
    Настраивает режима ввода. В качестве аргументов может служит комбинация одного или нескольких параметров:
    EM_CURSOR_DIR - использовать автоматическое перемещение курсора слева-направо
    EM_DISPLAY_SHIFT - разрешить смещение дисплея
  • void wh1602b_displayctl(uint8_t t)
    Устанавливает параметры управления дисплеем. В качестве аргументов может служит комбинация одного или нескольких параметров:
    DC_DISPLAY_ON - дисплей включён (по умолчанию выключен, DC_DISPLAY_OFF)
    DC_CURSOR_BLINK - использовать мигающий курсор
    DC_CURSOR_ON - использовать курсор
  • void wh1602b_puts(const char* s)
    Выводит строку s из RAM на дисплей.
  • void wh1602b_putsP(const char* s)
    Выводит строку s из памяти программ на дисплей.
  • void wh1602b_putn(uint16_t n)
    Выводит десятичное число n на дисплей.
  • void wh1602b_puth(uint8_t w, uint16_t h)
    Выводит шестнадцатеричное число h на дисплей. Ширина поля определяется параметром w
  • void wh1602b_genc(uint8_t n, const uint8_t* p)
    Записывает знак на который указывает p в знакогенератор. Если это последний записываемый знак, и при этом ожидается вывод на дисплей, то необходимо после выполнения genc вызвать wh1602b_home() или wh1602b_move().
  • void wh1602b_home()
    Возвращает курсор в начальное положение. Если дисплей при этом смещён то функция восстанавливает его.
  • void wh1602b_move(uint8_t r, uint8_t c)
    Перемещает курсор на заданную позицию (r - строка, c - столбец). Нумерация начинается с нуля.
  • void wh1602b_putc(uint8_t c)
    Выводит символ c на экран дисплея.
  • void wh1602b_clear()
    Очищает экран дисплея.
Скачать
Исходный код библиотеки вместе с примером можно скачать здесь.

понедельник, 30 ноября 2009 г.

Пишем драйвер для лампочки=) Часть 2.

В прошлый раз я писал как создавал драйвер для лампочки. Эта часть будет посвящена тому как я использовал свой драйвер в отдельном проекте.

Написание основного проекта
Свой проект я разместил в папке ~/hard_project, основной и единственный файл которого имеет следующее содержание:
// файл ~/hard_project/libdrv/main.c
#define F_CPU 16000000UL /* тактовая частота МК */
#include "leddrv/leddrv.h"

int main(void)
{
  led_init(); // инициализация лампочки
  for (;;) // бесконечный цикл
    led_blink_ms(500); // мигнуть с частотой 0,5сек
}
Этот код настолько прост и понятен что в дополнительных комментариях и не нуждается.

Упрощённую версию Makefile для сборки проекта можно скачать тут. Скомандовав в консоли, я и получил целевой hex файл:
$ make
avr-gcc -g -Wall -O2 -mmcu=atmega168 -o .main.o -c main.c
avr-gcc -g -Wall -O2 -mmcu=atmega168  -o .main.elf .main.o -static -Lleddrv/ -lleddrv
avr-objcopy -j .text -j .data -O ihex .main.elf main.hex

А затем и прошил свой МК:
$ make program
avrdude -v -c dapa -p atmega168 -P /dev/parport0 -b 115200 -U flash:w:main.hex

И вот оно, о чудо - работает!



Итог
Всё круто! Цель достигнута. В моих планах написать аналогичные библиотеки для ЖК-модуля и датчика температуры. Надеюсь это произойдёт в недалёкой перспективе:)

---
Скачать hard_project.tar.gz

воскресенье, 29 ноября 2009 г.

Пишем драйвер для лампочки=) Часть 1.

Хочу описать собственную идею (возможно она не нова), по которой я собираюсь создавать драйверы для различных аппаратных модулей, микросхем и тп.. Базируется она на основе использования статических библиотек, которые будут подключаться к основному проекту и представлять собой драйвер для какого-нибудь отдельно взятого модуля. Статическую библиотеку предлагаю писать на ассемблере, с целью сокращения объёма кода а также получения наивысшей производительности=) Такая библиотека, подключённая к основному проекту будет занимать минимум, при этом останется больше свободного места в памяти МК для написания основной логики.

В качестве примера можно рассмотреть тривиальный пример - драйвер для лампочки (индикатора). Данный пример подходит только для демонстрации самой идеи, а не её практического применения. Лампочка уж слишком простая вещь чтобы для неё писать целый драйвер. А вообще хотелось бы написать таковые для ЖК-дисплея, микросхемы ds1307, да много чего можно ещё придумать:) Главное желание, и свободное время. С последним у меня пока проблемы... Итак, приступим!

Подготовительная часть
1) Необходимо установить в систему пакеты gcc-avr, binutils-avr, и avr-libc. (если ещё не были установлены)
2) Вкратце изучить документацию по avr-libc. Там содержится очень много интересного(:
3) Создать папку для основного проекта, у меня это ~/hard_project.
4) Создать папку для проекта библиотеки, у меня это ~/hard_project/libdrv.

Написание библиотеки
Основной файл будет иметь следующее содержание:
;; файл ~/hard_project/libdrv/leddrv.S:
#include <avr/io.h>
#include "conf.h"

.global led_init ; void led_init()
; предварительная настройка порта
led_init:
  sbi _SFR_IO_ADDR(LED_DDR),LED_NPIN ; установить ножку на вывод данных
  ret

.global led_on ; void led_on()
; включение лампочки
led_on:
  sbi _SFR_IO_ADDR(LED_PORT),LED_NPIN ; записать логическую 1 в порт
  ret

.global led_off ; void led_off()
; выключение лампочки
led_off:
  cbi _SFR_IO_ADDR(LED_PORT),LED_NPIN ; записать логический 0 в порт
  ret

.global led_blink ; void led_blink(uint16_t f)
; мигание лампочки с заданной частотой
led_blink:
  sbi _SFR_IO_ADDR(LED_PORT),LED_NPIN ; записать логическую 1 в порт
  push r24
  push r25
  rcall delay ; задержать выполнение
  cbi _SFR_IO_ADDR(LED_PORT),LED_NPIN ; записать логический 0 в порт
  pop r25
  pop r24
  rcall delay ; задержать выполнение
  ret

; задержка на заданное значение
delay: ; до 770*65535/F_CPU, сек
   clr r0
0: com r0
1: dec r0
    brne 1b
    sbiw r24,1
    brne 0b ; 770 тактов за цикл
    ret

Здесь первый параметр для функции led_blink будет передаваться через регистр r24:r25, согласно соглашениям компилятора avr-gcc. Далее, заголовочный файл будет иметь следующее содержание:
// файл ~/hard_project/libdrv/leddrv.h:
#include <stdint.h>
#ifdef F_CPU
#define led_blink_ms(x) led_blink(((x)*(uint64_t)F_CPU)/(770*1000ul))
#endif

void led_init();
void led_on();
void led_off();
void led_blink(uint16_t f);

Здесь определён макрос led_blink_ms для удобства. При его вызове нужно указывать значение в миллисекундах, в отличии от led_blink, в которой указывается значение в условных единицах =) Последний файл проекта будет иметь следующее содержание:
// файл ~/hard_project/libdrv/conf.h:
#define LED_PORT PORTC
#define LED_DDR  DDRC
#define LED_NPIN PC1

Ну здесь наверное понятно, указываются параметры порта и ножки на которой будет сидеть лампочка =)

Это всё. Теперь необходимо собрать нашу бибиотеку. Упрощённую версию Makefile для сборки проекта можно скачать тут. Скомандовав в консоли, я и получил целевой файл libleddrv.a:
$Make
avr-gcc -Wall -x assembler-with-cpp -mmcu=atmega168 -o .leddrv.o -c leddrv.S
avr-ar rcs libleddrv.a .leddrv.o

В следующий раз я покажу как использовал эту библиотеку в своём проекте=)

---
Скачать исходники libdrv.tar.gz

среда, 18 ноября 2009 г.

Программа IBCrypt v1.00

IBCrypt - программа реализующая возможность шифрования информации с использованием блочного алгоритма на основе сети Фейштеля.
С использованием IBCrypt можно зашифровывать и расшифровывать файлы, а также простые текстовые сообщения написанные во встроенном текстовом редакторе. Более подбробно сведения об алгоритме шифрования описаны в справочном руководстве. Программа является моей курсовой работой по курсу «Информационная безопасность».

  

Лицензия: GNU GPL v3. Программа написана с использованием библиотеки Qt4

Скачать (с rapidshare.de):
  • Версия для GNU/Linux, с включёнными зависимостями: (i686, amd64)
  • Пакет для Arch Linux (i686, amd64)
  • Пакет для Debian/Ubuntu (i686, amd64)
  • Версия для Windows
  • Исходный код
Скачать (с альтернативного источника):
  • Версия для GNU/Linux, с включёнными зависимостями: (i686, amd64)
  • Пакет для Arch Linux (i686, amd64)
  • Пакет для Debian/Ubuntu (i686, amd64)
  • Версия для Windows
  • Исходный код

вторник, 17 ноября 2009 г.

Этот мир сошёл с ума...

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

А если представить себе такую жену? Сидишь с ней знач в одной комнате. Она за ноутом пишет операционку для 8-битного AVR... Ну а ты рядом за другим ноутом, клепаешь PKGBUILD для своей недавно написанной на Си супернавороченной проги... Общаешься с ней через жабир, типа эта - "Жена, жрать хочу, давай заканчивай там..." :-D

Всё, нет пути. Мы все скоро умрём..

Мои впечатления о Chromium..

Давно хотел сменить браузер. Долгое время использовал оперу, и меня она устраивала. Она быстро работала, и хорошо интегрировалась с KDE/Oxygen при помощи темы opera_oxygen_project. Но в последнее время стала меня раздражать своими глюками. Во-первых: почему-то стало не возможным переход по ссылке до тех пор пока страница не загрузится. В соседних вкладках происходили так же подобные артефакты. Во-вторых: иногда происходили проблемы с отображением страницы - текст за текст налазил. Ну и в третьих: если долго не чистить историю, то со временем опера начинала жутко тормозить. Кроме того, шрифты без предварительного шаманства в настройках выглядили просто ужасно. Всё это меня достало. Файрфокс хоть и хорош своей расширяемостью, он очень плохо интегрируется с KDE. Да меня бы и устраивал Konqueror, если бы не пожирал огромное количество памяти..

Поэтому, решил я попробовать Chromium от гугла. И не пожалел. Загружается за 1-2сек. на моём нетбуке Acer One. Очень лёгкий, прорисовка страниц работает очень быстро. Глюков пока не заметил. Памяти жрёт приемлемо, но не больше чем опера. Есть конечно и некоторые минусы - шрифты уж слишком размазывает (может как-то лечиться?), ну и выглядит не настолько нативно как мне хотелось бы. И ещё меня насторожила вот эта ссылка, в которой сказано что Chromium отсылает "лишнюю" информацию гуглу. В связи с этим был создан проект Iron, который является форком хромиума, и с которого удалён "плохой" код. Будем на это надеятся...


понедельник, 16 ноября 2009 г.

Программа MMixMulator

MMixMulator - симулятор компьютера MMIX. Представляет собой интегрированную среду, способную эмулировать поведение компьютера MMIX, архитектура которого описывается в книге Д. Кнута "Искусство программирования". Составляющими компонентами MMixMulator являются: компилятор ассемблера MMIX (mmixas), интерпретатор машинного кода (mmixex), и среда для разработки приложений (mmixbuger).
  • MMixBuger - среда для разработки приложений MMIX. Представляет собой программу, имеющую графический интерфейс, которая содержит редактор кода с подсветкой синтаксиса, обозреватель памяти, регистров общего и специального назначения, встроенный отладчик. С помощью данной среды, программа MMIX может быть составлена, скомпилирована и выполнена. Результат выполнения отображается в командной строке (консоли). Программа может быть отлажена по шагам, точкам останова.
  • MMixAS - ассемблер, совместимый с тем который описывается в книге Кнута (при этом некоторые фишки не поддерживаются на данный момент)
  • MMixEx - интепретатор машинного кода MMIX. Позволяет запускать программы скомпилированные при помощи MMIX-ассемблера.

Также стоит отметить, что на данный момент отсутствует поддержка инструкций с плавающей точкой, TRIP, и ещё нескольких не особо важных=) MMixMulator - это тема моего дипломного проекта. Документация находится в процессе написания присутствует.
Лицензия: GNU GPL v3. Программа написана с использованием библиотеки Qt4

Скачать:

четверг, 15 октября 2009 г.

Программа «Я Труп» v1.0!)

Однажды, холодными зимними буднями, меня попросили создать программу.. и не то чтобы просили, просто умоляли. Браться за работу я не очень то и хотел, но сжалился перед человеком, тем более что просили сделать не безвозмездно. Я был четвёртым программистом который за неё взялся, как я в последствии об этом узнал.
Работёнка предстояла сложная и для меня не привычная - надо было написать программу которая моделирует поведение устройства, конкретно устройства оперативного регулирования напряжения. Данное устройство описывалось в кандидатской работе заказчика, так что мне пришлось ещё в неё вникать. Там почти голая математика, работа с комплексными числами, многомерными матрицами, и при этом огромные по объёму расчёты которые программа должна была вычислять.
Создавалась сия програмка очень медленно, мешала учёба и основная работа в которой я работал...
В один прекрасный солнечный денёк, меня сильно обругал заказчик, и пригрозил мне сделать "ахтунг" если я не потороплюсь)) Тогда и родилось такое замечательное название к программе:) Пришлось делать..
Через два года, и с большими перерывами, работа над программой всё-таки была завершена.
О программе
Программа была написана на языке Си и Си++. Внешний вид показан на следующих рисунках:

 
Вкратце, принцип действия таков: При помощи редактора конфигурации вводятся исходные параметры системы. Затем после нажатия кнопки "Начать вычисления" происходят различные расчёты и моделирование (используя механизм псевдослучайных чисел). Результат работы сохраняется в виде отчёта в формате pdf, где содержаться различные результаты промежуточных вычислений, и графики.
Промежуточные вычисления в удобочитаемом формате, а также графики и конвертирование в pdf производятся при помощи утилит latex, gnuplot, и dvipdf, поэтому программа требует наличие в системе установленных пакетов gnuplot, texlive, и texlive-langcyrillic.
Лицензия: GNU GPL v3. Программа написана с использованием библиотеки Qt4.

Скачать (с rapidshare.de):
Скачать (с альтернативного источника):Пример генерируемого файла отчёта можно посмотреть здесь

вторник, 10 марта 2009 г.

Решение проблемы с модулем CGI::Ajax при передаче символов "+" и "%"

Наткнулся на один неприятный баг в данном модуле для Perl. Например есть вот такой код:
#!/usr/bin/perl -w
use strict;
use CGI; # or any other CGI:: form handler/decoder
use CGI::Ajax;

my $cgi = new CGI;
my $pjx = new CGI::Ajax( 'exported_func' => \&perl_func );
print $pjx->build_html( $cgi, \&Show_HTML);

sub perl_func {
 return( join("<br>",@_) );
}

sub Show_HTML {
 my $html = <<EOHTML;
 <HTML>
 <BODY>
  Enter something: 
  <input type="text" id="val">
  <input type="button" onclick='exported_func(["val"],["resultdiv"])' value="Do!"/>
  <br>
  <div id="resultdiv"></div>
 </BODY>
 </HTML>
EOHTML
 return $html;
}


Так вот, если запустить этот Perl-скрипт из браузера, ввести в поле ввода "плохую" строку вроде "text % text + text", нажать кнопку "Do!" мы не увидим знака "+" в "resultdiv".

Есть решение, в нашем примере, после строки:
 my $pjx = new CGI::Ajax( 'exported_func' => \&perl_func );

нужно добавить:
 $pjx->js_encode_function('encodeURIComponent');

В таком случае, при вводе в поле "+", мы получим "+", но если добавить/заменить "+" на "%" то ничего не получим. Кроме того, браузер выдаст различные ошибки JavaScript.

Методом копания в исходниках CGI::Ajax, была обнаружено что неполадки возникают при попытке повторного раскодирования запрошенной строки. Файл /usr/share/perl5/CGI/Ajax.pm, строка 942:

<...>
 pjxInitialized : function(){},
 pjxCompleted : function(){},
 readyState4 : function(){
  var rsp = $decodefn(this.r.responseText); /* the response from perl */
  var splitval = '__pjx__'; /* to split text */
  /* fix IE problems with undef values in an Array getting squashed*/
  rsp = rsp.replace(splitval+splitval+'g',splitval+" "+splitval);
  var data = rsp.split(splitval);

<...>

В данном куске кода строка:
  var rsp = $decodefn(this.r.responseText); /* the response from perl */
должна быть заменена на:
 var rsp = this.r.responseText; /* the response from perl */

А также в нашем примере, после строки:
 my $pjx = new CGI::Ajax( 'exported_func' => \&perl_func );

нужно добавить:
 $pjx->js_encode_function('encodeURIComponent');

И ошибка будет решена! :-)


воскресенье, 22 февраля 2009 г.

Решение проблемы неработоспособности Slax 6.х при его загрузке.

Slax имеет возможность сохранять настройки сделанные пользователем на диск. Для этого служит параметр загрузки "changes=". В таком случае, выяснилось, что при интенсивном использовании, может случится что система перестанет загружатся и станет неработоспособной. Здесь я приведу свой (возможно не самый удачный) вариант решения проблемы.

Исходные данные таковы:
Есть раздел с NTFS. Предполагается что на этот раздел должны сохранятся настройки slax. Настройки будут сохранятся в одном файле - slax.dat

Мой вариант решения проблемы это каждый раз при загрузке проверять slax.dat на ошибки и автоматически их исправлять при помощи утилиты fsck.ext2

Приступим:
1) Загружаемся в Slax, открываем консоль :)

2) Подготовим файл в котором будут хранится настройки:
# dd if=/dev/zero of=/mnt/sda1/slax.dat bs=1M count=512
# /sbin/mkfs.ext2 /mnt/sda1/slax.dat


где:
/mnt/sda1/slax.dat - место хранения файла (в моём случае он хранится на разделе NTFS)
count=512 - размер файла в мб.

3) Находим файл boot/initrd.gz на CD-ROM, копируем его в /root

4) Распаковываем
# gzip -d initrd.gz

5) Монтируем:
# mkdir fs
# mount -t ext2 initrd fs -o loop


6) Качаем пакет e2fsprogs и его зависимости. Я нашёл его в репозиториях Debian (packages.debian.org):
# wget http://ftp.de.debian.org/debian/pool/main/e/e2fsprogs/e2fsprogs_1.39+1.40-WIP-2006.11.14+dfsg-2etch1_i386.deb
# wget http://ftp.de.debian.org/debian/pool/main/e/e2fsprogs/e2fslibs_1.39+1.40-WIP-2006.11.14+dfsg-2etch1_i386.deb
# wget http://ftp.de.debian.org/debian/pool/main/e/e2fsprogs/libcomerr2_1.39+1.40-WIP-2006.11.14+dfsg-2etch1_i386.deb


Далее необходимо извлечь нужные нам файлы из .deb пакетов и положить их в initrd. Для этого нам потребуется утилита dpkg, которая существует в составе модуля "debian package tools" для Slax. Данный модуль можно скачать с сайта slax.org.

7) Собственно извлекаем:
# mkdir e2fsprogs
# dpkg --extract "e2fsprogs_1.39+1.40-WIP-2006.11.14+dfsg-2etch1_i386.deb" e2fsprogs
# cp e2fsprogs/sbin/fsck.ext2 fs/sbin
# cp e2fsprogs/sbin/e2fsck fs/sbin


# mkdir e2fslibs
# dpkg --extract "e2fslibs_1.39+1.40-WIP-2006.11.14+dfsg-2etch1_i386.deb" e2fslibs
# cp e2fslibs/lib/* fs/lib


# mkdir libcomerr2
# dpkg --extract "libcomerr2_1.39+1.40-WIP-2006.11.14+dfsg-2etch1_i386.deb" libcomerr2
# cp libcomerr2/lib/* fs/lib



8) Редактируем файл fs/linuxrc: до строки `mount_device "$CHANGESMNT" $MEMORY` прописываем:

if [ "$(basename "$CHANGESMNT" .dat)" != "$CHANGESMNT" ]; then
  /sbin/fsck.ext2 -y "$CHANGESMNT"
fi


9) Заключительные шаги: Отмонтируем наш initrd и сожмём обратно программой gzip
# umount fs
# gzip initrd


В итоге, оригинальный initrd.gz надо будет заменить полученным нами. Делается это путём перепрожига диска со Slax обновлённым initrd.gz

При дальнейшей загрузке Slax не забываем указывать параметр "changes=/slax.dat"

суббота, 31 мая 2008 г.

Запуск Linux прямо из Glofiish M700

Это возможно! Сегодня я в этом убедился. Для этого нам понадобится следующее:

1) Ядро Linux
2) Наличие в устройстве карточки MicroSD объёмом не менее 128мб
3) Haret - программа-загрузчик ядра запускающийся прямо из Windows CE
4) Текстовый файл с параметрами загрузки ядра
5) Образ OPIE или GPE

Приступим.

1) Скачаем образ ядра (здесь)
2) Скачаем haret
3) Скопируем всё это хозяйство в память коммуникатора (не на карточку а на встроенную память)
4) Создадим в той папке в которую скопировали, файл default.txt с таким содержанием:
set MTYPE 0x664
set KERNEL "zImage_x500"
set RAMADDR 0x30000000
set RAMSIZE 0x4000000
set CMDLINE "root=/dev/mmcblk0p2 rootdelay=5 panic=360"
bootlinux

5) Необходимо подключить microSD карту к компьютеру (через кардридер или напрямую через USB кабель+WM5Storage), при этом убедится что не один раздел не смонтировался автоматически.

6) Создадим разделы на карточке согласно такой схеме - минимум 50мб для раздела с ext3, всё оставшееся свободное пространство оставим для fat16. Для своей карты объемом 2ГБ я разбил диск на разделы по такой схеме:
+================================================+
| 1800мб - FAT16                 | 213мб - EXT3  |
+================================================+
для выполнения этого шага использовалась утилита cfdisk.

7) Необходимо записать на созданные разделы файловую систему:
$/sbin/mkfs.msdos /dev/sdc1 -n WINDOWS
$/sbin/mkfs.ext3 /dev/sdc2 -j -L LINUX

где:
/dev/sdc - имя устройства microSD карты
/dev/sdc1 - первый раздел с windows fat
/dev/sdc2 - второй раздел с linux ext3

8) Скачаем OPIE например отсюда

9) Скопируем всё содержимое скачаного архива на раздел с линуксом (/dev/sdc2 в моём случае)

10) Отмонтируем карточку с компьютера(если использовался кард-ридер), вставим обратно в зверька, убедимся что FAT раздел на карточке виден с КПК

Перед загрузкой ядра обязательно отключаем проверку пароля при загрузке мастдая, иначе потом придётся делать хард-ресет!

11) Запускаем haret, нажимаем кнопку RUN, и... нервно ожидаем загрузки....

Приятного использования :)

Да, совсем забыл сказать, чтобы обратно перезагрузится в винду нажмите одновременно Soft-Reset и клавишу выключения (только не удерживая долго)



          
    
          












Полезные программы для OPIE, а также пакеты с локализацией можно скачать здесь

Полезные ссылки:
1) http://linux.cybernet.net.ru (битая?)
2) http://www.handhelds.org
3) http://ipkgfind.handhelds.org