Доделал таки я игрушку - безделушку.
Почти законченное изделие получилось, этакий гиковский брелок. Осталось корпус подобрать или просто эпоксидкой залить, что бы можно было батарейку только менять и "тиньку" снимать для перепрошивки, если вдруг, захочется чего в код добавить. А так, вот... Можно нажимать на кнопку и получать случайное число от 1 до 6. Как на кубике.
Click to view
Игрушка очень понравилась моему двухлетнему сынишке. Потыкав в единственную кнопку и помигав светодиодами, он отдаёт мне игрушку и на пальцах просит зажечь определённое количество светодиодов. Объяснить ему, что они зажигаются случайным образом мне не удалось :) Пришлось тыкать в кнопку и ждать когда загорится нужное количество.
Код прошивки "тиньки" привожу ниже...
#include
#include
#define F_CPU 1200000UL // 1.2 MHz
#define SLEEP_DELAY (30UL * F_CPU / 256UL) // Задержка примерно в 5 - 10 секунд для перехода в режим сна
volatile uint8_t random_counter; // счётчик "случайного" значения
volatile uint8_t show_time; // счётчик ограничивающий по времени показ отдельного сегмента значения на кубике
volatile uint8_t cur_dice_value; // текущее значение на грани кубика
volatile uint32_t sleep_counter; // счётчик тиков до засыпания
volatile boolean button = 0; // флаг нажатия на кнопку
volatile uint8_t cur_bit; // номер сегмента, который нужно отобразить в текущий момент изменяется от 1 до 4 (PINB1..PINB4)
volatile uint8_t cur_portb_status; // текущие состояние регистра, отвечающего за отображение значения на кубике (PORTB)
// Для платы с выводными компонентами
unsigned char digits[6] = {
0b00010000, //1
0b00000010, //2
0b00010100, //3
0b00000110, //4
0b00010110, //5
0b00001110 //6
};
// Прерывание по нажатию на кнопку
ISR(PCINT0_vect)
{
// Если кнопка нажата и флаг нажатия на кнопку равен нулю
if ((!(PINB&0b00000001)) && (!button)){
button = 1; // Поднимаем флаг нажатия на кнопку
sleep_counter = 1; //Сбрасываем счётчик тиков до засыпания
}
}
int main(void){
cli();
// Инициируем порты
PORTB = 0b00000000;
DDRB = 0b00011110;
GIMSK = 0b00100000;
PCMSK = 0b00000001;
WDTCR |= 0b00000000; // отключаем watchdog timer
// Инициируем переменные, обнуляем счётчики
random_counter = 0;
sleep_counter = 0;
show_time = 0;
cur_bit = 1;
cur_dice_value = digits[5]; // показывает шестёрку при включении
sei();
while(1){
sleep_counter++; // Увеличиваем счётчик тиков
// Если счётчик тиков досчитал до заданного для сна значения, отправляем микроконтроллер спать
if (sleep_counter >= SLEEP_DELAY)
{
sleep_counter = 0;
system_sleep();
}
// Постоянно увеличиваем счётчик "случайного" значения, если досчитали до пяти, то обнуляем его
if (random_counter < 5) random_counter++;
else random_counter = 0;
// Если поднят флаг нажатия на кнопку
if (button){
animation(); // показываем анимацию
cur_dice_value = digits[random_counter]; // сохраняем в переменной cur_dice_value выпавшее на кубике значение
button = 0; // опускаем флаг нажатия на кнопку
}
// Увеличиваем счётчик показа текущего сегмента значения на кубике
show_time++;
// При достижении заданного значения меняем сегмент, который надо сейчас показать на следующи и обнуляем счётчик показа текущего сегмента
if (show_time > 50){
if (cur_bit < 4) cur_bit++;
else cur_bit = 1;
show_time = 0;
cur_portb_status = (1 << cur_bit) & cur_dice_value;
}
// Отображаем текущий сегмент значения на кубике
PORTB = cur_portb_status;
}
}
// Функция отображения мельтешения граней на кубике
void animation(){
for(int i=0; i<6; i++){
PORTB = digits[i];
_delay_ms(30);
}
}
// Функция сна
void system_sleep(){
PORTB = 0b00000000;
ADCSRA &= ~(1 << ADEN); // перед сном отключим АЦП
ACSR |= (1 << ACD); // и компаратор
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
while(!sleep_counter){
sleep_enable();
sleep_cpu();
}
}