STMicroelectronics 系列:STM32L4 系列_(1).STM32L4系列概述
STM32L4系列概述
1. 引言
STM32L4系列是STMicroelectronics公司推出的一款低功耗、高性能的32位ARM Cortex-M4内核微控制器。该系列微控制器广泛应用于各种嵌入式系统中,特别适合于需要高处理性能和低功耗的应用场景,如物联网设备、便携式医疗设备、智能家居等。本文将详细介绍STM32L4系列的特性、架构、引脚配置、电源管理、外设功能以及开发环境的设置。
2. 特性概述
2.1 性能特点
STM32L4系列微控制器采用ARM Cortex-M4内核,具备以下主要性能特点:
高性能: 最高主频可达80MHz,提供强大的处理能力。
低功耗: 支持多种低功耗模式,如睡眠模式、停止模式和待机模式,适合电池供电的应用。
高集成度: 集成了多种外设,如ADC、DAC、UART、SPI、I2C、USB等,减少外部组件需求。
大容量存储: 提供不同容量的闪存和RAM,满足不同应用需求。
安全特性: 集成硬件加密引擎,支持AES、TRNG等安全功能。
2.2 功耗管理
STM32L4系列的低功耗特性是其一大亮点。以下是一些功耗管理的关键技术:
动态电压调整 (DVS): 根据当前的负载情况动态调整供电电压,降低功耗。
多种低功耗模式: 支持睡眠模式、停止模式和待机模式,每种模式下的功耗都有详细的数据说明。
低功耗定时器: 如RTC和LPTIM,可在低功耗模式下继续运行,实现定时唤醒功能。
2.3 集成外设
STM32L4系列集成了丰富的外设,以下是一些主要的外设:
模拟外设: 包括12位和16位ADC、12位DAC,支持多种采样率和转换模式。
通信接口: 支持UART、SPI、I2C、USART、CAN、USB等多种通信接口,方便与其他设备进行数据交换。
定时器: 包括通用定时器、高级定时器、低功耗定时器等,适用于各种定时和计数需求。
实时钟 (RTC): 提供精确的时间基准,支持闰秒和闰年处理。
外设接口: 如GPIO、DMA、SDIO、Ethernet MAC等,提供灵活的I/O操作和数据传输。
2.4 存储器
STM32L4系列提供了不同容量的闪存和RAM,满足不同应用的需求:
闪存: 最高可达1MB,支持快速读写和擦除操作。
RAM: 最高可达320KB,提供高速数据处理能力。
嵌入式EEPROM: 用于存储关键数据,支持多次擦写操作。
2.5 安全特性
STM32L4系列集成了多种安全特性,确保数据的安全性和系统的可靠性:
硬件加密引擎: 支持AES、TRNG等加密算法,保护数据传输的安全性。
安全启动: 支持安全启动和安全更新,防止固件被篡改。
故障检测和保护: 内置多种故障检测机制,如电源电压监测、温度监测等,确保系统稳定运行。
3. 架构详解
3.1 内核架构
STM32L4系列采用ARM Cortex-M4内核,具备以下特点:
浮点运算单元 (FPU): 提供高效的浮点运算能力,适用于复杂算法的处理。
嵌套向量中断控制器 (NVIC): 支持优先级中断处理,确保中断的及时响应。
总线矩阵: 采用多层AMBA总线结构,确保高效的数据传输和处理。
3.2 存储器架构
STM32L4系列的存储器架构如下:
闪存: 用于存储程序代码和常量数据。
RAM: 用于存储运行时数据和堆栈。
嵌入式EEPROM: 用于存储关键数据,支持多次擦写操作。
系统存储器: 包含启动代码和配置数据。
3.3 电源管理
STM32L4系列的电源管理架构如下:
多电源域: 支持多个电源域,可根据需要关闭或开启某些域以降低功耗。
低功耗模式: 包括睡眠模式、停止模式和待机模式,每种模式下的功耗和唤醒时间都有详细的数据说明。
电源管理单元 (PWR): 负责电源的管理和控制,提供多种功耗优化策略。
4. 引脚配置
4.1 引脚功能
STM32L4系列的引脚功能多样,以下是一些常见的引脚功能:
电源引脚: VDD、VSS、VDDA、VSSA等,用于供电和接地。
复位引脚: NRST,用于复位微控制器。
时钟引脚: 晶振引脚、外部时钟引脚等,用于提供系统时钟。
I/O引脚: GPIO引脚,用于输入输出操作。
模拟引脚: ADC和DAC引脚,用于模拟信号的采集和输出。
通信引脚: UART、SPI、I2C、USART等通信接口的引脚。
4.2 引脚映射
STM32L4系列的引脚映射如下:
GPIO: 每个引脚可以配置为不同的功能,如输入、输出、外设功能等。
外设引脚: 每个外设都有对应的引脚映射,可以在引脚配置表中查找。
4.3 引脚配置示例
以下是一个简单的GPIO配置示例,展示如何配置一个引脚为输出模式并控制其状态:
#include "stm32l4xx_hal.h"
// 定义GPIO端口和引脚
#define LED_PORT GPIOA
#define LED_PIN GPIO_PIN_5
void GPIO_Init(void) {
// 初始化GPIO端口
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
// 配置GPIO引脚
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
}
void Toggle_LED(void) {
// 切换LED状态
HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
}
int main(void) {
HAL_Init(); // 初始化HAL库
GPIO_Init(); // 初始化GPIO
while (1) {
Toggle_LED(); // 切换LED状态
HAL_Delay(500); // 延时500ms
}
}
4.4 电源引脚配置
以下是一个简单的电源引脚配置示例,展示如何正确连接电源引脚:
#include "stm32l4xx_hal.h"
int main(void) {
HAL_Init(); // 初始化HAL库
// 使能PWR时钟
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess(); // 使能备份寄存器访问
// 配置电源引脚
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOB时钟
// 连接VDD和VSS
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; // 模拟模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 连接VDDA和VSSA
GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
while (1) {
// 主循环
}
}
5. 开发环境设置
5.1 开发工具
STM32L4系列的开发工具如下:
STM32CubeMX: 用于初始化配置和代码生成。
STM32CubeIDE: 用于编写、编译和调试代码。
STM32CubeProgrammer: 用于烧录和调试固件。
5.2 STM32CubeMX配置
使用STM32CubeMX进行初始化配置的步骤如下:
-
选择微控制器: 在STM32CubeMX中选择STM32L4系列的微控制器型号。
-
配置时钟: 根据应用需求配置系统时钟和外设时钟。
-
配置GPIO: 选择需要的GPIO引脚并配置其功能和模式。
-
配置外设: 选择需要的外设并配置其参数。
-
生成代码: 生成初始化代码并导入到STM32CubeIDE中。
5.3 STM32CubeIDE配置
使用STM32CubeIDE进行代码开发的步骤如下:
-
导入项目: 将STM32CubeMX生成的项目导入到STM32CubeIDE中。
-
编写代码: 在主函数中编写应用代码。
-
编译代码: 编译项目以生成二进制文件。
-
烧录固件: 使用STM32CubeProgrammer将固件烧录到微控制器中。
-
调试代码: 使用调试工具进行代码调试和测试。
5.4 代码生成示例
以下是一个使用STM32CubeMX生成代码的示例:
-
选择微控制器: 选择STM32L476RG。
-
配置时钟: 设置系统时钟为80MHz,外设时钟为48MHz。
-
配置GPIO: 选择PA5引脚,配置为推挽输出模式。
-
配置外设: 选择USART1,配置为异步通信模式。
-
生成代码: 生成初始化代码并导入到STM32CubeIDE中。
生成的初始化代码如下:
#include "stm32l4xx_hal.h"
// 定义GPIO端口和引脚
#define LED_PORT GPIOA
#define LED_PIN GPIO_PIN_5
// 定义USART1
UART_HandleTypeDef huart1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
int main(void) {
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化GPIO
MX_USART1_UART_Init(); // 初始化USART1
while (1) {
HAL_GPIO_TogglePin(LED_PORT, LED_PIN); // 切换LED状态
HAL_Delay(500); // 延时500ms
}
}
void SystemClock_Config(void) {
// 配置系统时钟为80MHz
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 使能HSE振荡器
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
Error_Handler();
}
}
static void MX_GPIO_Init(void) {
// 初始化GPIO
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
}
static void MX_USART1_UART_Init(void) {
// 初始化USART1
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
}
void Error_Handler(void) {
// 错误处理函数
while (1) {
// 无限循环
}
}
5.5 代码调试示例
以下是一个使用STM32CubeIDE进行代码调试的示例:
-
设置断点: 在
main
函数中设置断点,以便在调试时暂停执行。 -
启动调试: 使用STM32CubeIDE启动调试会话。
-
查看变量: 在调试模式下查看变量的值,确保程序按预期运行。
-
单步执行: 使用单步执行功能逐步执行代码,观察每一步的执行效果。
6. 外设功能详解
6.1 ADC功能
STM32L4系列的ADC功能强大,支持12位和16位转换,具备以下特点:
多通道: 支持多个通道的模拟信号采集。
采样率: 支持多种采样率,满足不同应用需求。
触发模式: 支持软件触发和硬件触发,如定时器触发、外部事件触发等。
6.2 ADC配置示例
以下是一个简单的ADC配置示例,展示如何配置ADC并进行模拟信号采集:
#include "stm32l4xx_hal.h"
// 定义ADC通道
#define ADC_CHANNEL ADC_CHANNEL_1
// 定义ADC句柄
ADC_HandleTypeDef hadc;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC_Init(void);
int main(void) {
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化GPIO
MX_ADC_Init(); // 初始化ADC
while (1) {
uint32_t adc_value;
HAL_ADC_Start(&hadc); // 开始ADC转换
HAL_ADC_PollForConversion(&hadc, 10); // 等待转换完成
adc_value = HAL_ADC_GetValue(&hadc); // 获取转换结果
HAL_ADC_Stop(&hadc); // 停止ADC转换
// 处理ADC值
if (adc_value > 2048) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 点亮LED
} else {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 熄灭LED
}
HAL_Delay(1000); // 延时1000ms
}
}
void SystemClock_Config(void) {
// 配置系统时钟为80MHz
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 使能HSE振荡器
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
Error_Handler();
}
}
static void MX_GPIO_Init(void) {
// 初始化GPIO
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
static void MX_ADC_Init(void) {
// 初始化ADC
__HAL_RCC_ADC12_CLK_ENABLE(); // 使能ADC1时钟
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; // 时钟预分频
hadc.Init.Resolution = ADC_RESOLUTION_12B; // 12位分辨率
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 右对齐
hadc.Init.ScanConvMode = DISABLE; // 禁用扫描模式
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV; // 单次转换结束
hadc.Init.ChannelConfig = ADC_CHANNEL_1; // 选择通道1
hadc.Init.ContinuousConvMode = DISABLE; // 禁用连续转换模式
hadc.Init.DiscontinuousConvMode = DISABLE; // 禁用不连续转换模式
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发
hadc.Init.DMAContinuousRequests = DISABLE; // 禁用DMA连续请求
hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; // 覆盖数据
if (HAL_ADC_Init(&hadc) != HAL_OK) {
Error_Handler();
}
// 配置ADC通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
Error_Handler();
}
}
void Error_Handler(void) {
// 错误处理函数
while (1) {
// 无限循环
}
}
6.3 DAC功能
STM32L4系列的DAC功能同样强大,支持12位输出,具备以下特点:
多通道: 支持多个通道的模拟信号输出。
采样率: 支持高速和低速模式,满足不同应用需求。
触发模式: 支持软件触发和硬件触发,如定时器触发、外部事件触发等。
6.4 DAC配置示例
以下是一个简单的DAC配置示例,展示如何配置DAC并输出模拟信号:
#include "stm32l4xx_hal.h"
// 定义DAC通道
#define DAC_CHANNEL DAC_CHANNEL_1
// 定义DAC句柄
DAC_HandleTypeDef hdac;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DAC_Init(void);
int main(void) {
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化GPIO
MX_DAC_Init(); // 初始化DAC
while (1) {
// 输出模拟信号
HAL_DAC_SetValue(&hdac, DAC_CHANNEL, DAC_ALIGN_12B_R, 2048); // 设置输出值为2048
HAL_Delay(1000); // 延时1000ms
HAL_DAC_SetValue(&hdac, DAC_CHANNEL, DAC_ALIGN_12B_R, 1024); // 设置输出值为1024
HAL_Delay(1000); // 延时1000ms
}
}
void SystemClock_Config(void) {
// 配置系统时钟为80MHz
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 使能HSE振荡器
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
Error_Handler();
}
}
static void MX_GPIO_Init(void) {
// 初始化GPIO
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
static void MX_DAC_Init(void) {
// 初始化DAC
__HAL_RCC_DAC12_CLK_ENABLE(); // 使能DAC时钟
hdac.Instance = DAC1;
hdac.Init.Trigger = DAC_TRIGGER_NONE; // 无触发
hdac.Init.OutputBuffer = DAC_OUTPUTBUFFER_ENABLE; // 使能输出缓冲
if (HAL_DAC_Init(&hdac) != HAL_OK) {
Error_Handler();
}
// 配置DAC通道
if (HAL_DAC_ConfigChannel(&hdac, &DAC_ChannelConfTypeDef, DAC_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
}
void Error_Handler(void) {
// 错误处理函数
while (1) {
// 无限循环
}
}
6.5 UART功能
STM32L4系列的UART功能支持多种通信模式,具备以下特点:
多通道: 支持多个UART通道。
数据格式: 支持多种数据格式,如8位、9位、奇偶校验等。
波特率: 支持多种波特率,满足不同通信需求。
中断和DMA: 支持中断和DMA传输,提高通信效率。
6.6 UART配置示例
以下是一个简单的UART配置示例,展示如何配置UART并进行串口通信:
#include "stm32l4xx_hal.h"
// 定义UART句柄
UART_HandleTypeDef huart1;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
int main(void) {
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化GPIO
MX_USART1_UART_Init(); // 初始化UART
while (1) {
// 发送字符串
char *message = "Hello, STM32L4!\r\n";
HAL_UART_Transmit(&huart1, (uint8_t *)message, strlen(message), HAL_MAX_DELAY);
HAL_Delay(1000); // 延时1000ms
}
}
void SystemClock_Config(void) {
// 配置系统时钟为80MHz
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 使能HSE振荡器
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
Error_Handler();
}
}
static void MX_GPIO_Init(void) {
// 初始化GPIO
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10; // UART1_TX 和 UART1_RX
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
GPIO_InitStruct.Alternate = GPIO_AF7_USART1; // 选择USART1复用功能
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
static void MX_USART1_UART_Init(void) {
// 初始化UART
__HAL_RCC_USART1_CLK_ENABLE(); // 使能UART1时钟
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
}
void Error_Handler(void) {
// 错误处理函数
while (1) {
// 无限循环
}
}
7. 实际应用案例
7.1 物联网设备
STM32L4系列微控制器在物联网设备中具有广泛的应用。以下是一个简单的物联网设备示例,展示如何使用STM32L4系列进行数据采集和传输:
#include "stm32l4xx_hal.h"
#include "stdio.h"
// 定义ADC通道
#define ADC_CHANNEL ADC_CHANNEL_1
// 定义UART句柄
UART_HandleTypeDef huart1;
// 定义ADC句柄
ADC_HandleTypeDef hadc;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_ADC_Init(void);
int main(void) {
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化GPIO
MX_USART1_UART_Init(); // 初始化UART
MX_ADC_Init(); // 初始化ADC
while (1) {
uint32_t adc_value;
HAL_ADC_Start(&hadc); // 开始ADC转换
HAL_ADC_PollForConversion(&hadc, 10); // 等待转换完成
adc_value = HAL_ADC_GetValue(&hadc); // 获取转换结果
HAL_ADC_Stop(&hadc); // 停止ADC转换
// 发送ADC值
char buffer[50];
sprintf(buffer, "ADC Value: %lu\r\n", adc_value);
HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY);
HAL_Delay(1000); // 延时1000ms
}
}
void SystemClock_Config(void) {
// 配置系统时钟为80MHz
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 使能HSE振荡器
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
Error_Handler();
}
}
static void MX_GPIO_Init(void) {
// 初始化GPIO
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
static void MX_USART1_UART_Init(void) {
// 初始化UART
__HAL_RCC_USART1_CLK_ENABLE(); // 使能UART1时钟
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
}
static void MX_ADC_Init(void) {
// 初始化ADC
__HAL_RCC_ADC12_CLK_ENABLE(); // 使能ADC1时钟
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; // 时钟预分频
hadc.Init.Resolution = ADC_RESOLUTION_12B; // 12位分辨率
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 右对齐
hadc.Init.ScanConvMode = DISABLE; // 禁用扫描模式
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV; // 单次转换结束
hadc.Init.ChannelConfig = ADC_CHANNEL_1; // 选择通道1
hadc.Init.ContinuousConvMode = DISABLE; // 禁用连续转换模式
hadc.Init.DiscontinuousConvMode = DISABLE; // 禁用不连续转换模式
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发
hadc.Init.DMAContinuousRequests = DISABLE; // 禁用DMA连续请求
hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; // 覆盖数据
if (HAL_ADC_Init(&hadc) != HAL_OK) {
Error_Handler();
}
// 配置ADC通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
Error_Handler();
}
}
void Error_Handler(void) {
// 错误处理函数
while (1) {
// 无限循环
}
}
7.2 便携式医疗设备
STM32L4系列微控制器在便携式医疗设备中也有广泛应用。以下是一个简单的便携式医疗设备示例,展示如何使用STM32L4系列进行心率监测并显示结果:
7.2.1 硬件配置
心率传感器: 连接到ADC通道。
显示屏: 通过SPI或I2C接口连接。
电池: 通过低功耗模式延长电池寿命。
7.2.2 代码示例
#include "stm32l4xx_hal.h"
#include "stdio.h"
// 定义ADC通道
#define ADC_CHANNEL ADC_CHANNEL_1
// 定义UART句柄
UART_HandleTypeDef huart1;
// 定义ADC句柄
ADC_HandleTypeDef hadc;
// 定义I2C句柄
I2C_HandleTypeDef hi2c1;
// 定义显示屏地址
#define DISPLAY_I2C_ADDRESS 0x3C
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
static void MX_ADC_Init(void);
static void MX_I2C1_Init(void);
static void I2C_Write(uint8_t reg, uint8_t value);
static void I2C_Read(uint8_t reg, uint8_t *value);
int main(void) {
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化GPIO
MX_USART1_UART_Init(); // 初始化UART
MX_ADC_Init(); // 初始化ADC
MX_I2C1_Init(); // 初始化I2C
// 初始化显示屏
I2C_Write(0x00, 0x01); // 命令:显示开启
while (1) {
uint32_t adc_value;
HAL_ADC_Start(&hadc); // 开始ADC转换
HAL_ADC_PollForConversion(&hadc, 10); // 等待转换完成
adc_value = HAL_ADC_GetValue(&hadc); // 获取转换结果
HAL_ADC_Stop(&hadc); // 停止ADC转换
// 发送心率值
char buffer[50];
sprintf(buffer, "Heart Rate: %lu BPM\r\n", adc_value / 10); // 假设ADC值与心率成正比
HAL_UART_Transmit(&huart1, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY);
// 显示心率值
char display_buffer[10];
sprintf(display_buffer, "%lu BPM", adc_value / 10);
I2C_Write(0x40, display_buffer[0]); // 显示第一位
I2C_Write(0x41, display_buffer[1]); // 显示第二位
I2C_Write(0x42, display_buffer[2]); // 显示第三位
HAL_Delay(1000); // 延时1000ms
}
}
void SystemClock_Config(void) {
// 配置系统时钟为80MHz
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 使能HSE振荡器
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
// 配置系统时钟
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
Error_Handler();
}
}
static void MX_GPIO_Init(void) {
// 初始化GPIO
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
static void MX_USART1_UART_Init(void) {
// 初始化UART
__HAL_RCC_USART1_CLK_ENABLE(); // 使能UART1时钟
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
}
static void MX_ADC_Init(void) {
// 初始化ADC
__HAL_RCC_ADC12_CLK_ENABLE(); // 使能ADC1时钟
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV2; // 时钟预分频
hadc.Init.Resolution = ADC_RESOLUTION_12B; // 12位分辨率
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 右对齐
hadc.Init.ScanConvMode = DISABLE; // 禁用扫描模式
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV; // 单次转换结束
hadc.Init.ChannelConfig = ADC_CHANNEL_1; // 选择通道1
hadc.Init.ContinuousConvMode = DISABLE; // 禁用连续转换模式
hadc.Init.DiscontinuousConvMode = DISABLE; // 禁用不连续转换模式
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件触发
hadc.Init.DMAContinuousRequests = DISABLE; // 禁用DMA连续请求
hadc.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; // 覆盖数据
if (HAL_ADC_Init(&hadc) != HAL_OK) {
Error_Handler();
}
// 配置ADC通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
Error_Handler();
}
}
static void MX_I2C1_Init(void) {
// 初始化I2C
__HAL_RCC_I2C1_CLK_ENABLE(); // 使能I2C1时钟
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 100kHz
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
Error_Handler();
}
}
static void I2C_Write(uint8_t reg, uint8_t value) {
HAL_I2C_Master_Transmit(&hi2c1, DISPLAY_I2C_ADDRESS, ®, 1, HAL_MAX_DELAY);
HAL_I2C_Master_Transmit(&hi2c1, DISPLAY_I2C_ADDRESS, &value, 1, HAL_MAX_DELAY);
}
static void I2C_Read(uint8_t reg, uint8_t *value) {
HAL_I2C_Master_Transmit(&hi2c1, DISPLAY_I2C_ADDRESS, ®, 1, HAL_MAX_DELAY);
HAL_I2C_Master_Receive(&hi2c1, DISPLAY_I2C_ADDRESS, value, 1, HAL_MAX_DELAY);
}
void Error_Handler(void) {
// 错误处理函数
while (1) {
// 无限循环
}
}
作者:kkchenkx