Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Random Numbers for PIC

Status
Not open for further replies.

PDubya

New Member
I'm trying to figure out a way to randomly pop individual ports on a 16f628a.

I've looked at some of the examples but they aren't very clear in how they work.

What I'd like to have ideally is just a single random bit value so I can turn on individual ports with the method below (code below is still untested, and somewhat pseudo):

Code:
; Based on the 'Tutorial 1.6' by Nigel Goodwin

LIST	p=16F628		;tell assembler what chip we are using
include "P16F628.inc"		;include the defaults for the chip
__config 0x3D18			;sets the configuration settings (oscillator type etc.)

cblock 	0x20 			;start of general purpose registers
	count1 			;used in delay routine
	counta 			;used in delay routine 
	countb 			;used in delay routine
endc

LEDPORT	Equ	PORTB		;set constant LEDPORT = 'PORTB'
LEDTRIS	Equ	PORTB		;set constant for TRIS register
SWPORT	Equ	PORTA		;set constant SWPORT = 'PORTA'
SWTRIS	Equ	PORTA		;set constant for TRIS register

; ===============================
; set constants for the switches
; ===============================
SWFOOT	Equ	7		;foot switch
SWRESET	Equ	6		;back reset switch
SWTEST  Equ     5		;internal test switch

; ===============================
; set constants for the outputs
; ===============================
LEDB0	Equ	0		;blink port 1
LEDB1	Equ	1		;blink port 2
LEDB2	Equ	2		;blink port 3
LEDB3	Equ	3		;blink port 4
LEDPWR	Equ	7		;power led
BUZZER	Equ	6		;buzzer

org	0x0000			;org sets the origin, 0x0000 for the 16F628,
				;this is where the program starts running	
movlw	0x07
movwf	CMCON			;turn comparators off (make it like a 16F84)



; ===============================
; Initialize Ports
; ===============================
bsf 	STATUS,	RP0		;select bank 1
movlw 	b'00000000'		;set PortB all outputs
movwf 	LEDTRIS

bcf	STATUS,	RP0		;select bank 0
clrf	LEDPORT			;set all outputs low

movlw 	b'11111111'		;set PortA all inputs
movwf 	SWTRIS

; ===============================
; Initialize LEDs
; ===============================
bsf	LEDPORT, LEDPWR		;turn power light on


; ===============================
; Main Loop
; ===============================
Main
	btfss	SWPORT,	SWTEST		;Check for internal test button press
	call	BlinkTest		;This line gets skipped over if the switch isn't pushed
	
	
	<generate_random_bit>
	
	<if random bit = 1 then>
	bsf	LEDPORT, LEDB0		;turn blink light on
	
	<if random bit = 0 then>
	bcf	LEDPORT, LEDB0		;turn blink light off
	
	call	Delay			;this waits for a while!
	
	goto 	Main			;Endless Loop


BlinkTest
	; ============================================================
	; This routine just scans through all the ports on LEDPORT in
	; a pre-defined pattern for testing
	; ============================================================	
	movlw	b'11111111'		;initial port test
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	call	Delay			;this waits for a while!
	call	Delay			;this waits for a while!
	call	Delay			;this waits for a while!
	movlw	b'00000000'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00000001'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00000010'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00000100'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00001000'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00010000'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00001000'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00000100'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00000010'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00000001'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	movlw	b'00000000'
	movwf	LEDPORT
	call	Delay			;this waits for a while!
	
	; ============================================================
	; check to see if the reset button is being held. 
	; If it is, jump out of the test loop back into the main loop
	; ============================================================
	btfss	SWPORT,	SWRESET		; Check for button push, and reset if necessary
	call	Main			; This line gets skipped over if the switch isn't pushed
	
	goto	BlinkTest		;go back and do it again

Delay	movlw	d'250'			;delay 250 ms (4 MHz clock)
	movwf	count1
d1	movlw	0xC7
	movwf	counta
	movlw	0x01
	movwf	countb
Delay_0
	decfsz	counta, f
	goto	$+2
	decfsz	countb, f
	goto	Delay_0

	decfsz	count1	,f
	goto	d1
	retlw	0x00

	end

Am I even going about this the right way?

Thanks!!!
 
Last edited:
A pseudo random sequence is the best you can do. If you are interested you should check Knuth, D.E. The Art of Computer Programming. There is a detailed explanation and analysis of Linear Congruential Methods in Volume 2.

Timers are most definitley NOT random or pseudo random in fact the are completely deterministic.
 
Last edited:
A pseudo-random value is fine. It's only for, basically a toy. It's not crucial that it's truly random. I thought about maybe just making a table of a sequence but the table would need to be somewhat large to make it not obvious. Even if the random generation was random enough not to be obvious, that would be great.
 
I've used the "tapped shift register" (google it) method before with 4 bytes and it works good enough. The problem is that it generates the same sequence each time the circuit is powered up, so you need to include some method of 'seeding' the shift registers with 'random' data. If your circuit includes a user pushbutton, you can time the amount of time it's pressed and seed your registers with that random amount. If you are coding in assembly and have the time/program structure to wait for the button release, then you can simply add a few 'DECF shiftregister' each time though the wait loop while you are waiting for the button release. This will reseed the registers and change the random sequence each time the button is pressed since the length of time that a user holds down a button is different each time (when timed to the µs).

