Programació en mpasm del PIC 16F690

Referència Trucs Perifèrics   Recursos CITCEA
Tutorial Exemples Projectes   Inici

Generació de valors aleatoris

Per a la generació de números aleatoris podem fer servir la funció Random que dona resultats de 16 bits. En realitat, es tracta d'un generador de nombres pseudo-aleatoris, per tant comença amb un valor inicial (llavor) i va generant una seqüència a partir d'ella. El funcionament es basa en una rotació de LED cap a l'esquerra en la que, per la dreta, s'entra un bit que s'ha generat a partir dels bits de la llavor. El valor generat serà la llavor per a la següent vegada. El següent gràfic (on el símbol representa la funció o exclusiva) ens mostra com funciona. La funció o exclusiva dona 1 si els dos bits d'entrada són diferents i zero si són iguals.

Generació de nombres pseudo-aleatoris

En el programa següent generem, a cada cicle, un valor aleatori i en mostrem els quatre bits menys significatius als LED.

#include <p16F690.inc>
	__config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
	cblock	0x20
Rand:2			; Dos bytes on guardarem el valor aleatori
Auxrand:2		; Variable auxiliar per a Random
Retard1			; Variables de retard	
Retard2
Retard3
	endc
	org 0
Inici
	bsf	STATUS,RP0		; Tria el banc 1
	clrf	TRISC			; Posa tots els bits del port C com a sortida
	bcf	STATUS,RP0		; Tria el banc 0
	movlw	b'01010101'		; Agafa un valor concret
	movwf	Rand			; Ho posa com a llavor
	movwf	Rand+1			; Ho posa com a llavor
	clrf	PORTC			; Apaga els LED
Bucle
	call	Random			; Genera un valor aleatori
	movf	Rand,w			; Llegeix el valor aleatori
	movwf	PORTC			; I ho posa als LED
	movlw	.5			; Retard de 1 s
	call	Rets
	goto	Bucle			; Repetim-ho...
					;
					; Funció que genera valors aleatoris
					;
Random
	clrf	Auxrand			; Posa a zero les variables auxiliars
	clrf	Auxrand+1		; Posa a zero les variables auxiliars
					; Rand+1:	15 14 13 12 11 10  9  8
					; Rand:		 7  6  5  4  3  2  1  0
	rlf	Rand+1,w		; Roda a l'esquerra i deixa el resultat a W
					; Rand+1:	15 14 13 12 11 10  9  8
					; W:		14 13 12 11 10  9  8  X
	xorwf	Rand+1,w		; Fa un xor amb l'original i deixa el resultat a W
	movwf	Auxrand+1		; El bit de l'esquerra serà la xor del 14 i el 15
					;
	swapf	Rand+1,f		; Permuta els nibbles
					; Rand+1:	11 10  9  8 15 14 13 12
					; Rand:		 7  6  5  4  3  2  1  0
	swapf	Rand,w			; Guarda els nibbles permutats a W
	movwf	Auxrand			; Auxrand:	 3  2  1  0  7  6  5  4
	bcf	STATUS,C		; Entrarem un zero
	rlf	Auxrand,f		; Hem rodat a l'esquerra, ara el bit 3 és a C
	btfsc	STATUS,C		; Si C és zero no fem res
	bsf	Auxrand,0		; Si C és 1, activem el bit de la dreta
					; Auxrand:	 2  1  0  7  6  5  4  3
					; Rand+1:	11 10  9  8 15 14 13 12
					; Rand:		 7  6  5  4  3  2  1  0
	movf	Auxrand,w		; Copia Auxrand a W
	xorwf	Rand+1,w		; El bit de la dreta de W serà la xor de 3 i 12
	swapf	Rand+1,f		; Ho torna a deixar com estava
					; Rand+1:	15 14 13 12 11 10  9  8
					; Rand:		 7  6  5  4  3  2  1  0
	andlw	b'00000001'		; Posa w a zero excepte el bit de la dreta
	rlf	Auxrand+1,f		; Agafem el bit de l'esquerra i el posem a C
					; Aquest bit era la xor del 14 i el 15
	rlf	Rand,f			; Rodem Rand a l'esquerra entrant el bit que volem per la dreta
	xorwf	Rand,f			; Li fem una xor amb W que afecta només al bit de la dreta
	rlf	Rand+1,f		; Rodem Rand+1 a l'esquerra entrant el bit que ha sortit de Rand
	return
Rets					; Funció de retard de 0,2 W s
	movwf	Retard3
Bucles
	decfsz	Retard1,f		
	goto	Bucles		
	decfsz	Retard2,f		
	goto	Bucles		
	decfsz	Retard3,f	
	goto	Bucles
	return
	end

Si provem el programa, hauríem d'obtenir una seqüència que pot semblar aleatòria però que és la mateixa cada cop que tornem a posar en marxa el microcontrolador. Això passa perquè aquesta funció el que fa és generar nombres pseudoaleatoris. Per aconseguir millors resultats, cal definir una llavor que sigui diferent cada vegada, així la seqüència serà diferent cada cop. El següent programa agafa com a llavor el valor de TMR0 que sempre està variant.

