NBDK-L4:基础实验教程
教程介绍
1 GPIO实验
第一个实验我们给大家带来的是最简单的外设控制,也就是 IO 口操作,通过这个实验我们可以了解到如何让STM32L476RC的一个 IO 输出高低电平,并以此控制 LED 的点亮和熄灭。
1.1 STM32L476 IO简介
每个GPIO引脚都可以通过软件配置为输出(推挽或漏极开路),输入(带或不带上拉或下拉)或外设备用功能。 大多数GPIO引脚与数字或模拟备用功能共用。 由于它们在AHB2总线上的映射,可以实现快速I / O切换。 如果需要,可以锁定I / O备用功能配置序列,以避免虚假写入I / O寄存器。
经过上一段对GPIO口模式的说明,在这里对它的工作模式进行一个小结,它一共有八种组合,即有八种可配置的工作模式,分别是:
- 输入浮空
- 输入上拉
- 输入下拉
- 模拟
- 带上拉或下拉的开漏输出
- 带上拉或下拉的推挽输出
- 带上拉或下拉的复用功能推挽
- 带上拉或下拉的复用功能开漏
1.2 硬件设计
1.3 实验准备
- 使用miniUSB线及10pin排线,通过Jlink仿真器连接PC端和开发板
- 使用Keil打开基础实验 01-led实验工程
- 下载程序,并完成功能测试
1.4 实验验证
下载完成后,可以看下开发板上的LED灯周期闪烁,点亮及熄灭周期时间为500ms。
1.5 源码详解
1.5.1 stm32l4xx_hal_conf.h
103 // 使能的宏
104 #define HAL_MODULE_ENABLED // 芯片
105 #define HAL_FLASH_MODULE_ENABLED // Flash
106 #define HAL_PWR_MODULE_ENABLED // 电源
107 #define HAL_RCC_MODULE_ENABLED // 时钟
108 #define HAL_CORTEX_MODULE_ENABLED // NVIC
109
110 #define HAL_GPIO_MODULE_ENABLED // GPIO
1.5.2 main.c
31 int main(void)
32 {
33 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
34 // 重置所有外设、flash界面以及系统时钟
35 HAL_Init();
36
37 // 配置系统时钟(包含振荡器、系统时钟、总线时钟等等)
38 SystemClock_Config();
39
40 // 初始化LED引脚
41 LED_Init();
42
43 //
44 while (1)
45 {
46 LED_SET(GPIO_PIN_SET); // 设置LED引脚(PA15)输出高电平,LED点亮
47 HAL_Delay(500); // 延时500ms
48 LED_SET(GPIO_PIN_RESET); // 设置LED引脚(PA15)输出低电平,LED熄灭
49 HAL_Delay(500); // 延时500ms
50 }
51 }
1.5.3 gyu_util.c
49 void SystemClock_Config(void)
50 {
51 RCC_OscInitTypeDef RCC_OscInitStruct; // 定义RCC内部/外部振荡器结构体
52 RCC_ClkInitTypeDef RCC_ClkInitStruct; // 定义RCC系统,AHB和APB总线时钟配置结构体
53 RCC_PeriphCLKInitTypeDef PeriphClkInit; // 定义RCC扩展时钟结构体
54
55 // 配置LSE驱动器功能为低驱动能力
56 __HAL_RCC_LSEDRIVE_CONFIG(RCC_LSEDRIVE_LOW);
57
58 // 初始化CPU,AHB和APB总线时钟
59 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSE
60 |RCC_OSCILLATORTYPE_LSE; // 设置需要配置的振荡器为HSI、HSE、LSE
61 // 配置HSE
62 RCC_OscInitStruct.HSEState = RCC_HSE_ON; // 激活HSE时钟(开发板外部为8MHz)
63 // 配置LSE
64 RCC_OscInitStruct.LSEState = RCC_LSE_ON; // 激活LSE时钟(32.768KHz,低驱动)
65 // 配置HSI
66 RCC_OscInitStruct.HSIState = RCC_HSI_ON; // 激活HSI时钟
67 RCC_OscInitStruct.HSICalibrationValue = 16; // 配置HSI为16MHz
68 // 配置PLL
69 RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; // 打开PLL
70 RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; // 选择HSE时钟作为PLL入口时钟源,8MHz
71 RCC_OscInitStruct.PLL.PLLM = 1; // 配置PLL VCO输入分频为1,8/1 = 8MHz
72 RCC_OscInitStruct.PLL.PLLN = 20; // 配置PLL VCO输入倍增为20,8MHz*20 = 160MHz
73 RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7; // SAI时钟7分频,160/7 = 22.857143MHz
74 RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,160/2 = 80MHz
75 RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2; // 系统主时钟分区2分频,160/2 = 80MHz
76 // RCC时钟配置,出错则进入错误处理函数
77 if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
78 {
79 _Error_Handler(__FILE__, __LINE__);
80 }
81
82 // 初始化CPU,AHB和APB总线时钟
83 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
84 |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; // 需要配置的时钟HCLK、SYSCLK、PCLK1、PCLK2
85 RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 配置系统时钟为PLLCLK输入,80MHz
86 RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟为系统时钟1分频,80/1 = 80MHz
87 RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; // APB1时钟为系统时钟1分频,80/1 = 80MHz
88 RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟为系统时钟1分频,80/1 = 80MHz
89 // RCC时钟配置,出错则进入错误处理函数
90 if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) // HCLK=80MHz,Vcore=3.3V,所以选择SW4(FLASH_LATENCY_4)
91 {
92 _Error_Handler(__FILE__, __LINE__);
93 }
94
95 // 初始化外设时钟
96 PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2
97 |RCC_PERIPHCLK_LPUART1|RCC_PERIPHCLK_LPTIM1
98 |RCC_PERIPHCLK_I2C2|RCC_PERIPHCLK_ADC; // 需要初始化的外设时钟:USART1、USART2、LPUART1、LPTIM1、I2C2、ADC
99 PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; // 配置串口USART1时钟为PCLK2,80MHz
100 PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1; // 配置串口USART2时钟为PCLK1,80MHz
101 PeriphClkInit.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_HSI; // 配置LPUART时钟为HSI,16MHz
102 PeriphClkInit.I2c2ClockSelection = RCC_I2C2CLKSOURCE_PCLK1; // 配置I2C2时钟为PCLK1,80MHz
103 PeriphClkInit.Lptim1ClockSelection = RCC_LPTIM1CLKSOURCE_LSE; // 配置LPTIM1时钟为LSE,32.768KHz
104 PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_PLLSAI1; // 配置ADC时钟为PLLSAI1,现在为80MHz,下面会重新定义
105 PeriphClkInit.PLLSAI1.PLLSAI1Source = RCC_PLLSOURCE_HSE; // 配置PLLSAI1时钟为HSE,8MHz
106 PeriphClkInit.PLLSAI1.PLLSAI1M = 1; // 配置PLLSAI1分频为1
107 PeriphClkInit.PLLSAI1.PLLSAI1N = 8; // 配置PLLSAI1倍增为8
108 PeriphClkInit.PLLSAI1.PLLSAI1P = RCC_PLLP_DIV7; // SAI时钟7分频,64/7 = 9.142857MHz
109 PeriphClkInit.PLLSAI1.PLLSAI1Q = RCC_PLLQ_DIV2; // SDMMC、RNG、USB时钟2分频,64/2 = 32MHz
110 PeriphClkInit.PLLSAI1.PLLSAI1R = RCC_PLLR_DIV2; // 系统主时钟分区2分频,64/2 = 32MHz
111 PeriphClkInit.PLLSAI1.PLLSAI1ClockOut = RCC_PLLSAI1_ADC1CLK; // 配置PLLSAI1输出为ADC1时钟,也就是配置ADC1时钟,32MHz
112 // 外设时钟配置,出错则进入错误处理函数
113 if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
114 {
115 _Error_Handler(__FILE__, __LINE__);
116 }
117
118 // 配置内部主稳压器输出电压,配置为稳压器输出电压范围1模式,也就是:典型输出电压为1.2V,系统频率高达80MHz
119 if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
120 {
121 _Error_Handler(__FILE__, __LINE__);
122 }
123
124 // 配置系统定时器中断时间,配置为HCLK的千分频
125 HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
126
127 // 配置系统定时器,配置为HCLK
128 HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
129
130 // 系统定时器中断配置,设置系统定时器中断优先级最高(为0),且子优先级最高(为0)
131 HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
132 }
1.5.4 gyu_led.c
31 void LED_Init(void)
32 {
33 GPIO_InitTypeDef GPIO_InitStructure; // 定义引脚参数结构体
34
35 __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
36
37 GPIO_InitStructure.Pin= GPIO_PIN_15; // 引脚编号为15
38 GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
39 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; // 低频率
40 GPIO_InitStructure.Pull = GPIO_PULLUP; // 上拉
41 HAL_GPIO_Init(GPIOA, &GPIO_InitStructure); // 初始化PA15
42
43 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, GPIO_PIN_RESET); // 设置PA15默认输出低电平
44 }
54 void LED_SET(GPIO_PinState pinSate)
55 {
56 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_15, pinSate); // 设置PA15输出
57 }