510
个编辑
更改
→STM32L476 随机发生器简介
==== stm32l4xx_hal_conf.h ====
此例程我们主要给大家展示STM32L4的串口功能,所以我们宏定义中打开UART相关的。<syntaxhighlight lang="c" line="1" start="103">
# 使用miniUSB线,连接PC与开发板USB接口。
# 将SW1拨到USB端,SW2拨到MCU。
# 使用Keil打开基础实验 1015-串口打印实验工程。RNG随机发生器实验工程。# 使用Xshell打开miniUSB虚拟出的COM口使用Xshell打开miniUSB虚拟出的COM口。
# 下载程序,并完成功能测试。
=== 实验验证 ===
[[文件:NBDK-XSHELL-RNG.png|边框|居中|无框|759x759像素]]
==== stm32l4xx_hal_conf.h ====
#define HAL_MODULE_ENABLED // 芯片
#define HAL_FLASH_MODULE_ENABLED // Flash
#define HAL_DMA_MODULE_ENABLED // DMA
#define HAL_UART_MODULE_ENABLED // UART
#define HAL_SPI_MODULE_ENABLED // SPI
#define HAL_RNG_MODULE_ENABLED // RNG随机发生器
</syntaxhighlight>
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
在while()循环中,我们每隔100ms通过格式化输出"TimeCount = xx:xx:xx"。循环中,我们调用KEY_Poll()函数去轮询是否有按键被按下。
<syntaxhighlight lang="c++" line="1" start="3441">
int main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
// 重置所有外设、flash界面以及系统时钟
// 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
SystemClock_Config();
// 初始化USART1 MX_USART1_UART_Init(); // 初始化按键引脚 MX_KEY_Init(); // 注册按钮回调函数 KEY_RegisterCb(AppKey_cb); // LCD SPI初始化 LCD_GPIO_Init(); // 初始化串口USART1LCD IO控制引脚(例如背光) MX_USART1_UART_InitMX_SPI1_Init(); // LCD SPI控制引脚 // 图形界面初始化 GUI_Init(); // GUI界面初始化 GUI_Clear(); // 清屏 // 打印logo到位置X->0,Y->0 GUI_DrawBitmap(&bmLogo,0,0); // 随机发生器初始化 MX_RNG_Init();
//
while (1) { KEY_Poll(); // 按键轮训,监测是否有按键被按下 }}</syntaxhighlight>在按键回调函数中,可以看到,每次S1(key_up)按键被按下,都是调用RNG_Get()函数去获取一次随机数。<syntaxhighlight lang="c++" line="1" start="89">void AppKey_cb(uint8_t key){ // 如果有相应按键被按下,则串口打印调试信息 if(key & KEY_UP)
{
}
}
</syntaxhighlight>
==== gyu_util.c ====
void SystemClock_Config(void)
{
// 初始化CPU,AHB和APB总线时钟
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE
|RCC_OSCILLATORTYPE_LSE|RCC_OSCILLATORTYPE_MSI; // 设置需要配置的振荡器为HSI、HSE、LSE设置需要配置的振荡器为HSI、HSE、LSE、MSI
// 配置HSE
RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 激活HSE时钟(开发板外部为8MHz)
// 配置HSI
RCC_OscInitStruct.HSIState = RCC_HSI_ON; // 激活HSI时钟
RCC_OscInitStruct.HSICalibrationValue = 16; // 配置HSI为16MHz // 配置MSI RCC_OscInitStruct.MSIState = RCC_MSI_ON; // 激活MSI时钟(内部高频,最高可配置48MHz) RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_11; // 配置为48MHz
// 配置PLL
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 选择HSE时钟作为PLL入口时钟源,8MHz
RCC_OscInitStruct.PLL.PLLM = 1; // 配置PLL VCO输入分频为1,8/1 = 8MHz
RCC_OscInitStruct.PLL.PLLN = 2015; // 配置PLL VCO输入倍增为20,8MHz*20 15 = 160MHz120MHz RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI时钟7分频,160SAI时钟7分频,120/7 = 2217.857143MHz14MHz RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,160SDMMC、RNG、USB时钟2分频,120/2 = 80MHz60MHz RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 系统主时钟分区2分频,160系统主时钟分区2分频,120/2 = 80MHz60MHz
// RCC时钟配置,出错则进入错误处理函数
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; // 需要配置的时钟HCLK、SYSCLK、PCLK1、PCLK2
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 配置系统时钟为PLLCLK输入,80MHz配置系统时钟为PLLCLK输入,60MHz RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟为系统时钟1分频,80AHB时钟为系统时钟1分频,60/1 = 80MHz60MHz RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1时钟为系统时钟1分频,80APB1时钟为系统时钟1分频,60/1 = 80MHz60MHz RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟为系统时钟1分频,80APB2时钟为系统时钟1分频,60/1 = 80MHz60MHz
// RCC时钟配置,出错则进入错误处理函数
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4FLASH_LATENCY_2) != HAL_OK) // HCLK=80MHz,Vcore60MHz,Vcore=3.3V,所以选择SW4(FLASH_LATENCY_4)3V,所以选择SW2(FLASH_LATENCY_2)
{
_Error_Handler(__FILE__, __LINE__);
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2
|RCC_PERIPHCLK_LPUART1|RCC_PERIPHCLK_LPTIM1
|RCC_PERIPHCLK_I2C2|RCC_PERIPHCLK_ADC |RCC_PERIPHCLK_RNG; // 需要初始化的外设时钟:USART1、USART2、LPUART1、LPTIM1、I2C2、ADCUSART1、USART2、LPUART1、LPTIM1、I2C2、ADC、RNG PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; // 配置串口USART1时钟为PCLK2,80MHz配置串口USART1时钟为PCLK2,60MHz PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1; // 配置串口USART2时钟为PCLK1,80MHz配置串口USART2时钟为PCLK1,60MHz
PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_HSI; // 配置LPUART时钟为HSI,16MHz
PeriphClkInit.I2c2ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; // 配置I2C2时钟为PCLK1,80MHz配置I2C2时钟为PCLK1,60MHz
PeriphClkInit.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE; // 配置LPTIM1时钟为LSE,32.768KHz
PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; // 配置ADC时钟为PLLSAI1,现在为80MHz,下面会重新定义配置ADC时钟为PLLSAI1,现在为60MHz,下面会重新定义
PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE; // 配置PLLSAI1时钟为HSE,8MHz
PeriphClkInit.PLLSAI1.PLLSAI1M = 1; // 配置PLLSAI1分频为1
PeriphClkInit.PLLSAI1.PLLSAI1N = 8; // 配置PLLSAI1倍增为8
PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7; // SAI时钟7分频,64/7 = 9.142857MHz
PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,64SDMMC、USB时钟2分频,64/2 = 32MHz
PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; // 系统主时钟分区2分频,64/2 = 32MHz
PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_ADC1CLK; // 配置PLLSAI1输出为ADC1时钟,也就是配置ADC1时钟,32MHz
PeriphClkInit.RngClockSelection = RCC_RNGCLKSOURCE_MSI;
// 外设时钟配置,出错则进入错误处理函数
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
</syntaxhighlight>
==== gyu_usartc gyu_rng.c ====串口初始化函数,配置串口协议:波特率115200,数据位8位,停止位1位,无校验位,无流控制。初始化RNG。
<syntaxhighlight lang="c++" line="1" start="3743">void MX_USART1_UART_InitMX_RNG_Init(void)
{
{
_Error_Handler(__FILE__, __LINE__); // 如果初始化失败,进入错误处理任务
}
}
</syntaxhighlight>配置串口硬件,使能GPIOA以及USART1的时钟,配置PA9和PA10为串口的TX及RX引脚。使能RNG时钟,并且配置和使能RNG中断。<syntaxhighlight lang="c++" line="1" start="6760">void HAL_UART_MspInitHAL_RNG_MspInit(UART_HandleTypeDefRNG_HandleTypeDef* uartHandlehrng)
{
{
// 使能GPIOA引脚时钟(因为选择的TX和RX分别为PA9和PA10)使能RNG时钟 __HAL_RCC_GPIOA_CLK_ENABLE__HAL_RCC_RNG_CLK_ENABLE();
// 使能USART1时钟使能NVIC中断及优先级 __HAL_RCC_USART1_CLK_ENABLEHAL_NVIC_SetPriority(RNG_IRQn, 10, 0); // GPIO配置 GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_10; // 选择USART1的TX和RX引脚(TX:PA9,RX:PA10) GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 推挽输出 GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉 GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;// 引脚频率5-80MHz GPIO_InitStruct.Alternate = GPIO_AF7_USART1; // 配置为USART1 HAL_GPIO_InitHAL_NVIC_EnableIRQ(GPIOA, &GPIO_InitStructRNG_IRQn); // 初始化引脚
}
}
</syntaxhighlight>配置fputc随机数的获取函数,在此处打开中断。此处中断开启,会触发HAL_RNG_ErrorCallback()函数,用于格式化打印,当我们进行了如下代码配置,就可以调用printf或者HAL_RNG_ReadyDataCallback()函数去格式化打印调试信息。回调函数,当有随机数成功生成时,返回HAL_RNG_ReadyDataCallback()回调。<syntaxhighlight lang="c++" line="1" start="9981">#define PUTCHAR_PROTOTYPE int fputcvoid RNG_Get(int ch, FILE *fvoid){ HAL_RNG_GenerateRandomNumber_IT(&hrng); // 随机数获取函数(开启中断)}PUTCHAR_PROTOTYPE</syntaxhighlight>真随机数成功生成的回调函数,参数random32bit就是生成的随机数,我们利用printf函数将随机数打印到串口。<syntaxhighlight lang="c++" line="1" start="108">void HAL_RNG_ReadyDataCallback(RNG_HandleTypeDef* hrng, uint32_t random32bit)
{
}
</syntaxhighlight>
=== 16-RTC实时时钟实验 ===
[[分类:NB-IOT]]
[[分类:NBDK-L4]]
[[分类:教程]]
__强显目录__