Игрушечный светофор
- Войдите на сайт для отправки комментариев
увидел сразу на этом и другом форумах обсуждение светофоров и загорелся сделать такой для ребенка (или для себя?)
чтобы проект не получился банальным, решено было его сделать на современных решениях - используя stepup преобразователь с питанием от одной батарейки и самый маленький МК в линейки атмела - tiny9 (в этой серии есть еще tiny4/5/10).
Извините, что не совсем ардуино, но разница минимальна, код моей программы можно практически напрямую вставлять в ардуино. Его особенность в том, что основной цикл программы практически пустой и все делается на прерываниях, по сути алгоритм работы светофора представлен в виде массива данных - пара чисел цвет и время. Еще одна особенность в том, что питание ни когда не отключается, но потребление в режиме сна минимизировано.
Нажатие кнопки сначала обрабатывается в прерывании PCINT где запускается таймер, по истечении таймера уже в обработчике таймера проверяется нажата ли еще кнопки и если нажато генерится событие нажатия. Так решена проблема с дребезгом контактов. Но так обрабатывается кнопка только в активном режиме. В режиме сна таймер не работает и мы реагируем на первое событие нажатия кнопки.
Во фьюзах запрограмирован RSTDISBL, чтобы использовать все четыре пина - три для светодиодов и один для кнопки. Кнопкой можно включать и выключать. Еще осталось место и можно запрограмировать на кнопку 2-3 режима работы. Питание отключается и по тайматуту в 20 минут. Надеюсь такие хитрости обеспечат весьма продолжительную работу от одной пальчиковой батарейки. Напряжение питания выбрано минимально-оптимальноре - 2.4В, оно по идее должно сохраняться до глубокой разрядки батарейки (степап перестает работать при падении напряжения ниже 0,35в). Частота МК снижена до оптимального значения 128000/16=8000Гц
степаап сделан на mcp1640, заодно хотелось освоить эту микросхему, планирую ее дальше использовать в батарейных сенсорах.
схема:



