打开主菜单

谷雨文档中心 β

更改

NBDK-L4:基础实验教程

删除5,314字节2019年2月18日 (一) 17:28
无编辑摘要
== 16-RTC实时时钟实验 ==
=== STM32L476 UART简介 RTC时钟简介 ===
''<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虚拟出的COM口,可以看到串口周期性的打印计数值。[[文件:NBDK-XSHELL-UARTPRINTF.png|边框|居中|无框|759x759像素]]下载完成后,可以看到TFT屏幕上打印当前的实时时间、日期以及星期,且10s后会触发闹钟(表现为TFT打印"Alarm"、蜂鸣器哔一声)。
=== 源码详解 ===
main函数,我们的例程由此处开始执行,首先调用HAL_Init()函数初始化我们的模块,接着调用SystemClock_Config()函数初始化此例程用到的时钟,具体有哪些时钟被初始化,在gyu_util.c部分有详细说明。
接下来我们初始化串口UASRT1。接下来我们初始化TFT彩屏相关的SPI控制接口,以及GUI图形界面初始化。并且格式化打印一些内容,字体显示为红色的部分。
在while()循环中,我们每隔100ms通过格式化输出"TimeCount = xx:xx:xx"。 <syntaxhighlight lang="c++" line="1" start="3444">
int main(void)
{
uint32_t hour = 0HAL_StatusTypeDef status; uint32_t minute = 0 RTC_TimeTypeDef sTime; uint32_t second = 0RTC_DateTypeDef sDate; HAL_Init();
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
// 重置所有外设、flash界面以及系统时钟
HAL_Init();
 
// 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
SystemClock_Config();
// 初始化串口USART1
MX_USART1_UART_Init();
// LCD SPI初始化 LCD_GPIO_Init(); // LCD IO控制引脚(例如背光) MX_SPI1_Init(); // LCD SPI控制引脚 // 图形界面初始化 GUI_Init(); // GUI界面初始化 GUI_Clear(); // 清屏 GUI_SetColor(GUI_Crimson); // 红色字体 GUI_SetBkColor(GUI_Gold); // 金色背景 // 格式化打印如下的内容 GUI_DispStringAt("Time: : :",24,24); GUI_DispStringAt("Date: 20 / /",24,72); GUI_DispStringAt("Week: ",24,120); // 初始化蜂鸣器 Buzzer_Init(); // RTC初始化 MX_RTC_Init(); RTC_TIME_Set(16,12,30); // 设置当前时间 时,分,秒 RTC_DATE_Set(2,18,12,25); // 设置当前日期 星期,年,月,日 RTC_AlarmA_Set(16,12,40,2); // 设置闹钟 时,分,秒,星期 // while (1)
{
// 模拟时钟计时,这边的1s实际只是100ms HAL_DelayGUI_SetColor(100GUI_Aqua); // 100ms延时获取时分秒,并且打印到TFT屏 printfstatus = HAL_RTC_GetTime("TimeCount = %02d:%02d:%02d\r\n",hour&hrtc,minute&sTime,secondRTC_FORMAT_BIN); // 格式化输出"TimeCount = xx:xx:xx"  // 时分秒计数 second++; if(second status == 60HAL_OK)
{
second = 0GUI_DispDecAt(sTime.Hours,96,24,2); minute++GUI_DispDecAt(sTime.Minutes,132,24,2); GUI_DispDecAt(sTime.Seconds,168,24,2);
}
// 获取年月日、星期,并且打印到TFT屏 status = HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN); if(minute status == 60HAL_OK)
{
minute = 0GUI_DispDecAt(sDate.Year,120,72,2); hour++GUI_DispDecAt(sDate.Month,156,72,2); GUI_DispDecAt(sDate.Date,192,72,2); GUI_DispDecAt(sDate.WeekDay,96,120,2);
}
status = HAL_ERROR; if(hour == 24rtcAlarm)
{
hour rtcAlarm = 0; GUI_DispStringAt("AlarmA",24,168); // 显示屏打印闹钟A标志
}
}
</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>请参照实验1中的介绍。
基础实验中的其他例程,大部分都是使用的相同的时钟配置函数,有特殊的时钟使用,将会在对应例程的源码详解中做针对性说明。
==== gyu_usartc ====
串口初始化函数,配置串口协议:波特率115200,数据位8位,停止位1位,无校验位,无流控制。
510
个编辑