0
点赞
收藏
分享

微信扫一扫

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

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


本章,我们将介绍STM32MP157的硬件随机数发生器。我们使用KEY0按键来获取硬件随机数,并且将获取到的随机数值通过串口打印出来。同时,使用LED0指示程序运行状态。

本章分为如下几个小节:

20.1、 随机数发生器简介;

20.2、 硬件设计;

20.3、 软件设计;

20.4、 编译和测试;


20.1 随机数发生器简介

STM32MP157自带了硬件随机数发生器(RNG),其中RNG1给A7内核使用,RNG2给M4内核使用。RNG处理器是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个32位的随机数。

20.1.1 RNG框图

下面先来学习RNG框图,通过学习RNG框图会有一个很好的整体掌握,同时对之后的编程也会有一个清晰的思路。STM32MP157的随机数发生器框图如下图所示:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_串口


图20.1.1. 1随机数发生器(RNG)框图

随机数发生器有2个时钟域:AHB时钟域和RNG时钟域。AHB 时钟用于为 AHB 存储寄存器和调节组件提供时钟信号,RNG 时钟用于噪声源采样。如果 RNG_CR 寄存器中的 CED 位设置为“0”了,即启用时钟错误检测,那么 RNG 时钟频率 必须大于 AHB 时钟频率的32 分之一,否则时钟检查器会指示时钟错误,RNG 将停止生成随机数。在配置时钟时,注意此范围。

RNG时钟域时钟为rng_clkrng_clkRCC_RNG2CKSELR寄存器的RNG2SRC[1:0]位选择具体的时钟源,具体选择关系为:

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:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_寄存器_02


图20.1.1. 2默认采用CSI作为时钟源

AHB时钟域的时钟来自AHB3(最大为209MHz),用于访问相关寄存器等,通过RCC_MC_AHB3ENSETRRNG2EN位(bit6)使能。可见,4MHz小于209/32,如果开启时钟错误检测,注意配置RNG2的时钟范围。

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_随机数_03


图20.1.1. 3时钟

RCC_RNG2CKSELRRCC_MC_AHB3ENSETR这两个寄存器我们就不做介绍了,大家参考手册。从RNG框图整体上知道,RNG有两个输入和一个输出。具体如下表:

信号名称

信号类型

说明

rng_it

数字输出

RNG 全局中断请求

rng_hclk2

数字输入

AHB 时钟

rng_clk

数字输入

RNG 专用时钟,与 rng_hclk2 异步

表20.1.1. 1 RNG内部输入/输出信号

STM32MP157的随机数发生器(RNG)采用模拟电路实现,由内部两个模拟噪声源产生种子,经过采样和归一化处理,再经过线性移位寄存器和判断逻辑,最终输出到RNG_DR,生成32 位随机数。

每个模拟噪声源由3个环形振荡器组成,振荡器产生的输出经过异或运算产生种子,经过采样归一化处理后,输出到RNG内部的线性移位寄存器。采样频率由rng_clk时钟提供,因此,随机数质量与 HCLK 频率无关。当将大量种子引入线性移位寄存器后,经过判断逻辑,最终输出到数据寄存器 (RNG_DR)。

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

20.1.2 RNG寄存器

1. RNG控制寄存器(RNG_CR)

RNG控制寄存器描述如下图所示:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_随机数_04


图20.1.2. 1寄存器

该寄存器我们只关心RNGEN位,该位用于使能随机数发生器,所以必须设置为1。

2. RNG状态寄存器(RNG_SR)

RNG状态寄存器描述如下图所示:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_随机数_05


图20.1.2. 2寄存器

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

3. RNG数据寄存器(RNG_DR)

RNG数据寄存器描述如下图所示:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_随机数_06


图20.1.2. 3寄存器

RNG_DR寄存器是只读寄存器,我们可以读取该寄存器获得32位随机数值。此寄存器在最多216AHB时钟周期后,又可以提供新的随机数值。

20.1.3 RNG的HAL库驱动

RNG在HAL库中的驱动代码在stm32mp1xx_hal_rng.c文件(及其头文件)中。

1. HAL_RNG_Init函数

RNG的初始化函数,其声明如下:

HAL_StatusTypeDef HAL_RNG_Init(RNG_HandleTypeDef *hrng);​

  1. 函数描述: 用于初始化RNG
  2. 函数形参: 形参1是RNG_HandleTypeDef结构体类型指针变量,其定义如下:

typedef struct​
{​
RNG_TypeDef *Instance; /* RNG基地址 */​
RNG_InitTypeDef Init; /* RNG初始化配置结构体 */​
HAL_LockTypeDef Lock; /* RNG锁设置 */​
__IO HAL_RNG_StateTypeDef State; /* RNG设备访问状态 */​
__IO uint32_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;​


  1. 3Lock:用于配置锁状态。
    4StateRNG设备访问状态。
    5)ErrorCode:RNG错误代码
    6)RandomNumber:该变量存储RNG最后生成的随机数
  2. 函数返回值:HAL_StatusTypeDef枚举类型的值。

