第18章 硬件随机数发生器介绍

第十八章 硬件随机数发生器介绍

1. STM32F4 随机数发生器简介

STM32F4 自带了硬件随机数发生器(RNG), RNG 处理器是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个 32 位的随机数。 STM32F4 的随机数发生器框图如图:

屏幕截图 2024 10 10 212253

STM32F4 的随机数发生器(RNG)采用模拟电路实现。此电路产生馈入线性反馈移位寄存器 (RNG_LFSR) 的种子,用于生成 32 位随机数。

该模拟电路由几个环形振荡器组成,振荡器的输出进行异或运算以产生种子。 RNG_LFSR由专用时钟 (PLL48CLK) 按恒定频率提供时钟信息,因此随机数质量与 HCLK 频率无关。当将大量种子引入 RNG_LFSR 后, RNG_LFSR 的内容会传入数据寄存器 (RNG_DR)。

同时,系统会监视模拟种子和专用时钟 PLL48CLK,当种子上出现异常序列,或 PLL48CLK时钟频率过低时,可以由 RNG_SR 寄存器的对应位读取到,如果设置了中断,则在检测到错误时,还可以产生中断。

接下来,我们介绍下 STM32F4 随机数发生器(RNG)的几个寄存器。

2. 相关寄存器介绍

2.1 控制寄存器(RNG_CR)

屏幕截图 2024 10 10 212835

该寄存器只有 bit2 和 bit3 有效,用于使能随机数发生器和中断。我们一般不用中断,所以只需要设置 bit2 为 1,使能随机数发生器即可。

  1. 使能 RNG:
    要启动 RNG 模块,需要将 EN 位设置为 1。

  2. 中断配置:
    如果需要在生成随机数时使用中断,可以设置 IE 位为 1。

#include "stm32f4xx.h"

void RNG_Init(void) {
    RCC->AHB2ENR |= RCC_AHB2ENR_RNGEN; // 使能 RNG 时钟
    RNG->CR |= RNG_CR_EN; // 使能 RNG
}
void EnableRNGInterrupt(void) {
    RNG->CR |= RNG_CR_IE; // 使能 RNG 中断
}
// 中断服务程序示例
void RNG_IRQHandler(void) {
    if (RNG->SR & RNG_SR_DRDY) {
        uint32_t random_value = RNG->DR; // 获取随机数
        // 处理随机数
    }
}
int main(void) {
    RNG_Init(); // 初始化 RNG
    EnableRNGInterrupt(); // 启用 RNG 中断
    while (1) {
        // 主循环
    }
}

2.2 状态寄存器(RNG_SR)

屏幕截图 2024 10 10 213010

该寄存器我们仅关心最低位(DRDY 位),该位用于表示 RNG_DR 寄存器包含的随机数数据是否有效,如果该位为 1,则说明 RNG_DR 的数据是有效的,可以读取出来了。读 RNG_DR后,该位自动清零。

2.3 数据寄存器(RNG_DR)

屏幕截图 2024 10 10 213307

在 RNG_SR 的 DRDY 位置位后,我们就可以读取该寄存器获得 32 位随机数值。此寄存器在最多 40 个 PLL48CK 时钟周期后,又可以提供新的随机数值。

3. 随机数发生器操作步骤

3.1 使能随机数发生器时钟

要使用随机数发生器,必须先使能其时钟。随机数发生器时钟来自 PLL48CK,通过AHB2ENR 寄存器使能。 所以我们调用使能 AHB2 总线外设时钟的函数使能 RNG 时钟即可:

__HAL_RCC_RNG_CLK_ENABLE(); // 使能 RNG 时钟

3.2 初始化(使能)随机数发生器

HAL 库提供了 HAL_RNG_Init 函数,该函数非常简单,主要作用是引导调用 RNG 的 MSP回调函数,然后使能随机数发生器。该函数声明如下:

HAL_StatusTypeDef HAL_RNG_Init(RNG_HandleTypeDef *hrng);

该函数非常简单,这里我们就不做过多讲解。使用方法如下:

RNG_HandleTypeDef RNG_Handler; // RNG 句柄
RNG_Handler.Instance = RNG;
HAL_RNG_Init(&RNG_Handler); // 初始化 RNG

