一、RNG簡介
二、RNG框圖介紹
三、RNG相關寄存器介紹
四、RNG相關HAL庫驅動介紹
五、RNG基本驅動步驟
六、編程實戰
七、總結
一、RNG簡介
隨機數發生器(RNG)在計算機科學和密碼學中具有廣泛的應用場景,包括但不限于以下幾個方面:
-
驗證碼:在網絡安全和用戶驗證中,隨機數用于生成驗證碼,增加系統的安全性,防止機器人攻擊和惡意訪問。
-
密碼學:隨機數在密碼學中扮演著重要角色,用于生成密鑰、初始化向量(IV)和鹽等,增加加密算法的安全性。
-
概率學和統計學:隨機數被用于模擬概率分布和隨機變量,進行蒙特卡羅模擬、蒙特卡羅積分等,用于解決統計學和概率學中的問題。
-
游戲:在游戲開發中,隨機數被廣泛用于生成游戲中的隨機事件、隨機地圖、隨機怪物屬性等,增加游戲的趣味性和挑戰性。
隨機數的重要特性是無法預測的、無規律性的、獨立分布的。真隨機數由物理過程生成,具有完全隨機的性質,而偽隨機數則是通過確定性算法計算出來的,雖然看起來像是隨機的,但是在一定條件下可能會被預測到。在安全性要求較高的場景中,通常會使用真隨機數發生器。
二、RNG框圖介紹
RNG(Random Number Generator)采用模擬電路實現,其基本原理如下:
-
模擬電路結構:RNG通常由幾個環形振蕩器組成。這些振蕩器的輸出經過異或運算,產生種子(seed),作為隨機數生成的初始值。
-
LFSR(線性反饋移位寄存器):LFSR類似于一個“生產車間”,接收大量的種子輸入。種子通過LFSR處理后,其內容傳送到RNG數據寄存器(RNG_DR),用于隨機數的生成。
-
時鐘檢查器和故障檢測器:類似于“質檢”,時鐘檢查器和故障檢測器負責檢測種子是否出現異常序列以及fpll48clk是否過低。這些異常情況會在RNG_SR寄存器的相關位顯示,并可以觸發中斷。
-
中斷功能:RNG通常設置了中斷功能,當種子出現異常序列或fpll48clk過低時,會產生中斷信號,以便系統進行相應的處理或響應。
總體而言,RNG通過模擬電路實現,利用環形振蕩器、LFSR和時鐘檢查器等組件,生成隨機數種子,并在檢測到異常情況時產生中斷,確保隨機數生成的安全性和可靠性。
三、RNG相關寄存器介紹
這是RNG模塊的關鍵寄存器及其作用:
-
RNG_CR(RNG控制寄存器):
- 用途:控制隨機數發生器的啟用和中斷的使能。
- 作用:通過設置該寄存器的位來啟用或禁用隨機數發生器,并控制中斷功能的使能。
-
RNG_SR(RNG狀態寄存器):
- 用途:顯示RNG當前的一些狀態信息。
- 作用:該寄存器的特定位用于顯示RNG模塊的狀態,例如隨機數生成完成、時鐘故障等。
-
RNG_DR(RNG數據寄存器):
- 用途:存儲生成的32位隨機數值。
- 作用:隨機數生成器生成的隨機數會被存儲在該寄存器中,供后續程序使用。
四、RNG相關HAL庫驅動介紹
這是與RNG模塊相關的驅動函數及其功能描述以及關聯的寄存器:
-
HAL_RNG_Init(…):
- 關聯寄存器:RNG_CR
- 功能描述:用于初始化RNG模塊,配置RNG控制寄存器,啟用或禁用隨機數發生器以及中斷。
-
HAL_RNG_MspInit(…):
- 初始化回調
- 功能描述:在初始化過程中調用,用于初始化RNG模塊的外設、時鐘和選擇時鐘源等。
-
HAL_RCCEx_PeriphCLKConfig(…):
- 關聯寄存器:RCC_BDCR
- 功能描述:設置RNG模塊的時鐘源,通常設置為PLL。
-
HAL_RNG_GenerateRandomNumber(…):
- 關聯寄存器:RNG_DR
- 功能描述:用于生成隨機數,會檢查DRDY位以確定是否有隨機數可用,并讀取隨機數。
-
__HAL_RCC_RNG_CLK_ENABLE(…):
- 關聯寄存器:AHB2ENR
- 功能描述:使能RNG模塊的時鐘,確保RNG模塊能夠正常工作。
-
__HAL_RNG_GET_FLAG(…):
- 關聯寄存器:RNG_SR
- 功能描述:用于獲取RNG模塊相關的標記,例如隨機數生成完成標志位等。
五、RNG基本驅動步驟
RNG基本驅動步驟:
-
使能RNG時鐘:
- 使用
__HAL_RCC_RNG_CLK_ENABLE()
函數來啟用RNG模塊的時鐘。
- 使用
-
初始化隨機數發生器:
- 使用
HAL_RNG_Init()
函數初始化RNG模塊,配置RNG控制寄存器等。 - 在初始化過程中,需要調用
HAL_RNG_MspInit()
函數來初始化RNG模塊的外設、時鐘以及選擇時鐘源等設置,可能需要使用HAL_RCCEx_PeriphCLKConfig()
函數來配置RNG模塊的時鐘源。
- 使用
-
讀取隨機數值:
- 使用
HAL_RNG_GenerateRandomNumber()
函數來生成隨機數。 - 在生成隨機數之前,通常會判斷 DRDY 位,以確保隨機數已經就緒可以讀取。
- 使用
六、編程實戰
按鍵輸入
rng.c
#include "./BSP/RNG/rng.h"
#include "./SYSTEM/delay/delay.h"RNG_HandleTypeDef g_rng_handle; /* RNG控制句柄 *//*** @brief 初始化RNG模塊* @retval 返回值:0成功,1失敗*/
uint8_t rng_init(void)
{uint16_t retry = 0; /* 重試計數器 */g_rng_handle.Instance = RNG; /* RNG實例 */HAL_RNG_Init(&g_rng_handle); /* 初始化RNG模塊 *//* 等待RNG準備就緒 */while ((__HAL_RNG_GET_FLAG(&g_rng_handle, RNG_FLAG_DRDY) == RESET) && (retry < 10000)){retry++;delay_us(10); /* 延時等待 */}/* 檢查RNG是否正常工作 */if (retry >= 10000){return 1; /* 隨機數產生器工作不正常 */}return 0; /* 初始化成功 */
}/*** @brief 初始化RNG模塊的外設、時鐘并選擇時鐘源* @param hrng: RNG句柄*/
void HAL_RNG_MspInit(RNG_HandleTypeDef *hrng)
{RCC_PeriphCLKInitTypeDef rcc_periphclk_init_struct; /* 外設時鐘初始化結構體 *//* RNG時鐘使能 */__HAL_RCC_RNG_CLK_ENABLE();/* 配置RNG時鐘源為PLL */rcc_periphclk_init_struct.PeriphClockSelection = RCC_PERIPHCLK_RNG;rcc_periphclk_init_struct.RngClockSelection = RCC_RNGCLKSOURCE_PLL;HAL_RCCEx_PeriphCLKConfig(&rcc_periphclk_init_struct);
}/*** @brief 獲取隨機數* @retval 返回32位隨機數*/
uint32_t rng_get_random_num(void)
{uint32_t random_num = 0; /* 隨機數變量 */HAL_RNG_GenerateRandomNumber(&g_rng_handle, &random_num); /* 生成隨機數 */return random_num; /* 返回隨機數 */
}/*** @brief 獲取指定范圍內的隨機數* @param min: 最小值* @param max: 最大值* @retval 返回在[min, max]范圍內的隨機數*/
uint32_t rng_get_random_range(int min, int max)
{uint32_t random_num = 0; /* 隨機數變量 */HAL_RNG_GenerateRandomNumber(&g_rng_handle, &random_num); /* 生成隨機數 */return random_num % (max - min + 1) + min; /* 返回指定范圍內的隨機數 */
}
rng.h
#ifndef __RNG_H
#define __RNG_H#include "./SYSTEM/sys/sys.h"uint8_t rng_init(void); // 初始化RNG模塊的外設、時鐘并選擇時鐘源
uint32_t rng_get_random_num(void); // 獲取隨機數
uint32_t rng_get_random_range(int min, int max); // 獲取指定范圍內的隨機數#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/RNG/rng.h"int main(void)
{uint8_t key;uint32_t value = 0;uint32_t value_range = 0;sys_cache_enable(); /* 打開L1-Cache */HAL_Init(); /* 初始化HAL庫 */sys_stm32_clock_init(240, 2, 2, 4); /* 設置時鐘, 480Mhz */delay_init(480); /* 延時初始化 */usart_init(115200); /* 初始化串口 */led_init(); /* 初始化LED */key_init(); /* 初始化按鍵 */LED0(0); /* 先點亮LED0 */rng_init(); /* RNG初始化 */while (1){key = key_scan(0); /* 得到鍵值 */if (key){switch (key){case WKUP_PRES: /* 控制LED0(RED)翻轉 */value = rng_get_random_num();printf("value:%d \r\n", value);LED0_TOGGLE(); /* LED0狀態取反 */break;case KEY1_PRES: /* 控制LED1(GREEN)翻轉 */value_range = rng_get_random_range(0, 9);printf("value_range:%d \r\n", value_range);LED1_TOGGLE(); /* LED1狀態取反 */break;case KEY0_PRES: /* 控制LED2(BLUE)翻轉 */LED2_TOGGLE(); /* LED2狀態取反 */break;} }else{delay_ms(10);}}
}
源碼
rng.c
#include "./BSP/RNG/rng.h" // 引入RNG頭文件
#include "./SYSTEM/delay/delay.h" // 引入延時函數頭文件RNG_HandleTypeDef rng_handle; // 聲明RNG句柄變量/*** @brief 初始化RNG* @param 無* @retval 0,成功;1,失敗*/
uint8_t rng_init(void)
{uint16_t retry = 0;rng_handle.Instance = RNG; // 設置RNG句柄的實例HAL_RNG_DeInit(&rng_handle); // 重新初始化RNGHAL_RNG_Init(&rng_handle); // 初始化RNG// 等待RNG準備就緒while ((__HAL_RNG_GET_FLAG(&rng_handle, RNG_FLAG_DRDY) == RESET) && (retry < 10000)){retry++; // 嘗試次數加1delay_us(10); // 延時10微秒}if (retry >= 10000){return 1; // 隨機數產生器工作不正常,返回1}return 0; // 返回0表示初始化成功
}/*** @brief RNG底層驅動,時鐘源設置和使能* @note 此函數會被HAL_RNG_Init()調用* @param hrng:RNG句柄* @retval 無*/
void HAL_RNG_MspInit(RNG_HandleTypeDef *hrng)
{RCC_PeriphCLKInitTypeDef RNGClkInitStruct;// 設置RNG時鐘源,選擇PLL,時鐘為480MHzRNGClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RNG; // 設置RNG時鐘源為PLLRNGClkInitStruct.RngClockSelection = RCC_RNGCLKSOURCE_PLL; // RNG時鐘源選擇PLLHAL_RCCEx_PeriphCLKConfig(&RNGClkInitStruct); // 配置RNG時鐘源__HAL_RCC_RNG_CLK_ENABLE(); // 使能RNG時鐘
}/*** @brief 得到隨機數* @param 無* @retval 獲取到的隨機數(32bit)*/
uint32_t rng_get_random_num(void)
{uint32_t randomnum;HAL_RNG_GenerateRandomNumber(&rng_handle, &randomnum); // 生成隨機數return randomnum; // 返回隨機數
}/*** @brief 得到某個范圍內的隨機數* @param min,max: 最小,最大值.* @retval 得到的隨機數(rval),滿足:min<=rval<=max*/
int rng_get_random_range(int min, int max)
{ uint32_t randomnum;HAL_RNG_GenerateRandomNumber(&rng_handle, &randomnum); // 生成隨機數return randomnum % (max - min + 1) + min; // 返回在[min, max]范圍內的隨機數
}
rng.h
#ifndef __RNG_H
#define __RNG_H #include "./SYSTEM/sys/sys.h"uint8_t rng_init(void); /* RNG初始化 */
uint32_t rng_get_random_num(void); /* 得到隨機數 */
int rng_get_random_range(int min,int max); /* 得到屬于某個范圍內的隨機數 */#endif
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./USMART/usmart.h"
#include "./BSP/MPU/mpu.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/RNG/rng.h"int main(void)
{uint32_t random; // 存儲隨機數的變量uint8_t t = 0, key; // t用于定時計數,key存儲按鍵狀態sys_cache_enable(); // 打開L1-CacheHAL_Init(); // 初始化HAL庫sys_stm32_clock_init(240, 2, 2, 4); // 設置時鐘, 480Mhzdelay_init(480); // 延時初始化usart_init(115200); // 串口初始化為115200usmart_dev.init(240); // 初始化USMARTmpu_memory_protection(); // 保護相關存儲區域led_init(); // 初始化LEDlcd_init(); // 初始化LCDkey_init(); // 初始化按鍵// 在LCD上顯示歡迎信息lcd_show_string(30, 50, 200, 16, 16, "STM32", RED);lcd_show_string(30, 70, 200, 16, 16, "RNG TEST", RED);lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);// 初始化隨機數發生器while (rng_init()) {lcd_show_string(30, 110, 200, 16, 16, " RNG Error! ", RED);delay_ms(200);lcd_show_string(30, 110, 200, 16, 16, "RNG Trying...", RED);}lcd_show_string(30, 110, 200, 16, 16, "RNG Ready! ", RED);lcd_show_string(30, 130, 200, 16, 16, "KEY0:Get Random Num", RED);lcd_show_string(30, 150, 200, 16, 16, "Random Num:", RED);lcd_show_string(30, 180, 200, 16, 16, "Random Num[0-9]:", RED); while (1){key = key_scan(0); // 檢測按鍵狀態// 如果按鍵KEY0被按下,獲取一個隨機數并在LCD上顯示if (key == KEY0_PRES){random = rng_get_random_num();lcd_show_num(30 + 8 * 11, 150, random, 10, 16, BLUE);}// 每200ms翻轉一次LED0,并顯示[0,9]范圍內的隨機數if ((t % 20) == 0){LED0_TOGGLE(); // 每200ms,翻轉一次LED0random = rng_get_random_range(0, 9); // 獲取[0,9]范圍內的隨機數lcd_show_num(30 + 8 * 16, 180, random, 1, 16, BLUE); // 在LCD上顯示隨機數}delay_ms(10); // 延時10mst++; // 定時計數器遞增}
}
七、總結