在单片机中使用汇编语言获得伪随机数,暨伪随机数发生器
关于单片机产生随机数,汇编
有一个产品需要用到随机数发生器
8位单片机
初步构想:
1、系统开机时取一个随机数作为种子;
2、产生伪随机数。
在网上找了一些程序
21IC的帖子也搜了一遍
周航慈那本《单片机程序设计基础》的书也在手头
开动吧!
1、keil中的rand函数反编译结果:
/* TEST.c */
#include
void main(void)
{
char i, j, k;
i = 0x5f;
j = rand();
k = 0xf5;
}
反编译结果,随机数放在R7中
;==========================================================================
Q0003: MOV R4,08H ;0003 AC 08
MOV R5,09H ;0005 AD 09
MOV R6,0AH ;0007 AE 0A
MOV R7,0BH ;0009 AF 0B
MOV R0,#0FH ;000B 78 0F
Q000D: MOV A,R7 ;000D EF
MOV C,ACC.2 ;000E A2 E2
MOV A,R4 ;0010 EC
JB ACC.6,Q0015 ;0011 20 E6 01
CPL C ;0014 B3
Q0015: ANL A,R5 ;0015 5D
ANL A,R6 ;0016 5E
ANL A,R7 ;0017 5F
CPL A ;0018 F4
JNZ Q001F ;0019 70 04
MOV R4,A ;001B FC
MOV R5,A ;001C FD
MOV R6,A ;001D FE
MOV R7,A ;001E FF
Q001F: MOV A,R7 ;001F EF
RRC A ;0020 13
MOV R7,A ;0021 FF
MOV A,R6 ;0022 EE
RRC A ;0023 13
MOV R6,A ;0024 FE
MOV A,R5 ;0025 ED
RRC A ;0026 13
MOV R5,A ;0027 FD
MOV A,R4 ;0028 EC
RRC A ;0029 13
MOV R4,A ;002A FC
DJNZ R0,Q000D ;002B D8 E0
MOV 08H,R4 ;002D 8C 08
MOV 09H,R5 ;002F 8D 09
MOV 0AH,R6 ;0031 8E 0A
MOV 0BH,R7 ;0033 8F 0B
MOV A,R6 ;0035 EE
ANL A,#7FH ;0036 54 7F
MOV R6,A ;0038 FE
RET ;0039 22
;==========================================================================
这个没有认真看
2、一个简单的随机数发生函数:
rand8reg是得到的随机数,为了更随机,可以把它与定时器相加
如果可以把rand8reg保存在非易失性存储器中更好,但是什么时候保存需要认真考虑
;==============================================================
; Name: rand8
; Description: get a 8bit random number
; Function: This routine sends calc a 8bit random number
; Attention: get a random "rand8reg" before initialize the program
; Calls: None
; Input: rand8reg
; Outputs: rand8reg
; Register Usage: A, psw
;--------------------------------------------------------------
rand8:
mov a, rand8reg
jnz rand8b
cpl a
mov rand8reg, a
rand8b:
anl a, #10111000b
mov c, p
mov a, rand8reg
rlc a
mov rand8reg, a
ret
;==============================================================
3、这个可能复杂一些:
其实和周航慈《单片机程序设计基础》上的例子是一样的,使用线性移位寄存器构成随机数发生器
;**********************************************************;
;* Random *;
;*Random_Buf 6个字节看成48个位,可以表示280亿个数值,原理:用反*;
;*馈函数F()把这280亿个无顺序的放在一个可以旋转数码盘上,程序 *;
;*有定时器在跑,当随机发生时,让转盘转计数器的值圈数即可得到一个*;
;*随机数. *;
;*奇校验反馈函数d0=d48+d7+d5+d4+d2+d1+1,6Byte有280亿种状态 *;
;**********************************************************;
Random:
PUSH ACC
MOV RandomREG,Count
Data_Rotate:
MOV A,Random_Buf+5 ;取D41-D48的当前状态
RLC A ;将D48提取到标志中去
MOV A,Random_Buf ;取D1-D8的当前状态
ANL A,#5BH ;提取D7,D5,D4,D2,D1的当前值
MOV ACC.7,C ;再加上D48的当前值
MOV C,PSW.0 ;取得偶校验的结果
CPL C ;转换成奇校验,且得到反馈函数的值
MOV A,Random_Buf ;开始移位操作,先处理低字节
RLC A ;将反馈函数的值移入,最高位移出
MOV Random_Buf,A ;保存移位后的结果
MOV A,Random_Buf+1 ;再依次进行其他字节的移位操作
RLC A
MOV Random_Buf+1,A
MOV A,Random_Buf+2
RLC A
MOV Random_Buf+2,A
MOV A,Random_Buf+3
RLC A
MOV Random_Buf+3,A
MOV A,Random_Buf+4
RLC A
MOV Random_Buf+4,A
MOV A,Random_Buf+5
RLC A
MOV Random_Buf+5,A ;完成巨型数字转盘转动一格
DJNZ RandomREG,Data_Rotate ;取当前时间随机时间转动n格
Random_Exit:
POP ACC
RET
4、我最终使用的程序
使用一个简单的随机数发生器产生种子
运行中用TL0作为干扰
; test.asm
; test
;--------------------------------------------------------------
;==============================================================
; 创建及修改记录
; -------------------------------------------------------------
; 创建人:
; 创建时间:
; 文件名:
; 程序功能:
; 初始版本:
; -------------------------------------------------------------
; 修改日期:
; 修改原因:
; 修改人:
; 版本更改:
; -------------------------------------------------------------
; 修改日期:
; 修改原因:
; 修改人:
; 版本更改:
;==============================================================
;==============================================================
; Define
;--------------------------------------------------------------
STACK_TOP EQU 0D0H ; STACK : 48Bytes
Random_Buf EQU 70H ; VARIABLE BUFFER (70H~75H)
RND0 EQU 70H ; VARIABLE 第1字节
RND1 EQU 71H ; VARIABLE 第2字节
RND2 EQU 72H ; VARIABLE 第3字节
RND3 EQU 73H ; VARIABLE 第4字节
RND4 EQU 74H ; VARIABLE 第5字节
RND5 EQU 75H ; VARIABLE 第6字节
RandCount EQU 76H ; VAIRABLE 跳动格数
rand8reg EQU 77H ; VAIRABLE 简单随机数,用来产生种子和跳动格数
;==============================================================
; MAIN PROGRAM : 程序入口
;==============================================================
ORG 0000H ; ; main program
LJMP main ;
ORG 0003H ; ; Int0 Interrupt
RETI ;
ORG 000BH ; ; T0 Interrupt
RETI ;
ORG 0013H ; ; Int1 Interrupt
RETI ;
ORG 001BH ; ; T1 Interrupt
RETI ;
ORG 0023H ; ; Si1 Interrupt
RETI ;
ORG 002BH ; ; T2 Interrupt
RETI ;
ORG 0033H ; ;
RETI ;
ORG 003BH ; ; Si2 Interrupt
RETI ;
ORG 0043H ; ; Int2 Interrupt
RETI ;
ORG 004BH ; ; Int3 Interrupt
RETI ;
ORG 0053H ; ; Int4 Interrupt
RETI ;
ORG 005BH ; ; Int5 Interrupt
RETI ;
ORG 0063H ; ; WatchDog Interrupt
RETI ;
ORG 0100H
; 程序入口
main:
CLR EA
; Reset RAM
MOV R0, #0FFH
ResetRam:
MOV @R0, #00H
DJNZ R0, ResetRam
MOV SP, #STACK_TOP
LCALL Init_Random ; 初始化随机数发生器
MOV R0, #20H
MOV R1, #40H
RANDLOOP:
LCALL Random ; 产生随机数
MOV A, RND0
MOV @R0, A ; 把随机数暂存到RAM中
INC R0
DJNZ R1, RANDLOOP ; 循环取40H个随机数
NOP
NOP
LJMP main
;==============================================================
; Name: Init_Random
; Description: initialize the random
; Function: give the value of Random_Buf and RandCount
; Calls: None
; Input: Random_Buf, RandCount
; Outputs: Random_Buf
; Register Usage: A, psw
;--------------------------------------------------------------
Init_Random:
ADD A, B
ADD A, R0
ADD A, R1
ADD A, TH0
ADD A, TL0
ADD A, TH1
ADD A, TL1
MOV R0, #Random_Buf
MOV rand8reg, A ; 给随机数发生器rand8赋初值
LCALL rand8
MOV @R0, rand8reg ; 给随机数发生器Random赋初值
INC R0
LCALL rand8
MOV @R0, rand8reg
INC R0
LCALL rand8
MOV @R0, rand8reg
INC R0
LCALL rand8
MOV @R0, rand8reg
INC R0
LCALL rand8
MOV @R0, rand8reg
INC R0
LCALL rand8
MOV @R0, rand8reg
RET
;==============================================================
; Name: Random
; Description: get a 6 bytes random number
; Function: This routine sends calc a 6 bytes random number
; Random_Buf 6个字节看成48个位,可以表示280亿个数值,原理:用反
; 馈函数F()把这280亿个无顺序的放在一个可以旋转数码盘上,程序
; 有定时器在跑,当随机发生时,让转盘转计数器的值圈数即可得到一个
; 随机数.详见周航慈《单片机程序设计基础》P242
; 奇校验反馈函数d0=d48+d7+d5+d4+d2+d1+1,6Byte有280亿种状态
; Calls: None
; Input: Random_Buf, RandCount
; Outputs: Random_Buf
; Register Usage: A, psw
;--------------------------------------------------------------
Random:
LCALL rand8
MOV A, rand8reg
ADD A, TL0
ANL A, #0FH
JNZ Get_Count
MOV A, #0FH
Get_Count:
MOV RandCount, A ; 获得一个1~16的跳动格数
Data_Rotate:
MOV A, Random_Buf+5 ;取D41-D48的当前状态
RLC A ;将D48提取到标志中去
MOV A, Random_Buf ;取D1-D8的当前状态
ANL A, #5BH ;提取D7,D5,D4,D2,D1的当前值
MOV ACC.7, C ;再加上D48的当前值
MOV C, PSW.0 ;取得偶校验的结果
CPL C ;转换成奇校验,且得到反馈函数的值
MOV A, Random_Buf ;开始移位操作,先处理低字节
RLC A ;将反馈函数的值移入,最高位移出
MOV Random_Buf, A ;保存移位后的结果
MOV A, Random_Buf+1 ;再依次进行其他字节的移位操作
RLC A
MOV Random_Buf+1, A
MOV A, Random_Buf+2
RLC A
MOV Random_Buf+2, A
MOV A, Random_Buf+3
RLC A
MOV Random_Buf+3, A
MOV A, Random_Buf+4
RLC A
MOV Random_Buf+4, A
MOV A, Random_Buf+5
RLC A
MOV Random_Buf+5, A ; 完成巨型数字转盘转动一格
DJNZ RandCount, Data_Rotate ; 随机跳动n格
Random_Exit:
RET
;==============================================================
; Name: rand8
; Description: get a 8bit random number
; Function: This routine sends calc a 8bit random number
; Attention: get a random "rand8reg" before initialize the program
; Calls: None
; Input: rand8reg
; Outputs: rand8reg
; Register Usage: A, psw
;--------------------------------------------------------------
rand8:
mov a, rand8reg
jnz rand8b
cpl a
mov rand8reg, a
rand8b:
anl a, #10111000b
mov c, p
mov a, rand8reg
rlc a
mov rand8reg, a
ret
;==============================================================
; END
end