正点原子 发表于 2022-11-12 17:13:11

《STM32MP1 M4裸机HAL库开发指南》第二十七章 硬件随机数实验

本帖最后由 正点原子 于 2022-11-12 17:13 编辑

1)实验平台:正点原子STM32MP157开发板
2)购买链接:https://item.taobao.com/item.htm?&id=629270721801
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-318813-1-1.html
4)正点原子官方B站:https://space.bilibili.com/394620890
5)正点原子STM32MP157技术交流群:691905614





第二十七章 硬件随机数实验

本章,我们将介绍STM32MP157的硬件随机数发生器。我们使用KEY0按键来获取硬件随机数,并且将获取到的随机数值通过串口打印出来。同时,使用LED0指示程序运行状态。
本章分为如下几个小节:
27.1、 随机数发生器简介;
27.2、 硬件设计;
27.3、 程序设计;
27.4、 编译和测试;


27.1 随机数发生器简介
      STM32MP157自带了硬件随机数发生器(RNG),其中RNG1给A7内核使用,RNG2给M4内核使用。RNG处理器是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个32位的随机数。
27.1.1 RNG框图
      下面先来学习RNG框图,通过学习RNG框图会有一个很好的整体掌握,同时对之后的编程也会有一个清晰的思路。STM32MP157的随机数发生器框图如下图所示:
图27.1.1. 1随机数发生器(RNG)框图
      随机数发生器有2个时钟域:AHB时钟域和RNG时钟域。AHB 时钟用于为 AHB 存储寄存器和调节组件提供时钟信号,RNG 时钟用于噪声源采样。如果 RNG_CR 寄存器中的 CED 位设置为“0”了,即启用时钟错误检测,那么 RNG 时钟频率 必须大于 AHB 时钟频率的32 分之一,否则时钟检查器会指示时钟错误,RNG 将停止生成随机数。在配置时钟时,注意此范围。
      RNG时钟域时钟为rng_clk,rng_clk由RCC_RNG2CKSELR寄存器的RNG2SRC位选择具体的时钟源,具体选择关系为:
      0x0: csi_ker_ck
      0x1: pll4_r_ck
      0x2: lse_ck clock
      0x3: lsi_ck
      默认情况下STM32CubeMX会选择0x0: csi_ker_ck(4 MHz)作为rng_clk的时钟源,最大可通过PLL4配置为48MHz:
图27.1.1. 2 RNG2默认采用CSI作为时钟源
      AHB时钟域的时钟来自AHB3(最大为209MHz),用于访问相关寄存器等,通过RCC_MC_AHB3ENSETR的RNG2EN位(bit6)使能。可见,4MHz小于209/32,如果开启时钟错误检测,注意配置RNG2的时钟范围。
图27.1.1. 3 RNG时钟
      RCC_RNG2CKSELR和RCC_MC_AHB3ENSETR这两个寄存器我们就不做介绍了,大家参考手册。从RNG框图整体上知道,RNG有两个输入和一个输出。具体如下表:
表27.1.1. 1 RNG内部输入/输出信号
      STM32MP157的随机数发生器(RNG)采用模拟电路实现,由内部两个模拟噪声源产生种子,经过采样和归一化处理,再经过线性移位寄存器和判断逻辑,最终输出到RNG_DR,生成32 位随机数。
      每个模拟噪声源由3个环形振荡器组成,振荡器产生的输出经过异或运算产生种子,经过采样归一化处理后,输出到RNG内部的线性移位寄存器。采样频率由rng_clk时钟提供,因此,随机数质量与 HCLK 频率无关。当将大量种子引入线性移位寄存器后,经过判断逻辑,最终输出到数据寄存器 (RNG_DR)。
      同时,系统会监视模拟种子和专用时钟 rng_clk,当种子上出现异常序列,或rng_clk时钟频率过低时,可以由RNG_SR寄存器的对应位读取到,如果设置了中断,则在检测到错误时,还可以产生中断。
27.1.2 RNG寄存器
1. RNG控制寄存器(RNG_CR)
      RNG控制寄存器描述如下图所示:
图27.1.2. 1 RNG_CR寄存器
      该寄存器我们只关心RNGEN位,该位用于使能随机数发生器,所以必须设置为1。
2. RNG状态寄存器(RNG_SR)
      RNG状态寄存器描述如下图所示:
图27.1.2. 2 RNG_SR寄存器
      该寄存器我们仅关心最低位(DRDY位),该位用于表示RNG_DR寄存器包含的随机数数据是否有效,如果该位为1,则说明RNG_DR的数据是有效的,可以读取出来了。读RNG_DR后,该位自动清零。
