В простых встраиваемых устройствах есть большая проблема: очень хочется рандом. Нормальный такой рандом. Но если подумать, то контроллеру совершенно не от чего "рандомизировать" генератор случайных чисел. Ибо каждый старт контроллера "жизнь начинается с 0".
Эта проблема всплыла в пресловутом moodlamp (лампа настроения, переливающиеся цвета светодиода). Хочется чтоб "переливы" каждый раз начинались с нового цвета :)
После долгих дум пришёл к следующим идеям
Использовать шумы
Вокруг туча замечательных шумов и наводок! Хоть где то их можно использовать. Например читать с ног ADC и использовать для рандомизации эти значения.
Запоминать значение
По умолчанию библиотека gcc-avr инициализирует random seed value в 1. Можно заменить на что нить другое. Мы можем запоминать в EEPROM и инкрементировать это каждый запуск контоллера. Разумеется инициализировать генератор этим значением через srandom(val)
Использовать температуру
Некоторые контроллеры теперь идут с возможностью измерить температуру кристалла. Или же применение контроллера связанно с измерением каких-либо физических величин. В общем случае это поможет получать хоть сколько-то случайные величины
Использовать ^ (xor)
Вместо установки только что полученного значения с ADC/датчиков можно сделать с этим значением xor. Навроде srandom(newvalue ^ oldvalue).
Почему xor? Эта операция гарантированно возвращает новое значение (сущность побитовой операции такая, можно взглянуть в таблицу истинности)
Читать память
В процессе работы контроллера меняется значение каких-то переменных, мы может хаотично выбирать их из всего пространства доступной памяти. Это несколько может помочь рандомизировать значения. Причём было бы прикольно читать память при каком-нибудь событии, например по таймеру или по внешнему прерыванию
Пример будет ниже в моём решении
Сохранять собранные данные в своём EEPROM
Любые (или часть, или случайная часть) собранных цифр с ADC, датчиков, рандома, памяти сохранять в программируемую память. Это позволит при загрузки использовать больше цифр при начальной инициализации
Ещё...
Можно напридумывать куча способов, рассуждая в этом же направлении :)
Я очень доверяю народу из форумов avrfreaks, поэтому привожу их совет :
int makeRandom(int upper) {
return (int)((double)rand() / ((double)RAND_MAX + 1) * upper);
}
Моё решение
Скомпоновав мысли выше, вывел вот такое:
int makeRandom(int upper) {
return (int)((double)rand() / ((double)RAND_MAX + 1) * upper);
}
void storeRandomToEEPROM() {
uint8_t i;
for(i = 0; i < 20; i++)
eeprom_write_byte((uint8_t *)i, (uint8_t)(makeRandom(1000)%255));
for(i = 0; i < 20; i++)
if (makeRandom(1000) % 2)
eeprom_write_byte((uint8_t *)i, *((uint8_t*)(makeRandom(1000)%255)));
}
void randomizeFromEEPROM() {
uint16_t rndVal = (uint16_t)makeRandom(12345), i;
for(i = 0; i < 10; i++) rndVal ^= eeprom_read_word((uint16_t*)i);
srandom(rndVal);
}
. . .
void init() {
. . .
randomizeFromEEPROM();
storeRandomToEEPROM();
}
ISR (TIM0_OVF_vect) {
static uint16_t softcount=0;
if(++softcount == 0) storeRandomToEEPROM();
. . . .
}
Что получилось
До запуска прошивки состояние EEPROM:
>>> read eeprom 0 30 0000 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................| 0010 ff ff ff ff ff ff ff ff ff ff ff ff ff ff |.............. |
После первого запуска(и последующих картина памяти будет менятся)
>>> read eeprom 0 30 0000 00 35 97 bf 07 00 5d bc 53 4a 00 ea 07 13 06 01 |.5....].SJ......| 0010 00 00 dd 5a ff ff ff ff ff ff ff ff ff ff |...Z.......... |
При первом запуске генератор инициализируется значениями из EEPROM (при этом xor-ятся друг с другом), и тут же заполняет это место другими значениями, полученными из обычного рандома, после этого поверх случайным образом из случайного места в памяти записывается значение.