#include <p16F690.inc>
	__config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
	cblock	0x20
Rand:2			; Dos bytes on guardarem el valor aleatori
Auxrand:2		; Variable auxiliar per a Random
Retard1			; Variables de retard	
Retard2
Retard3
	endc
	org 0
Inici
	bsf	STATUS,RP0		; Tria el banc 1
	movlw	0xFF			; Posa l'acumulador a FFh (tot uns)
	movwf	TRISA			; Posa tots els bits del port A com a entrada
	clrf	TRISC			; Posa tots els bits del port C com a sortida
	movlw	b'10000000'		; Configuració de Timer0
					; Com a temporitzador basat en rellotge
					; 000 - Factor d'escala de 2
					; I resistències de pull-up desactivades (valor per defecte)
	movwf	OPTION_REG		; Ho guarda
	bcf	STATUS,RP0		; Tria el banc 0
	clrf	PORTC			; Apaga els LED
	movf	TMR0,w			; Agafa el valor de TMR0
	movwf	Rand+1			; I el posa com a byte més significatiu de la llavor
	swapf	TMR0,w			; Agafa el valor permutat de TMR0
	movwf	Rand			; I el posa com a byte menys significatiu de la llavor
Bucle
	call	Random			; Genera un valor aleatori
	movf	Rand,w			; Llegeix el valor aleatori
	movwf	PORTC			; I ho posa als LED
	movlw	.5			; Retard de 1 s
	call	Rets
	goto	Bucle			; Repetim-ho...
					;
					; Funció que genera valors aleatoris
					;
Random
	clrf	Auxrand			; Posa a zero les variables auxiliars
	clrf	Auxrand+1		; Posa a zero les variables auxiliars
					; Rand+1:	15 14 13 12 11 10  9  8
					; Rand:		 7  6  5  4  3  2  1  0
	rlf	Rand+1,w		; Roda a l'esquerra i deixa el resultat a W
					; Rand+1:	15 14 13 12 11 10  9  8
					; W:		14 13 12 11 10  9  8  X
	xorwf	Rand+1,w		; Fa un xor amb l'original i deixa el resultat a W
	movwf	Auxrand+1		; El bit de l'esquerra serà la xor del 14 i el 15
					;
	swapf	Rand+1,f		; Permuta els nibbles
					; Rand+1:	11 10  9  8 15 14 13 12
					; Rand:		 7  6  5  4  3  2  1  0
	swapf	Rand,w			; Guarda els nibbles permutats a W
	movwf	Auxrand			; Auxrand:	 3  2  1  0  7  6  5  4
	bcf	STATUS,C		; Entrarem un zero
	rlf	Auxrand,f		; Hem rodat a l'esquerra, ara el bit 3 és a C
	btfsc	STATUS,C		; Si C és zero no fem res
	bsf	Auxrand,0		; Si C és 1, activem el bit de la dreta
					; Auxrand:	 2  1  0  7  6  5  4  3
					; Rand+1:	11 10  9  8 15 14 13 12
					; Rand:		 7  6  5  4  3  2  1  0
	movf	Auxrand,w		; Copia Auxrand a W
	xorwf	Rand+1,w		; El bit de la dreta de W serà la xor de 3 i 12
	swapf	Rand+1,f		; Ho torna a deixar com estava
					; Rand+1:	15 14 13 12 11 10  9  8
					; Rand:		 7  6  5  4  3  2  1  0
	andlw	b'00000001'		; Posa w a zero excepte el bit de la dreta
	rlf	Auxrand+1,f		; Agafem el bit de l'esquerra i el posem a C
					; Aquest bit era la xor del 14 i el 15
	rlf	Rand,f			; Rodem Rand a l'esquerra entrant el bit que volem per la dreta
	xorwf	Rand,f			; Li fem una xor amb W que afecta només al bit de la dreta
	rlf	Rand+1,f		; Rodem Rand+1 a l'esquerra entrant el bit que ha sortit de Rand
	return
Rets					; Funció de retard de 0,2 W s
	movwf	Retard3
Bucles
	decfsz	Retard1,f		
	goto	Bucles		
	decfsz	Retard2,f		
	goto	Bucles		
	decfsz	Retard3,f	
	goto	Bucles
	return
	end

Aquest programa va bé si reprogramem el microcontrolador sense desconnectar-lo. Però si desconnectem el microcontrolador i el tornem a connectar és probable que la seqüència es repeteixi. El motiu és que el temps que triga en fer les instruccions prèvies és sempre el mateix o sigui que si l'apago i el torno a engegar, el valor de TMR0 que dóno és sempre igual. En el programa següent afegim un detall que li dona una aleatorietat pràcticament total ja que no es llegirà el valor de TMR0 fins que l'usuari hagi premut el polsador i després l'hagi deixat anar. Hem afegit un retard abans de mirar el polsador ja que el programador (que comparteix pota amb el polsador) pot interferir amb la lectura del polsador durant els primers microsegons.

