一、STM32的随机数发生器
" g2 ~7 a* z. m! J9 _/ `% l 1.1 RGN 简介
+ W$ i, m$ \. K9 u0 U STM32的RGN(Random Number Generator,随机数发生器),是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个由模拟量发生器产生的 32 位随机数。- E- q- ], g; p) p7 Z% s+ ?
! ^0 H1 M3 [1 f& l RGN由实时熵源(模拟)和内部调节组件组成,它将全熵输出作为32位样本提供给应用程序。通常MCU中去读取两个连续随机数的间隔为 40 个 PLL48CLK时钟信号周期。产生随机数的模拟量发生器,由几个环形振荡器组成,振荡器的输出进行异或运算以产生馈入线性反馈移位寄存器(RNG_LFSR)种子,这些种子用于生成 32 位随机数。RGN支持中断功能,通过检测状态位(RNG_SR 寄存器),检测种子异常(RNG_FLAG_SECS)、时钟异常(RNG_SR_CECS)等现象,检测到错误时生成中断。) L" u; a6 ?0 V; W5 S1 n6 n
9 i f* Z \9 |1 g' I
1.2 RNG配置说明
, \' b( s( y: K" R7 Y) M 在CubeMX上开启RNG功能非常简单,只需要激活即可,在其参数栏设置无任何参数需要设置,在NVIC栏可以开启中断功能。( `7 E. X0 ~: c
) u$ I' |! X9 l0 f
! } d- k7 i* x( O/ L. R6 v+ v
! N+ h" p; t% {' r0 Z RNG依赖时钟源可有在时钟树选择器设置,时钟频率大小可以通过倍频器、分频器设置,例如本文采用STM32L496VGTx芯片时,设置其PLL48CLK时钟依赖源PLLSAl1Q,输出时钟频率是48MHz,如下图所示。( w: P( a% X/ ]
' R% u7 ~+ }5 ]! R% Q' j. e
" O; g/ P4 @5 @4 d* h
9 P9 [$ D) O/ _! z) [ STM32的RGN内部通信通常是通过AHB总线实现,例如STM32L496VGTx芯片通过AHB2总线。% c% z: c8 T8 R3 c2 ^6 |
k j2 K1 g# s/ F* `6 C+ ~
( Z/ ?1 m9 K4 D8 R% v6 ?& A
( A3 R0 E# j6 D6 K7 f: X5 j8 ~9 \
RNG在STM32芯片正常、睡眠的工模式下有效。
3 Y, h" ^4 q& i7 H# V, Y' G/ W2 C4 l& ^
- J! @" B) i6 n5 N5 d9 D/ {' _
8 S* `0 \9 r% ~! t2 o 在STM32L496VGTx芯片中,RNG的寄存器空间大小为0X3FF(1024),用于存储RNG的数据寄存器、状态寄存器、控制寄存器以及缓存参数、组件信息等。
d. T( x ?" _- _6 b5 h9 p
- B' x9 r- h7 u( L0 z- o) U6 ]
7 o4 Q8 B% m6 W5 ~" \+ E
; O/ k% w p; A8 F
二、创建RNG测试工程及HLA源码分析
& O' ]! ?) l2 q3 C7 q. ?5 y( E1 C: h 2.1 工程创建
7 u) l. h0 n) s) V7 m) v$ L 本文为采用的是stm32L496VGTx-ali开发板来建立测试工程,并按本专栏前面的博文移植好了lpuart1串口调试及按键功能,请自行参考。" Z# Z& Z) f4 k+ Q- U
! q+ C: G0 J. b* T1 p现双击.ioc文件,打开CubeMX配置界面,开启RNG功能,并开启中断,时钟树上设置最终输出频率48MHz。; s5 O5 R7 U' p/ z( ]
/ y1 q& F* K5 R5 }8 T
' C! P- w$ A" @/ ?% x, k8 J
9 r/ u# w% Q: W. n
0 z+ W2 ~* ?2 z2 _1 S g
( M/ g1 J3 o" T' a: u+ k
保成生成输出代码
8 r1 z' @% P5 x1 ^& C( [0 i( L. [- `# K5 V( \7 e* B$ k0 r+ ^
2.3 RNG的HLA库源码分析5 f5 U0 R n; D( B" ^
cubeMX生成代码时,会在Core源码目录下的Inc及Src目录,分别生成rng.h和rng.c驱动文件。
8 r' L2 \- \3 O4 [
5 _- `' y9 t0 ?9 m1 x3 X- H 在rng.c文件中,主要定义了MX_RNG_Init函数和HAL_RNG_MspInit函数。MX_RNG_Init主要做两件事情,一是生成RNG句柄Instance(寄存器),二是调用HLA库的HAL_RNG_Init来实现真正的初始化设定。HAL_RNG_MspInit是HLA内的弱函数,根据实际配置CubeMX会生成新的函数,完成真正的MCU底层设置任务(MspInit,MCU Specific Package init,即指和MCU相关的初始化),覆盖原来的弱函数,而在HAL_RNG_Init函数中会调用到HAL_RNG_MspInit函数。4 a6 [& _4 _( o, x& g% w/ F
( c6 A# t& [* b! B1 a0 D- Z1 y
% l, W" p. j' r% e
/ T5 c) `4 R1 y/ i n 在stm32l4xx_hal_rng.c源文件中定义了HAL_RNG_Init函数,它做以下事情:诊断配置参数是否合规;
, v9 V, b& y, i5 k( z9 `+ `# b+ t0 T( P6 S( A' W6 [4 _/ ]1 H8 J" o
9 w) }4 L( M. U
) [+ B' H+ \0 L( `+ H: n 以及调用HAL_RNG_MspInit函数完成CRC时钟开启及中断设置与开启(如果cubeMX配置开启中断功能的话);
5 l; c3 ?3 i0 C, n
, b! }: ?& e, y5 Y1 V
- u* [1 F5 ?" u3 k
9 X' N% i9 P/ m6 X% X, @
最后开启RNG,检测种子、数据寄存器的有效性。5 K: ^3 ]0 M* \6 ^/ e
- I, _3 a$ k0 t3 W
) U+ m" ^, Y z, w& ^1 p
( A% F, }# {' c
再回到rng.c内,HAL_RNG_MspInit函数实现了RNG外设时钟设置(如果配置依赖外设的话)与开启,以及中断设置与开启(如果cubeMX配置开启中断功能的话)。- O- h% T A0 [# t+ g6 M- y* a
, z2 ^# T2 w; F& ]
( `+ U" R- G; B+ Y( C( R! K- E- P2 T9 c
& l& w1 Q" E8 ^4 R% X, h* ?% U 在stm32l4xx_hal_rng.c源文件除了定义初始化功外,还定义了RNG的MSP初始化及删除函数,各种方式读取随机数函数,状态读取函数,中断请求及中断回调函数等。较常用的是:读取随机数函数如HAL_RNG_GetRandomNumber、HAL_RNG_GetRandomNumber_IT、HAL_RNG_ReadLastRandomNumber,状态读取函数HAL_RNG_GetState,以及回调函数HAL_RNG_ReadyDataCallback和HAL_RNG_ErrorCallback。0 t' A1 @/ d! x0 N, t
) W0 h; U& E, d# N g9 b! Z! ~+ l
- h/ n z" ~1 j6 [
& A1 s9 c. y% p# U' z2 F" C0 Y+ a 2.3 RNG使用设计
- ^( _ Z# b2 z5 f7 \# I+ \* d 由于本文开启了RNG中断功能,在rng.c源码中,重新编码HAL_RNG_ReadyDataCallback函数如下:
1 ~: h/ U/ K" I- /* USER CODE BEGIN 1 */& p3 G5 U0 w& G# f3 G% D7 c( Z7 G
- void HAL_RNG_ReadyDataCallback(RNG_HandleTypeDef *hrng, uint32_t random32bit)
# z$ a' R- h3 [$ c8 h. D! X - {
4 s+ I M: E# | - printf("RNG_ReadyDataCallback:%lu\r\n",random32bit);
+ Y% @- y8 w- X; X - }' C( M6 O0 y" b& Z
- /* USER CODE END 1 */
复制代码 - L0 m4 x0 s2 s# B; k/ m
在main.c源码文件中,添加各个外设及功能模块的头文件依赖。9 Z7 m$ R0 G# t7 C3 U
- /* Private includes ----------------------------------------------------------*/
" C% q+ W9 Q( y - /* USER CODE BEGIN Includes */( S; N) D/ ?& \8 e; h% h
- #include "../../ICore/key/key.h"
9 \& a/ H% ~# M1 M+ Q - #include "../../ICore/led/led.h"
* ~: m7 B& |% l, W0 {5 N - #include "../../ICore/print/print.h"
) s" h$ `9 b. u - #include "../../ICore/usart/usart.h"
( [' P, s( n* X( [ - #include "../../ICore/delay/delay.h"
}/ W- x4 |7 {8 `: p( d* \3 \ - /* USER CODE END Includes */
复制代码
5 v5 W) |: y1 e% D; C 在main.c源码文件中,添加RNG句柄声明,用于读取随机数时使用。3 L8 o# O3 d6 l* _9 B3 c# u
- /* Private user code ---------------------------------------------------------*/
2 n- l' x, P% l* Y. O; x - /* USER CODE BEGIN 0 */
, ~1 ?8 N" h+ U+ M - extern RNG_HandleTypeDef hrng;
& O8 o3 `; o: j% ^$ } - /* USER CODE END 0 */
复制代码
. p, _7 G( A+ f- t! { 在main函数内,启动串口中断功能及初始化其他辅助参数。
/ j1 R0 A8 O. N; C+ I6 x. D- /* Initialize all configured peripherals */
4 h( p! X- U, U - MX_GPIO_Init();
! b+ I2 p# ?/ S" v6 y9 L, A - MX_LPUART1_UART_Init();4 N' c7 U% y: _) h$ ]: X
- MX_RNG_Init();& c/ q# @/ \2 p! x' C" ]
- /* USER CODE BEGIN 2 */5 w3 H- s/ R2 E I Z/ ]- d
- ResetPrintInit(&hlpuart1);
: |3 k3 {. g" N( S V/ B Q - HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
) `: H& `; T6 {( Z4 r - HLPUSART_RX_STA = 0;3 N2 _5 h% E1 O) L
- //+ s/ G6 Z& n4 [, e& R$ P3 d& d
- printf("app restart now!\r\n");
. K* N% ^" s1 a: [% \! d, ? - uint32_t rng_val = 0;2 Z @$ Y1 l5 A# t3 I. s( }. z1 {
- /* USER CODE END 2 */
复制代码
# R# ?+ d( Z4 E4 _1 Y% Z, s) k 在main函数循环体内,通过按键触发调用不同的随机数获取函数,并打印输出到lpuart1上。3 v7 Y' H5 `( [% [( a* S8 f f
- /* Infinite loop *// s/ I7 J; b' {
- /* USER CODE BEGIN WHILE */7 M1 K" j: R/ k/ N
- while (1)! h6 K4 c9 h5 b) _
- {# b! p, X" A& y5 v( ?6 `
- if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始6 K. b, w9 ?6 e, w' s
- //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);: e% T% m5 P7 L, H4 v
- HLPUSART_RX_STA=0;//接收错误,重新开始
% J3 ^; m7 G$ d$ m - HAL_Delay(100);//等待- P8 Z6 x2 y: C5 d, R O3 C
- }
. ]& u- e$ o+ F z - if(KEY_0())
W- L" Q! O; a" ? - {
: D, |% z4 O; ?1 n/ L; c - rng_val = HAL_RNG_GetRandomNumber(&hrng);
! Q8 J/ e; S4 W: a A% P& E7 [ - printf("KEY_0->RNG_GetRandomNumber:%lu\r\n",rng_val);$ V7 f, E! O0 F8 g$ ~6 G
- }
) e! E% F# }6 i7 E) i$ d, Q7 t - if(KEY_1())
" e0 _# a# x5 j6 _4 t$ T" k - {+ e' m. H! e5 g# T2 n* i
- rng_val = HAL_RNG_GetRandomNumber_IT(&hrng);
+ x( ~( {" w) s% X( r* C - printf("KEY_1->RNG_GetRandomNumber_IT:%lu\r\n",rng_val);* U1 `/ o- t& c; ^5 [; m
- }' t3 F3 }# E3 i3 O" I2 m2 O
- if(KEY_2())
$ w4 J ^+ K0 F" a: M, V - {
) c: S7 Y# v+ v0 S5 v* M - rng_val = HAL_RNG_ReadLastRandomNumber(&hrng);
# I4 b0 m! H- c6 W: k& u0 O - printf("KEY_2->RNG_ReadLastRandomNumber:%lu\r\n",rng_val);
$ y# l2 T4 o! W% d9 k - }' O: x7 o! i# i! Y9 S/ Z' ~
- /* USER CODE END WHILE */
复制代码 6 O% D; w7 C( v" u
三、编译及测试/ l# u5 n- D- k+ X5 c/ Q
3.1 编译及下载" C) V% E. p4 f4 `1 E7 q1 @8 P ]
点击编译按钮确保编译正确输出,并配置运行配置后,点击运行完成下载9 g; g6 v( |2 a/ k* d
$ V, S% a. M" O( u
$ J/ O1 I' G5 y& u, Q3 i g
% U1 U+ n3 b9 ]0 R4 `9 R# D E* [ 3.2 测试0 \1 G, t% k% m! ^& _' r2 I- q
串口助手链接到开发板上,分别按键KEY0、KEY1、KEY2输出log如下图:
% w1 _& u, A" u( n4 N i
$ ^; e& G0 h& w. I+ `8 C$ [& T, N
& D- `5 H( S8 w8 g. r0 _
. K" M' Y. K, E3 O8 `, _
由于KEY1是中断方式读取随机数,因此我们前面重写的回调函数也会生效,但需要注意的是HAL_RNG_GetRandomNumber_IT先于HAL_RNG_ReadyDataCallback,读取了数据寄存器,连个函数是分别读取数据寄存器,因此得到的随机数是不一致的,实际项目中按需选择来应用。- O! k. Z2 ]+ {3 M; [7 H
4 Y* ~8 N6 T4 @- [% h
) N2 R, t& P& c+ {$ c
1 j( ]4 r* _) w% x3 h+ a
————————————————
u1 r; J' p: }$ `! v版权声明:py_free-物联智能
) W g& o: Q& e4 `如有侵权请联系删除- A4 U. f' |; \9 j7 u/ s
' z7 \7 G3 D" _8 F( k. Q" F* ` |