2. HAL_RNG_GenerateRandomNumber函数

HAL_RNG_GenerateRandomNumberRNG生成随机数函数。其声明如下:

HAL_StatusTypeDef HAL_RNG_GenerateRandomNumber(RNG_HandleTypeDef *hrng, ​
uint32_t *random32bit);​

  1. 函数描述: 该函数用于RNG生成随机数。
  2. 函数形参: 形参1是RNG_HandleTypeDef结构体类型指针变量,即RNG的句柄。
    形参2uint32_t类型指针变量,随机32位指针,生成随机变量。
  3. 函数返回值:

HAL_StatusTypeDef枚举类型的值。

20.2 硬件设计​

1. 例程功能​

本实验使用STM32MP157自带的硬件随机数生成器(RNG),获取随机数,并通过串口打印出来。按KEY0可以获取一次随机数。同时程序自动获取0~9范围内的随机数,显示在屏幕上。LED0闪烁用于提示程序正在运行。

2. 硬件资源​

1)LED灯:LED0

2)独立按键 KEY0

3)串口4

4)RNG(硬件随机数生成器)

LED0

KEY0

UART4_TX

UART4_RX

PI0

PG3

PG11

PB2

表20.2. 1硬件资源

3. 原理图​

RNG属于STM32MP157内部资源,通过软件设置好就可以了。

20.3 软件设计​

本实验配置好的实验工程已经放到了开发板光盘中,路径为:开发板光盘A-基础资料\1、程序源码\11、M4 CubeIDE裸机驱动例程\CubeIDE_project\ 13 RNG

20.3.1 程序流程图​

下面看看本实验的程序流程图:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_随机数_07


图20.3.1. 1硬件随机数实验程序流程图

20.3.2 软件配置​

1. 新建和配置工程​

本节实验可以在前面的串口通信实验的基础上实现,因为用到串口UART4,也可以重新新建一个新的工程。这里新建工程RNG,然后配置串口UART4和按键KEY0以及LED0,具体的配置步骤我们前面的实验章节都有介绍过,如果不记得配置步骤,大家可以回顾前面相关的实验章节。本节实验中,LED和按键只用到LED0和KEY0,其它LED和按键没有用到,如果直接使用前面BSP文件夹里的led.c、led.h、ket.h和key.c文件的话,可以直接将其他LED和按键也一起配置了。

(1)GPIO配置

LED0和KEY0配置如下:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_随机数_08


图20.3.2. 1和KEY0配置

UART4配置如下:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_寄存器_09


图20.3.2. 2配置

(2)NVIC配置:

UART4参数使用默认配置:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_串口_10


图20.3.2. 3参数配置

我们使用串口中断,先开启串口全局中断,

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_寄存器_11


图20.3.2. 4开启UART4全局中断

串口中断优先级配置如下:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_串口_12


图20.3.2. 5配置UART4中断优先级

下面,我们直接配置RNG2:

(3)配置RNG2

如果不知道RNG在哪里,可以直接搜索RNG2:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_随机数_13


图20.3.2. 6查找RNG2

在SecurityàRNG2下找到RNG2的配置,按照如下图配置,先勾选Cortex-M4,再选择Activated,即激活随机数发生器。其它配置保持默认,实际上RNG没有其它参数需要配置的。这里,我们不开启时钟错误检测:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_串口_14


图20.3.2. 7配置RNG2

(4)配置时钟

RRNG2时钟默认采用CSI的时钟,其它时钟也可以保持默认配置。这里就保持和前面一样的配置,配置PCLK1最大为104.5MHz:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_寄存器_15


图20.3.2. 8配置时钟

(5)配置生成独立的.c和.h文件

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_寄存器_16


图20.3.2. 9配置生成独立的文件

2. 生成工程​

以上的配置检查无误后,按下键盘的“Ctrl+S”组合键保存配置并生成工程。将前面按键实验用到的BSP文件夹拷贝到本工程的Src目录下,只留下按键和LED相关的代码:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_串口_17


图20.3.2. 10生成工程

3. 添加用户代码​

上一步操作中,我们已经将BSP文件夹拷贝到了工程中,因为本节实验只用到LED0和KEY0相关的代码,所以将其它LED1和按键的代码注释掉。当然,也可以在STM32CubeMX上将LED1和其它按键也一起配置了,这样就不需要注释这些代码了,或者也可以自己重新手动编写这部分代码。

(1)key.h、key.c、led.c和led.h的代码比较简单,这里就不列出来了,大家可以直接参考本工程中的代码。

(2)串口相关的代码可直接参考前面串口通信实验章节部分,这里就不再列出来了。

(3)RNG2初始化代码已经在rng.c文件中生成了,代码比较简单,如下:

#include "rng.h"​

RNG_HandleTypeDef hrng2; /* RNG2句柄 */​

/**​
* @brief底层驱动,初始化RNG2​
* @note此函数会被HAL_RNG_Init()调用​
* @param句柄​
* @retval无​
*/​
void MX_RNG2_Init(void)​
{​
hrng2.Instance = RNG2; /* RNG2 */​
hrng2.Init.ClockErrorDetection = RNG_CED_DISABLE; /* 关闭时钟错误检查 */​
if (HAL_RNG_Init(&hrng2) != HAL_OK) /* 初始化RNG2 */​
{​
Error_Handler();​
}​

}​
/**​
* @brief底层驱动,时钟源设置和使能​
* @note此函数会被HAL_RNG_Init()调用​
* @param句柄​
* @retval无​
*/​
void HAL_RNG_MspInit(RNG_HandleTypeDef* rngHandle)​
{​
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};​
if(rngHandle->Instance==RNG2)​
{​
if(IS_ENGINEERING_BOOT_MODE())​
{​
/* 外设外RNG2 */​
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RNG2;​
/* 时钟源设置为CSI */​
PeriphClkInit.Rng2ClockSelection = RCC_RNG2CLKSOURCE_CSI;​
/* 初始化RCC扩展外设时钟,也就是RNG2时钟 */​
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)​
{​
Error_Handler();​
}​
}​
__HAL_RCC_RNG2_CLK_ENABLE(); /* 使能RNG2时钟 */​
}​
}​
/**​
* @brief底层驱动反初始化函数​
* @note如果需要,可以调用此函数​
* @param句柄​
* @retval无​
*/​
void HAL_RNG_MspDeInit(RNG_HandleTypeDef* rngHandle)​
{​
if(rngHandle->Instance==RNG2)​
{​
__HAL_RCC_RNG2_CLK_DISABLE();/* 关闭RNG2时钟,即关闭RNG2 */​
}​
}​
(4)手动添加获取随机数的代码​
在rng.c文件中添加如下代码:​
/* USER CODE BEGIN 1 */​
/**​
* @brief得到随机数​
* @param无​
* @retval获取到的随机数(32bit)​
*/​
uint32_t rng_get_random_num(void)​
{​
uint32_t randomnum;​
HAL_RNG_GenerateRandomNumber(&hrng2, &randomnum);​
return randomnum;​
}​

/**​
* @brief得到某个范围内的随机数​
* @param最小,最大值.​
* @retval得到的随机数(rval),满足:min<=rval<=max​
*/​
int rng_get_random_range(int min, int max)​
{​
uint32_t randomnum;​
HAL_RNG_GenerateRandomNumber(&hrng2, &randomnum);​
return randomnum%(max-min+1) + min;​
}​
/* USER CODE END 1 */​

以上代码比较简单,主要是通过HAL库中的HAL_RNG_GenerateRandomNumber函数获取随机数rng_get_random_num函数主要是获取随机数rng_get_random_range主要是返回某个范围内的随机数

(5)修改main.c文件

#include "main.h"​
#include "rng.h"​
#include "usart.h"​
#include "gpio.h"​

/* USER CODE BEGIN Includes */​
#include "./BSP/Include/led.h"​
#include "./BSP/Include/key.h"​
/* USER CODE END Includes */​

void SystemClock_Config(void);​

int main(void)​
{​
HAL_Init();​
if(IS_ENGINEERING_BOOT_MODE())​
{​
SystemClock_Config();​
}​
MX_GPIO_Init();​
MX_RNG2_Init();​
MX_UART4_Init();​
/* USER CODE BEGIN 2 */​
/* printf("请输入字符,并按下回车键结束\r\n"); */​
HAL_UART_Receive_IT(&huart4,&RxBuffer,1);/* 以中断方式接收函数 */​
/* USER CODE END 2 */​
while (1)​
{​
/* USER CODE BEGIN 3 */​
uint32_t random;​
uint8_t t = 0, key;​
key = key_scan(0);​
if (key == KEY0_PRES)​
{​
random = rng_get_random_num();​
printf("Random Num:%lu\r\n", random);​
}​
if ((t % 20) == 0)​
{​
t = 0;​
LED0_TOGGLE(); /* 每200ms,翻转一次LED0 */​
random = rng_get_random_range(0, 9);/* 取[0,9]区间的随机数 */​
printf("Random Num[0-9]:%lu\r\n", random);​
}​
HAL_Delay(10);​
t++;​
}​
/* USER CODE END 3 */​
}​

以上代码中,每隔200ms会自动打印一次[0,9]区间的随机数同时LED0会翻转一次。如果有按键按下,那么串口就会打印获取到的随机数。

20.4 编译和测试​

编译无报错,测试结果如下:

《STM32MP1 M4裸机CubeIDE开发指南》第二十章 硬件随机数实验_寄存器_18


图20.4. 1测试结果


举报

相关推荐

0 条评论