Однажды сын попросил сделать ему лохотрон
, а по научному – генератор случайных чисел (ГСЧ). Так, чтобы был в виде небольшого приборчика. Вначале хотел реализавать такую идею на Ардуино, но недавно ко мне из Китая пришла посылка и в ней 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, точка, гашение const byte 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 // Время удержания индикации значения const unsigned long delay_look = 720000; // мкс unsigned int myCount; /* Настройка программы */ void setup() { 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); } /* Рабочая программа */ void loop() { 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 до 7 do { 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(unsigned long delay_max) { const unsigned int 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 дня. Получилась занятная штучка, надеюсь пригодится в жизни
Используемые библиотеки и программы: