Блок с датойБлок с временемБлок с возрастом сайта
Mr.ALB

    Анатолий Беляев (aka Mr.ALB). Персональный сайт

    Да пребудут с вами Силы СВЕТА!

     

    Ардуино (Arduino). #15

    Генератор случайных чисел

    Однажды сын попросил сделать ему лохотрон, а по научному – генератор случайных чисел (ГСЧ). Так, чтобы был в виде небольшого приборчика. Вначале хотел реализавать такую идею на Ардуино, но недавно ко мне из Китая пришла посылка и в ней Digispark – родственная, для Ардуино, платформа. Захотелось эту штучку освоить и тут как раз – и идея есть, и железо. Что получилось – читайте далее на странице.

    ГСЧ – Лохотрон
    ГСЧ – Лохотрон

    Подразделы


     

    Описание

    Генератор случайных чисел интересен для всевозможных случайных выборов. Его можно использовать для игр, как электронный кубик, или выбора чего-либо из пунктов некоего списка.

    Так как реализовывать этот проект решил на платформе Digispark, то соответственно был ограничен всего 6-ю портами ввода/вывода, даже 5-ю, т.к. порт P5 используется для сброса (reset). Соответственно устройство хотелось сделать автономным – значит питание от аккумулятора и компактным по размерам. Благо сама плата Digispark имеет размеры всего 24 * 18 мм. Вывод числа решил следать на семисегментном одноразрядном индикаторе.

    Digispark построен на микроконтроллере ATtiny85-20U. Чтобы его запрограммировать пришлось настроить программную среду Arduino IDE. Установить соответственные драйверы под Digispark и т.п. Описывать этот процесс не буду, так как в интернете достаточно информации по этой платформе и её программированию. Некоторая поясняющая информация имеется в комментариях скетча. Смотрите внимательнее.

    Digispark на микроконтроллере ATtiny85-20SU
    Digispark на микроконтроллере ATtiny85-20SU

    Можно дополнительно отметить, что питание ГСЧ осуществляется от Li-Ion аккумулятора напрямую. Поэтому важно энергопотребление. Пытаясь снизить энергопотребление – пришлось отпаять стабилизатор V1 78L05 и резистор R4 1кОм, подключающий светодиод питания (PWR LED). Энергопотребление было снижено до 12.2 мА в режиме готовности к выбору (светится точка индикатора). Потом убрал ещё два стабилитрона (D1 и D2) на шине USB у D- и D+ ток потребления снизился до 5,7 мА (светится точка индикатора).

    Digispark. Схема принципиальная
    Digispark. Схема принципиальная

     

    Схема генератора случайных чисел

    Схема электрическая принципиальная
    Pic 1. Схема электрическая принципиальная

    Для индикации числа используется одноразрядный семисегментный индикатор АЛС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 В (плюсом напряжения аккумулятора).

    Макет устройства
    Pic 2. Макет устройства

    На плате размерами 48 * 35 мм разместил электронные компоненты. Соединения сделаны объёмным монтажом. Индикатор и сдвиговый регистр установлены на панельки 14 и 16 выводные соответственно. Digispark наложен сверху на плату и порты припаяны к плате короткими облуженными проводками.

    Вид сверху
    Pic 3. Вид сверху

    На обороте видно, что сопротивления R1...R8 и конденсатор C1 у меня планарные, что позволяет уменьшить размеры платы.

    Вид снизу
    Pic 4. Вид снизу

    После того, как была проверена смонтированная плата на работоспособность, со стороны пайки на жёстких медных проводниках припаял плату заряда аккумулятора.

    Вид снизу + плата заряда аккумулятора
    Pic 5. Вид снизу + плата заряда аккумулятора

    Далее контактную колодку для аккумулятора отпаял, а сам аккумулятор припаял к плате. Отладка и монтаж железа закончен.

    Вид снизу. Плата заряда аккумулятора установлена
    Pic 6. Вид снизу. Плата заряда аккумулятора установлена

    У меня есть плата заряда с разъёмом microUSB, но установил с разъёмом miniUSB, чтобы не путать с разъёмом microUSB Digispark-а. В ходе доработок придётся, может быть, ещё несколько раз программировать Digispark.

    Вид сбоку
    Pic 7. Вид сбоку

    Корпус закончен. Общие размеры устройства: 62 * 39 * 32 мм. Далее небольшой фотоотчёт по конструкции корпуса. В нём три детали: сам корпус из ABS, верхняя боковая крышка из ABS, верхняя крышка из оргстекла. Всё крепится четырьмя винтиками М2 и четырьмя саморезами на 2,5 мм. Для кнопок сделаны два толкателя из пластиковой трубочки диаметром 3,5 мм. В них вплавлены небольшие проволочки для удержания толкателя в корпусе.

    Комплект сборки ГСЧ
    Pic 8. Комплект сборки ГСЧ

    При сборке устройства плата ГСЧ и аккумулятор задвигаются в пазы в основном корпусе.

    Процесс сборки
    Pic 9. Процесс сборки

    Далее боковая стенка закрывается крышкой и она закрепляется винтиками М4.

    Процесс сборки
    Pic 10. Процесс сборки

    Потом устанавливаются толкатели на кнопки и всё закрывается прозрачной крышкой, которая закрепляется саморезами 2,5 мм.

    Устройство собрано
    Pic 11. Устройство собрано

    Далее разные виды готового устройства.

    Вид на micro-USB для программирования
    Pic 12. Вид на micro-USB для программирования
    Вид на mini-USB для зарядки аккумулятора
    Pic 13. Вид на mini-USB для зарядки аккумулятора

    Для наглядности реальных размеров получившегося приборчика, положил рядом стандартный спичечный коробок.

    Сравнительный вид
    Pic 14. Сравнительный вид

    Разработка и изготовление такого устройства одно из самых быстрых. На всё ушло 4,5 дня. Получилась занятная штучка, надеюсь пригодится в жизни улыбка

    2020-07-06
    Анатолий Беляев.

     

    Приложение

    Используемые библиотеки и программы:


    . Mr.ALB
    Предыдущая страница Страница 16