#include <p16F690.inc>
	__config (_INTRC_OSC_NOCLKOUT&_WDT_OFF&_PWRTE_OFF&_MCLRE_OFF&_CP_OFF&_BOR_OFF&_IESO_OFF&_FCMEN_OFF)
	cblock	0x20
Rand:2			; Dos bytes on guardarem el valor aleatori
Auxrand:2		; Variable auxiliar per a Random
Retard1			; Variables de retard	
Retard2
Retard3
	endc
	org 0
Inici
	bsf	STATUS,RP0		; Tria el banc 1
	movlw	0xFF			; Posa l'acumulador a FFh (tot uns)
	movwf	TRISA			; Posa tots els bits del port A com a entrada
	clrf	TRISC			; Posa tots els bits del port C com a sortida
	movlw	b'10000000'		; Configuració de Timer0
					; Com a temporitzador basat en rellotge
					; 000 - Factor d'escala de 2
					; I resistències de pull-up desactivades (valor per defecte)
	movwf	OPTION_REG		; Ho guarda
	bcf	STATUS,RP0		; Tria el banc 0
	clrf	PORTC			; Apaga els LED
	movlw	.10			; Retard de 2 s
	call	Rets
	btfsc	PORTA,3			; Mira si està desactivada l'entrada A3
					; Si està desactivada, no fa la instrucció següent
					; Fa la instrucció si l'entrada està activada
	goto	$-1			; Si no està premut, espera
	btfss	PORTA,3			; Mira si està activada l'entrada A3
					; Si està activada, no fa la instrucció següent
					; Fa la instrucció si l'entrada està desactivada
	goto	$-1			; Si està premut, espera
	movf	TMR0,w			; Agafa el valor de TMR0
	movwf	Rand+1			; I el posa com a byte més significatiu de la llavor
	swapf	TMR0,w			; Agafa el valor permutat de TMR0
	movwf	Rand			; I el posa com a byte menys significatiu de la llavor
Bucle
	call	Random			; Genera un valor aleatori
	movf	Rand,w			; Llegeix el valor aleatori
	movwf	PORTC			; I ho posa als LED
	movlw	.5			; Retard de 1 s
	call	Rets
	goto	Bucle			; Repetim-ho...
					;
					; Funció que genera valors aleatoris
					;
Random
	clrf	Auxrand			; Posa a zero les variables auxiliars
	clrf	Auxrand+1		; Posa a zero les variables auxiliars
					; Rand+1:	15 14 13 12 11 10  9  8
					; Rand:		 7  6  5  4  3  2  1  0
	rlf	Rand+1,w		; Roda a l'esquerra i deixa el resultat a W
					; Rand+1:	15 14 13 12 11 10  9  8
					; W:		14 13 12 11 10  9  8  X
	xorwf	Rand+1,w		; Fa un xor amb l'original i deixa el resultat a W
	movwf	Auxrand+1		; El bit de l'esquerra serà la xor del 14 i el 15
					;
	swapf	Rand+1,f		; Permuta els nibbles
					; Rand+1:	11 10  9  8 15 14 13 12
					; Rand:		 7  6  5  4  3  2  1  0
	swapf	Rand,w			; Guarda els nibbles permutats a W
	movwf	Auxrand			; Auxrand:	 3  2  1  0  7  6  5  4
	bcf	STATUS,C		; Entrarem un zero
	rlf	Auxrand,f		; Hem rodat a l'esquerra, ara el bit 3 és a C
	btfsc	STATUS,C		; Si C és zero no fem res
	bsf	Auxrand,0		; Si C és 1, activem el bit de la dreta
					; Auxrand:	 2  1  0  7  6  5  4  3
					; Rand+1:	11 10  9  8 15 14 13 12
					; Rand:		 7  6  5  4  3  2  1  0
	movf	Auxrand,w		; Copia Auxrand a W
	xorwf	Rand+1,w		; El bit de la dreta de W serà la xor de 3 i 12
	swapf	Rand+1,f		; Ho torna a deixar com estava
					; Rand+1:	15 14 13 12 11 10  9  8
					; Rand:		 7  6  5  4  3  2  1  0
	andlw	b'00000001'		; Posa w a zero excepte el bit de la dreta
	rlf	Auxrand+1,f		; Agafem el bit de l'esquerra i el posem a C
					; Aquest bit era la xor del 14 i el 15
	rlf	Rand,f			; Rodem Rand a l'esquerra entrant el bit que volem per la dreta
	xorwf	Rand,f			; Li fem una xor amb W que afecta només al bit de la dreta
	rlf	Rand+1,f		; Rodem Rand+1 a l'esquerra entrant el bit que ha sortit de Rand
	return
Rets					; Funció de retard de 0,2 W s
	movwf	Retard3
Bucles
	decfsz	Retard1,f		
	goto	Bucles		
	decfsz	Retard2,f		
	goto	Bucles		
	decfsz	Retard3,f	
	goto	Bucles
	return
	end

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.