一、STM32的随机数发生器
3 g$ B4 Q# f/ |$ [& i- z 1.1 RGN 简介8 v. [) C; @: h8 f
STM32的RGN(Random Number Generator,随机数发生器),是一个以连续模拟噪声为基础的随机数发生器,在主机读数时提供一个由模拟量发生器产生的 32 位随机数。8 y$ O6 l4 X8 n n) g) U& v
6 ^1 N0 |8 X/ Q0 c
RGN由实时熵源(模拟)和内部调节组件组成,它将全熵输出作为32位样本提供给应用程序。通常MCU中去读取两个连续随机数的间隔为 40 个 PLL48CLK时钟信号周期。产生随机数的模拟量发生器,由几个环形振荡器组成,振荡器的输出进行异或运算以产生馈入线性反馈移位寄存器(RNG_LFSR)种子,这些种子用于生成 32 位随机数。RGN支持中断功能,通过检测状态位(RNG_SR 寄存器),检测种子异常(RNG_FLAG_SECS)、时钟异常(RNG_SR_CECS)等现象,检测到错误时生成中断。' M9 ~2 @6 w. P1 T
# d* m7 | y( Y* Y3 S: I
1.2 RNG配置说明. `+ N0 ]; y/ @
在CubeMX上开启RNG功能非常简单,只需要激活即可,在其参数栏设置无任何参数需要设置,在NVIC栏可以开启中断功能。
a- I8 e, @& L( x0 q4 g* }
, W6 Z- F8 y6 G0 z' V- i; t
c$ f' j4 q% s9 B+ x- V1 I' l& H& V* h/ a: h. }) T
RNG依赖时钟源可有在时钟树选择器设置,时钟频率大小可以通过倍频器、分频器设置,例如本文采用STM32L496VGTx芯片时,设置其PLL48CLK时钟依赖源PLLSAl1Q,输出时钟频率是48MHz,如下图所示。$ q2 z6 [7 i% u- z
6 E/ x5 [5 A2 A
2 V* E( A! s; S" N
, j8 }) n' v2 b STM32的RGN内部通信通常是通过AHB总线实现,例如STM32L496VGTx芯片通过AHB2总线。! L0 T( C# L. v* ?8 N8 @
5 f' ^% P" d0 i5 c4 d' W
4 J/ b2 ^- u8 A" ?3 m) k1 o
9 |% l( P- }8 E K RNG在STM32芯片正常、睡眠的工模式下有效。# Q1 t/ H0 }7 F* V
" a& j5 |% }+ |& o/ f0 T' P
% K* y0 c3 \. ~' U8 l$ q4 s# z7 F0 V; t$ e
在STM32L496VGTx芯片中,RNG的寄存器空间大小为0X3FF(1024),用于存储RNG的数据寄存器、状态寄存器、控制寄存器以及缓存参数、组件信息等。0 l8 v2 H- {+ R+ G
, o2 d& X C8 K7 N7 i
) {$ a F q: b; B% w
: V$ I" n0 _+ m* o" M 二、创建RNG测试工程及HLA源码分析' C( |# X+ y8 F0 d# j4 \2 S
2.1 工程创建
. ]9 n1 d8 g) R' K+ k& g: X6 d3 U 本文为采用的是stm32L496VGTx-ali开发板来建立测试工程,并按本专栏前面的博文移植好了lpuart1串口调试及按键功能,请自行参考。) `' n$ J; n+ U9 {4 J
% A2 R* z: f" w+ r6 ^+ f1 P
现双击.ioc文件,打开CubeMX配置界面,开启RNG功能,并开启中断,时钟树上设置最终输出频率48MHz。+ G0 {& V3 F1 G/ P6 x
9 h8 g" S z3 z; U. m8 u: ]
+ Q% M: i |6 s$ j1 X- a c
1 t- @: R) t$ X0 L, [2 K& `
( t3 Z) a0 U* n7 b6 _$ Q+ [3 T1 Q) r; d1 F5 @5 [# \4 X
保成生成输出代码: d% F+ T/ T: _# K+ C* K2 C
7 h7 t9 R6 H& f+ |& j& {5 v6 ~; y 2.3 RNG的HLA库源码分析3 p P; l8 a' s3 F
cubeMX生成代码时,会在Core源码目录下的Inc及Src目录,分别生成rng.h和rng.c驱动文件。2 ^! c! u0 f3 v; ] `( n
9 O& x4 i3 l4 A4 ^, y0 w% G 在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函数。
* Z2 P" s o$ K7 y: _" T
0 y! K, _% i2 c/ E
& S* y; M$ }2 E* a- E4 O; F
) x& Y6 c' k1 u2 a- n 在stm32l4xx_hal_rng.c源文件中定义了HAL_RNG_Init函数,它做以下事情:诊断配置参数是否合规;
) f- x. w& l0 V5 }. A# a8 [. u4 ?4 ?6 `
8 c- f7 V' w* C4 }; h3 J
+ u g* M% [' }# m 以及调用HAL_RNG_MspInit函数完成CRC时钟开启及中断设置与开启(如果cubeMX配置开启中断功能的话);( u& c6 j1 U+ x+ X& W y/ }0 Z/ _% f
; a& v7 x' N/ [' J% V
# J i" i1 e! P4 l- n6 N
3 x" F. d L* k5 w" F 最后开启RNG,检测种子、数据寄存器的有效性。
0 m5 _2 t$ Z4 r1 d; d1 o( q* {- z! N7 _& f
" Q: z9 d( X* W, _
) n7 y; ?6 Z, |2 a1 S+ R
再回到rng.c内,HAL_RNG_MspInit函数实现了RNG外设时钟设置(如果配置依赖外设的话)与开启,以及中断设置与开启(如果cubeMX配置开启中断功能的话)。
" j5 L& Z- ~. ]: J( O) B
! u# a! W% @9 e: |7 C. j
! G# \1 L0 y6 H$ }3 I2 s, y
: _6 k# @5 Q4 H/ S* f" n 在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。, b+ t$ T) b( j* e) ^7 f
9 f5 U- k- k C: _; K# m( ^
* v$ t$ Z7 J) N$ R5 S7 I4 R: U
3 i, M6 R( `, y8 O8 y" V
2.3 RNG使用设计3 u9 l G# Y* F% Z' [
由于本文开启了RNG中断功能,在rng.c源码中,重新编码HAL_RNG_ReadyDataCallback函数如下:6 _, Y# h& |$ C0 Z, \: U) o
- /* USER CODE BEGIN 1 */( n8 T: Z/ c, f( a0 a0 ?
- void HAL_RNG_ReadyDataCallback(RNG_HandleTypeDef *hrng, uint32_t random32bit)& N0 F5 |3 D& e9 I
- {+ g1 C- S$ }3 C5 O
- printf("RNG_ReadyDataCallback:%lu\r\n",random32bit);
/ g% G0 t s/ B - }& T" b- d4 l* \ E5 u
- /* USER CODE END 1 */
复制代码
' b7 W" f6 w( q( v 在main.c源码文件中,添加各个外设及功能模块的头文件依赖。& e9 Q( q y/ g0 R9 |# t7 _9 i
- /* Private includes ----------------------------------------------------------*/
* j5 b8 N) @1 i, G - /* USER CODE BEGIN Includes */
+ J, a8 k8 v o( c - #include "../../ICore/key/key.h"8 W8 l5 E$ O8 z/ J" b
- #include "../../ICore/led/led.h"6 r- T M; g( M `, M
- #include "../../ICore/print/print.h"3 `9 O$ G. X. s$ g4 ]% @: H* m0 \
- #include "../../ICore/usart/usart.h"
9 }! U, a+ R3 E' @ E, P1 I - #include "../../ICore/delay/delay.h"
1 @ Q- @% B0 ^( S" U - /* USER CODE END Includes */
复制代码 " S3 T, R0 z5 s6 B9 O! q
在main.c源码文件中,添加RNG句柄声明,用于读取随机数时使用。3 n" W* Y0 B3 E0 N. I( K. @
- /* Private user code ---------------------------------------------------------*/
) v- J# e1 s! v# Q) `3 F _% h ~, O - /* USER CODE BEGIN 0 */4 c8 Q$ X S; p4 I. L" D' ?
- extern RNG_HandleTypeDef hrng;
% k" G. }4 N) x- ~% Y3 ]3 n - /* USER CODE END 0 */
复制代码
6 V5 G% ` l- m2 ^* T1 X 在main函数内,启动串口中断功能及初始化其他辅助参数。0 K0 C2 [2 c3 t: r |' a
- /* Initialize all configured peripherals */8 E8 I g# A0 f0 D8 I) b8 g
- MX_GPIO_Init();
& h( U) q s2 B! q/ z% f; q - MX_LPUART1_UART_Init();
- M8 j- s% s3 V, r6 j t; } - MX_RNG_Init();
- V1 M7 K @# e! P) @1 d' I - /* USER CODE BEGIN 2 */
, l8 {5 o& @$ G' b3 Z - ResetPrintInit(&hlpuart1);
& C8 Z: Y6 N* ~2 N# O - HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
V& j7 g, q5 C1 ]& ]3 W - HLPUSART_RX_STA = 0;' C' N" o0 m" C% w. N" I8 J/ o, |
- //
/ G8 u0 @, P( R# q) |* F) B0 H0 U - printf("app restart now!\r\n");
& u; [, G* v, a; E. d- ` - uint32_t rng_val = 0;
. m5 G# d# U% Q3 \ P - /* USER CODE END 2 */
复制代码 / v; f6 `) X4 C, d' G4 R% Q
在main函数循环体内,通过按键触发调用不同的随机数获取函数,并打印输出到lpuart1上。1 m5 r% ^8 S, h* Q+ O1 i
- /* Infinite loop */
& r8 I7 ]5 ~" t4 m5 p! D - /* USER CODE BEGIN WHILE */2 G& |( V7 S! ]! ~
- while (1)
& W5 `& S5 X3 i - {
; ~# D/ P6 @6 H; o/ K7 i - if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始 V7 h) }1 c0 X* B6 l. N/ y
- //printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);+ P' K: C8 [$ s; j7 `
- HLPUSART_RX_STA=0;//接收错误,重新开始) w9 Z9 I2 ?; Y: O7 K
- HAL_Delay(100);//等待! |, h$ _) B! X
- }
p" [ K% ^9 b+ d - if(KEY_0())
' O0 C+ v1 Y1 X. q - {
, H z# E( A4 |, r1 \8 |3 f - rng_val = HAL_RNG_GetRandomNumber(&hrng);
+ @% b9 i+ E8 u - printf("KEY_0->RNG_GetRandomNumber:%lu\r\n",rng_val);
8 i/ z" K. }" y" C - }
: U( A5 _- H o1 w6 I* r - if(KEY_1())3 t4 p' W( s1 w9 Q( k
- {6 H4 _+ X3 d8 N( Q" ~6 k8 W
- rng_val = HAL_RNG_GetRandomNumber_IT(&hrng);
* \" U, S2 i# Q - printf("KEY_1->RNG_GetRandomNumber_IT:%lu\r\n",rng_val);
) `# ?4 P& e- g" ]) [2 `+ I - }
6 e- E' |3 k8 [, l3 H6 ^ - if(KEY_2())
; G4 _" Q: S t9 _- W l! V4 _ - {/ H* n! v, A7 u- ^) Z( i! y: U7 j8 K
- rng_val = HAL_RNG_ReadLastRandomNumber(&hrng);! m3 [1 k% l1 Z8 A3 U1 n6 F+ K) S5 r
- printf("KEY_2->RNG_ReadLastRandomNumber:%lu\r\n",rng_val);% m& q4 P* g D* {6 F7 N e* n S
- }3 e8 \" n3 m/ D8 `
- /* USER CODE END WHILE */
复制代码 6 r8 q. C7 v! J: `. h: j' C
三、编译及测试
% F: L$ Y: b+ b1 _' [ 3.1 编译及下载
8 v( G- s- v, H. ~% N 点击编译按钮确保编译正确输出,并配置运行配置后,点击运行完成下载
6 v" @& O" I; ^: a0 M
5 [6 E; f2 t9 e1 D" h& L2 K8 d
# E8 W7 ^& D- t' Z: S! Q2 D
J3 v. w0 t5 R( L& R) X) P/ L( J 3.2 测试
# u, Z5 H6 U. g# | 串口助手链接到开发板上,分别按键KEY0、KEY1、KEY2输出log如下图:& l/ M/ O1 N6 o
' ~) f4 @0 M& o$ n3 _/ C
. P5 k! q7 y. K4 M# f' M: T
& t' n; K* c6 g$ X/ k
由于KEY1是中断方式读取随机数,因此我们前面重写的回调函数也会生效,但需要注意的是HAL_RNG_GetRandomNumber_IT先于HAL_RNG_ReadyDataCallback,读取了数据寄存器,连个函数是分别读取数据寄存器,因此得到的随机数是不一致的,实际项目中按需选择来应用。
+ z2 B& M/ O) O, B. |# g. a( V( b" W# x' k: C
% w7 k U. ^3 _$ h4 ~
5 Y* @& A: W2 Y" B9 n————————————————2 A" _4 f+ o8 N) E" L4 ? P
版权声明:py_free-物联智能9 j% W0 A7 z" F1 P2 P6 ?
如有侵权请联系删除
) T1 S0 Q4 T- ]5 a" k+ i \
$ X* J. g& N+ R( g0 ~4 `' R |