ADC独立单通道采集
使用的是野火stm32f103vet6指南者开发板,硬件连接图如下:
实验一:独立单通道中断读取ADC值
编程要点:
- 初始化ADC用到的GPIO;
- 初始化ADC初始化结构体;
- 配置ADC时钟,配置通道的转换顺序和采样时间;
- 使能ADC转换完成中断,配置中断优先级;
- 使能ADC,准备开始转换;
- 校准ADC;
- 软件触发ADC,真正开始转换;
- 编写中断服务函数,读取ADC转换数据;
- 编写main函数,把转换的数据打印出来;
代码如下:
bsp_adc.c
#include "bsp_adc.h"
static void ADCx_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 打开 ADC IO端口时钟
ADC_GPIO_APBxClock_FUN(ADC_GPIO_CLK, ENABLE);
// 配置 ADC IO 引脚模式
// 必须为模拟输入
GPIO_InitStructure.GPIO_Pin = ADC_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
// 初始化 ADC IO
GPIO_Init(ADC_PORT, &GPIO_InitStructure);
}
static void ADCx_Mode_Config(void)
{
ADC_InitTypeDef ADC_InitStruct;
// 打开ADC的时钟
ADC_APBxClock_FUN(ADC_CLK, ENABLE);
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // 独立模式
ADC_InitStruct.ADC_ScanConvMode = DISABLE; // 不使用扫描模式
ADC_InitStruct.ADC_ContinuousConvMode = ENABLE; // 连续转换
/* 不使用外部触发 */
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStruct.ADC_NbrOfChannel = 1;
ADC_Init(ADC_x, &ADC_InitStruct);
RCC_ADCCLKConfig(RCC_PCLK2_Div8); // 配置ADC时钟为8分频 ADCCLK = 9M
ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL, 1, ADC_SampleTime_55Cycles5);
ADC_ITConfig(ADC_x, ADC_IT_EOC, ENABLE); // 配置ADC转换完成中断
ADC_Cmd(ADC_x, ENABLE); // 使能ADC
ADC_ResetCalibration(ADC_x); // 初始化ADC 校准寄存器
while(ADC_GetResetCalibrationStatus(ADC_x)); // 等待校准寄存器初始化完成
ADC_StartCalibration(ADC_x); // ADC开始校准
while(ADC_GetCalibrationStatus(ADC_x)); // 等待校准完成
ADC_SoftwareStartConvCmd(ADC_x, ENABLE); // 使用软件触发
}
static void ADC_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// 配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = ADC_IRQ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief ADC初始化
* @param 无
* @retval 无
*/
void ADCx_Init(void)
{
ADCx_GPIO_Config();
ADCx_Mode_Config();
ADC_NVIC_Config();
}
bsp_adc.h
#ifndef __BSP_ADC_H
#define __BSP_ADC_H
#include "stm32f10x.h"
// ADC GPIO宏定义
#define ADC_GPIO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADC_GPIO_CLK RCC_APB2Periph_GPIOC
#define ADC_PORT GPIOC
#define ADC_PIN GPIO_Pin_1
// ADC 编号选择
// 可以是 ADC1/2,如果使用ADC3,中断相关的要改成ADC3的
#define ADC_APBxClock_FUN RCC_APB2PeriphClockCmd
#define ADC_x ADC2
#define ADC_CLK RCC_APB2Periph_ADC2
// ADC 通道宏定义(PC1对应通道11)
#define ADC_CHANNEL ADC_Channel_11
// ADC 中断相关宏定义
#define ADC_IRQ ADC1_2_IRQn
#define ADC_IRQHandler ADC1_2_IRQHandler
void ADCx_Init(void);
#endif /* __BSP_ADC_H */
中断服务函数中,当转换完成后,把转换值不断刷新到全局变量中:
void ADC_IRQHandler(void)
{
if (ADC_GetITStatus(ADC_x, ADC_IT_EOC) == SET)
{
ADC_ConvertedValue = ADC_GetConversionValue(ADC_x);
}
ADC_ClearITPendingBit(ADC_x, ADC_IT_EOC); // 清中断标志位,防止一直在中断里
}
最后在主函数中进行测试:
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_adc.h"
#include <stdio.h>
__IO uint16_t ADC_ConvertedValue;
// 局部变量,用于保存转换计算后的电压值
float ADC_ConvertedValueLocal;
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
int main(void)
{
LED_GPIO_Config();
USART_config();
ADCx_Init();
printf("这是一个ADC独立单通道读取实验\t\n");
while (1)
{
ADC_ConvertedValueLocal =(float) ADC_ConvertedValue / 4096 * 3.3;
printf("The current AD value = %#X \t\n",
ADC_ConvertedValue);
printf("The current voltage = %f V \t\n",
ADC_ConvertedValueLocal);
printf("\n");
LED_B_TOGGLE;
Delay(0x2fffee);
}
}
实验现象如下:
实验二:独立单通道DMA读取
当要传输大量的数据时,一般都是采用DMA。ADC + DMA本身的配置非常简单,就只需要注意,只能使用ADC1或ADC3,而ADC2无DMA传输功能。
这里主要看一下ADC + DMA配置:
/**
* @brief ADC - DMA配置
* @note 注意,ADC - DMA只能使用ADC1 或 ADC3,ADC2无DMA功能
*/
static void ADCx_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(ADC_DMA_CHANNEL);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&(ADC_x->DR));
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
DMA_Cmd(ADC_DMA_CHANNEL , ENABLE);
}
然后在ADC配置函数中进行调用,再使能ADC + DMA:
/* ADC - DMA设置要在使能ADC完成 */
ADCx_DMA_Config(); // 配置ADC - DMA
ADC_DMACmd(ADC_x, ENABLE); // 使能 ADC DMA 请求
然后在main.c中进行测试,结果如下: