打开主菜单

谷雨文档中心 β

更改

NBDK-L4:基础实验教程

添加17,422字节2019年1月28日 (一) 10:53
无编辑摘要
</syntaxhighlight>
==16-RTC实时时钟实验 ===== STM32L476 UART简介 === ''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''=== 硬件设计 ===选择STM32L4 ''<span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span><span class="tlid-translation-gender-indicator translation-gender-indicator"></span>''=== 实验准备 ===# 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板。# 使用miniUSB线,连接PC与开发板USB接口。# 将SW1拨到USB端,SW2拨到MCU。# 使用Keil打开基础实验 10-串口打印实验工程。# 使用Xshell打开miniUSB虚拟出的COM口# 下载程序,并完成功能测试。 === 实验验证 ===下载完成后,我们打开miniUSB虚拟出的COM口,可以看到串口周期性的打印计数值。[[文件:NBDK-XSHELL-UARTPRINTF.png|边框|居中|无框|759x759像素]] === 源码详解 ===本节中的源码说明,仅针对此例程中的重要功能,详细的源码介绍请大家参照代码后的注释。 ==== stm32l4xx_hal_conf.h ====此文件位于“10-串口打印实验\Inc”路径中,主要用途是选择使能此例程使用到的库文件。 此例程我们主要给大家展示STM32L4的串口功能,所以我们宏定义中打开UART相关的。<syntaxhighlight lang="c" line="1" start="103">// 使能的宏#define HAL_MODULE_ENABLED // 芯片#define HAL_FLASH_MODULE_ENABLED // Flash#define HAL_PWR_MODULE_ENABLED // 电源#define HAL_RCC_MODULE_ENABLED // 时钟#define HAL_CORTEX_MODULE_ENABLED // NVIC #define HAL_GPIO_MODULE_ENABLED // GPIO#define HAL_DMA_MODULE_ENABLED // DMA#define HAL_UART_MODULE_ENABLED // UART</syntaxhighlight> ==== main.c ====main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。 接下来我们初始化串口UASRT1。 在while()循环中,我们每隔100ms通过格式化输出"TimeCount = xx:xx:xx"。 <syntaxhighlight lang="c++" line="1" start="34">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界面以及系统时钟 HAL_Init();  // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等) SystemClock_Config(); // 初始化串口USART1 MX_USART1_UART_Init(); // while (1) { // 模拟时钟计时,这边的1s实际只是100ms HAL_Delay(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外设总线时钟以及单个外设的时钟。 主要包含了三个部分的初始化配置。 1.内部或者外部振荡器选择,也就是选择时钟信号的来源,是内部振荡,还是外部晶振。 2.时钟配置,选择系统、AHB总线及APB总线的时钟来源。 3.外设时钟配置,选择外设时钟来源。 为了给大家比较全面的展示各个时钟,我们振荡器选择HSI(内部16MHz高频)、HSE(外部8MHz高频)以及LSE(外部32.768KHz低频)三个。选择HSE作为PLL(锁相回路)时钟源,配置PLLCLK为80MHz。配置系统时钟SYSCLK、AHB高性能总线、APB外设总线(APB1及APB2)为80MHz。另外我们还分别配置了ADC、UART以及I2C的外设时钟。 基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。<syntaxhighlight lang="c++" line="1" start="49">void SystemClock_Config(void){ RCC_OscInitTypeDef RCC_OscInitStruct; // 定义RCC内部/外部振荡器结构体 RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义RCC系统,AHB和APB总线时钟配置结构体 RCC_PeriphCLKInitTypeDef PeriphClkInit; // 定义RCC扩展时钟结构体 // 配置LSE驱动器功能为低驱动能力 __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);  // 初始化CPU,AHB和APB总线时钟 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE |RCC_OSCILLATORTYPE_LSE; // 设置需要配置的振荡器为HSI、HSE、LSE // 配置HSE RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 激活HSE时钟(开发板外部为8MHz) // 配置LSE RCC_OscInitStruct.LSEState = RCC_LSE_ON; // 激活LSE时钟(32.768KHz,低驱动) // 配置HSI RCC_OscInitStruct.HSIState = RCC_HSI_ON; // 激活HSI时钟 RCC_OscInitStruct.HSICalibrationValue = 16; // 配置HSI为16MHz // 配置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 = 20; // 配置PLL VCO输入倍增为20,8MHz*20 = 160MHz RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI时钟7分频,160/7 = 22.857143MHz RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,160/2 = 80MHz RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 系统主时钟分区2分频,160/2 = 80MHz // RCC时钟配置,出错则进入错误处理函数 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } // 初始化CPU,AHB和APB总线时钟 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 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟为系统时钟1分频,80/1 = 80MHz RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1时钟为系统时钟1分频,80/1 = 80MHz RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟为系统时钟1分频,80/1 = 80MHz // RCC时钟配置,出错则进入错误处理函数 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) // HCLK=80MHz,Vcore=3.3V,所以选择SW4(FLASH_LATENCY_4) { _Error_Handler(__FILE__, __LINE__); }  // 初始化外设时钟 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2 |RCC_PERIPHCLK_LPUART1|RCC_PERIPHCLK_LPTIM1 |RCC_PERIPHCLK_I2C2|RCC_PERIPHCLK_ADC; // 需要初始化的外设时钟:USART1、USART2、LPUART1、LPTIM1、I2C2、ADC PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; // 配置串口USART1时钟为PCLK2,80MHz PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1; // 配置串口USART2时钟为PCLK1,80MHz PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_HSI; // 配置LPUART时钟为HSI,16MHz PeriphClkInit.I2c2ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; // 配置I2C2时钟为PCLK1,80MHz PeriphClkInit.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE; // 配置LPTIM1时钟为LSE,32.768KHz PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; // 配置ADC时钟为PLLSAI1,现在为80MHz,下面会重新定义 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分频,64/2 = 32MHz PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; // 系统主时钟分区2分频,64/2 = 32MHz PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_ADC1CLK; // 配置PLLSAI1输出为ADC1时钟,也就是配置ADC1时钟,32MHz // 外设时钟配置,出错则进入错误处理函数 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); }  // 配置内部主稳压器输出电压,配置为稳压器输出电压范围1模式,也就是:典型输出电压为1.2V,系统频率高达80MHz if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); }  // 配置系统定时器中断时间,配置为HCLK的千分频 HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);  // 配置系统定时器,配置为HCLK HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);  // 系统定时器中断配置,设置系统定时器中断优先级最高(为0),且子优先级最高(为0) HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);}</syntaxhighlight> ==== gyu_usartc ====串口初始化函数,配置串口协议:波特率115200,数据位8位,停止位1位,无校验位,无流控制。 <syntaxhighlight lang="c++" line="1" start="37">void MX_USART1_UART_Init(void){ // 配置串口参数 huart1.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_DISABLE; // 1位过采样禁能 huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT; // 没有串口高级功能初始化 // 串口初始化 if (HAL_UART_Init(&huart1) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); // 如果初始化失败,进入错误处理任务 } }</syntaxhighlight>配置串口硬件,使能GPIOA以及USART1的时钟,配置PA9和PA10为串口的TX及RX引脚。<syntaxhighlight lang="c++" line="1" start="67">void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle){ // 定义GPIO结构体 GPIO_InitTypeDef GPIO_InitStruct; // 判断选择的是否为USART1 if(uartHandle->Instance==USART1) { // 使能GPIOA引脚时钟(因为选择的TX和RX分别为PA9和PA10) __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能USART1时钟 __HAL_RCC_USART1_CLK_ENABLE(); // 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-RTC实时时钟实验 80MHz GPIO_InitStruct.Alternate = GPIO_AF7_USART1; // 配置为USART1 HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 初始化引脚 }}</syntaxhighlight>配置fputc()函数,用于格式化打印,当我们进行了如下代码配置,就可以调用printf()函数去格式化打印调试信息。<syntaxhighlight lang="c++" line="1" start="99">#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) PUTCHAR_PROTOTYPE{ // 配置格式化输出到串口USART1 HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF); return ch;} </syntaxhighlight> 
[[分类:NB-IOT]]
[[分类:NBDK-L4]]
[[分类:教程]]
__强显目录__
510
个编辑