一、STM32的随机数发生器6 `! B; X4 o r! ~" [ r
1.1 RGN 简介
. P3 x. v. X8 x3 f8 q STM32的RGN(Random Number Generator,随机数发生器),是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个由模拟量发生器产生的 32 位随机数。) r8 P X. j2 |& X
# B7 n: u: b( s5 E3 b1 L RGN由实时熵源(模拟)和内部调节组件组成,它将全熵输出作为32位样本提供给应用程序。通常MCU中去读取两个连续随机数的间隔为 40 个 PLL48CLK时钟信号周期。产生随机数的模拟量发生器,由几个环形振荡器组成,振荡器的输出进行异或运算以产生馈入线性反馈移位寄存器(RNG_LFSR)种子,这些种子用于生成 32 位随机数。RGN支持中断功能,通过检测状态位(RNG_SR 寄存器),检测种子异常(RNG_FLAG_SECS)、时钟异常(RNG_SR_CECS)等现象,检测到错误时生成中断。7 S" J: ?0 L1 z$ X- f% z: E
+ W6 W- U/ N2 t 1.2 RNG配置说明( ?5 q H, ]- C% E, @& z [7 V
在CubeMX上开启RNG功能非常简单,只需要激活即可,在其参数栏设置无任何参数需要设置,在NVIC栏可以开启中断功能。/ T1 e0 y( j) n6 e: n
: {6 S4 d* q% |. w5 `
& W# P9 g% Q, T1 _
9 H3 n( [& K. y RNG依赖时钟源可有在时钟树选择器设置,时钟频率大小可以通过倍频器、分频器设置,例如本文采用STM32L496VGTx芯片时,设置其PLL48CLK时钟依赖源PLLSAl1Q,输出时钟频率是48MHz,如下图所示。2 T& ~" Y" U" ]- x' ], `
$ U i% L, \8 U+ }. m: q
. b: W& x2 \/ q
: S. x' |! { M, y: [9 h# D
STM32的RGN内部通信通常是通过AHB总线实现,例如STM32L496VGTx芯片通过AHB2总线。
5 M K, I8 A0 r$ f3 C
) m9 u7 @$ J9 P% ~# o& e) Y0 H" ]
! i; e- |% L: Y! P2 j8 J. P7 d$ f+ H4 R# l
RNG在STM32芯片正常、睡眠的工模式下有效。% I( J8 c. m& K& x9 _
6 ~8 h, o( W. s
8 u* A3 g# p* B
5 K! J0 |$ Q6 M5 v P3 h 在STM32L496VGTx芯片中,RNG的寄存器空间大小为0X3FF(1024),用于存储RNG的数据寄存器、状态寄存器、控制寄存器以及缓存参数、组件信息等。
. G3 U- e. o6 C8 E) t- O. g4 Q8 z8 V" `7 l5 k) {7 i4 b
1 O7 i- I J. o ]
, O# _6 F1 N2 m/ F2 `" w0 b. R% Y3 |
二、创建RNG测试工程及HLA源码分析6 m' ], J" v3 C; E* l' `( T
2.1 工程创建
) B+ p3 U2 Z0 {, g J/ d; T) F 本文为采用的是stm32L496VGTx-ali开发板来建立测试工程,并按本专栏前面的博文移植好了lpuart1串口调试及按键功能,请自行参考。5 W- e# p3 G8 ?5 c2 d; `( `
0 L8 X: j" A+ A6 i: d- A; a现双击.ioc文件,打开CubeMX配置界面,开启RNG功能,并开启中断,时钟树上设置最终输出频率48MHz。
2 A+ ~# p9 @0 E% q9 P$ [0 l5 c" O/ L- i2 y. M
8 \/ a: v+ y& T) a: x" q1 @$ t' f$ }: E# b3 l7 Q- U: s2 C7 R9 _
, C- Z4 B t5 |/ b! w L# R" e" e! c% B
保成生成输出代码% H6 b5 e4 O8 q
0 X1 {8 ^4 L5 I9 c" |! ?. G1 M
2.3 RNG的HLA库源码分析
0 K/ S' d3 h* s6 c& R+ G3 b) l& f cubeMX生成代码时,会在Core源码目录下的Inc及Src目录,分别生成rng.h和rng.c驱动文件。/ W2 J% n/ f6 }9 z$ I" K
( u, I+ N0 p, ?) X9 x6 ^0 e 在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函数。
# D) I6 N$ J$ c+ W/ M1 H
% C1 }8 a2 x: W. ~
5 o8 N: V# r' D( H% U
3 w, a" Q% j2 m 在stm32l4xx_hal_rng.c源文件中定义了HAL_RNG_Init函数,它做以下事情:诊断配置参数是否合规;9 f8 J1 ^: }2 @4 q" n' ]$ n
, C8 h6 J+ B; H
: c2 R8 [& b( Q0 A5 q" a( F, r7 u+ H$ u- j* X2 Y4 I
以及调用HAL_RNG_MspInit函数完成CRC时钟开启及中断设置与开启(如果cubeMX配置开启中断功能的话);/ Q/ a: R% ?* e
) i8 ^( z' a% x3 e* e0 g5 V5 `, X
- M+ Z& z, G C/ O4 P: V
L9 |3 z: b7 P0 m# I& ` 最后开启RNG,检测种子、数据寄存器的有效性。7 ?; z' C7 a) ]2 z6 C
: e, S! S; ^" o4 ^, U2 D
P: k& h" B) ], J+ b1 ]2 _8 I' y G7 A, ~
再回到rng.c内,HAL_RNG_MspInit函数实现了RNG外设时钟设置(如果配置依赖外设的话)与开启,以及中断设置与开启(如果cubeMX配置开启中断功能的话)。
" J5 O/ U% x" o2 v# ]
4 q* k8 S& P$ }8 X. ?) P& J- X
+ p* Y2 P' r2 {& C, G; j# k
4 K! ]/ h7 G) |8 c4 o 在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。
( ]4 F L# f" t7 e- v- e) F
% y7 n! V1 N1 v- b- h* c5 a
* W' B$ s7 J) D- k& S$ @* f8 k( M
# ?! t7 g6 `" v! F x" U! v1 H 2.3 RNG使用设计
! }, v. U' @; V+ p$ V 由于本文开启了RNG中断功能,在rng.c源码中,重新编码HAL_RNG_ReadyDataCallback函数如下:
# C8 @/ ?0 W* j- /* USER CODE BEGIN 1 */
2 Q* {& w6 N/ c, I - void HAL_RNG_ReadyDataCallback(RNG_HandleTypeDef *hrng, uint32_t random32bit)
9 z6 A* l2 O ?& U! I - {
& P! {2 h" O r - printf("RNG_ReadyDataCallback:%lu\r\n",random32bit);4 i* [5 ^ n* L1 Q \
- }
3 G8 T3 m+ M8 O& B. {5 K4 ^8 d# h- d - /* USER CODE END 1 */
复制代码 ( [. [# c4 J/ v1 h) f& c6 n* c2 D
在main.c源码文件中,添加各个外设及功能模块的头文件依赖。8 {/ J4 t4 {) J, P& v0 }
- /* Private includes ----------------------------------------------------------*/
$ w3 K6 l% r! ?. z/ r - /* USER CODE BEGIN Includes */5 p& a: q1 U. A% ~6 s. |5 U( L
- #include "../../ICore/key/key.h"* M! F2 e' B6 w
- #include "../../ICore/led/led.h"
( c" P8 \4 j. [; N - #include "../../ICore/print/print.h"+ Q! U' t5 d9 \& P2 }5 p
- #include "../../ICore/usart/usart.h"% D, k5 j" i: @, r; D
- #include "../../ICore/delay/delay.h". C8 S/ p9 I1 b) U: L% \: m% G
- /* USER CODE END Includes */
复制代码 # Q" Z3 p% L0 I9 [. F( K3 z
在main.c源码文件中,添加RNG句柄声明,用于读取随机数时使用。
4 f% T6 t0 p5 Q* b* F7 I- /* Private user code ---------------------------------------------------------*/
! Y+ r |% l8 A* `' o1 H: I, d - /* USER CODE BEGIN 0 */( i3 A4 U) ^3 \% S' L z% i4 r
- extern RNG_HandleTypeDef hrng;; t8 S( K, @' w% I# Y
- /* USER CODE END 0 */
复制代码 + k) m. V& W; C5 _
在main函数内,启动串口中断功能及初始化其他辅助参数。
9 n+ X5 B/ Y: v% U6 `7 b8 z- /* Initialize all configured peripherals */
3 p8 Y" R/ Q! ]+ J$ p - MX_GPIO_Init();
; }* t8 K# u5 E* o - MX_LPUART1_UART_Init();* Q$ I' }5 Q* [3 s
- MX_RNG_Init();9 f' q$ `4 P" b+ L% t" ^9 A
- /* USER CODE BEGIN 2 */
, @2 s, L" d( e* c - ResetPrintInit(&hlpuart1);
; u: R6 ]. q6 h5 z6 ~$ b m. z - HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断( F0 m1 F0 W* Z! K: W
- HLPUSART_RX_STA = 0;2 {( G% j7 `4 F' p' K5 M6 k
- //
/ e- t9 x% S' _+ y - printf("app restart now!\r\n");7 `- B, f5 ^4 i# |$ p
- uint32_t rng_val = 0;' e2 a( p0 }- E/ Q; b# u4 o+ I
- /* USER CODE END 2 */
复制代码 2 K6 f3 M$ q/ J V# T: B$ |
在main函数循环体内,通过按键触发调用不同的随机数获取函数,并打印输出到lpuart1上。
1 z- i+ u5 z3 J7 r5 r- /* Infinite loop */
" U/ z! E" G% L J( t9 D( x4 V - /* USER CODE BEGIN WHILE */- o; m7 R! r. s' O: Z4 F
- while (1)$ l1 ]+ w/ W8 |+ G
- {
. x/ H( Y8 p# m9 c1 v3 E - if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始! ]3 L/ r1 g- q
- //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
# ~4 }) J! k& O8 O# E( X( f# \ - HLPUSART_RX_STA=0;//接收错误,重新开始6 |% a! [, C1 w) W9 l* ^' I
- HAL_Delay(100);//等待
, U9 Y, P0 l3 K" V0 `* W1 ], d h - }
9 _& V% _9 z: j6 L- M x0 @ - if(KEY_0())) b- @: v8 j$ e' x
- {
" h0 z& \. C7 p4 Q& ]3 {' Y& w - rng_val = HAL_RNG_GetRandomNumber(&hrng);
. ~ Z& F- |: X% f0 L - printf("KEY_0->RNG_GetRandomNumber:%lu\r\n",rng_val);
. s8 u% m; t# g - }
" ]9 f( n/ E: U# Y# w& y5 B - if(KEY_1())
$ g9 `5 b! @* N7 D) }: x5 `- j! ]# q - {7 {( o& _% ~: V% K
- rng_val = HAL_RNG_GetRandomNumber_IT(&hrng);' s4 s; e# p$ s" X
- printf("KEY_1->RNG_GetRandomNumber_IT:%lu\r\n",rng_val);( ?8 G& k$ c$ N: d/ c. I8 e- K( @
- }& C6 }6 g. J. m' t6 D# x: H
- if(KEY_2())
( W/ M$ Z6 ], l; [) t! q( G - {. \4 j- g" }. A) N3 R
- rng_val = HAL_RNG_ReadLastRandomNumber(&hrng);% W0 J2 r, `# z6 {9 b6 V5 p
- printf("KEY_2->RNG_ReadLastRandomNumber:%lu\r\n",rng_val);
7 |, U) M1 Z3 x - }
; ^% G3 D0 z1 V& B' g, ]5 A) R - /* USER CODE END WHILE */
复制代码 : J8 L( b5 P# c5 t3 N* P1 x
三、编译及测试" o+ ^8 ~* J9 V w+ n
3.1 编译及下载
. r- f5 G% c% f8 i& n 点击编译按钮确保编译正确输出,并配置运行配置后,点击运行完成下载
\' b# S f. G- L# o4 y( b+ p: L* V+ y
0 s: j1 j" n" @% L7 M
" p$ g$ R% Q. w6 X# }8 m( c 3.2 测试
- p$ t7 Y+ {8 e- C$ w. t) [, y5 Y 串口助手链接到开发板上,分别按键KEY0、KEY1、KEY2输出log如下图:! X: J) {' n) g
& S2 X' q$ `; z4 l# R* D1 f" ^
8 r# G6 z1 P D9 C9 k! N4 m' R, J; D
# P6 y8 o Z. |2 w3 i 由于KEY1是中断方式读取随机数,因此我们前面重写的回调函数也会生效,但需要注意的是HAL_RNG_GetRandomNumber_IT先于HAL_RNG_ReadyDataCallback,读取了数据寄存器,连个函数是分别读取数据寄存器,因此得到的随机数是不一致的,实际项目中按需选择来应用。
8 e x6 r) N- c# l0 `) O( z% _" s- z
! [( L0 X0 u& Y7 P3 ^; @# N$ l3 z. o/ `# z `9 O2 q
————————————————
9 V U. S1 w' J, Q版权声明:py_free-物联智能0 @9 D$ }' Y0 `1 D9 @: x
如有侵权请联系删除
- n! u" \+ |8 d! x
' C; }) E T- T c4 E+ G4 s: }! f& w |