Игрушечный светофор

axill
Offline
Зарегистрирован: 05.09.2011

увидел сразу на этом и другом форумах обсуждение светофоров и загорелся сделать такой для ребенка (или для себя?)

чтобы проект не получился банальным, решено было его сделать на современных решениях - используя 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;
		}
    }
}

 

axill
Offline
Зарегистрирован: 05.09.2011

все детали на этой платке, приведу ее для представления миниатюрности:

com
Offline
Зарегистрирован: 06.09.2013

симпатично!

а из чего корпус делали?

axill
Offline
Зарегистрирован: 05.09.2011

Корпус напечатан на 3д принтере

http://radiokot.ru/forum/viewtopic.php?f=9&t=90961

Lipt0n
Offline
Зарегистрирован: 04.11.2013

Как аккуратно! Лудили обычным паяльником?

axill
Offline
Зарегистрирован: 05.09.2011

Lipt0n пишет:

Как аккуратно! Лудили обычным паяльником?

ничего сложного - ЛУТ + лужение сплавом розе в кипятке с лимонной кислотой

alexbmd
Offline
Зарегистрирован: 15.01.2016

axill пишет:

(для 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() только вконце включаем

Green
Онлайн
Зарегистрирован: 01.10.2015

Да, у вас всё серьёзно.) А мы с малой делали из подручных средств - картонка, бумага, 10 мм фанера в качестве подскавки. Правда, индикация на всех 4х сторонах, а на двух по 2 7-сегментных индикатора, типа как у взрослых.) Вот эта порнография.)

ua6em
ua6em аватар
Онлайн
Зарегистрирован: 17.08.2016

CW предпочитаете? )))

Green
Онлайн
Зарегистрирован: 01.10.2015

Ну никак от вас не спрячешься.) Да, есть такой грех.)