скетч ниже (для atmel studio)
/*
* P7005_traffic_lights_v1.c
*
* Created: 09.11.2013 18:46:39
* Author: axillent
*/
#define RED_PIN (1 << PB2)
#define YELLOW_PIN (1 << PB1)
#define GREEN_PIN (1 << PB0)
#define BUTTON (1 << PB3)
#define TIMEOUT 20 * 60 * 10 // seconds * 10 - timeout to switch off
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
volatile enum { power_off=0, power_on, power_to_on } status;
enum color_enum { led_red=0, led_yellow, led_green, led_none };
typedef enum color_enum color_enum;
typedef struct {
uint8_t color;
uint8_t delay;
} led_data;
volatile struct {
uint8_t timer;
uint8_t pressed;
} button;
volatile struct {
uint8_t index;
uint8_t timer;
// const led_data* data;
uint8_t size;
} run_data;
volatile uint16_t timeout;
const led_data data01[] PROGMEM = {
{ led_green, 150 },
{ led_none, 5 },
{ led_green, 5 },
{ led_none, 5 },
{ led_green, 5 },
{ led_none, 5 },
{ led_green, 5 },
{ led_yellow, 15 },
{ led_red, 150 },
{ led_red, 5 },
{ led_none, 5 },
{ led_red, 5 },
{ led_none, 5 },
{ led_red, 5 },
{ led_none, 5 },
{ led_yellow, 15 }
};
void redOff() { PORTB |= RED_PIN; }
void redOn() { PORTB &= ~RED_PIN; }
void yellowOff() { PORTB |= YELLOW_PIN; }
void yellowOn() { PORTB &= ~YELLOW_PIN; }
void greenOff() { PORTB |= GREEN_PIN; }
void greenOn() { PORTB &= ~GREEN_PIN; }
void switchLed(color_enum color, uint8_t s) {
switch(color) {
case led_green:
if(s) greenOn(); else greenOff();
break;
case led_yellow:
if(s) yellowOn(); else yellowOff();
break;
case led_red:
if(s) redOn(); else redOff();
break;
case led_none:
break;
}
}
void start(const led_data* d, uint8_t size) {
//run_data.data = d;
run_data.index = 0;
run_data.size = size;
switchLed(pgm_read_byte(&data01[0].color), 1);
run_data.timer = pgm_read_byte(&data01[0].delay);
}
ISR(TIM0_COMPA_vect) {
if(status == power_on) {
if(run_data.timer) {
if(!--run_data.timer) {
//redOn();
switchLed(pgm_read_byte(&(data01[run_data.index].color)), 0);
if(++run_data.index >= run_data.size) run_data.index = 0;
switchLed(pgm_read_byte(&(data01[run_data.index].color)), 1);
run_data.timer = pgm_read_byte(&data01[run_data.index].delay);
}
}
}
// button
if(button.timer) if(!--button.timer && (PINB & BUTTON)) button.pressed = 1;
// timeout
if(timeout) if(!--timeout) status = power_off;
}
ISR(PCINT0_vect) {
if(status == power_on) {
if(!button.pressed && (PINB & BUTTON)) button.timer = 1;
} else {
if(PINB & BUTTON) button.pressed = 1;
}
}
int main(void)
{
// switch to 128000 hz
CCP = 0xD8;
CLKMSR = (1 << CLKMS0);
CCP = 0xD8;
CLKPSR &= ~0b1111;
CCP = 0xD8;
CLKPSR |= (1 << CLKPS2);
status = power_to_on;
button.pressed = 0;
button.timer = 0;
while(1)
{
switch(status) {
case power_off:
//DDRB &= ~(RED_PIN | YELLOW_PIN | GREEN_PIN);
PORTB |= (RED_PIN | YELLOW_PIN | GREEN_PIN | BUTTON);
ACSR |= (1 << ACD);
//PRR |= (1 << PRTIM0) | (1 << PRADC);
//SMCR = (1 << SM1);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sei();
sleep_enable();
//SMCR |= (1 << SE);
sleep_cpu();
//SMCR &= ~(1 << SE);
sleep_disable();
//PRR &= ~(1 << PRTIM0);
//sei();
if(button.pressed) {
status = power_to_on;
button.pressed = 0;
}
break;
case power_on:
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
sei();
sleep_cpu();
sleep_disable();
//sei();
if(button.pressed) {
status = power_off;
button.pressed = 0;
}
break;
case power_to_on:
cli();
PORTB |= (RED_PIN | YELLOW_PIN | GREEN_PIN | BUTTON);
DDRB |= RED_PIN | YELLOW_PIN | GREEN_PIN;
ACSR |= (1 << ACD);
PCICR |= (1 << PCIE0);
PCMSK |= (1 << PCINT3);
TCCR0B = (1 << CS01) | (1 << WGM02);
OCR0A = 1600 / 16;
TCNT0 = 0;
TIMSK0 |= (1 << OCIE0A);
timeout = TIMEOUT;
cli();
start(data01, sizeof(data01)/sizeof(data01[0]));
status = power_on;
sei();
break;
}
}
}
все детали на этой платке, приведу ее для представления миниатюрности:
симпатично!
а из чего корпус делали?
Корпус напечатан на 3д принтере
http://radiokot.ru/forum/viewtopic.php?f=9&t=90961
Как аккуратно! Лудили обычным паяльником?
Как аккуратно! Лудили обычным паяльником?
ничего сложного - ЛУТ + лужение сплавом розе в кипятке с лимонной кислотой
(для atmel studio)
/* * P7005_traffic_lights_v1.c * * Created: 09.11.2013 18:46:39 * Author: axillent */ case power_to_on: cli(); PORTB |= (RED_PIN | YELLOW_PIN | GREEN_PIN | BUTTON); DDRB |= RED_PIN | YELLOW_PIN | GREEN_PIN; ACSR |= (1 << ACD); PCICR |= (1 << PCIE0); PCMSK |= (1 << PCINT3); TCCR0B = (1 << CS01) | (1 << WGM02); OCR0A = 1600 / 16; TCNT0 = 0; TIMSK0 |= (1 << OCIE0A); timeout = TIMEOUT; cli(); start(data01, sizeof(data01)/sizeof(data01[0])); status = power_on; sei(); break; } } }простите а второй cli() тут зачем? мы же один уже отправил ранее, а sei() только вконце включаем
Да, у вас всё серьёзно.) А мы с малой делали из подручных средств - картонка, бумага, 10 мм фанера в качестве подскавки. Правда, индикация на всех 4х сторонах, а на двух по 2 7-сегментных индикатора, типа как у взрослых.) Вот эта порнография.)
CW предпочитаете? )))
Ну никак от вас не спрячешься.) Да, есть такой грех.)