STM32F407ZG开发板学习(8)
硬件随机数发生器
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
/*********************************
函数名:rng_init
简介:初始化RNG
参数:无
返回:0 --初始化成功
1 --初始化超时,失败
**********************************/
uint8_t rng_init(void)
{
uint16_t retry = 0;
//使能RNG时钟
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE);
RNG_Cmd(ENABLE);//使能RNG
while(RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET && retry < 10000)
{
retry++;
delay_us(100);
}
if(retry >= 10000)return 1;//100us×10000=1s
return 0;
}
/*********************************
函数名:rng_get_random
简介:获取32位随机数
参数:无
返回:32位随机数
**********************************/
uint32_t rng_get_random(void)
{
while(RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET);//等待至随机数准备好
return RNG_GetRandomNumber();
}
/*********************************
函数名:rng_get_range_random
简介:获取[min, max]内随机整数
参数:int min --下限
int max --上限
返回:[min, max]内随机整数
**********************************/
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); //设置系统中断优先级分组2
delay_init(168); //延时初始化
usart_init(); //串口初始化波特率为115200
LED_Init(); //初始化与LED连接的硬件接口
while(rng_init()) //初始化RNG
{
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();
//USART_SendData(USART3, random);
//while(USART_GetFlagStatus(USART3, USART_FLAG_TC) != SET);
printf("%u", random);
printf("\r\n\r\n");
delay_ms(200);
num = rng_get_range_random(0, 9);
//USART_SendData(USART3, (u16)num);
//while(USART_GetFlagStatus(USART3, USART_FLAG_TC) != SET);
printf("%u", num);
printf("\r\n\r\n");
while(USART_GetFlagStatus(USART3, USART_FLAG_TC) != SET);
delay_ms(200);
}
}
另外,之所以能使用 printf 函数在串口中打印数据,是在 usart.c 中有如下预编译代码:
//支持printf
#if 1
#pragma import(__use_no_semihosting) //不使用半主机模式
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
_sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART3->SR & 0X40)==0);//循环发送,直到发送完毕,这里根据自己使用的串口更改
USART3->DR = (u8) ch;
return ch;
}
#endif
实验结果
如图所示,每200ms分别显示一个二进制32位随机数和一个0到9间的随机整数(十进制显示)。