用 STM32 HAL/LL + Arduino 混合编程

💡 用 STM32 HAL/LL + Arduino 混合编程,这样可以在 Arduino 简易框架下实现 STM32 的底层高级功能(比如高性能 ADC、DMA、PWM 等)。下面梳理一下步骤、注意事项和示例代码。**


🚀 混合编程的核心思路

在 Arduino_Core_STM32 环境里,你可以直接写:

✅ HAL API(例如 HAL_ADC_Start())

✅ LL API(例如 LL_ADC_Enable())

✅ 直接操作寄存器

👉 因为底层 HAL 和 LL 库都已经集成到 Arduino Core 里了,你只需包含对应头文件并调用。


🛠 具体步骤

① 在 Arduino 代码中包含 HAL/LL 库头文件

根据芯片型号,包含对应头文件。例如:

cpp 复制代码
#include "stm32f1xx_hal.h"
#include "stm32f1xx_ll_adc.h"

如果是 F4 芯片就是 stm32f4xx_hal.h 等。


② 在 setup() 中初始化你的外设(用 HAL 或 LL 配置)

cpp 复制代码
void setup() {
  Serial.begin(115200);
  HAL_Init(); // 通常 Arduino Core 已自动调用,但可再次确保
  
  // 初始化 ADC
  __HAL_RCC_ADC1_CLK_ENABLE();  // 打开 ADC1 时钟
  __HAL_RCC_GPIOA_CLK_ENABLE(); // 打开 GPIOA 时钟 (假设你用 PA0)

  // 配置 PA0 为模拟输入
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  // 配置 ADC
  ADC_HandleTypeDef hadc1;
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  HAL_ADC_Init(&hadc1);

  // 配置通道
  ADC_ChannelConfTypeDef sConfig = {0};
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);

  // 启动 ADC
  HAL_ADC_Start(&hadc1);
}

③ 在 loop() 中采样数据

cpp 复制代码
void loop() {
  // 开始转换
  HAL_ADC_Start(&hadc1);

  // 等待转换完成
  HAL_ADC_PollForConversion(&hadc1, 10);

  // 获取值
  uint32_t value = HAL_ADC_GetValue(&hadc1);

  Serial.println(value);
  delay(100);
}

🌟 关键点

✅ 可以在 setup() 中配置所有 HAL / LL 外设。

✅ loop() 中可以混合 HAL、LL 或 Arduino 函数(例如 Serial.print)。

✅ 如果用 LL,可以直接写:

cpp 复制代码
LL_ADC_Enable(ADC1);
LL_ADC_REG_StartConversionSWStart(ADC1);

✅ HAL/LL 底层配置能获得更高性能和灵活性(比如 DMA、定时触发、内部通道等)。


⚠ 注意事项

⚠ HAL 对象(如 ADC_HandleTypeDef hadc1)如果在多个函数中用,最好声明成全局变量或静态变量。

⚠ 如果用 DMA,要初始化对应的 DMA 通道和 NVIC 配置。

⚠ HAL_Init() 一般 Arduino Core 已经调用,不要重复初始化系统时钟。


💡 完整 Arduino + HAL 混合 ADC 示例 (STM32F103 PA0)

cpp 复制代码
#include "stm32f1xx_hal.h"

ADC_HandleTypeDef hadc1;

void setup() {
  Serial.begin(115200);

  __HAL_RCC_ADC1_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
  hadc1.Init.ContinuousConvMode = DISABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  HAL_ADC_Init(&hadc1);

  ADC_ChannelConfTypeDef sConfig = {0};
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}

void loop() {
  HAL_ADC_Start(&hadc1);
  HAL_ADC_PollForConversion(&hadc1, 10);
  if (HAL_IS_BIT_SET(HAL_ADC_GetState(&hadc1), HAL_ADC_STATE_REG_EOC)) {
    uint32_t adcVal = HAL_ADC_GetValue(&hadc1);
    Serial.println(adcVal);
  }
  delay(100);
}

STM32 Arduino + HAL 混合编程多通道 ADC + DMA 示例。

🎯 目标

✅ 使用 ADC1 多通道采样(例如 PA0 和 PA1)

✅ 用 DMA 自动传输数据到内存

✅ Arduino 环境里可以通过 Serial.println 打印结果

✅ 高效连续采样


⚡ 代码示例:STM32F103 多通道 ADC + DMA

cpp 复制代码
#include "stm32f1xx_hal.h"

