硬件随机数实验
& I: B% ^3 }. @: ]0 @: L; q: Q5 J. e( I本章,我们将介绍STM32MP157的硬件随机数发生器。我们使用KEY0按键来获取硬件随机数,并且将获取到的随机数值通过串口打印出来。同时,使用LED0指示程序运行状态。. t: D0 j2 P' `1 J" G; `( z
5 R* v9 q/ ]' e. n. X5 N2 V20.1 随机数发生器简介3 V ]: p: D% k2 ~" e& N' `9 x
STM32MP157自带了硬件随机数发生器(RNG),其中RNG1给A7内核使用,RNG2给M4内核使用。RNG处理器是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个32位的随机数。: F, E# }$ }3 S
20.1.1 RNG框图
% q* n6 P! U1 L8 a4 |( o下面先来学习RNG框图,通过学习RNG框图会有一个很好的整体掌握,同时对之后的编程也会有一个清晰的思路。STM32MP157的随机数发生器框图如下图所示:
5 m0 @: z* B2 t1 M
~* X, b; k2 @
6 Y* l* j/ x7 `- Y8 Z, C5 o) J) ?/ H8 g6 ]/ b
图20.1.1. 1随机数发生器(RNG)框图% F) f$ f& a" H* | q
随机数发生器有2个时钟域:AHB时钟域和RNG时钟域。AHB 时钟用于为 AHB 存储寄存器和调节组件提供时钟信号,RNG 时钟用于噪声源采样。如果 RNG_CR 寄存器中的 CED 位设置为“0”了,即启用时钟错误检测,那么 RNG 时钟频率 必须大于 AHB 时钟频率的32 分之一,否则时钟检查器会指示时钟错误,RNG 将停止生成随机数。在配置时钟时,注意此范围。
- z) @$ @) e; V- h; P \0 [RNG时钟域时钟为rng_clk,rng_clk由RCC_RNG2CKSELR寄存器的RNG2SRC[1:0]位选择具体的时钟源,具体选择关系为:
: h: a" U+ M- {+ S, @0x0: csi_ker_ck
* y$ k* x1 v) ]3 _0x1: pll4_r_ck
5 C+ `* c/ }' D0 P& h; ]1 I; O0x2: lse_ck clock$ ~# z) j3 v; M+ d
0x3: lsi_ck
1 f9 c- y9 n, `7 R( D默认情况下STM32CubeMX会选择0x0: csi_ker_ck(4 MHz)作为rng_clk的时钟源,最大可通过PLL4配置为48MHz:6 \4 L2 f6 O9 {# \& N) U n
% b) T* x% H4 D& E* r9 j: K) [
* |2 T4 v% G: ~' g; K- e: p* F- l" r' V3 o6 p6 g
图20.1.1. 2 RNG2默认采用CSI作为时钟源) n( b/ S4 U* Y9 F) ]' v) J0 R
AHB时钟域的时钟来自AHB3(最大为209MHz),用于访问相关寄存器等,通过RCC_MC_AHB3ENSETR的RNG2EN位(bit6)使能。可见,4MHz小于209/32,如果开启时钟错误检测,注意配置RNG2的时钟范围。/ K: w9 A( D9 e3 P" |
9 R1 X/ p/ S) J& N: [
6 u. t, m! x" E5 J5 J7 D0 X& i( Z( ~
9 N- w& l9 T# Z% K5 m, q图20.1.1. 3 RNG时钟
. I. }2 z: c5 \1 q$ sRCC_RNG2CKSELR和RCC_MC_AHB3ENSETR这两个寄存器我们就不做介绍了,大家参考手册。从RNG框图整体上知道,RNG有两个输入和一个输出。具体如下表:2 A% R# i8 i+ p
' Z$ l* `! t* B l
a9 q. K4 C8 C }
) h- ~1 Z9 t9 c) p6 L1 A6 A表20.1.1. 1 RNG内部输入/输出信号
/ m% P9 m, Z( u+ M$ M5 USTM32MP157的随机数发生器(RNG)采用模拟电路实现,由内部两个模拟噪声源产生种子,经过采样和归一化处理,再经过线性移位寄存器和判断逻辑,最终输出到RNG_DR,生成32 位随机数。) v: }) z: g& \( ~8 R# X
每个模拟噪声源由3个环形振荡器组成,振荡器产生的输出经过异或运算产生种子,经过采样归一化处理后,输出到RNG内部的线性移位寄存器。采样频率由rng_clk时钟提供,因此,随机数质量与 HCLK 频率无关。当将大量种子引入线性移位寄存器后,经过判断逻辑,最终输出到数据寄存器 (RNG_DR)。2 L, y% v; a7 K
同时,系统会监视模拟种子和专用时钟 rng_clk,当种子上出现异常序列,或rng_clk时钟频率过低时,可以由RNG_SR寄存器的对应位读取到,如果设置了中断,则在检测到错误时,还可以产生中断。+ z! L! m& s3 Y! K
20.1.2 RNG寄存器
& Z& |* [. t+ w/ j% D1.RNG控制寄存器(RNG_CR)
. [- E+ l" ]+ L: G/ O/ XRNG控制寄存器描述如下图所示:) p. F( L; r! U) F. ?
% M5 \% x3 Z& n- Q0 U7 L
O5 t& ?* a4 K2 l! t9 _1 N
+ Y2 n6 N2 \( P6 A
图20.1.2. 1 RNG_CR寄存器" c, z$ f+ S; c
该寄存器我们只关心RNGEN位,该位用于使能随机数发生器,所以必须设置为1。* F: w* Q. F/ s3 N; t& C
2. RNG状态寄存器(RNG_SR)
4 L1 ]5 r% c, t. B, `RNG状态寄存器描述如下图所示:+ }% l9 X4 L+ o, r" ?- f
" ~" z# [; R: h* _8 `
: q1 v6 o3 W8 m [; ^7 \. t5 e; g; O0 X9 d+ S5 W7 l9 V3 O
图20.1.2. 2 RNG_SR寄存器
! [* f! q1 ?2 q+ ]( R6 |8 v/ L该寄存器我们仅关心最低位(DRDY位),该位用于表示RNG_DR寄存器包含的随机数数据是否有效,如果该位为1,则说明RNG_DR的数据是有效的,可以读取出来了。读RNG_DR后,该位自动清零。
8 O/ \; i9 b0 d# U- w8 q5 n( H8 B `6 N3. RNG数据寄存器(RNG_DR)
& H$ f3 @. C% S# c |: {; y" vRNG数据寄存器描述如下图所示:
+ R. X2 @* K; d& `9 G
; L6 ?9 r) K- c
! N; Y6 ^( a9 a3 o) J0 \
( F; k Y( g$ R: G6 n: P图20.1.2. 3 RNG_DR寄存器4 l9 M; [0 a& [
RNG_DR寄存器是只读寄存器,我们可以读取该寄存器获得32位随机数值。此寄存器在最多216个AHB时钟周期后,又可以提供新的随机数值。
9 P) d" r: P. C, h1 Y1 Q
# u) E$ }6 A L: v ]20.1.3 RNG的HAL库驱动* @8 ~) b* s. ]* I
RNG在HAL库中的驱动代码在stm32mp1xx_hal_rng.c文件(及其头文件)中。
! F; E- K( G Y) s9 B6 X4 P H8 j8 `1.HAL_RNG_Init函数
1 _( Q8 ]. T. j+ J+ F URNG的初始化函数,其声明如下:
* P2 l" w/ @! a3 r5 H: k; E( ~- HAL_StatusTypeDef HAL_RNG_Init(RNG_HandleTypeDef *hrng);
4 d, F- M; x. z) s4 i4 M: k, Z1 ~3 I7 p - ●函数描述:: r$ u6 N9 o0 _: t
- 用于初始化RNG。( j `( _: l9 M' X2 A7 I
- ●函数形参:6 h8 q/ j" K' x0 d
- 形参1是RNG_HandleTypeDef结构体类型指针变量,其定义如下:' r' Q% A2 i. Y; K$ K
- typedef struct
3 Y! M0 \. {- a9 ~( j- ?2 v - {
$ ^' o U( ?' T0 X* c. G5 E& i - RNG_TypeDef *Instance; /* RNG基地址 */$ a) v% n; z- a2 d
- RNG_InitTypeDef Init; /* RNG初始化配置结构体 */; f1 c2 d, b8 K" { V/ v! z
- HAL_LockTypeDef Lock; /* RNG锁设置 */) D/ `- R6 ]+ I: Z" D1 D/ M' ~
- __IO HAL_RNG_StateTypeDef State; /* RNG设备访问状态 */- I# @2 S3 @! V! F7 y
- __IO uint32_t ErrorCode; /* RNG错误代码 */
" s6 \ ?, L$ t$ R) D - uint32_t RandomNumber; /* RNG最后生成的随机数 */
; F! h4 r9 ~7 `3 W- _' ? - } RNG_HandleTypeDef;
2 v h, c2 A8 I6 _( }# i0 h - 1)Instance:指向RNG寄存器基地址。
, |' s2 O& [3 O" E- E - 2)Init:是的RNG初始化结构体,其结构体类型RTC_InitTypeDef定义如下:9 @5 T+ f0 F7 I3 y: ?- z
- typedef struct8 {& A& }5 g' x, @" `
- {, b9 l; m3 s/ x& c7 O
- uint32_t ClockErrorDetection; /* CED时钟错误检测 */
2 P' m! V1 W9 l5 Y+ N, H+ W - } RNG_InitTypeDef;
复制代码
2 M. d8 ?/ c, b- B, D3)Lock:用于配置锁状态。- r* O4 m9 T5 z0 K; X- i
4)State:RNG设备访问状态。
+ `1 K8 G7 t) T- U# w* @5)ErrorCode :RNG错误代码* x1 o8 q) A: M4 r
6)RandomNumber :该变量存储RNG最后生成的随机数. |0 o& |; d& L0 ~ c
' m; ~* q% f4 f' R●函数返回值:
3 s. L! A' g$ E5 HHAL_StatusTypeDef枚举类型的值。8 c" |0 E, k8 N* {0 m
2. HAL_RNG_GenerateRandomNumber函数+ D L1 g4 Z9 v! w
HAL_RNG_GenerateRandomNumber是RNG生成随机数函数。其声明如下:
0 x' ~4 f/ J4 B* Z! o" [+ }4 zHAL_StatusTypeDef HAL_RNG_GenerateRandomNumber(RNG_HandleTypeDef *hrng,/ [( m9 H1 x1 ?/ n/ }( `2 ~
uint32_t *random32bit);
5 c: M/ b1 K3 U" L- Q●函数描述:
3 B! T; P+ c- C, |' s1 F( T该函数用于RNG生成随机数。0 K% j/ o2 U# H& J" |# x
●函数形参:3 K7 n1 X8 M- C# x4 _4 g6 i
形参1是RNG_HandleTypeDef结构体类型指针变量,即RNG的句柄。( X P D' {# L
形参2是uint32_t类型指针变量,随机32位指针,生成随机变量。! a7 n4 Y& h2 r2 b$ l
●函数返回值:- H7 L* n3 ?4 |! f
HAL_StatusTypeDef枚举类型的值。9 d9 L, J `9 ]. U, }( k
$ a# G% U9 X& ?2 u! U/ a20.2 硬件设计
2 H+ Y( J, ^. f1 c! Q0 G0 x1.例程功能7 t l4 \* @2 L
本实验使用STM32MP157自带的硬件随机数生成器(RNG),获取随机数,并通过串口打印出来。按KEY0可以获取一次随机数。同时程序自动获取0~9范围内的随机数,显示在屏幕上。LED0闪烁用于提示程序正在运行。
; r. t; ^; w2 H) Y; T2.硬件资源
6 u, ], g$ U# m. ], A1)LED灯:LED0* c5 m! o' H' [; P7 ]9 N
2)独立按键 KEY0+ F' P: H H* D
3)串口42 f. n* f; G3 z& o7 C8 @' ?
4)RNG(硬件随机数生成器)
2 \. M. S: O$ J* C2 tLED0 KEY0 UART4_TX UART4_RX9 G0 _$ ~! S3 q4 V, t: M
PI0 PG3 PG11 PB2/ F0 ?4 H4 Y( b$ `/ T4 A( K
表20.2. 1 硬件资源
: ?5 L) Q3 \4 X G9 t0 e3.原理图$ D- {& E7 q# S, \; x# T
RNG属于STM32MP157内部资源,通过软件设置好就可以了。
4 |# n$ y# B, {: }# X( G" r8 N
1 X8 C5 S1 u/ [- G# m9 W( \. ]) V20.3 软件设计
8 C5 w% A: {) P% _* \3 g: N本实验配置好的实验工程已经放到了开发板光盘中,路径为:开发板光盘A-基础资料\1、程序源码\11、M4 CubeIDE裸机驱动例程\CubeIDE_project\ 13 RNG。
7 v/ w) [8 A4 p0 k+ q' ^. ]+ y( i20.3.1 程序流程图
- P5 S0 G7 c8 M' J下面看看本实验的程序流程图:& A8 b; G: b+ ]7 ], E5 H+ ^( S
6 Z. g4 u6 E9 K, Y$ I. j
$ L: s( m A! c9 {/ {4 V
/ ?2 B. e) y8 r, _# o
图20.3.1. 1硬件随机数实验程序流程图1 p0 h v- u: R
9 T. x" |* a; H; V$ ^/ v
20.3.2 软件配置; w+ Y% X6 i; j4 t+ W
1.新建和配置工程
7 g( L! j3 Z, @5 h0 u& G本节实验可以在前面的串口通信实验的基础上实现,因为用到串口UART4,也可以重新新建一个新的工程。这里新建工程RNG,然后配置串口UART4和按键KEY0以及LED0,具体的配置步骤我们前面的实验章节都有介绍过,如果不记得配置步骤,大家可以回顾前面相关的实验章节。本节实验中,LED和按键只用到LED0和KEY0,其它LED和按键没有用到,如果直接使用前面BSP文件夹里的led.c、led.h、ket.h和key.c文件的话,可以直接将其他LED和按键也一起配置了。
( c8 e3 N1 ~7 D) T6 H" K. N! S1 d(1)GPIO配置% ]5 c$ P7 G: N j
LED0和KEY0配置如下:
: u! G5 I' l; ~. v
8 N& ]5 C. k+ p9 e
: M3 |2 X. Y e* `6 y4 I# K, |4 x+ C$ @5 I( N0 ]2 D0 y& C6 b+ C
图20.3.2. 1 LE0和KEY0配置: s' D( T, b3 l I$ l' {% G2 V
UART4配置如下:
/ z3 g: h$ c- m z; W0 C; J2 [7 q: J
4 X0 h0 F s% p5 m/ L. }$ ?+ |
. E- h- M3 e; }3 r. T1 r8 X" G3 U% m
图20.3.2. 2 UART4配置% j8 Z2 A3 y3 Z8 f% X* Q9 B
(2)NVIC配置:4 l( D# l3 e* L# j
UART4参数使用默认配置:2 s+ b% Y$ v9 ?
~' B X3 j7 O3 E9 u. y2 z/ ]
" N |/ y% }, ?& s4 P# s( H; V
; Y# U. l. _" Y ^6 _2 B图20.3.2. 3 UART4参数配置8 V7 ?' I: |8 [; Y. A& F
我们使用串口中断,先开启串口全局中断, P; [ `7 y9 s2 N6 P
L! R1 m2 J5 J* Z
0 |1 J( }; \' p9 W- `; \# m
; Y/ m- q8 M. p- Y3 ?# V) e" J
图20.3.2. 4 开启UART4全局中断% M* P; p, h& S4 c& J
串口中断优先级配置如下:
- }; \, K5 N% J; C. {" e4 ~% u
$ r7 k6 l2 g# g
6 B; h: I5 D9 f. E
, b: H* S) v) t3 X: u! Z# U- F图20.3.2. 5 配置UART4中断优先级& J0 _7 d1 z4 k0 W. |" r- `
下面,我们直接配置RNG2:7 t4 ^% ^; `, X/ w, T% ]$ p
(3)配置RNG2
$ G% E6 G5 r- z& Z c# m6 @$ R如果不知道RNG在哪里,可以直接搜索RNG2:8 I9 h5 T' e s( a- _
b" x1 C0 O9 s- }% y3 G# L5 e
: a0 \) z+ i% D! o' W5 F" a) C% v6 K" x& R$ T- U1 I$ r1 l) Q
图20.3.2. 6 查找RNG2& R' O, I6 u% P5 U+ @! [
在SecurityRNG2下找到RNG2的配置,按照如下图配置,先勾选Cortex-M4,再选择Activated,即激活随机数发生器。其它配置保持默认,实际上RNG没有其它参数需要配置的。这里,我们不开启时钟错误检测:
" r# x$ \% N; g c$ a3 u% C3 V! F8 `! h/ V1 p/ R8 r
* o( I6 E6 T6 }1 r9 u8 F [. q
% l+ i& a1 E, i: ]图20.3.2. 7 配置RNG2
( ?5 X. g/ `% p. i0 C( }5 Q& y(4)配置时钟 }6 Y* {8 Z$ {! G
RRNG2时钟默认采用CSI的时钟,其它时钟也可以保持默认配置。这里就保持和前面一样的配置,配置PCLK1最大为104.5MHz:
/ @5 g" m& c4 d m) h7 G0 C2 E" ?! V/ o
: ~/ X. W2 X, `
) y, a6 {( ]/ P. R/ U$ q图20.3.2. 8 配置时钟
$ E7 O, \. {! O9 J% x9 c(5)配置生成独立的.c和.h文件" K; t6 d/ u( u1 d
7 C) B! C7 i1 [, N' ^. y: q
, M9 M( A- h* N
5 U( b( n; E$ E; ]4 r" q( D图20.3.2. 9配置生成独立的文件
+ c+ S! \! t+ E+ S9 X9 k1 R F2. 生成工程- M" }9 a( j, o: q4 l
以上的配置检查无误后,按下键盘的“Ctrl+S”组合键保存配置并生成工程。将前面按键实验用到的BSP文件夹拷贝到本工程的Src目录下,只留下按键和LED相关的代码:7 ?: x6 n& E7 @: e# Z( L
* ^- C* s; g0 x/ _" V
; S9 W% M% `) c( C `
; d8 j/ P) R! x0 H" K
图20.3.2. 10生成工程, Y" O' u9 @* @; `% Q
3. 添加用户代码. @9 g. T" X5 t! d5 m' D
上一步操作中,我们已经将BSP文件夹拷贝到了工程中,因为本节实验只用到LED0和KEY0相关的代码,所以将其它LED1和按键的代码注释掉。当然,也可以在STM32CubeMX上将LED1和其它按键也一起配置了,这样就不需要注释这些代码了,或者也可以自己重新手动编写这部分代码。
' [; [1 t7 _5 b- O3 M! R(1)key.h、key.c、led.c和led.h的代码比较简单,这里就不列出来了,大家可以直接参考本工程中的代码。
8 W, S) Q& t" {& e a9 u: f& K(2)串口相关的代码可直接参考前面串口通信实验章节部分,这里就不再列出来了。
( {2 c+ F7 R! F: a" K# ~+ t(3)RNG2初始化代码已经在rng.c文件中生成了,代码比较简单,如下:
) g* z2 U% C2 Z7 t8 m9 o% O) @8 h, P
- #include "rng.h"
3 q% y( z7 a& F$ I/ ?2 X+ n. E. E - 4 A, }0 l! e4 y; S" L, a1 p- U
- RNG_HandleTypeDef hrng2; /* RNG2句柄 */& t# E4 t! v& F" f% d& i3 q
- 2 d( M' l, w. t0 U& o' D
- /*** [3 Y0 W% ~1 q' p: @3 S: D
- * @brief RNG底层驱动,初始化RNG2
E" J0 O" Q4 { R0 J( x5 j: x, y" V - * @note 此函数会被HAL_RNG_Init()调用
4 y0 h j$ p0 a+ u: [( F5 m - * @param hrng:RNG句柄
( g) c! A7 f; S* } - * @retval 无9 w/ S. p6 Z' O$ L; Z
- */
: t3 s, U/ m7 U, ^. f6 n - void MX_RNG2_Init(void)4 P$ X7 k% y; Y' ~- c W) C
- {7 D3 V6 R0 e" Z/ ^; F& T
- hrng2.Instance = RNG2; /* RNG2 *// t! W# d+ x8 o
- hrng2.Init.ClockErrorDetection = RNG_CED_DISABLE; /* 关闭时钟错误检查 */
* |( F, a5 s% Z - if (HAL_RNG_Init(&hrng2) != HAL_OK) /* 初始化RNG2 */1 s& q, R; y m: T
- {
4 z1 ]9 j. A" `4 Z9 h7 Z( {$ k - Error_Handler();+ ?# r. S, G' P h) c4 o, P: e$ ^
- }
3 g( b/ r6 [3 V& m
& k8 u4 _- P/ q6 ^# ]2 M7 {: `! |- }; N/ h. O* t0 t" |
- /**
! @6 P, \" k8 U- h" n - * @brief RNG底层驱动,时钟源设置和使能
! U9 s, N% @: R9 s3 p7 v/ g - * @note 此函数会被HAL_RNG_Init()调用7 _7 K' t4 ^' U; Z2 n
- * @param hrng:RNG2句柄 O( @! {# W! d2 ~3 W* f' i
- * @retval 无
2 M3 P2 Z3 U* L: H W - */
4 u8 S9 W( J! r5 l - void HAL_RNG_MspInit(RNG_HandleTypeDef* rngHandle)
3 @& ]3 F6 \, i+ g4 w! [ - {
- a u% t/ n6 y8 B - RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};0 z, E5 F. q5 O3 ]2 C m( \
- if(rngHandle->Instance==RNG2)) ~8 R5 `. E8 A( d& l V7 ^
- {
( ~/ ?' c; h) @! U W( x7 t - if(IS_ENGINEERING_BOOT_MODE())
/ Q9 J3 \0 j; E6 S$ E. p - {
2 L: e4 Q* x: M+ x( Y+ X - /* 外设外RNG2 */
7 d0 {" U* j/ M# K* E# ~9 d7 V - PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RNG2;
& }( y0 `3 ?3 m i( ] |! C9 P - /* 时钟源设置为CSI */
8 h }1 X; P ] - PeriphClkInit.Rng2ClockSelection = RCC_RNG2CLKSOURCE_CSI;
4 ]6 P$ Q! K" m2 e! q7 S - /* 初始化RCC扩展外设时钟,也就是RNG2时钟 */3 y0 C2 ]( H6 ?; G! i( b4 j) ~- M# V
- if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)1 e0 ^3 t6 |2 _9 w6 T+ F2 S
- {5 ]( ?- S% ~$ W* D8 h% ~
- Error_Handler();
9 d# U3 b* w5 |" _9 r; J - }
, m- ^* ^; \: p1 w" ?+ v - }
' O4 K9 i4 r( Z* ]5 D - __HAL_RCC_RNG2_CLK_ENABLE(); /* 使能RNG2时钟 */# w! @: D- T8 b% z
- }- e7 F/ l2 {( Y; b4 j
- }5 p: U$ W' `8 J/ D4 I7 u. E
- /**
# {8 [) S9 L" |7 [, C0 ^4 f - * @brief RNG底层驱动反初始化函数
' `4 i, K5 u7 T - * @note 如果需要,可以调用此函数3 `; f w- Z @5 Y7 c% b
- * @param hrng:RNG句柄# d! A0 \% T" H) a$ }
- * @retval 无: I8 i' m# C0 z [" H- \ @
- */$ D7 P* l, U5 \7 T
- void HAL_RNG_MspDeInit(RNG_HandleTypeDef* rngHandle)
! e P9 I4 j! @$ P P - {/ }4 I4 k( i0 j/ g+ Y' x
- if(rngHandle->Instance==RNG2)1 H; K3 C |' J( }" d
- {
6 x A. f, Z. L" W& w! N- r - __HAL_RCC_RNG2_CLK_DISABLE();/* 关闭RNG2时钟,即关闭RNG2 */ u) f, @' ~; x6 c+ k8 U6 ~
- }
% M" s; [0 L. n6 I2 {* O - }
4 z) ^( Z4 q ` - (4)手动添加获取随机数的代码3 i+ ^3 u5 k8 C
- 在rng.c文件中添加如下代码:" h6 |$ h0 B$ C7 ^
- /* USER CODE BEGIN 1 *// S$ I9 i1 t( d
- /**; o1 R( _4 ?7 }
- * @brief 得到随机数7 z3 }- i" L2 v! J
- * @param 无
" o5 r: Q* J* j2 Q3 T! j/ g: R% @ - * @retval 获取到的随机数(32bit)
9 l& e5 E$ ]; u* } - */. x3 B v2 ~1 i
- uint32_t rng_get_random_num(void)5 q, K3 @/ S' i& m* q9 ?" H. G
- {* n/ j4 z: x5 ^/ Y E g6 j8 p9 o/ S
- uint32_t randomnum;% c: i; E* s o3 e
- HAL_RNG_GenerateRandomNumber(&hrng2, &randomnum);: |: ^& d7 X3 z9 o9 q
- return randomnum;- l( U$ [9 f2 t1 d
- }$ c# e& @% w* v8 l# F
" p, d2 {( X' m x& R. V' s+ h- /**5 \( J% m3 R! k* ?
- * @brief 得到某个范围内的随机数
& S7 ] T5 g1 v J3 v6 k - * @param min,max: 最小,最大值.
- T& B4 C! @- F: G5 F7 a - * @retval 得到的随机数(rval),满足:min<=rval<=max
3 [7 ^$ L/ B- `6 ?1 M; { - */9 B; K0 c, K( ?; r( Q
- int rng_get_random_range(int min, int max)0 h9 {# Q% G! A( L% j% s! \
- {5 y8 i! X) ?- C" r9 l* |1 n' q
- uint32_t randomnum;# L+ w3 B3 p9 Q j' s; [& }
- HAL_RNG_GenerateRandomNumber(&hrng2, &randomnum); d# z% p, C. v, s, ^) \8 M
- return randomnum%(max-min+1) + min;4 j& X q) d% b$ @
- }% C5 E/ C5 H) X- h
- /* USER CODE END 1 */
复制代码 7 L" @9 ~, h h' p8 c
以上代码比较简单,主要是通过HAL库中的HAL_RNG_GenerateRandomNumber函数获取随机数。rng_get_random_num函数主要是获取随机数,rng_get_random_range主要是返回某个范围内的随机数。. S+ N# R3 P4 f7 p+ M, Z3 V
(5)修改main.c文件
7 y: X" p% A6 e1 ]% U3 u; v, e1 S4 K3 p G
- #include "main.h"
9 ]0 P4 U: q+ Y' @ - #include "rng.h"
$ Y( n3 ]$ O# u( e. `) y - #include "usart.h"
; D' r8 i2 y# v% A6 e. l. @ - #include "gpio.h"
6 @& O* Q8 ?; j7 h8 _ - ) x7 R7 d9 S. T9 V9 Z3 X6 }3 t
- /* USER CODE BEGIN Includes */
" {2 P5 P! b& { S. R - #include "./BSP/Include/led.h"! ?; J d' p5 }" a* a, H
- #include "./BSP/Include/key.h"
* b' m& M" ~ V3 C8 m - /* USER CODE END Includes */+ H2 m# Z1 L1 h
- % P/ c+ f" q( e+ {4 u7 G
- void SystemClock_Config(void);
& I# }" b6 D: ~1 O - / D( i* {( x9 O. y% x- L8 _0 L
- int main(void)0 C U, ^ X8 ?8 b" b/ O
- {- k$ b4 D: P+ M( a
- HAL_Init();
# ]- l: }2 Z$ U0 N% h$ ]: Y - if(IS_ENGINEERING_BOOT_MODE())9 e" E1 x: i7 v" }# P
- {
9 r1 s& n3 \$ { H- l$ R2 A - SystemClock_Config();' \. r" e8 X2 i7 i9 f% ]5 v
- }
S9 `8 M. ]4 a - MX_GPIO_Init();
! H) T3 w/ l {. I- { - MX_RNG2_Init();
/ l6 ?0 W( n. h3 S( H5 x/ ~ - MX_UART4_Init();( Z1 r5 ^4 o3 w) C6 } S: T8 Y
- /* USER CODE BEGIN 2 */+ R) m! j2 e x( ~
- /* printf("请输入字符,并按下回车键结束\r\n"); */" c: ?; B [6 x
- HAL_UART_Receive_IT(&huart4,&RxBuffer,1);/* 以中断方式接收函数 */( ~; K: q4 ?+ |( T2 ^
- /* USER CODE END 2 */' t/ j3 w8 E9 l' k$ L
- while (1)5 m: i. Z* ^, ]7 ?
- {
4 m; t6 g- q+ V" i# {& b5 V7 Q - /* USER CODE BEGIN 3 */+ [- v+ P0 a4 F/ ~
- uint32_t random;% j' z3 ^, G$ R' L# \% n. H: L
- uint8_t t = 0, key;
$ L+ Z4 J% V* S4 _- Y - key = key_scan(0);! r8 b6 p; ~) R% l6 Y
- if (key == KEY0_PRES)
! C' M4 }+ n4 U% S# e - {
0 e, ?4 @9 {1 p$ J. ~ - random = rng_get_random_num();
]2 r$ U G7 T: E2 a/ q1 c! } - printf("Random Num:%lu\r\n", random);' ?2 W$ k2 }- j* |. J
- }; [# _* [9 x3 |4 p- L/ w1 O
- if ((t % 20) == 0)
, L! M- ]" W# q; m" l - {6 x+ j, P# o2 ?( ] p7 u7 ~
- t = 0;' }( }* ?9 S* _% M4 { k- G# S9 j
- LED0_TOGGLE(); /* 每200ms,翻转一次LED0 */
" G& X) R+ |( J: v1 D - random = rng_get_random_range(0, 9);/* 取[0,9]区间的随机数 */( {! [8 Y, ]/ y$ P7 N$ A: P0 S
- printf("Random Num[0-9]:%lu\r\n", random);
5 u1 E& j8 t3 F1 l. L - }
. ?' ?& B; u' { - HAL_Delay(10);
9 k% @) c. ?) p3 V- W - t++;
* U( o; Z" D Y4 b" w/ }; W1 d - }
7 h4 v/ s; x- [& ^2 d0 j( a+ C - /* USER CODE END 3 */
% U/ B/ \! y3 D' P - }
复制代码 7 ^3 ^' f- k0 o4 P
以上代码中,每隔200ms会自动打印一次[0,9]区间的随机数,同时LED0会翻转一次。如果有按键按下,那么串口就会打印获取到的随机数。
# x, O# k' `! p) `4 D! K, Q7 M& y
20.4 编译和测试
; S3 @3 @/ S/ z' J4 m. w6 O4 {编译无报错,测试结果如下:
, U# w" k# D% ]" s& c) Z! C4 F) P
# b4 U4 s! i& r6 w1 x) t& q* y1 }) J4 f
图20.4. 1测试结果) q$ j% J( L2 {
————————————————* ]/ Z5 H. \/ w* x
版权声明:正点原子+ c: V0 d! ~. n# a H" ^1 L
( U0 }9 S) B+ _+ S0 j
! G* U) p1 L: [/ o$ } |