Last active
April 6, 2022 18:05
-
-
Save DeeFuse/9eccade5aa0b8fb2557a9ea17916403c to your computer and use it in GitHub Desktop.
Random number generation using WDT + TIMER1 on AVR MCUs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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