打开主菜单

谷雨文档中心 β

更改

NBDK-L4:基础实验教程

删除983字节2019年1月28日 (一) 10:23
STM32L476 随机发生器简介
==== stm32l4xx_hal_conf.h ====
此文件位于“09此文件位于“10-红外线接收实验串口打印实验\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示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口。
# 下载程序,并完成功能测试。
=== 实验验证 ===
下载完成后,我们打开miniUSB虚拟出的COM口,可以看到串口周期性的打印计数值。下载完成后,我们打开miniUSB虚拟出的COM口,每按下一次S1(btn_up),STM32L476都会向串口打印一个随机数。
[[文件:NBDK-XSHELL-RNG.png|边框|居中|无框|759x759像素]]
==== stm32l4xx_hal_conf.h ====
此文件位于“09此文件位于“15-红外线接收实验RNG随机发生器实验\Inc”路径中,主要用途是选择使能此例程使用到的库文件。
此例程我们主要给大家展示STM32L4的串口功能,所以我们宏定义中打开UART相关的。此例程我们主要给大家展示STM32L4的随机发生器生成随机数的功能,所以我们宏定义中打开RNG相关的。<syntaxhighlight lang="c" line="1" start="103">// 使能的宏
#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部分有详细说明。
接下来我们初始化串口UASRT1。接下来我们初始化串口UASRT1、按键、以及TFT显示屏。 接下来是我们这个工程的关键,初始化RNG部分。
在while()循环中,我们每隔100ms通过格式化输出"TimeCount = xx:xx:xx"。循环中,我们调用KEY_Poll()函数去轮询是否有按键被按下。
<syntaxhighlight lang="c++" line="1" start="3441">
int main(void)
{
uint32_t hour = 0;
uint32_t minute = 0;
uint32_t second = 0;
 
/* 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)
{
// 模拟时钟计时,这边的1s实际只是100ms HAL_DelayRNG_Get(100); // 100ms延时 printf("TimeCount = %02d:%02d:%02d\r\n",hour,minute,second); // 格式化输出"TimeCount = xx:xx:xx"  // 时分秒计数 second++; if(second == 60) { second = 0; minute++; } if(minute == 60) { minute = 0; hour++; } if(hour == 24) { hour = 0; }获取随机数
}
}
</syntaxhighlight>
 
==== gyu_util.c ====
时钟初始化函数,用于配置我们模块运行的系统时钟、AHB高性能总线时钟、APB外设总线时钟以及单个外设的时钟。此实验因为真随机数发生器的条件限制,我们配置的时钟和之前的例程有稍许不同。
主要包含了三个部分的初始化配置。# 内部或者外部振荡器选择,也就是选择时钟信号的来源,是内部振荡,还是外部晶振。# 时钟配置,选择系统、AHB总线及APB总线的时钟来源。# 外设时钟配置,选择外设时钟来源。为了给大家比较全面的展示各个时钟,我们振荡器选择HSI(内部16MHz高频)、HSE(外部8MHz高频)以及LSE(外部32.768KHz低频)三个。选择HSE作为PLL(锁相回路)时钟源,配置PLLCLK为80MHz。配置系统时钟SYSCLK、AHB高性能总线、APB外设总线(APB1及APB2)为80MHz。另外我们还分别配置了ADC、UART以及I2C的外设时钟。首先是我们的AHB时钟,此实验配置为60MHz,之前的实验都是80MHz。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。其次是此实验我们使用到了MSI(并且配置为RCC_MSIRANGE_11,也就是48MHz),在下面的外设功能配置中,我们选择此48MHz时钟作为RNG的时钟。<syntaxhighlight lang="c++" line="1" start="49">
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)
{
// 配置串口参数 huart1hrng.Instance = USART1; // UART寄存器基础地址,定义为USART1的 huart1.Init.BaudRate = 115200; // 串口波特率为115200 huart1.Init.WordLength = UART_WORDLENGTH_8B; // 串口数据位为8位 huart1.Init.StopBits = UART_STOPBITS_1; // 串口停止位为1位 huart1.Init.Parity = UART_PARITY_NONE; // 串口无校验位 huart1.Init.Mode = UART_MODE_TX_RX; // 串口模式,TX和RX作用 huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 串口无流控制 huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 16位过采样 huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLERNG; // 1位过采样禁能 huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; // 没有串口高级功能初始化 // 串口初始化 if (HAL_UART_InitHAL_RNG_Init(&huart1hrng) != HAL_OK)
{
_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)
{
// 定义GPIO结构体 GPIO_InitTypeDef GPIO_InitStruct; // 判断选择的是否为USART1 if(uartHandlehrng->Instance==USART1RNG)
{
// 使能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)
{
// 配置格式化输出到串口USART1 HAL_UART_Transmit(&huart1, printf(uint8_t *)&ch"%u\r\n", 1, 0xFFFFrandom32bit); return ch;// 将随机数打印到串口
}
 
</syntaxhighlight>
=== 16-RTC实时时钟实验 ===
[[分类:NB-IOT]]
[[分类:NBDK-L4]]
[[分类:教程]]
__强显目录__
510
个编辑