используйте спойлеры!
Данный проект представляет собой лампу на основе светодиодной УФ ленты с таймером. Диапазон таймера от 1 до 9999 секунд (~2.8 часа). Как показала практика для засветки фоторезиста вполне хватает 90-120 секунд. Что получилось можно посмотреть на youtube.
Для проекта понадобится:
Некоторые замечания:
Питание "лампы" 12V. Вся логика работы завязана на МК atmega8а. Питание для микроконтроллера и индикатора 3.3V, подается через стабилизатор напряжения AMS1117 3.3V.
С помощью энкодера задается время экспонирования, затем по нажатию нижней кнопки запускается процесс засветки при этом управление через энкодер отключается. При истечении времени засветка прекращается. Верхняя кнопки - сброс. Сброс реализован просто замыканием контакта reset на землю.
Процесс разработки:
Вклеиваем ленту в рамку для фотографий:
Прототип я собирал на базе atmega8515 и все кнопки обрабатывались внешними прерываниями, но с переходом на младшую модель пришлось отказаться от одного прерывания, т.к. у atmega8 их 2 против 3 у 8515.
Проверка прототипа на обычной ленте:
Готовая плата:
С процессом разработки все стандартно: травим плату, сверлим отверстия, распаиваем компоненты начиная с SMD и заканчивая экраном и энкодером. Дополнительно на энкодер припеваем конденсаторы 104 (100nF) для того, что бы избежать дребезга контактов при срабатывании кнопок.
Прошиваю чип:
Разбор кода:
Проект можно скачать с github. Проект написан на C с использованием CVAVR.
Итак, если нужный индикатор найти не удалось необходимо изменить значения в данном массиве:
Указанный массив представляет собой маску для порта B. Как можно понять из комментария к коду, здесь биты расположены от пина7 порта B до пина 0 порта B (//PB7...PB0). Так же в комментарии указано, какой пин какой сегмент зажигает (//FBGCDpDEA): 7-F, 6-B и т.д. Включение сегмента осуществляется подачей 5v на ногу. На примере "0" видно, что не горят сегменты G и Dp (точка). Порт B конфигурируем как выход:
За переключение разрядов отвечают биты 0-3 порта C. Конфигурируем порты следующим образом:
Создаем маску для включения разряда:
Теперь что бы отображать все 4 числа на индикаторе нужно просто каждый цикл передавать на порт C один из элементов массива digit, например:
У микроконтроллера atmega8a есть возможность обрабатывать два внешних прерывания. Для этого нужно подключиться к ногам PD2, PD3. Внешние прерывания используются для работы с энкодером. На PD2 подключен контакт энкодера отвечающий за поворот. Срабатывание этого прерывания означает что энкодер был повернут. Что бы определить в какую сторону был повернут энкодер считываем значение с другого контакта. высокий или низкий уровень на этом контакте говорит о направлении вращения:
Второе прерывание отвечает за кнопку на энкодере и двигает разряды позволяя задавать 4-х значные числа. Переменная digitNumber в данном случае номер разряда:
Последние, что нужно сделать, включить внешние прерывания прерывания и разрешить их
И наконец прерывание по таймеру. Таймер включается при нажатии на кнопку старт. Т.к. для обработки кнопки старт внешних прерываний не хватило, проверяем постоянно уровень на ноге микроконтроллера и в случае его изменения включаем таймер:
Т.к. таймер 8-ми битный, что бы отсчитать одну секунду ему понадобиться отработать 30 раз:
Проверяем, что таймер переполнился 30 раз после чего уменьшаем количество секунд на одну, а если досчитали до 0, останавливаем таймер и снова готовимся к работе:
Схема устройства и схема платы:
Для проекта понадобится:
Некоторые замечания:
- Обратите внимание, что для работы нужен индикатор конкретной модели: kem-5461ar. Если индикатора для данной модели нет придется переопределить цифры в коде, как это сделать см. "Разбор кода"
- Так же лучше взять не очень высокие электролиты, так как их можно "положить" на плату как можно увидеть на фото ниже.
- Микроконтроллер прошивается после распайки всех компонентов на плату, для этого предусмотрены контакты: MISO, SCK, MOSI
Питание "лампы" 12V. Вся логика работы завязана на МК atmega8а. Питание для микроконтроллера и индикатора 3.3V, подается через стабилизатор напряжения AMS1117 3.3V.
С помощью энкодера задается время экспонирования, затем по нажатию нижней кнопки запускается процесс засветки при этом управление через энкодер отключается. При истечении времени засветка прекращается. Верхняя кнопки - сброс. Сброс реализован просто замыканием контакта reset на землю.
Процесс разработки:
Вклеиваем ленту в рамку для фотографий:
Прототип я собирал на базе atmega8515 и все кнопки обрабатывались внешними прерываниями, но с переходом на младшую модель пришлось отказаться от одного прерывания, т.к. у atmega8 их 2 против 3 у 8515.
Проверка прототипа на обычной ленте:
Готовая плата:
С процессом разработки все стандартно: травим плату, сверлим отверстия, распаиваем компоненты начиная с SMD и заканчивая экраном и энкодером. Дополнительно на энкодер припеваем конденсаторы 104 (100nF) для того, что бы избежать дребезга контактов при срабатывании кнопок.
Прошиваю чип:
Разбор кода:
Проект можно скачать с github. Проект написан на C с использованием CVAVR.
Итак, если нужный индикатор найти не удалось необходимо изменить значения в данном массиве:
C:
// Цифры для kem-5461ar
unsigned char numbers[11] =
{
//PB7...PB0
//FBGCDpDEA
0b11010111, //0
0b01010000, //1
0b01100111, //2
0b01110101, //3
0b11110000, //4
0b10110101, //5
0b10110111, //6
0b01010001, //7
0b11110111, //8
0b11110101, //9
0b00100000 //-
};
C:
// Port B initialization
DDRB=(1<<DDB7) | (1<<DDB6) | (1<<DDB5) | (1<<DDB4) | (1<<DDB3) | (1<<DDB2) | (1<<DDB1) | (1<<DDB0);
PORTB=(1<<PORTB7) | (1<<PORTB6) | (1<<PORTB5) | (1<<PORTB4) | (1<<PORTB3) | (1<<PORTB2) | (1<<PORTB1) | (1<<PORTB0);
C:
// Port C initialization
DDRC=(0<<DDC6) | (0<<DDC5) | (0<<DDC4) | (1<<DDC3) | (1<<DDC2) | (1<<DDC1) | (1<<DDC0);
PORTC=(0<<PORTC6) | (0<<PORTC5) | (0<<PORTC4) | (1<<PORTC3) | (1<<PORTC2) | (1<<PORTC1) | (1<<PORTC0);
C:
// Разряды.
unsigned char digit[4] =
{
0b11111101, // 1 разряд слева.
0b11111011, // 2 разряд слева.
0b11110111, // 3 разряд слева.
0b11111110 // 4 разряд слева.
};
PORTC = digit[step];
, где step разряд, который нужно зажечь, а на порт B подать элемент нужный элемент массива numbers: PORTB = numbers[digitByNumbers]
, где digitByNumbers число от 0 до 10 - цифра, 11 - знак дефиса.У микроконтроллера atmega8a есть возможность обрабатывать два внешних прерывания. Для этого нужно подключиться к ногам PD2, PD3. Внешние прерывания используются для работы с энкодером. На PD2 подключен контакт энкодера отвечающий за поворот. Срабатывание этого прерывания означает что энкодер был повернут. Что бы определить в какую сторону был повернут энкодер считываем значение с другого контакта. высокий или низкий уровень на этом контакте говорит о направлении вращения:
C:
// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
// Считываем значения порта D4 и если уровень высокий,
// отнимаем единицу, если низкий, прибавляем единицу.
if(PIND.4)
{
if(digitByNumbers[digitNumber] < 9)
{
digitByNumbers[digitNumber]++;
}
}
else
{
if(digitByNumbers[digitNumber] > 0)
{
digitByNumbers[digitNumber]--;
}
}
}
C:
// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
if(digitNumber == 0)
{
digitNumber = 3;
}
else
{
digitNumber--;
}
}
#asm("sei")
. Включаем прерывания устанавливая в регистры GICR, MCUCR, GIFR следующие значения:
C:
// External Interrupt(s) initialization
// INT0: On
// INT0 Mode: Rising Edge
// INT1: On
// INT1 Mode: Falling Edge
GICR|=(1<<INT1) | (1<<INT0);
MCUCR=(1<<ISC11) | (0<<ISC10) | (1<<ISC01) | (1<<ISC00);
GIFR=(1<<INTF1) | (1<<INTF0);
C:
// Если кнопка нажата.
if(PIND.5 == 1 && timerIsStart == 0){
timerIsStart = 1;
//Выключаем прерывания энкодера
GICR = (0<<INT1) | (0<<INT0);
// Прерывание при переполнении: Вкл.
TIMSK |= (1 << TOIE0);
// Предустановленное значение: 0
TCNT0 = 0;
// Делитель: Clk/1024
TCCR0 = (1<<CS02) | (0<<CS01) | (1<<CS00);
PORTD.1 = 1;
}
Проверяем, что таймер переполнился 30 раз после чего уменьшаем количество секунд на одну, а если досчитали до 0, останавливаем таймер и снова готовимся к работе:
C:
// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0 = 0;
OverflowsRemain--;
if(OverflowsRemain == 0)
{
OverflowsRemain = 30;
if(digitByNumbers[3] > 0)
{
digitByNumbers[3]--;
return;
}
if(digitByNumbers[2] > 0)
{
digitByNumbers[2]--;
digitByNumbers[3] = 9;
return;
}
if(digitByNumbers[1] > 0)
{
digitByNumbers[1]--;
digitByNumbers[2] = 9;
digitByNumbers[3] = 9;
return;
}
if(digitByNumbers[0] > 0)
{
digitByNumbers[0]--;
digitByNumbers[1] = 9;
digitByNumbers[2] = 9;
digitByNumbers[3] = 9;
return;
}
// Тут таймер досчитал до нуля.
PORTD.1 = 0;
// Выключаем таймер.
TCCR0=(0<<CS02) | (0<<CS01) | (0<<CS00);
//Включаем прерывания энкодера
GICR |= (1<<INT1) | (1<<INT0);
// Предустановленное значение: 0
TCNT0 = 0;
timerIsStart = 0;
}
}
Изменено: