Обычно в AVR при помощи мультиплексора можно подключить вход АЦП к земле или к внутреннему источнику опорного напряжения. При этом никаких внешних цепей не требуется (желательно повесить конденсатор на AREF, но для генерации случайных чисел можно обойтись и без него).
Итак, алгоритм:
1. Настраиваем АЦП (опорное напряжение - AVCC, тактовая частота - лучше побольше, но в пределах допустимого, разрядность 10 бит).
2. Мультиплексором подключаем вход АЦП к земле.
3. запускаем преобразование.
4. Ждём 10 мкс.
5. Переключаем вход на источник опорного напряжения
6. Без каких либо задержек выполняем 16 преобразований подряд, младшие биты результатов собираем в 16-разрядную переменную.
7. Полученное значение используем как аргумент для srand().
8. Выключаем АЦП, чтобы что-нибудь не поломалось в остальном коде, если вдруг он использует АЦП.
Случайное число появляется за счёт того, что напряжение на выходе мультиплексора не может измениться мгновенно, и АЦП успевает промежуточные значения между 0 В и напряжением опорного источника. В даташите рекомендуют сделать задержку после переключения входа перед следующим преобразованием. А вот если её не делать, то можно получить случайное число.
Пример кода для ATmega8. Для других МК нужно изменить коды входов мультиплексора в соответствии с даташитом.
Код: Выделить всё
#include <inttypes.h>
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
uint16_t adc_get_random(void){
//This is a tricky way to generate a random number.
//First AVCC is selected as reference, and GND is selected as input, the conversion is performed.
//Then the input is changed to Vbg, and WITHOUT ANY DELAY 16 conversions are performed.
//Because of the voltage on the MUX output can't change immediately, random numbers are generated.
ADCSRA = (1 << ADEN) | (0 << ADATE) | (1 << ADPS2) | (1 << ADPS1) | (0 << ADPS0); //F_CPU / 64
ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR) | 0b1111; //GND as ADC input
ADCSRA |= (1 << ADSC);
while(ADCSRA & (1 << ADSC));
_delay_us(10);
ADMUX = (0 << REFS1) | (1 << REFS0) | (0 << ADLAR) | 0b1110; //Vbg as ADC input
uint16_t result = 0;
uint8_t tmp;
uint8_t i;
for(i = 0; i < 16; i++){
ADCSRA |= (1 << ADSC);
while(ADCSRA & (1 << ADSC));
tmp = ADCL;
result <<= 1;
if(tmp & 0x01)
result |= 0x01;
tmp = ADCH;
}
ADCSRA = 0;
return result;
}
//Usage example
int main(void){
//...
srand(adc_get_random());
//...
DDRB = 0xFF;
while(1){
PORTB = rand() & 0xFF;
_delay_ms(1000);
//...
}
}
Дополнительно можно скомбинировать этот способ с основанным, например, на начальном состоянии RAM. Достаточно в качестве аргумента srand() указать, например,
Код: Выделить всё
adc_get_random() ^ seed
слева!



