53. 硬件随机数
一、随机数发生器简介
STM32F407 自带了硬件随机数发生器(RNG),RNG 处理器是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个 32 位的随机数。
STM32F407 的随机数发生器(RNG)采用模拟电路实现。此电路产生馈入线性反馈移位寄存器(RNG_LFSR)的种子,用于生成 32 位随机数。
该模拟电路由几个环形振荡器组成,振荡器的输出进行异或运算以产生种子。RNG_LFSR 由专用时钟(PLL48CLK)按恒定频率提供时钟信息,因此随机数质量与 HCLK 频率无关。当将大量种子引入 RNG_LFSR 后,RNG_LFSR 的内容会传入数据寄存器(RNG_DR)。
同时,系统会监视模拟种子和专用时钟 rng_clk,当种子上出现异常序列,或 rng_clk 时钟频率过低时,可以由 RNG_SR 寄存器的对应位读取到,如果设置了中断,则在检测到错误时,还可以产生中断。
二、RNG寄存器
2.1、RNG控制寄存器
该寄存器我们需要关注 RNGEN 位,该位用于使能随机数发生器,所以必须设置为 1。
2.2、RNG状态寄存器
该寄存器我们需要关注最低位(DRDY 位),该位用于表示 RNG_DR 寄存器包含的随机数数据是否有效,如果该位为 1,则说明 RNG_DR 的数据是有效的,可以读取出来了。读 RNG_DR 后,该位自动清零。
2.3、RNG数据寄存器
RNG_DR 寄存器是只读寄存器,我们可以读取该寄存器获得 32 位随机数值。此寄存器在最多 40 个 PLL48CLK 时钟周期后,又可以提供新的随机数值。
三、RNG配置步骤
3.1、使能随机数发生器时钟
#define __HAL_RCC_RNG_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB2ENR, RCC_AHB2ENR_RNGEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB2ENR, RCC_AHB2ENR_RNGEN);\
UNUSED(tmpreg); \
} while(0U)
3.2、初始化随机数发生器
HAL_StatusTypeDef HAL_RNG_Init(RNG_HandleTypeDef *hrng)
形参 hrng 是 RNG_HandleTypeDef 结构体类型指针变量,其定义如下:
typedef struct
{
RNG_TypeDef *Instance; // RNG基地址
HAL_LockTypeDef Lock; // RNG锁设置
__IO HAL_RNG_StateTypeDef State; // RNG设备访问状态
__IO uint32_t ErrorCode; // RNG错误代码
uint32_t RandomNumber; // RNG最后生成的随机数
} RNG_HandleTypeDef;
Instance:指向 RNG 寄存器基地址。
#define RNG ((RNG_TypeDef *) RNG_BASE)
该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功,HAL_ERROR 表示 错误,HAL_BUSY 表示 忙碌,HAL_TIMEOUT 表示 超时。
typedef enum
{
HAL_OK = 0x00U, // 成功
HAL_ERROR = 0x01U, // 错误
HAL_BUSY = 0x02U, // 忙碌
HAL_TIMEOUT = 0x03U // 超时
} HAL_StatusTypeDef;
3.3、生成随机数
HAL_RNG_GenerateRandomNumber() 是 RNG 生成随机数函数。
HAL_StatusTypeDef HAL_RNG_GenerateRandomNumber(RNG_HandleTypeDef *hrng, uint32_t *random32bit)
形参 hrng 是 RNG_HandleTypeDef 结构体类型指针变量,即 RNG 的句柄。
形参 random32bit 是 uint32_t 类型指针变量,随机 32 位指针,生成随机变量。
该函数的返回值是 HAL_StatusTypeDef 枚举类型的值,有 4 个,分别是 HAL_OK 表示 成功,HAL_ERROR 表示 错误,HAL_BUSY 表示 忙碌,HAL_TIMEOUT 表示 超时。
四、程序源码
RNG 初始化函数:
RNG_HandleTypeDef g_rng_handle;
/**
* @brief RNG初始化函数
*
* @return uint8_t 0: 初始化成功; 1: 初始化失败
*/
uint8_t RNG_Init(void)
{
uint16_t time = 0;
g_rng_handle.Instance = RNG;
HAL_RNG_Init(&g_rng_handle);
// 等待RNG初始化完成
while ((!__HAL_RNG_GET_FLAG(&g_rng_handle, RNG_FLAG_DRDY) == RESET) && (time < 1000))
{
time++;
HAL_Delay(1);
}
if (time >= 1000)
{
return 1;
}
return 0;
}
RNG 底层初始化函数:
/**
* @brief RNG底层初始化函数
*
* @param hrng
*/
void HAL_RNG_MspInit(RNG_HandleTypeDef *hrng)
{
if (hrng->Instance == RNG)
{
__HAL_RCC_RNG_CLK_ENABLE();
}
}
生成一个随机数函数
/**
* @brief 生成一个随机数函数
*
* @return uint32_t 32位的随机数值
*/
int RNG_GetRandomNumber(void)
{
uint32_t random_number = 0;
HAL_RNG_GenerateRandomNumber(&g_rng_handle, &random_number);
return random_number;
}
获取某一个范围内的随机数函数:
/**
* @brief 获取某一个范围内的随机数函数
*
* @param min 最小值
* @param max 最大值
* @return uint32_t 32位的随机数值
*/
int RNG_GetRandomNumberInRange(int min, int max)
{
uint32_t random_number = 0;
HAL_RNG_GenerateRandomNumber(&g_rng_handle, &random_number);
return random_number % (max - min + 1) + min;
}
main() 函数:
int main(void)
{
uint32_t number = 0;
HAL_Init();
System_Clock_Init(8, 336, 2, 7);
Delay_Init(168);
UART_Init(&g_usart1_handle, USART1, 115200);
RNG_Init();
while (1)
{
number = RNG_GetRandomNumber();
printf("number: %d\r\n", number);
number = RNG_GetRandomNumberInRange(0, 100);
printf("number: %d\r\n\r\n", number);
HAL_Delay(1000);
}
return 0;
}