Skip to content

Instantly share code, notes, and snippets.

@DeeFuse
Last active April 6, 2022 18:05
Show Gist options
  • Save DeeFuse/9eccade5aa0b8fb2557a9ea17916403c to your computer and use it in GitHub Desktop.
Save DeeFuse/9eccade5aa0b8fb2557a9ea17916403c to your computer and use it in GitHub Desktop.
Random number generation using WDT + TIMER1 on AVR MCUs
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/sfr_defs.h>
#ifndef F_CPU
#define F_CPU (0)
#endif
#define true 1
#define false 0
static inline uint8_t rotl(const uint8_t value, int shift);
//////////////////////////////////////////////////////////////////////////
// USART Print functions //
//////////////////////////////////////////////////////////////////////////
#define BAUDRATE 115200
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)
void USART_init(void)
{
UBRR1H = (uint8_t)(BAUD_PRESCALLER>>8);
UBRR1L = (uint8_t)(BAUD_PRESCALLER);
UCSR1B = (1<<RXEN1)|(1<<TXEN1);
UCSR1C = (3<<UCSZ10);
}
unsigned char USART_receive(void)
{
while(!(UCSR1A & (1<<RXC1)));
return UDR1;
}
void USART_send( unsigned char data)
{
while(!(UCSR1A & (1<<UDRE1)));
UDR1 = data;
}
//////////////////////////////////////////////////////////////////////////
// TIMER1 / WDT initialization //
//////////////////////////////////////////////////////////////////////////
uint8_t sample = 0;
uint8_t sample_waiting = false;
#if !defined(WDIE)
#error Chip does not support Watchdog Interrupt without reset
#endif
void initWatchdog() {
cli();
MCUSR = 0;
// Start timed sequence
WDTCSR |= _BV(WDCE) | _BV(WDE);
// Put WDT into interrupt mode
// Set shortest time-out value = 2048 cycles (~16 ms)
WDTCSR = _BV(WDIE);
sei();
}
// Watchdog Timer Interrupt
ISR(WDT_vect)
{
sample = TCNT1L; // Ignore higher bits
sample_waiting = true;
}
void initTIMER1()
{
#define TIMER1_US 25000 // >> 16000
#if !defined(TCNT2)
#define TIMER1_RESOLUTION 256UL // Timer1 is 8 bit
#else
#define TIMER1_RESOLUTION 65536UL // Timer1 is 16 bit
#endif
static unsigned short pwmPeriod;
static unsigned char clockSelectBits;
// Init timer1
TCCR1B = _BV(WGM13); // set mode as phase and frequency correct pwm, stop the timer
TCCR1A = 0; // clear control register A
// Calculate Timer prescalers
const unsigned long cycles = ((F_CPU/100000 * TIMER1_US) / 20);
if (cycles < TIMER1_RESOLUTION) {
clockSelectBits = _BV(CS10);
pwmPeriod = cycles;
} else
if (cycles < TIMER1_RESOLUTION * 8) {
clockSelectBits = _BV(CS11);
pwmPeriod = cycles / 8;
} else
if (cycles < TIMER1_RESOLUTION * 64) {
clockSelectBits = _BV(CS11) | _BV(CS10);
pwmPeriod = cycles / 64;
} else
if (cycles < TIMER1_RESOLUTION * 256) {
clockSelectBits = _BV(CS12);
pwmPeriod = cycles / 256;
} else
if (cycles < TIMER1_RESOLUTION * 1024) {
clockSelectBits = _BV(CS12) | _BV(CS10);
pwmPeriod = cycles / 1024;
} else {
clockSelectBits = _BV(CS12) | _BV(CS10);
pwmPeriod = TIMER1_RESOLUTION - 1;
}
ICR1 = pwmPeriod;
TCCR1B = _BV(WGM13) | clockSelectBits;
}
int main()
{
USART_init(); //Call the USART initialization code
initWatchdog();
initTIMER1();
uint8_t current_bit = 0;
uint8_t result = 0;
int16_t bytesToGenerate = 256;
do
{
if (sample_waiting)
{
sample_waiting = false;
result = rotl(result, 1); // Spread randomness around
result ^= sample; // XOR preserves randomness
current_bit++;
if (current_bit > 7)
{
current_bit = 0;
USART_send(result);
bytesToGenerate--;
// TODO: feed into hydro functions
}
}
} while (bytesToGenerate > 0);
wdt_disable();
while(1)
{
}
}
// Rotate bits to the left
// https://en.wikipedia.org/wiki/Circular_shift#Implementing_circular_shifts
static inline uint8_t rotl(const uint8_t value, int shift) {
if ((shift &= sizeof(value)*8 - 1) == 0)
{
return value;
}
return (value << shift) | (value >> (sizeof(value)*8 - shift));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment