¿Puedo generar números aleatorios en PIC desde INTOSC vs crystal?

7

La generación de números verdaderamente aleatorios en un microcontrolador es una tarea difícil conocida.

Una forma es ejecutar dos relojes diferentes y medir la desviación del reloj entre ellos.

El PIC16F1783 que estoy usando tiene dos relojes. Uno que usa un cristal externo y el LFINTOSC que alimenta al perro guardián.

¿Hay alguna forma de llegar al LFINTOSC mientras se ejecuta desde el reloj de cristal externo?

Una de las ideas que tuve fue dejar que el watchdog caduque y verificar el valor de TMR2, pero no funciona porque TMR2 se restablece al reiniciar el watchdog.

Uno podría escribir un contador a la EEPROM en un circuito cerrado y dejar que el perro guardián caduque, y luego inspeccionar el contenido de la EEPROM, pero esto parece muy poco elegante. Edición: es mucho mejor escribir en RAM, como se sugiere en uno de los comentarios.

¿Hay una mejor manera? ¿Sin utilizar circuitos externos y sin confiar en el ruido de ADC (que puede no estar siempre presente)?

Hoja de datos: enlace

Editar: quiero que el número aleatorio genere un GUID para poder distinguir varios dispositivos que comparten el mismo bus RS485. Mi idea es usar algo similar al descubrimiento de dispositivos de 1 cable. Pero para hacer esto, necesito tener diferentes GUID en diferentes dispositivos, y me gustaría evitar la molestia de tener que programarlos con una ID única: s.

    
pregunta avl_sweden

1 respuesta

4

Bien, he encontrado una manera de hacer exactamente lo que pregunté en la pregunta, sin involucrar al WDT.

Es un poco pirateado, por decir lo menos, y sacrifica dos pines (EDITAR: solo se pica un pin) (pero no requiere componentes externos, por lo que si tiene dos pines no utilizados, será "gratis") .

La idea es utilizar el PSMC (Control Programable Switch Mode) del PIC16F1783.

Esto se puede sincronizar desde el INTOSC, mediante la conexión del HFINTOSC al 4x PLL, produciendo una frecuencia de 64MHz para sincronizar la salida PWM.

La salida PWM se puede enrutar a otro pin en la PCB.

Ahora, utilizando el cristal externo para sincronizar la CPU, la señal PWM se puede leer en un circuito cerrado. Dado que los dos relojes no están sincronizados, debería haber fluctuaciones entre los dos relojes, y la entrada PWM debería contener alguna fluctuación impredecible.

La forma de utilizar este jitter para crear valores aleatorios podría ser tener una matriz de suma de comprobación de 16 bytes. El TMR1 podría configurarse para ejecutarse lo más rápido posible, y cada vez que cambia la señal PWM, el valor de TMR1 podría escribirse al inicio de la matriz. Luego, se podría tomar la suma MD5 de la matriz y volver a escribirla en la misma matriz.

Al repetir este procedimiento unos cuantos miles de veces, se podría crear un hash MD5 de 16 bytes, que debería ser completamente aleatorio.

Sin embargo, un algoritmo de suma de comprobación MD5 apenas cabe en el PIC16F1783, por lo que es más útil en chips ligeramente más potentes. Sin embargo, se podría utilizar la misma idea, simplemente incrementando un byte por el valor TMR1, y déjelo envolver unas mil veces antes de considerarlo "lo suficientemente aleatorio".

La única forma en que esto podría fallar es si la fuente interna de 500 kHz se sincronizaría de alguna manera con el oscilador de cristal. No tengo idea de si eso es posible.

Actualización:

El siguiente código parece funcionar en la práctica en mi laboratorio:

#define RAND_SIZE 255
unsigned char random_data[RAND_SIZE];
void make_random_data()
{
//Used output pin: RC3. Make sure it is unconnected!
PSMC1PRH=0x0; //choose a very short period period
PSMC1PRL=0x3;

PSMC1DCH=0x00; //set 50% duty
PSMC1DCL=0x2;

PSMC1PHH=0;
PSMC1PHL=0;

PSMC1CONbits.PSMC1EN=1;

PSMC1CLK=1; //64mhz    
PSMC1STR0bits.P1STRD=1;
PSMC1OENbits.P1OED=1;
PSMC1PRSbits.P1PRST=1;
PSMC1PHSbits.P1PHST=1;
PSMC1DCSbits.P1DCST=1;

// Zero the id, to make sure any previous value does not influence result.
for(int i=0;i<RAND_SIZE;++i)
    random_data[i]=0;

// Generate the new random id:
for(int pass=0;pass<1000;++pass)
{
    for(int j=0;j<RAND_SIZE;++j)
    {
        unsigned char bitmask=1;
        for(signed char bitnum=7;bitnum>=0;--bitnum)
        {
            if (PORTCbits.RC3)
                random_data[j] ^= bitmask;
            bitmask<<=1;
        }
    }
}
}

Actualización 2:

Sólo se sacrifica un pin, ya que es posible leer el pin PWM de salida, no es necesario enrutarlo a una entrada y leer eso.

    
respondido por el avl_sweden

Lea otras preguntas en las etiquetas