#define ADC_BUFFER_LENGTH 2

ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
uint16_t adcBuffer[ADC_BUFFER_LENGTH];

void setup() {
  Serial.begin(115200);
  
  // 启用时钟
  __HAL_RCC_ADC1_CLK_ENABLE();
  __HAL_RCC_DMA1_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();

  // 配置 GPIO (PA0 PA1 -> ADC)
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  // 配置 ADC
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;   // 启用扫描模式多通道
  hadc1.Init.ContinuousConvMode = ENABLE;      // 连续模式
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = ADC_BUFFER_LENGTH;
  HAL_ADC_Init(&hadc1);

  // 配置每个通道
  ADC_ChannelConfTypeDef sConfig = {0};

  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = ADC_REGULAR_RANK_1;
  sConfig.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);

  sConfig.Channel = ADC_CHANNEL_1;
  sConfig.Rank = ADC_REGULAR_RANK_2;
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);

  // 配置 DMA
  hdma_adc1.Instance = DMA1_Channel1;
  hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
  hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
  hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
  hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
  hdma_adc1.Init.Mode = DMA_CIRCULAR;  // 循环模式
  hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH;
  HAL_DMA_Init(&hdma_adc1);

  __HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);

  // 启动 ADC DMA
  HAL_ADC_Start_DMA(&hadc1, (uint32_t*)adcBuffer, ADC_BUFFER_LENGTH);
}

void loop() {
  // 打印 ADC DMA 采集数据
  Serial.print("PA0: ");
  Serial.print(adcBuffer[0]);
  Serial.print("\tPA1: ");
  Serial.println(adcBuffer[1]);
  delay(500);
}

📝 说明

✅ 这段代码用 DMA 自动把 ADC1 的转换数据搬运到 adcBuffer[]

✅ 配置了两个通道:PA0(ADC_CHANNEL_0),PA1(ADC_CHANNEL_1)

✅ ADC 连续扫描两个通道,循环 DMA 不断更新

✅ loop() 里直接读缓冲区,几乎实时打印


⚠ 注意

1️⃣ 如果用的是 F4/F3/F7,需要换成对应头文件(如 stm32f4xx_hal.h),并调整 DMA 通道(F4 是 DMA2_Stream0 等)。

2️⃣ 如果要支持更多通道,只需配置更多通道和缓冲区长度。

3️⃣ 确保 ADC 引脚输入电压 < Vref(一般3.3V)。

4️⃣ 可增加滤波或计算平均值提升稳定性。


🌟 拓展功能

✅ 定时器触发 ADC + DMA(硬件采样频率)

✅ FFT 分析直接基于 DMA 数据

✅ ADC + DMA + 双缓冲

✅ 多通道图形化输出(TFT / OLED)


相关推荐
路上有只喵20 分钟前
认识元器件 · 单片机(MCU/MPU/SOC) · 实战篇6
单片机·嵌入式硬件
国科安芯31 分钟前
核电站蒸汽发生器传热管在役无损检测模拟前端的低噪声供电研究
嵌入式硬件·ldo·低压差线性稳压器·无损检测·核电站·蒸汽发生器·传热管
集芯微电科技有限公司1 小时前
40V/24A高频率增强型GaN HEMT功率开关管/高频DC转换/电动工具/GaN充电器 方案首选
人工智能·科技·单片机·嵌入式硬件·深度学习·神经网络·生成对抗网络
十八岁牛爷爷2 小时前
STM32H7 长时间运行后 HTTP 断连:从“串口还活着”到 TX 描述符错账
stm32·单片机·嵌入式硬件
Lxx_Zw2 小时前
串口命令如何从字符串长成协议:帧头、长度、校验和与状态机
c语言·stm32·单片机·嵌入式硬件
凯基迪科技3 小时前
瑞萨R5F100解密 R8C/RL78 MCU 固件读取与保护机制详解
服务器·网络·单片机
PMAOS3 小时前
4G 功能机操作系统选型指南:KaiOS、Mocor、MAUI、PMAOS 的能力边界与验证方法
嵌入式硬件·ai·操作系统
mk0154 小时前
单片机同步降压稳压器输出最低1V,关断时损耗3uA
单片机·嵌入式硬件·硬件工程·智能硬件·pcb工艺
然后,是第八天5 小时前
platformio使用使用pioarduino流程
单片机
糖糖单片机设计5 小时前
基于 STM32 + LU-ASR01 的智能台灯
stm32·单片机·嵌入式硬件·51单片机·语音识别