3. RNG数据寄存器(RNG_DR)
      RNG数据寄存器描述如下图所示:
图27.1.2. 3 RNG_DR寄存器
      RNG_DR寄存器是只读寄存器,我们可以读取该寄存器获得32位随机数值。此寄存器在最多216个AHB时钟周期后,又可以提供新的随机数值。
27.1.3 RNG的HAL库驱动
      RNG在HAL库中的驱动代码在stm32mp1xx_hal_rng.c文件(及其头文件)中。
1. HAL_RNG_Init函数
      RNG的初始化函数,其声明如下:
HAL_StatusTypeDef HAL_RNG_Init(RNG_HandleTypeDef *hrng);
●函数描述:
      用于初始化RNG。
●函数形参:
      形参1是RNG_HandleTypeDef结构体类型指针变量,其定义如下:
typedef struct
{
RNG_TypeDef                   *Instance;    /* RNG基地址 */
RNG_InitTypeDef               Init;         /* RNG初始化配置结构体 */
HAL_LockTypeDef               Lock;         /* RNG锁设置 */
__IO HAL_RNG_StateTypeDef   State;      /* RNG设备访问状态 */
__IOuint32_t                ErrorCode;    /* RNG错误代码 */
uint32_t                      RandomNumber; /* RNG最后生成的随机数 */
} RNG_HandleTypeDef;
      1)Instance:指向RNG寄存器基地址。
      2)Init:是的RNG初始化结构体,其结构体类型RTC_InitTypeDef定义如下:
typedef struct
{
uint32_t            ClockErrorDetection; /* CED时钟错误检测 */
} RNG_InitTypeDef;
      3)Lock:用于配置锁状态。
      4)State:RNG设备访问状态。
      5)ErrorCode :RNG错误代码
      6)RandomNumber :该变量存储RNG最后生成的随机数
●函数返回值:
      HAL_StatusTypeDef枚举类型的值。
2. HAL_RNG_GenerateRandomNumber函数
      HAL_RNG_GenerateRandomNumber是RNG生成随机数函数。其声明如下:
HAL_StatusTypeDef HAL_RNG_GenerateRandomNumber(RNG_HandleTypeDef *hrng,
uint32_t *random32bit);
●函数描述:
      该函数用于RNG生成随机数。
●函数形参:
      形参1是RNG_HandleTypeDef结构体类型指针变量,即RNG的句柄。
      形参2是uint32_t类型指针变量,随机32位指针,生成随机变量。
●函数返回值:
      HAL_StatusTypeDef枚举类型的值。
27.2 硬件设计
1. 例程功能
      本实验使用STM32MP157自带的硬件随机数生成器(RNG),获取随机数,并通过串口打印出来。按KEY0可以获取一次随机数。同时程序自动获取0~9范围内的随机数,显示在屏幕上。LED0闪烁用于提示程序正在运行。
2. 硬件资源
      1)LED灯:LED0
      2)独立按键KEY0
      3)串口4
      4)RNG(硬件随机数生成器)
表27.2. 1 硬件资源
3. 原理图
      RNG属于STM32MP157内部资源,通过软件设置好就可以了。
27.3 程序设计
      本实验配置好的实验工程已经放到了开发板光盘中,路径为:开发板光盘A-基础资料\1、程序源码\3、M4裸机驱动例程\ MP157-M4 HAL库V1.2\实验16 硬件随机数实验。
27.3.1 程序流程图
      下面看看本实验的程序流程图:
图27.3.1. 1硬件随机数实验程序流程图
27.3.2 添加用户代码
RNG驱动代码我们把它放在rng.c和rng.h文件中。
1. rng.h文件代码
rng.h头文件主要是函数的声明,如下:
#ifndef __RNG_H
#define __RNG_H

#include "./SYSTEM/sys/sys.h"

#define RNG_PORT                  RNG2
#define RNG_CLK_ENABLE()          do{ __HAL_RCC_RNG2_CLK_ENABLE(); }while(0)   /* PE口时钟使能 */

uint8_t rng_init(void);                     /* RNG初始化 */
uint32_t rng_get_random_num(void);          /* 得到随机数 */
int rng_get_random_range(int min,int max);/* 得到属于某个范围内的随机数 */

#endif
2. rng.c文件代码
下面我们直接介绍rng.c的程序,先看RNG的初始化函数,其定义如下:
RNG_HandleTypeDef rng_handle;//RNG句柄
/**
* @brief       初始化RNG
* @param       无
* @retval      0,成功;1,失败
*/
uint8_t rng_init(void)
{
uint16_t retry = 0;
rng_handle.Instance = RNG_PORT;   
rng_handle.Init.ClockErrorDetection = RNG_CED_DISABLE; /* 关闭时钟错误检查 */
HAL_RNG_Init(&rng_handle);   /* 初始化RNG */
/* 等待RNG准备就绪 */
while (__HAL_RNG_GET_FLAG(&rng_handle, RNG_FLAG_DRDY) == RESET && retry < 10000)
{
    retry++;
    delay_us(10);
}
if (retry >= 10000)
{
    return 1; /* 随机数产生器工作不正常 */
}
   return 0;
}
rng_init函数中调用HAL_RNG_Init函数初始化RNG,然后等待RNG初始化完成,同时判断初始化过程是否超时。
我们用HAL_RNG_MspInit回调函数来编写RNG时钟配等代码,其定义如下:
/**
* @brief       RNG底层驱动,时钟源设置和使能
* @note      此函数会被HAL_RNG_Init()调用
* @param       hrng:RNG句柄
* @retval      无
*/
void HAL_RNG_MspInit(RNG_HandleTypeDef *hrng)
{
    RCC_PeriphCLKInitTypeDef RNGClkInitStruct;
    RNG_CLK_ENABLE();   /* 使能RNG时钟 */
    /* 设置RNG时钟源,选择CSI,时钟为4MHz */
RNGClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RNG2;
      /* 设置RNG时钟源 */
RNGClkInitStruct.Rng2ClockSelection = RCC_RNG2CLKSOURCE_CSI;
      /* 初始化RCC扩展外设时钟,也就是RNG2时钟 */
    HAL_RCCEx_PeriphCLKConfig(&RNGClkInitStruct);   
}
最后两个是我们获取随机数函数,它们的定义如下:
/**
* @brief       得到随机数
* @param       无
* @retval      获取到的随机数(32bit)
*/
uint32_t rng_get_random_num(void)
{
    uint32_t randomnum;
    HAL_RNG_GenerateRandomNumber(&rng_handle, &randomnum);
    return randomnum;
}
/**
* @brief       得到某个范围内的随机数
* @param       min,max: 最小,最大值.
* @retval      得到的随机数(rval),满足:min<=rval<=max
*/
int rng_get_random_range(int min, int max)
{
    uint32_t randomnum;
    HAL_RNG_GenerateRandomNumber(&rng_handle, &randomnum);
    return randomnum%(max-min+1) + min;
}
      以上代码比较简单,主要是通过HAL库中的HAL_RNG_GenerateRandomNumber函数获取随机数。rng_get_random_num函数主要是获取随机数,rng_get_random_range主要是返回某个范围内的随机数。
3. main.c文件代码
      main.c文件的部分代码如下:
int main(void)
{
    uint32_t random;
    uint8_t t = 0, key;
   
    HAL_Init();             /* 初始化HAL库 */
    /* 初始化M4内核时钟,209M */
    if(IS_ENGINEERING_BOOT_MODE())
    {
      sys_stm32_clock_init(34, 2, 2, 17, 6826);
    }
    usart_init(115200);               /* 串口初始化为115200   */
    delay_init(209);                        /* 延时初始化 */
    usmart_dev.init(209);                   /* 初始化USMART */
    led_init();                     /* 初始化LED */
    key_init();                           /* 初始化按键 */
    rng_init();                     /* 初始化随机数发生器 */

    while (1)
    {
      key = key_scan(0);            /* 获取键值 */
      if (key == KEY0_PRES)         /* 判断是否是KEY0按下 */
      {
         random = rng_get_random_num();
         printf("Random Num:%u\r\n", random);
      }
      if ((t % 20) == 0)
      {
          t = 0;
          LED0_TOGGLE();                              /* 每200ms,翻转一次LED0 */
          random = rng_get_random_range(0, 9);    /* 取区间的随机数 */
          printf("Random Num:%u\r\n", random); /* 打印随机数 */
      }

      delay_ms(10);                                                            /* 延时10ms */
      t++;
    }
}
      以上代码中,每隔200ms会自动打印一次区间的随机数,同时LED0会翻转一次。如果有按键按下,那么串口就会打印获取到的随机数。
27.4 编译和测试
      编译无报错,测试结果如下:
图27.4. 1测试结果
页: [1]
查看完整版本: 《STM32MP1 M4裸机HAL库开发指南》第二十七章 硬件随机数实验