here's a quick example (in CVASM for PIC)
Code:
button is active low (tied high)

mainloop	jnb	button, svcbutton	;is button low? jump if so
		.
		.
		.				;whatever...
		jmp	mainloop
		
		

svcbutton	djnz	shiftregister1, test	;dec first random register, check for underflow
		djnz	shiftregister2, test	;dec second random register, check for underflow
		djnz	shiftregister3, test	;dec third random register, check for underflow
		djnz	shiftregister4, test	;dec fourth random register, check for underflow
		
test		jnb	button, svcbutton	;is button still low? jump if so and check again

debounce					;whatever debounce routine/method....
DJNZ = decrease, jump if not zero

There is recent talk here about using a LED as an light level indicator. I suppose that you could use that to reseed and randomize your shift registers. This method may work well if you are using a RTOS type program structure; have it check the light level every so often, and continously reseed the shift registers on the fly. This should eliminate generating the same pattern of random bits (unless the light level is always exactly the same).

Rick
 
A good pseudo random number generator that I have used in the past is one that uses a 32 bit seed and generates a new bit by xoring bits 27 and 30 together and shifting it into bit zero.

This can be achieved on a PIC as follows,

Code:
Random
		movfw	Seed+3
		andlw	0x48;		keep bits 30 and 27
		addlw	0x38;		xor into bit 6
		rlf	Seed,F
		rlf	Seed+1,F
		rlf	Seed+2,F
		rlf	Seed+3,F
		bcf	Seed,0
		andlw	0x40;		test bit 6
		btfsc	STATUS,Z
		bsf	Seed,0;		bit 0 = bit 6 of W
		return

The above will only generate 1 new bit. To generate a random byte call the above 8 times. As already mentioned by rockin_rick the seed cannot be zero.

Mike.
 
For clairity... this method creates a seemingly random sequence of bits, but the sequence is always the same. The amount of seed (aka shift) registers and which bits you do the XOR with determine the length/'randomness' of the sequence. There are better/worse choices for XOR bit locations in that 32bit shift register - that is some create longer or shorter sequences and/or better randomness. IIRC (from many years ago) a good one with 32bits will create a sequence of bits that is tens of thousands long. I do not know which are best (or the quality of Mike's... he says it's good, I have no reason to doubt him); regardless, I imagine it is documented somewhere...

I think that the main problem is whatever you initially set the seed registers to determine where in that sequence you begin pulling bits. Without somehow randomizing/changing the seed registers on init from one powerup to another, your program will always pull the same sequence of bits and generate the same sequence of random data. That may be acceptable in certain situations, however for 'better' randomness, mixing it up would be nice. That's where the 'timed pushbutton' (or whatever) technique helps. Each time the button is pressed, it changes where in the sequence the bits gets pulled from, thus adding to the randomizing effect. Just how long it's pressed determines where in the sequence the new bits will get spit out from. Of course, if you need a random byte/bit before the button is pressed, then you will have the same sequence...

I suppose that you could also periodically save your seed in an EEPROM and recall it on init to combat the 'repetitive sequence on init' problem. Probably only useful if your design already has an EEPROM...

Rick
 
Last edited:
Ok, now for the seemingly dumb question - how do I get the single bit out to be able to use it in an <if bit = 1 / if bit = 0> type situation?
 
No no, I completely understand using the btfss or btfsc commands, I'm not sure exactly which value I should be checking, and which bit should I be looking at. For example, this pseudo-code:

Code:
Main
	btfss	SWPORT,	SWTEST		;Check for internal test button press
	call	BlinkTest		;This line gets skipped over if the switch isn't pushed
		
	<generate random number, assuming randnum>
	
	btfss randnum,2			; test bit 2 of randnum, skip one instruction if set
	bsf	LEDPORT, LED2		; turn LED2 on to indicate stand-by
	
	btfsc randnum,2			; test bit 2 of randnum, skip one instruction if clear
	bcf	LEDPORT, LED2		; turn LED2 on to indicate stand-by
	
	goto 	Main			;Endless Loop

...using the randomizing routine Pommie provided, what would I use in the code above for randnum, and which bit to check?
 
PDubya said:
...using the randomizing routine Pommie provided, what would I use in the code above for randnum, and which bit to check?

Just call Random and then check bit 0 of Seed.

Rockin_rick, I like the idea of saving the seed in EEPROM.

Mike.
 
Or, after calling Random, the Zero flag is the opposite of bit 0 of seed and the Carry flag also contains a random (but different) bit.

Mike.
 
Say, specifically what is this toy project and what are you using this randomizer for in the project?

I have used the random routine to generate probability (something other than 50-50), maybe your project needs something like this? I get a random byte (run the random routine 8 times) and do a < or > check against a set value. For instance, say you want something to happen 20% of the time, check to see if the random byte is > $CC. Assuming a perfect randomizer, 20% of the values generated would be between $CC and $FF.

Rick
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top