当我们使用 HAL_RNG_Init 之后,在该函数内部,会调用 RNG 的 MSP 回调函数,回调函数声明如下:

void HAL_RNG_MspInit(RNG_HandleTypeDef *hrng);

回调函数中一般编写与 MCU 相关的外设时钟初始化以及 NVIC 配置。同时, HAL 库也提供了单独使能随机数发生器的方法为:

__HAL_RNG_ENABLE(hrng); // 使能 RNG

3.3 判断DRDY位,读取随机数值

经过前面两个步骤,我们就可以读取随机数值了,不过每次读取之前,必须先判断 RNG_SR寄存器的 DRDY 位,如果该位为 1,则可以读取 RNG_DR 得到随机数值,如果不为 1,则需要等待。

在 HAL 库中,判断 DRDY 位并读取随机数值的函数为:

uint32_t HAL_RNG_GetRandomNumber(RNG_HandleTypeDef *hrng);

4. 简单运用

4.1. 硬件连接

  • 按键连接:将按键连接到某个 GPIO 引脚(例如 PA0),并确保使用内部上拉电阻。
  • 串口连接:将 STM32 的串口 TX 引脚连接到 USB 转串口模块,便于调试和输出随机数。

4.2. 软件配置

使用 STM32 HAL 库进行开发。以下是主要步骤:

  • 配置 GPIO 引脚以读取按键输入。
  • 配置 UART 以打印输出。
  • 配置 RNG 以生成随机数。

4.3. 示例代码

下面是完整的代码示例:

#include "stm32f4xx_hal.h"

// 函数声明
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_RNG_Init(void);

UART_HandleTypeDef huart2;
RNG_HandleTypeDef hrng;

// 主函数
int main(void) {
    HAL_Init(); // 初始化 HAL 库
    SystemClock_Config(); // 配置系统时钟
    MX_GPIO_Init(); // 初始化 GPIO
    MX_USART2_UART_Init(); // 初始化 USART
    MX_RNG_Init(); // 初始化 RNG
    while (1) {
        // 检查按键状态
        if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET) { // 按键被按下
            uint32_t random_number;
            // 生成随机数
            HAL_RNG_GenerateRandomNumber(&hrng, &random_number);
            // 打印随机数
            char buffer[50];
            int len = snprintf(buffer, sizeof(buffer), "Random Number: %lu\r\n", random_number);
            HAL_UART_Transmit(&huart2, (uint8_t*)buffer, len, HAL_MAX_DELAY);
            // 等待按键释放
            while (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET);
            HAL_Delay(100); // 消除抖动
        }
    }
}
// 系统时钟配置
void SystemClock_Config(void) {
    // 根据具体的 STM32F4 板子进行时钟配置
}
// GPIO 初始化
static void MX_GPIO_Init(void) {
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_0; // 按键引脚
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 输入模式
    GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
// USART 初始化
static void MX_USART2_UART_Init(void) {
    __HAL_RCC_USART2_CLK_ENABLE(); // 使能 USART2 时钟
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能 GPIOA 时钟
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3; // TX 和 RX 引脚
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
    GPIO_InitStruct.Pull = GPIO_NOPULL; // 不上拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 高速
    GPIO_InitStruct.Alternate = GPIO_AF7_USART2; // 复用功能
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    huart2.Instance = USART2;
    huart2.Init.BaudRate = 115200; // 波特率
    huart2.Init.WordLength = UART_WORDLENGTH_8B; // 数据位
    huart2.Init.StopBits = UART_STOPBITS_1; // 停止位
    huart2.Init.Parity = UART_PARITY_NONE; // 无奇偶校验
    huart2.Init.Mode = UART_MODE_TX_RX; // 收发模式
    HAL_UART_Init(&huart2);
}
// RNG 初始化
static void MX_RNG_Init(void) {
    __HAL_RCC_RNG_CLK_ENABLE(); // 使能 RNG 时钟
    hrng.Instance = RNG;
    HAL_RNG_Init(&hrng);
}
posted @ 2024-10-10 21:43  hazy1k  阅读(45)  评论(0编辑  收藏  举报