Емельянов Эдуард Владимирович (eddy_em) wrote,
Емельянов Эдуард Владимирович
eddy_em

Category:

Аппаратные псевдослучайные числа на микроконтроллере

Для того, чтобы получить псевдослучайные числа на МК, можно использовать его АЦП: как минимум один младший бит непременно будет плавать. Чтобы улучшить "энтропию", можно прицепить к какому-нибудь внешнему входу АЦП небольшую антенну. А можно просто использовать внутренние каналы: измерения температуры и напряжения.

Настроим на STM32F103 АЦП для работы в однократном режиме:
void adc_setup(){
    //GPIOC->CRL |= CRL(0, CNF_ANALOG|MODE_INPUT); // uncomment to use ADC10 @PC0
    uint32_t ctr = 0;
    // Enable clocking
    RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
    RCC->CFGR &= ~(RCC_CFGR_ADCPRE);
    RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8; // ADC clock = RCC / 8
    // sampling time - 7.5 cycles for channels 10, 16 and 17
    ADC1->SMPR1 = ADC_SMPR1_SMP10_0 | ADC_SMPR1_SMP16_0 | ADC_SMPR1_SMP17_0;
    // single mode, enable vref & Tsens; wake up ADC
    ADC1->CR2 |= ADC_CR2_TSVREFE | ADC_CR2_ADON;
    // wait for Tstab - at least 1us
    while(++ctr < 0xff) nop();
    // calibration
    ADC1->CR2 |= ADC_CR2_RSTCAL;
    ctr = 0; while((ADC1->CR2 & ADC_CR2_RSTCAL) && ++ctr < 0xfffff);
    ADC1->CR2 |= ADC_CR2_CAL;
    ctr = 0; while((ADC1->CR2 & ADC_CR2_CAL) && ++ctr < 0xfffff);
}

Считывать значения несложно:
// 16-Tsens, 17-Vref
static uint8_t const ADCchno[ADC_CHANNELS_NO] = {16, 17};

uint16_t getADCval(uint8_t ch){
    if(ch >= ADC_CHANNELS_NO) return 0;
    ADC1->SQR3 = ADCchno[ch];
    ADC1->SR = 0;
    // turn ON ADC
    ADC1->CR2 |= ADC_CR2_ADON;
    while(!(ADC1->SR & ADC_SR_EOC)); // wait end of conversion
    return ADC1->DR;
}

Выбор номера канала выполняется при помощи регистра SQR3. В данном случае нам не нужно, чтобы АЦП молотил постоянно. Иначе можно было бы настроить его в связке с DMA.
Ну, а дальше просто читаем младший бит то температуры, то напряжения, заполняем 32-битное число и "мешаем" биты по алгоритму XOR shift из википедии:
uint32_t getRand(){
    uint32_t r = 0;
    for(int i = 0; i < 16; ++i){
        r <<= 1;
        r |= (getADCval(0) & 1);
        r <<= 1;
        r |= (getADCval(1) & 1);
    }
    r ^= r << 13;
    r ^= r >> 17;
    r ^= r << 5;
    return r;
}

И если первоначальные данные не очень-то похожи на равномерное распределение, то после XOR shift получается очень даже ничего. Собранные 10000 псевдослучайных чисел:

Эти же значения в отсортированном виде:

Ну и гистограмма из распределения старших восьми битов:


Меня полученный результат вполне устраивает. И, в отличие от обычного генератора псевдослучайных чисел, здесь можно гарантировать, что повторяющихся последовательностей не будет. Reposted from dreamwidth: https://eddy-em.dreamwidth.org/288125.html.
Tags: stm32, железяки
Subscribe

  • Чем бы таким заменить STM32F072C8T6?

    Полез сейчас на али цены посмотреть, а там… В среднем уже по 600-700 рублей за штучку просят! Вообще охамели. И это - гарантированно БУшные ведь!.. А…

  • Релюшки на CAN-шине

    Закончил с прошивкой для новой железяки. Как "наследница" USB-CAN переходника, она умеет все то же самое + несколько специфичных вещей (опрос…

  • Дохлый SSD

    Писал уже о китайском SSD, сдохшем за полтора месяца работы. Вот он, герой: Сегодня у нас опять работы с оптоволоконным спектрографом на цейссе,…

promo eddy_em august 17, 2019 12:33 3
Buy for 10 tokens
Юра намедни напечатал корпус для хронометра. Для первого блина получилось неплохо: И еще немного фотографий:
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic
  • 5 comments