NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Project Help and Ideas » NerdKits RNG (Random Number Generator)

April 09, 2009
by wayward
wayward's Avatar

This is the very first thing I made with ATmega128 and parts in the NerdKit. Being a programmer, naturally I felt more at home with playing with the code itself than rewiring the components.

The code below is a very simple modification of the "tempsensor" project, included with your NerdKit. Instead of measuring temperature, it picks up the noise from the ADC (analog-to-digital converter), amplifies it, and generates an almost truly random 16-bit number. This value can then be used as the seed for the pseudorandom number generator in stdlib (for all you C folks out there). What you will use this for is entirely up to your imagination. :)

The code is commented throughout, but feel free to post questions.

Zoran

P.S. You will get "unused variable" warnings during compilation; they can be ignored. P.P.S. I can't test the code at the moment, I am fiddling with the LCD, but it should work. Let me know if it doesn't. :)

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>
#include <util/twi.h>

#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

void adc_init() {
  // set analog to digital converter
  // for external reference (5v), single ended input ADC0
  ADMUX = 0;

  // set analog to digital converter
  // to be enabled, with a clock prescale of 1/128
  // so that the ADC clock runs at 115.2kHz.
  ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

  // fire a conversion just to get the ADC warmed up
  ADCSRA |= (1<<ADSC);
}

uint8_t adc_get_next_bit() {
  // Lowest bit from ADC is the one most likely to change due to minute
  // variations in temperature as measured by LM37 and noise in power
  // supply.  Noise is generally a bad thing, but in our case,
  // the more - the better!  This function reads just the lowest bit
  // from ADC and discards the rest.

  // Wait until ADSC goes low (conversion completed).
  while (ADCSRA & (1<<ADSC)) { }

  // Read ADCL (AD low byte).  This one has the bit that we want.
  // (Mike mentions in tempsensor.c that ADCL has to be read first,
  // ATmega doc PDF page 259).
  uint16_t adc_lo = ADCL;

  // ADCH (AD high byte) has to be read for ADC to obey the next ADSC (see above).
  // We have no use for it, so we'll just XOR it with itself to
  // prevent the compiler from optimizing the read out of the code.
  uint16_t adc_hi_ignored = ADCH ^ ADCH;

  // Start the next conversion.
  ADCSRA |= (1<<ADSC);

  // Return the lowest bit of ADCL.
  return adc_lo & 1;
}

// Generate and return a random value.
uint16_t random_seed_from_ADC() {
  uint16_t seed = 0;
  int8_t i, b;

  // 'seed' is the value we are going to generate.
  // Starting with zeros in all 16 bits of the 16-bit unsigned integer,
  // we XOR the bits one by one with a highly volatile bit value from ADC,
  // and do it 100 times to mix things up really well.
  for (i = 0; i < 100; i++) {
    for (b = 0; b < 16; b++) {
      // XOR the seed with the random bit from ADC shifted to position b.
      seed ^= (adc_get_next_bit() << b);
    }
  }
  return seed;
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  // start up the Analog to Digital Converter
  adc_init();

  // start up the serial port
  uart_init();
  FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
  stdin = stdout = &uart_stream;

  while (1) {
    uint16_t seed = random_seed_from_ADC();
    lcd_home();
    lcd_write_int16(seed); lcd_write_string(PSTR("     "));
    printf_P(PSTR("%d\n"), seed);
  }

  return 0;
}
April 09, 2009
by wayward
wayward's Avatar

Very very interesting. I see only now that there is already one "hardware number generator" among the NerdKits projects. There wasn't one when I set out to write this. Oh well... :)

April 09, 2009
by hevans
(NerdKits Staff)

hevans's Avatar

You are right, a few months back we made a similar project to yours, but we only put it up as possible project and never made any tutorial around it, so I'm sure this post will help lots of people.

On another note, you mentioned that the seed you generate could be used to seed a pseudo random number generator. Another possible application we have thought of for this kind of program is to send the number generated back to the PC via the USB connector. The number could then be added into the Linux entropy pool. If you put this in a loop you could leave a NerdKit hooked up to your Linux box to make sure that any calls to /dev/random/ have plenty of entropy to work with even if you are not actively moving the mouse or touching any of its usual sources of randomness.

Humberto

April 09, 2009
by wayward
wayward's Avatar

Good idea, Humberto. I almost want to throw away my old PGP keypairs and generate new ones. :)

Another simple, but rather cool application would be a truly random electronic die, with 7 LEDs tied in 4 pairs plus one in the center. Or with the LCD, DIP switches to select the number of the sides on the "die", and the big switch to roll, make a universal die -- from 1d1 to 1d127. D&D fans can boast at their next RP session.

April 09, 2009
by thervey
thervey's Avatar

Wayward, this is pretty close to a project that I started working on. I used the included dip switch to be able to select any of the standard dice and used the temp sensor to generate a seed for the rand functions in stdlib. The only problem was that rand was generating very random numbers. My d20 would always roll like 4, 9, or 17. Haven't quite figured out what is going on with that though. Once I get it working I'll post the details.

Post a Reply

Please log in to post a reply.

Did you know that the Timer/Counter modules on the microcontroller can be configured to output a PWM (Pulse Width Modulation) signal? Learn more...