硬件随机数发生器
RNG处理器是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个 32 位的随机数。
- 提供由模拟量发生器产生的 32 位随机数
- 两个连续随机数的间隔为 40 个 PLL48CLK 时钟信号周期
- 通过监视 RNG 熵来标识异常行为(产生稳定值,或产生稳定的值序列)
- 可被禁止以降低功耗
原理
LFSR寄存器的原理介绍:详解线性反馈移位寄存器(LFSR)——引用知乎:come back
随机数发生器采用模拟电路实现。此电路产生馈入线性反馈移位寄存器 (RNG_LFSR) 的种子, 用于生成 32 位随机数。 该模拟电路由几个环形振荡器组成,振荡器的输出进行异或运算以产生种子。RNG_LFSR由专用时钟 (PLL48CLK) 按恒定频率提供时钟信息,因此随机数质量与 HCLK 频率无关。当将大量种子引入RNG_LFSR 后,RNG_LFSR 的内容会传入数据寄存器 (RNG_DR)。 同时,系统会监视模拟种子和专用时钟 PLL48CLK。状态位(RNG_SR 寄存器中)指示何时在种子上出现异常序列,或指示何时 PLL48CLK 时钟频率过低。检测到错误时生成中断。
寄存器
RNG 与控制寄存器、数据寄存器和状态寄存器相关联。必须通过字(32 位)进行访问。
RNG 控制寄存器 (RNG_CR)
仅使用2、3位,其他位保留,必须保持复位值。
RNG 状态寄存器 (RNG_SR)
RNG 数据寄存器 (RNG_DR)
RNG_DR 寄存器是只读寄存器,在读取时提供 32 位随机数值。读取后,此寄存器在最多 40 个 PLL48CLK 时钟周期后,提供新的随机数值。在读取 RNDATA 值之前,软件必须检查 DRDY 位是否已置 1。
实验
使用RNG生成随机数,通过串口显示。 此实验仅获取随机数,暂不对随机数产生错误时的中断做出处理。
使用及配置步骤
- 使能RNG时钟。该时钟来自PLL48CK。
- 使能RNG,即CR中的RNGEN位置1。
- 根据SR中的DRDY标志位可以判断是否能读取随机数。
代码
串口的作用只做显示,无需从PC发送数据,因此也无需配置中断。此处我直接使用了我另一篇文章 串口通信+固定首尾帧传输数据 中的串口配置源文件和头文件,不再贴出。
rng.c
uint8_t rng_init(void)
{
uint16_t retry = 0;
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE);
RNG_Cmd(ENABLE);
while(RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET && retry < 10000)
{
retry++;
delay_us(100);
}
if(retry >= 10000)return 1;
return 0;
}
uint32_t rng_get_random(void)
{
while(RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET);
return RNG_GetRandomNumber();
}
int rng_get_range_random(int min, int max)
{
return rng_get_random() % (max - min + 1) + min;
}
main.c
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "rng.h"
int main(void)
{
uint32_t random;
int num;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
delay_init(168);
usart_init();
LED_Init();
while(rng_init())
{
delay_ms(200);
printf("RNG准备中...");
printf("\r\n\r\n");
}
printf("RNG准备完成!");
printf("\r\n\r\n");
while(1)
{
LED0 = !LED0;
random = rng_get_random();
printf("%u", random);
printf("\r\n\r\n");
delay_ms(200);
num = rng_get_range_random(0, 9);
printf("%u", num);
printf("\r\n\r\n");
while(USART_GetFlagStatus(USART3, USART_FLAG_TC) != SET);
delay_ms(200);
}
}
另外,之所以能使用 printf 函数在串口中打印数据,是在 usart.c 中有如下预编译代码:
#if 1
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
_sys_exit(int x)
{
x = x;
}
int fputc(int ch, FILE *f)
{
while((USART3->SR & 0X40)==0);
USART3->DR = (u8) ch;
return ch;
}
#endif
实验结果
如图所示,每200ms分别显示一个二进制32位随机数和一个0到9间的随机整数(十进制显示)。
|