Однажды сын попросил сделать ему лохотрон, а по научному – генератор случайных чисел (ГСЧ). Так, чтобы был в виде небольшого приборчика. Вначале хотел реализавать такую идею на Ардуино, но недавно ко мне из Китая пришла посылка и в ней Digispark – родственная, для Ардуино, платформа. Захотелось эту штучку освоить и тут как раз – и идея есть, и железо. Что получилось – читайте далее на странице.
Генератор случайных чисел интересен для всевозможных случайных выборов. Его можно использовать для игр, как электронный кубик, или выбора чего-либо из пунктов некоего списка.
Так как реализовывать этот проект решил на платформе Digispark, то соответственно был ограничен всего 6-ю ножками ввода/вывода, даже 5-ю, т.к. вывод P5 используется для сброса (reset). Соответственно устройство хотелось сделать автономным – значит питание от аккумулятора и компактным по размерам. Благо сама плата Digispark имеет размеры всего 24 * 18 мм. Вывод числа решил следать на семисегментном одноразрядном индикаторе.
Digispark построен на микроконтроллере ATtiny85-20U. Чтобы его запрограммировать пришлось настроить программную среду Arduino IDE. Установить соответственные драйверы под Digispark и т.п. Описывать этот процесс не буду, так как в интернете достаточно информации по этой платформе и её программированию. Некоторая поясняющая информация имеется в комментариях скетча. Смотрите внимательнее.
Можно дополнительно отметить, что питание ГСЧ осуществляется от Li-Ion аккумулятора напрямую. Поэтому важно энергопотребление. Пытаясь снизить энергопотребление – пришлось отпаять стабилизатор V1 78L05 и резистор R4 1кОм, подключающий светодиод питания (PWR LED). Энергопотребление было снижено до 12.2 мА в режиме готовности к выбору (светится точка индикатора). Потом убрал ещё два стабилитрона (D1 и D2) на шине USB у D- и D+ ток потребления снизился до 5,7 мА (светится точка индикатора).
Схема генератора случайных чисел
Для индикации числа используется одноразрядный семисегментный индикатор АЛС324А1 с общим катодом. Управление этим индикатором осуществляется через сдвиговый регистр на микросхеме SN74HC595N. Иначе не получалось – не хватало портов управления.
Советский индикатор АЛС324А1 можно заменить ему подобными АЛС321А1, АЛС333А1, АЛС334А1.
Кнопка SB1 осуществляет выбор случайного числа. Кнопка SB2 позволяет установить максимальное число в списке выбора. По нажатию на эту кнопку выводится значение максимального числа начиная с 2 и до 9, так как максимальное число на одном разряде как раз = 9. При последующем нажитии на SB2 происходит смена значений по кругу. При включении устройства устанавливается максимальное число = 9.
Скетч
Ниже представлен скетч генератора случайных чисел. Как обычно стараюсь подробно комментровать код, чтобы легко было понять программу. Файл со скетчем можно скачать в подразделе Приложение.
Стоит обратить внимание, что в функции setup() реализован тест цифр. После включения генератора, на индикаторе высвечиваются все сегменты. После того, как начинает выполнятся функция setup(), в ней происходит последовательный перебор цифр. Когда тест заканчивается, то на индикаторе остаётся светиться точка, что указывает на подключенное питание и является приглашением для выбора числа (кнопкой SB1), или установки максимального числа (кнопкой SB2).
Чтобы реализовать случайность числа, пришлось вначале использовать функцию randomSeed(). Аргументом для неё служит число микросекунд делёное по модулю два на 9, и с добавлением 1. А далее уже происходит вызов функции random(). Такой подход позволяет из псевдослучайной последовательности создать реальную случайную последовательность.
/* Digispark */
/*
Программирование Digispark Mr.ALB
http://www.mralb.ru/sections/programming/arduino_15.php
"Лохотрон" - генератор случайных чисел
случайный выбор от 1 до 9(максимально)
2020-07-02 Mr.ALB v1.0 начало
ток потребления = 26 мА (из них 3 мА на PWR LED)
2020-07-06 Mr.ALB v1.1 оптимизация энергопотребления:
- использование функций сна
- отключил PWR LED
- отпаял стабилизатор 78L05
ток потребления снизился до 12.2 мА
2020-07-07 Mr.ALB v1.0
отпаял стабилитроны с платы Digispark
ток потребления = 5,7 мА
2020-07-08 Mr.ALB v1.1 Обнаружено,
что при выборе от 1 до 7 всегда выпадает 1
-> Изменил программу для этого диапазона
-> работает корректно
*********************************************
ОПИСАНИЕ:
1. При включении на индикаторе светятся все сегменты
2. Когда загрузится программа - индикация теста цифр
3. После окончания теста цифр светится точка - можно
выбирать в диапазоне от 1 до 9
4. При нажатии на кнопку SB1 происходит
выбор случайного числа
5. Для смены максимального числа выбора - SB2
Плата: "Digispark (Default - 16.5 mhz)"
Вначале не подключать Digispark
Скомпилировать скетч ->
-> будет приглашение подключить
Digispark в течении 60 сек
-> произойдёт закрузка кода в модуль Digispark
Индикатор АЛС324А1 один 7 сегментный разряд
Соответстивие контактов:
SN74HC595N - АЛС321А1, АЛС324А1, АЛС333А1, АЛС334А1
8 - GND pin4 & pin12
- DP pin9
7 - G pin2
6 - F pin1
5 - E pin6
4 - D pin7
3 - C pin8
2 - B pin13
1 - A pin14
Digispark - SN74HC595N
P2 - DS pin14
P3 - ST_CP pin12
P4 - SH_CP pin11
GND - GND pin8
+5V - VCC pin16
Кнопки:
SB1 - между GND и P0
SB2 - между +5V и P1
Функции сна взяты у AlexGyver
****************************************/#include<avr/wdt.h>#include<avr/sleep.h>#include<avr/interrupt.h>// disable ADC (before power-off)#define adc_disable() (ADCSRA &=~(1<<ADEN))
// re-enable ADC//#define adc_enable() (ADCSRA |= (1<<ADEN))// Контакты SN74HC595N на Digispark#define DS 2
#define ST_CP 3
#define SH_CP 4
// Контакт на кнопку#define BT_START 0
#define BT_SET 1
// Цифры 0...9, точка, гашениеconstbyte digits[12] =
{
//B_DP_A_B_C_D_E_F_G
B01111110,//0
B00110000,//1
B01101101,//2
B01111001,//3
B00110011,//4
B01011011,//5
B01011111,//6
B01110000,//7
B01111111,//8
B01111011,//9
B10000000,//DP
B00000000,//Гашение
};
// Переменные для хранения состояния кнопкиboolean lastButtonSB1 =HIGH;
boolean currentButtonSB1 =HIGH;
boolean lastButtonSB2 =LOW;
boolean currentButtonSB2 =LOW;
byte digit = 0; // Цифраbyte max_digit = 10;// Максимальное число +1// Время удержания индикации значенияconstunsignedlong delay_look = 720000; // мксunsignedint myCount;
/* Настройка программы */voidsetup()
{
pinMode(DS,OUTPUT); // пин как выходpinMode(ST_CP,OUTPUT); // пин как выходpinMode(SH_CP,OUTPUT); // пин как выходpinMode(BT_START,INPUT_PULLUP); // ЗапускpinMode(BT_SET,INPUT); // Установка//Тест индикатора 0...9, точкаfor (byte i = 0; i <= 10; i++)
{
fnDrawDigit(i); // Индикация цифры// Задержка индикации цифры
fnDelay(210000);// в мкс
}
adc_disable(); // отключить АЦП (экономия энергии)
wdt_reset(); // инициализация ватчдога
wdt_enable(WDTO_60MS);// разрешаем ватчдог// Варианты длительности сна:// _15MS, _30MS, _60MS, _120MS, _250MS,// _500MS, _1S, _2S, _4S, _8S// разрешаем прерывания по ватчдогу.// Иначе будет резет.
WDTCR |= _BV(WDIE);
sei(); // разрешаем прерывания// Установка режима сна
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}
/* Рабочая программа */voidloop()
{
myCount++;
if (myCount > 65000) myCount = 10;
// Опрос кнопки SB1 - срабатывает при соединении с GND
currentButtonSB1 = fnDebounce(lastButtonSB1, BT_START);
if (lastButtonSB1 ==HIGH&& currentButtonSB1 ==LOW)
{
// Случайное начало псевдослучайной// последовательностиrandomSeed(myCount % 9 + 32);
// Диапазон выбора от 1 до (max_digit - 1)if (max_digit == 8)
{ // 2020-07-08 Для выбора от 1 до 7do
{
digit =random(1, 9);
}
while (digit == 8);
}
else digit =random(1, max_digit);
// Индикация выбранного числа
fnDrawDigit(digit);
// Задержка индикации числа
fnDelay(delay_look);
}
// Опрос кнопки SB2 - срабатывает при соединении с VCC
currentButtonSB2 = fnDebounce(lastButtonSB2, BT_SET);
if (lastButtonSB2 ==LOW&& currentButtonSB2 ==HIGH)
{
max_digit++;
if (max_digit > 10) max_digit = 3;
// Индикация максимального числа
fnDrawDigit(max_digit - 1);
// Задержка индикации максимального числа
fnDelay(delay_look);
}
// Переписываем состояние кнопки SB1
lastButtonSB1 = currentButtonSB1;
if (lastButtonSB1 ==HIGH)
fnDrawDigit(10);// Индикация точки// Переписываем состояние кнопки SB2
lastButtonSB2 = currentButtonSB2;
sleep_enable();// разрешаем сон
sleep_cpu(); // спать!
}
/***********
ФУНКЦИИ
***********/
/* Функция задержки
2020-07-06 Mr.ALB
при использовании прерываний
delay() и millis() не работают
поэтому задержку делаю с помощью
delayMicroseconds() в цикле
максимальный шаг задержки 16383
использую 10000.
*/void fnDelay(unsignedlong delay_max)
{
constunsignedint delay_bt = 10000;// мксfor (byte i = 0; i < delay_max / delay_bt; i++)
{
delayMicroseconds(delay_bt);// в мкс
}
}
/* Функция вывода цифры в разряде */void fnDrawDigit(byte digit)
{
// Подготовка для передачи данных в микросхемуdigitalWrite(ST_CP,LOW);
// Передача данных справа-налевоshiftOut(DS, SH_CP,LSBFIRST, digits[digit]);
// Возвращение состояния защёлкиdigitalWrite(ST_CP,HIGH);
}
/*****************************************************
Функция для подавления дребезга
Модифицирована, добавлен опрос конкретного pin-а
2018-07-23 Mr.ALB
******************************************************/boolean fnDebounce(boolean last,int btnPin)
{
// Читаем состояние кнопкиboolean current =digitalRead(btnPin);
if (last != current)
{
delayMicroseconds(10000);
current =digitalRead(btnPin);
}
return current;
}
/* Обработка прерывания */
ISR (WDT_vect)
{
// Разрешаем прерывания по ватчдогу.// Иначе будет реcет.
WDTCR |= _BV(WDIE);
}
При тестировании программы было обнаружено, что при максимальном числе = 7 всегда выпадает число 1. С чем это связано обнаружить не смог, причём на любых других значениях максимального числа всё работает корректно. Пришлось изменить программу под случай, когда происходит выбор от 1 до 7. Сейчас программа работает корректно. Скетч в Приложении обновлён.
Реализация
Вначале создаём макет, делаем соединения между всеми компонентами и пишем скетч. Благо, что есть макетные панельки и можно всю схему собрать без пайки практически за несколько минут. А вот потом уже пишется и переписывается неоднократно скетч и ищется оптимальное решение. Хотя бывает и схему приходится изменять. В этом устройстве тоже пришлось менять схему и из-за того, что на порте P1 подключен светодиод, то пришлось кнопку SB2 подключать не как SB1 между портом и GND, а между портом и +5 В (плюсом напряжения аккумулятора).
На плате размерами 48 * 35 мм разместил электронные компоненты. Соединения сделаны объёмным монтажом. Индикатор и сдвиговый регистр установлены на панельки 14 и 16 выводные соответственно. Digispark наложен сверху на плату и порты припаяны к плате короткими облуженными проводками.
На обороте видно, что сопротивления R1...R8 и конденсатор C1 у меня планарные, что позволяет уменьшить размеры платы.
После того, как была проверена смонтированная плата на работоспособность, со стороны пайки на жёстких медных проводниках припаял плату заряда аккумулятора.
Далее контактную колодку для аккумулятора отпаял, а сам аккумулятор припаял к плате. Отладка и монтаж железа закончен.
У меня есть плата заряда с разъёмом microUSB, но установил с разъёмом miniUSB, чтобы не путать с разъёмом microUSB Digispark-а. В ходе доработок придётся, может быть, ещё несколько раз программировать Digispark.
Корпус закончен. Общие размеры устройства: 62 * 39 * 32 мм. Далее небольшой фотоотчёт по конструкции корпуса. В нём три детали: сам корпус из ABS, верхняя боковая крышка из ABS, верхняя крышка из оргстекла. Всё крепится четырьмя винтиками М2 и четырьмя саморезами на 2,5 мм. Для кнопок сделаны два толкателя из пластиковой трубочки диаметром 3,5 мм. В них вплавлены небольшие проволочки для удержания толкателя в корпусе.
При сборке устройства плата ГСЧ и аккумулятор задвигаются в пазы в основном корпусе.
Далее боковая стенка закрывается крышкой и она закрепляется винтиками М4.
Потом устанавливаются толкатели на кнопки и всё закрывается прозрачной крышкой, которая закрепляется саморезами 2,5 мм.
Далее разные виды готового устройства.
Для наглядности реальных размеров получившегося приборчика, положил рядом стандартный спичечный коробок.
Разработка и изготовление такого устройства одно из самых быстрых. На всё ушло 4,5 дня. Получилась занятная штучка, надеюсь пригодится в жизни .