一、STM32的随机数发生器
% e) G, s# Q( B, N2 c 1.1 RGN 简介
) M* c' [* m0 ]6 X+ j; {9 Z STM32的RGN(Random Number Generator,随机数发生器),是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个由模拟量发生器产生的 32 位随机数。* P: P0 G- I3 b( j
& l3 @# g1 U. A& a2 b) u RGN由实时熵源(模拟)和内部调节组件组成,它将全熵输出作为32位样本提供给应用程序。通常MCU中去读取两个连续随机数的间隔为 40 个 PLL48CLK时钟信号周期。产生随机数的模拟量发生器,由几个环形振荡器组成,振荡器的输出进行异或运算以产生馈入线性反馈移位寄存器(RNG_LFSR)种子,这些种子用于生成 32 位随机数。RGN支持中断功能,通过检测状态位(RNG_SR 寄存器),检测种子异常(RNG_FLAG_SECS)、时钟异常(RNG_SR_CECS)等现象,检测到错误时生成中断。# e; M* j) T$ `3 P
! C; s B) R- Z7 q. w 1.2 RNG配置说明' U; p/ d; m0 }- F+ G( J& h
在CubeMX上开启RNG功能非常简单,只需要激活即可,在其参数栏设置无任何参数需要设置,在NVIC栏可以开启中断功能。: @; N1 c7 v5 a* X1 m
4 \$ h2 P3 B! H/ f7 x
& |* z8 J. m% a9 z/ n
/ d7 Q; h- L' i* L' x& } RNG依赖时钟源可有在时钟树选择器设置,时钟频率大小可以通过倍频器、分频器设置,例如本文采用STM32L496VGTx芯片时,设置其PLL48CLK时钟依赖源PLLSAl1Q,输出时钟频率是48MHz,如下图所示。
& w$ e+ E1 [% K1 ]$ R+ j* J7 M# q" [& y1 p& M7 h/ `
4 v# Z- U4 C0 v8 Q! H/ g4 _
: j b" C9 g* K2 F STM32的RGN内部通信通常是通过AHB总线实现,例如STM32L496VGTx芯片通过AHB2总线。
& T$ A4 Z$ p% \% o2 { ?
1 H R5 p- o- U6 ~9 R
' r# L) d3 C/ f, o
8 y0 ^$ L8 q/ N6 V0 l. J/ ~- o1 r" P RNG在STM32芯片正常、睡眠的工模式下有效。
/ v: `; n" X _3 [' V! i* @7 ~6 _9 {2 V/ Z: U V& G$ M
$ A4 C. p" g. c* d$ _
* H4 _! z7 j5 Y+ I6 E' Z* x 在STM32L496VGTx芯片中,RNG的寄存器空间大小为0X3FF(1024),用于存储RNG的数据寄存器、状态寄存器、控制寄存器以及缓存参数、组件信息等。2 Q' R' I f- A3 D8 y4 _) S% s3 q( M
; x; H c$ q! P$ ?" r
2 \7 ^+ P) ^/ t9 o7 B: p, N3 Z: f* i
二、创建RNG测试工程及HLA源码分析+ U1 V8 [( Z; l+ ^2 v4 o$ V: R
2.1 工程创建4 ]+ D& _( t* _: ^
本文为采用的是stm32L496VGTx-ali开发板来建立测试工程,并按本专栏前面的博文移植好了lpuart1串口调试及按键功能,请自行参考。6 `1 }2 K5 D3 C% I4 U5 p9 I
9 \' i( ^$ S/ p& h7 @' f; Z1 a; W现双击.ioc文件,打开CubeMX配置界面,开启RNG功能,并开启中断,时钟树上设置最终输出频率48MHz。) A5 k2 D9 v( L& z/ X! Z
5 h5 G+ | Z3 m2 O) V6 G9 H
0 `" C9 }0 P7 W8 `% L6 t# Y1 T
" B# l6 D4 t7 u# T* [4 [
( D" `4 h) e: z: ~6 Q
6 w9 a! M( Q/ S: h
保成生成输出代码
1 r3 B3 E# o3 C. H9 D$ W( y9 ?: a
& V) M, U1 U: R! R K5 h; p5 M 2.3 RNG的HLA库源码分析. Y; X( X1 [+ T2 ?1 `$ Y8 T& L. `- o
cubeMX生成代码时,会在Core源码目录下的Inc及Src目录,分别生成rng.h和rng.c驱动文件。
# o% w! n& M# b6 m2 ~5 p% ]; s$ \
* q7 M0 p* A8 X. y9 O) K# ]' _ M0 j8 ` 在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函数。- [' a/ h; j: Z5 E% k! h, ^
0 P% B) Y6 D* ^! E
% u. M+ Z- Z0 k9 w3 M: L3 ~
. I% z0 [. a8 z4 U( H 在stm32l4xx_hal_rng.c源文件中定义了HAL_RNG_Init函数,它做以下事情:诊断配置参数是否合规;
7 m1 ?. G9 ^- R. S R! P3 ~
6 i8 \, A ?9 {1 S7 R
' _# z+ S5 @# S* l1 C4 M9 `0 P5 ]4 b: g" r9 \. G; A3 W6 T
以及调用HAL_RNG_MspInit函数完成CRC时钟开启及中断设置与开启(如果cubeMX配置开启中断功能的话);- t- u5 R) z- D4 ~ u( Y( f# s0 x3 `
8 r4 b/ w7 R% G. L+ S
' \8 A- @6 W. S! g! \+ t
5 t, F" m9 F) j# m6 A! i% ~ 最后开启RNG,检测种子、数据寄存器的有效性。
) m7 s5 Q/ H G$ g: j/ ^( I. t
# B# B* j0 i4 k; O, X6 t
0 D i( X( Y/ z
9 E, w: J' _ F- L! U: L; l 再回到rng.c内,HAL_RNG_MspInit函数实现了RNG外设时钟设置(如果配置依赖外设的话)与开启,以及中断设置与开启(如果cubeMX配置开启中断功能的话)。
' E8 D( S2 ]3 v/ b) D8 R& i6 n1 U9 h, j- o5 q4 p4 e2 f8 l# J, g
" ]' D& n3 I' a F6 g8 [/ x* Z4 R8 P4 z& e: V
在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。' i% |" \$ ~6 a
1 V5 q. z. f( H, v$ M' Z" a" g- o0 D
/ u8 L7 b7 Y, b* [& [! | F$ x: O
. k- o- I; F& b, y( l# _. c 2.3 RNG使用设计
7 B0 {4 z4 }: y% k, E! G$ h 由于本文开启了RNG中断功能,在rng.c源码中,重新编码HAL_RNG_ReadyDataCallback函数如下:
- M. z0 F0 j/ _5 D0 |- /* USER CODE BEGIN 1 */
- B) S. g) E2 K4 C% \ - void HAL_RNG_ReadyDataCallback(RNG_HandleTypeDef *hrng, uint32_t random32bit)
3 H7 c& m% G ~7 `9 o6 p3 p - {
5 A' U' m4 c) Z `! h A& N - printf("RNG_ReadyDataCallback:%lu\r\n",random32bit);
% V6 F: P0 b1 u- \0 G - }
5 M& w- W# ?4 f [ - /* USER CODE END 1 */
复制代码
2 c2 a/ ?, V( e! m; m% q j; P5 y 在main.c源码文件中,添加各个外设及功能模块的头文件依赖。. g' F+ p( L/ F" G5 V
- /* Private includes ----------------------------------------------------------*/8 m* Z/ {$ \7 @, G- }; V
- /* USER CODE BEGIN Includes */
4 h/ ]# G4 F: y7 A9 U - #include "../../ICore/key/key.h"
& _! K8 l0 {+ E. n! K - #include "../../ICore/led/led.h"
, x- x# I( W/ ~+ l, N7 X/ R - #include "../../ICore/print/print.h"
' e' Q* ^- z2 G. }% W! A- [: H - #include "../../ICore/usart/usart.h"
2 V2 p6 G0 k7 ~* e' i( c. ^1 K ^ - #include "../../ICore/delay/delay.h"
& x& o* M; ?' u& k- A5 `! } - /* USER CODE END Includes */
复制代码
~% ?6 y% ?3 S; L- q# V6 y 在main.c源码文件中,添加RNG句柄声明,用于读取随机数时使用。 h: M! g1 Z+ V9 {: ]: B, Q
- /* Private user code ---------------------------------------------------------*/
6 n1 v1 a) _! B7 N! S( s; y; A - /* USER CODE BEGIN 0 */
, m m7 F2 @" b$ ? - extern RNG_HandleTypeDef hrng;' U& I+ W( b8 ~' ~+ d9 z
- /* USER CODE END 0 */
复制代码
- e. T$ {5 h x6 I7 ` d& @# m 在main函数内,启动串口中断功能及初始化其他辅助参数。2 @* ]' {! I' q3 R8 q# v2 c( T2 E5 b
- /* Initialize all configured peripherals */
. ^$ K1 o$ Z, D' x' E+ C7 @) Y6 j - MX_GPIO_Init();; J1 G6 k5 u! D: z: u; y/ K
- MX_LPUART1_UART_Init();+ H% M; d% M( {& P' B+ P \8 J
- MX_RNG_Init();
$ J3 P6 o; U) @; [ - /* USER CODE BEGIN 2 */
5 ^" E5 @" c p4 f; q3 h0 M - ResetPrintInit(&hlpuart1);4 I, w: b# S2 c" S2 {; ~
- HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
5 A/ c9 y# y) Z - HLPUSART_RX_STA = 0;
! B% i: c& l* S - //
% v0 L& d; q7 g% {2 Y- h6 U/ d5 G - printf("app restart now!\r\n");
$ P) E9 t, \+ z* h. w - uint32_t rng_val = 0;2 {. o: e8 N1 a* s- a2 @+ ]1 o: B8 ]
- /* USER CODE END 2 */
复制代码 . T- F9 q' D$ X& ^) G6 D6 c' b. I; v/ L
在main函数循环体内,通过按键触发调用不同的随机数获取函数,并打印输出到lpuart1上。
9 b! U3 }+ ]8 l- /* Infinite loop */7 S& L8 s9 c2 H
- /* USER CODE BEGIN WHILE */
9 D0 @' }, R( G - while (1)5 y) @0 x4 r! U+ _4 T
- {
. K+ b6 {1 f2 n - if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始6 h" h3 }2 H. V- ^
- //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
4 H) Z n# l/ I7 o6 f9 [ - HLPUSART_RX_STA=0;//接收错误,重新开始
3 }4 }4 Q/ m/ |& ^3 X) C& q - HAL_Delay(100);//等待- z( K- Z: L) J+ \
- }; K, n# V3 a) c Y
- if(KEY_0())
, V5 m4 l* P0 e2 b* p) q - {
1 ^; _. u4 u, k) }8 H2 j: f - rng_val = HAL_RNG_GetRandomNumber(&hrng);3 M5 k# K$ ^2 X$ }$ L; ?+ O
- printf("KEY_0->RNG_GetRandomNumber:%lu\r\n",rng_val);
7 @. Z! I$ @* i - }
0 Z" z% I2 L& z5 f - if(KEY_1())
3 W% f( P+ q% a" \4 c7 I% q* _; p1 g - {9 @1 d3 ~% _ B
- rng_val = HAL_RNG_GetRandomNumber_IT(&hrng); ]( ?3 _9 x0 H9 e8 O; s
- printf("KEY_1->RNG_GetRandomNumber_IT:%lu\r\n",rng_val);; x# J$ o0 K& I/ u% x8 ]- @
- }
3 f$ U, ^3 U e2 l* W - if(KEY_2())
* Y# D+ m" I/ w0 D) c% B% @: P - {! X$ A" c7 Z% Z1 e% c1 b; G7 i
- rng_val = HAL_RNG_ReadLastRandomNumber(&hrng);2 y. G' P$ F& f
- printf("KEY_2->RNG_ReadLastRandomNumber:%lu\r\n",rng_val);
0 j# ^0 f" g5 j - }5 g6 J) y# o# d1 e
- /* USER CODE END WHILE */
复制代码
6 r3 y# H, n* a9 m/ R3 M0 c三、编译及测试0 t- r+ ?* l6 k m1 Y1 E2 g8 q
3.1 编译及下载
6 u! p6 f) X" k/ `: V 点击编译按钮确保编译正确输出,并配置运行配置后,点击运行完成下载
2 l, X: R( U2 ~7 x q) D7 P3 K7 _# b" f7 ^& k: W. ?
( I. }: `% m4 K2 s# c* ^0 I( f. W% o
3.2 测试7 T9 E9 V @1 K1 r! B
串口助手链接到开发板上,分别按键KEY0、KEY1、KEY2输出log如下图:! w& R, U0 k0 V/ b5 C, D
+ Q+ L8 G' W1 |/ x- A4 N- y! {9 x
2 d0 o+ X {8 F N. r5 k6 C; z m' D
由于KEY1是中断方式读取随机数,因此我们前面重写的回调函数也会生效,但需要注意的是HAL_RNG_GetRandomNumber_IT先于HAL_RNG_ReadyDataCallback,读取了数据寄存器,连个函数是分别读取数据寄存器,因此得到的随机数是不一致的,实际项目中按需选择来应用。
8 V/ i+ \& Z1 F8 l: @; E2 k* m$ Q c
# r6 K7 N5 c* `3 Z; g) n- z
+ ^( h4 m/ }; {# }- X# w————————————————& p- Z2 N8 w) h" g5 B) V
版权声明:py_free-物联智能5 D% h, W7 E/ C" l- B0 `
如有侵权请联系删除' `7 j) e, W& `0 Q
7 D: ~: g( ?) H$ n6 J
|