/******************************************************************************** * @file :my_adc.c * @brief :对STM32 HAL 库中 ADC 的二次封装,提供 ADC 的所有操作接口 *******************************************************************************/ #include "onchip_conf.h" #ifdef ONCHIP_MY_ADC_ENABLED #define MAX_ADC_CHANNEL ADC_CHANNEL_9 /* Private Typedef -------------------------------------------------------------------*/ /* Private Functions Declare ----------------------------------------------------------*/ static int my_adc_init(uint32_t xChn); static int my_adc_get(uint32_t xChn, float* fVol); /* Private Variables -------------------------------------------------------------------*/ //参数定义: ADC单例模型,扩展到外部使用定义 const c_my_adc my_adc = {my_adc_init, my_adc_get}; //参数定义: ADC句柄 static ADC_HandleTypeDef ADC_Handle; //参数定义: ADC初始化状态 static bool _adc_init_state = false; static bool _adc_chn_init_state[10] = {false}; //参数定义: ADC配置参数,通道对应引脚定义 static const struct { uint32_t xChn; GPIO_TypeDef* GPIOx; uint16_t GPIO_Pin; } s_my_adc_cfg[] = { {ADC_CHANNEL_0, GPIOA, GPIO_PIN_0}, {ADC_CHANNEL_1, GPIOA, GPIO_PIN_1}, {ADC_CHANNEL_2, GPIOA, GPIO_PIN_2}, {ADC_CHANNEL_3, GPIOA, GPIO_PIN_3}, {ADC_CHANNEL_4, GPIOA, GPIO_PIN_4}, {ADC_CHANNEL_5, GPIOA, GPIO_PIN_5}, {ADC_CHANNEL_6, GPIOA, GPIO_PIN_6}, {ADC_CHANNEL_7, GPIOA, GPIO_PIN_7}, {ADC_CHANNEL_8, GPIOB, GPIO_PIN_0}, {ADC_CHANNEL_9, GPIOB, GPIO_PIN_1}, }; /* Private Functions -------------------------------------------------------------------*/ /** *@brief 设置初始化ADC通道的状态 */ static int my_adc_init(uint32_t xChn) { GPIO_InitTypeDef GPIO_Initure; /*参数检查*/ if(xChn > MAX_ADC_CHANNEL){ return R_PARAM; } /*初始化相应IO*/ GPIO_Initure.Pin = s_my_adc_cfg[xChn].GPIO_Pin; GPIO_Initure.Mode = GPIO_MODE_ANALOG; //模拟 GPIO_Initure.Pull = GPIO_NOPULL; //不带上下拉 HAL_GPIO_Init(s_my_adc_cfg[xChn].GPIOx,&GPIO_Initure); _adc_chn_init_state[xChn] = true; /*对应的通道标记为初始化成功*/ if(!_adc_init_state){ /*使能 ADC1 外设时钟*/ __HAL_RCC_ADC1_CLK_ENABLE(); /*ADC 时钟初始化*/ RCC_PeriphCLKInitTypeDef ADC_CLKInit; ADC_CLKInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;//分频因子6时钟为72M/6=12MHz ADC_CLKInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;//ADC外设时钟 HAL_RCCEx_PeriphCLKConfig(&ADC_CLKInit); /*ADC 外设初始化*/ ADC_Handle.Instance = ADC1; ADC_Handle.Init.DataAlign = ADC_DATAALIGN_RIGHT; //右对齐 ADC_Handle.Init.ScanConvMode = DISABLE; //非扫描模式 ADC_Handle.Init.ContinuousConvMode = DISABLE; //非连续转换(单次转换) ADC_Handle.Init.NbrOfConversion = 1; //1个转换在规则序列中 也就是只转换规则序列1 ADC_Handle.Init.DiscontinuousConvMode = DISABLE; //禁止不连续采样模式 ADC_Handle.Init.NbrOfDiscConversion = 0; //不连续采样通道数为0 ADC_Handle.Init.ExternalTrigConv = ADC_SOFTWARE_START; //软件触发 HAL_ADC_Init(&ADC_Handle); //初始化 HAL_ADCEx_Calibration_Start(&ADC_Handle); //校准ADC _adc_init_state = true; } return R_OK; } /** *@brief 设置初始化ADC通道的状态 */ static int my_adc_get(uint32_t xChn, float* fVol) { ADC_ChannelConfTypeDef ADC1_ChanConf; uint32_t xadc_val; float fadc_val; /*检查参数*/ if(NULL == fVol || xChn > MAX_ADC_CHANNEL){ return R_PARAM; } /*检查初始化状态*/ if(false == _adc_init_state || false == _adc_chn_init_state[xChn]){ return R_ERROR; } /*配置ADC*/ ADC1_ChanConf.Channel = xChn; //通道 ADC1_ChanConf.Rank = 1; //第1个序列,序列1 ADC1_ChanConf.SamplingTime = ADC_SAMPLETIME_239CYCLES_5; //采样时间 HAL_ADC_ConfigChannel(&ADC_Handle, &ADC1_ChanConf); //通道配置 HAL_ADC_Start(&ADC_Handle); //开启ADC HAL_ADC_PollForConversion(&ADC_Handle, 10); //轮询转换 xadc_val = HAL_ADC_GetValue(&ADC_Handle); fadc_val = xadc_val * (3.3f / 4096.0f); *fVol = fadc_val; return R_OK; } #endif /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 帮我总结一下这串代码
时间: 2025-06-01 17:58:45 浏览: 17
<think>嗯,用户想了解STM32HAL库中ADC二次封装代码的功能总结。首先,我需要回顾一下HAL库的基础ADC函数,比如启动转换、等待转换完成和获取数据这些。用户提供的引用里提到了HAL_ADC_Start、HAL_ADC_PollForConversion和HAL_ADC_GetValue,这些是基本操作。然后,二次封装的目的应该是简化配置流程,提高代码复用性。比如,用户提到的adc_channel_config函数,它可能封装了通道配置的步骤,设置通道、规则组和采样时间。这可以让用户不用每次都填写结构体,直接调用函数传递参数就行。另外,引用里还提到了DMA的初始化,比如adc_dma_init函数,可能整合了ADC配置和DMA传输的设置。这样用户只需要指定内存地址,就能自动完成数据转运,减少重复代码。
多通道采集也是一个重点。二次封装可能支持多通道配置,自动处理多个通道的扫描和DMA传输。比如,启动DMA传输时指定多个内存地址,这样ADC可以自动切换通道并存储数据。错误处理机制可能也被封装进去,比如添加重试逻辑或状态检查,增强稳定性。此外,用户可能希望支持不同的触发模式,如定时器触发或外部触发,封装函数可以简化这些配置。
最后,需要总结这些功能,并生成相关问题,帮助用户深入理解优化方法和应用场景。确保回答结构清晰,引用正确的示例代码,比如引用[1]和[2]中的函数,说明封装后的优势。</think>### STM32 HAL库 ADC二次封装功能总结
在STM32 HAL库中,对ADC功能进行二次封装的主要目标是**简化配置流程**、**提高代码复用性**,并支持**多通道采集**与**DMA高效传输**。以下是核心功能总结:
---
#### 1. **通道配置封装**
- **功能**:通过函数`adc_channel_config()`统一配置ADC通道参数,包括:
- 指定物理通道(如$ADC\_CHANNEL\_1$)
- 设置规则组中的排名($Rank$)
- 定义采样时间(如$ADC\_SAMPLETIME\_239CYCLES\_5$)
- **示例代码**:
```c
void adc_channel_config(ADC_HandleTypeDef* hadc, uint32_t ch, uint32_t rank, uint32_t stime) {
ADC_ChannelConfTypeDef adc_ch_config = {0};
adc_ch_config.Channel = ch;
adc_ch_config.Rank = rank;
adc_ch_config.SamplingTime = stime;
HAL_ADC_ConfigChannel(hadc, &adc_ch_config);
}
```
- **优势**:避免重复初始化结构体,简化多通道切换[^2]。
---
#### 2. **DMA传输集成**
- **功能**:通过`adc_dma_init()`函数整合DMA配置与ADC启动:
- 指定数据存储地址(如内存数组指针)
- 自动关联ADC与DMA传输
- 支持单次或连续传输模式
- **示例代码**:
```c
void adc_dma_init(uint32_t *mar) {
adc_config(); // 基础ADC配置
adc_channel_config(&adc_handle, ADC_CHANNEL_1, ADC_REGULAR_RANK_1, ADC_SAMPLETIME_239CYCLES_5);
dma_config(); // DMA参数初始化
HAL_ADC_Start_DMA(&adc_handle, mar, 1); // 启动ADC+DMA
}
```
- **应用场景**:适用于需要实时采集且不占用CPU的场景(如传感器数据流)[^2]。
---
#### 3. **多通道支持**
- **功能**:扩展封装以支持多通道扫描模式:
- 通过循环配置多个通道的$Rank$和采样时间
- 调用`HAL_ADC_Start_DMA()`时指定多数据地址
- **关键参数**:DMA传输长度需与通道数匹配(如`HAL_ADC_Start_DMA(&hadc, buffer, 3)`表示3通道采集)。
---
#### 4. **错误处理与状态管理**
- **功能**:在封装函数中添加重试机制或状态检查:
- 检查`HAL_ADC_PollForConversion()`超时
- 处理DMA传输中断错误
- **示例逻辑**:
```c
if (HAL_ADC_PollForConversion(&hadc, 100) == HAL_OK) {
value = HAL_ADC_GetValue(&hadc);
} else {
// 错误处理
}
```
---
#### 5. **触发模式扩展**
- **功能**:支持外部触发(如定时器触发):
- 封装`HAL_ADC_Start_IT()`或`HAL_ADC_Start()`函数
- 配置触发源(如$ADC\_EXTERNALTRIG\_TIM2\_CC3$)
---
###
阅读全文