STM32——ADC采样实验(多通道(DMA方式/非DMA方式))
(前言:单片机型号是STM32FG030xx)
1、ADC代码
1.1、初始化函数
void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1; // ADC1
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV12; // ADC时钟分频值
hadc1.Init.Resolution = ADC_RESOLUTION_10B; // 分辨率是10,将输入电压转换为一个10位的二进制数字(1023)
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 数据右对齐,ADC转换结果的低位对齐到数据寄存器的最低有效位(LSB)
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE; // 扫描模式使能,可以依次进行多个通道的转换
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV; // 单次转换,每次触发只进行一次转换
hadc1.Init.LowPowerAutoWait = DISABLE; // 禁止低功耗自动等待模式
hadc1.Init.LowPowerAutoPowerOff = DISABLE; // 禁止低功耗自动关机模式
hadc1.Init.ContinuousConvMode = ENABLE; // 持续转换,转换完成后自动开始下一次转换
hadc1.Init.NbrOfConversion = 3; // 每次扫描转换的通道数量为3个
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // ADC的触发方式为软件触发,即由软件启动转换
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; // 外部触发边缘设置为无,因为这里是软件触发
hadc1.Init.DMAContinuousRequests = ENABLE; // 启用DMA连续请求,使得DMA可以连续地从ADC缓冲区中读取转换数据
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED; // 当发生数据覆盖时,保留最新的数据
hadc1.Init.SamplingTimeCommon1 = ADC_SAMPLETIME_1CYCLE_5; // 设置第一个通道的采样时间为1.5个时钟周期
hadc1.Init.SamplingTimeCommon2 = ADC_SAMPLETIME_1CYCLE_5; // 设置第二个和第三个通道的采样时间为1.5个时钟周期
hadc1.Init.OversamplingMode = DISABLE; // 禁用过采样模式
hadc1.Init.TriggerFrequencyMode = ADC_TRIGGER_FREQ_HIGH; // 设置ADC触发频率模式为高频率模式
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_4; // 配置第一个ADC通道为通道4
sConfig.Rank = ADC_REGULAR_RANK_1; // 设置第一个通道的转换排名为第1
sConfig.SamplingTime = ADC_SAMPLINGTIME_COMMON_1; // 设置第一个通道的采样时间为共用的1号采样时间
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_5; // rh_adc
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_7; // 配置第三个ADC通道为通道7(对应th1)
sConfig.Rank = ADC_REGULAR_RANK_3; // 第三个通道的转换排名为第3
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
HAL_ADCEx_Calibration_Start(&hadc1);//校准
}
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; // 定义并初始化一个外设时钟初始化结构体
if(adcHandle->Instance==ADC1)
{
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC; // 选择ADC作为外设时钟
PeriphClkInit.AdcClockSelection = RCC_ADCCLKSOURCE_SYSCLK; // 择系统时钟作为ADC时钟源
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) // 配置外设时钟,如果失败则调用错误处理函数
{
Error_Handler();
}
/* ADC1 clock enable */
__HAL_RCC_ADC_CLK_ENABLE(); // 启用ADC1的时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 启用GPIOA端口的时钟
/**ADC1 GPIO Configuration
PA7 ------> ADC1_IN7 th1
PA5 ------> ADC1_IN5 rh_adc
PA4 ------> ADC1_IN4 key_adc
*/
GPIO_InitStruct.Pin = GPIO_PIN_4|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ADC1 DMA Init */
/* ADC1 Init */
hdma_adc1.Instance = DMA1_Channel1; // 设置DMA实例为DMA1的通道1
hdma_adc1.Init.Request = DMA_REQUEST_ADC1; // 请求映射到ADC1
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; // 外设数据对齐方式:半字(16位)
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; // 内存数据对齐方式:半字(16位)
hdma_adc1.Init.Mode = DMA_CIRCULAR; // DMA工作在循环模式
hdma_adc1.Init.Priority = DMA_PRIORITY_VERY_HIGH; // DMA优先级设置为非常高
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1); // 将DMA句柄链接到ADC句柄
/* USER CODE BEGIN ADC1_MspInit 1 */
/* USER CODE END ADC1_MspInit 1 */
}
}
/***************DMA初始化函数******************/
void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
1.2、DMA方式读取AD值
uint16_t adc_val[3]; // 检测输出的ADC值
int mian()
{
// DMA初始化函数要放在ADC初始化前
MX_DMA_Init();
// ADC初始化
MX_ADC1_Init();
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adc_val, 3);
// 其他
while(1){
}
}
只需要在使用的时候开启就会一直转换,并且按通道顺序存储在adc_val数组内。
1.3、非DMA方式读取AD值
#define CHANEL_BUF_SIZE 1
#define ADC_CHANNEL_NUM 3
#define ADC_BUF_SIZE (CHANEL_BUF_SIZE*ADC_CHANNEL_NUM)
uint16_t adc_buf[ADC_BUF_SIZE];
/* USER CODE BEGIN 1 */
void adc_channel_set(uint32_t ch)
{
/* 配置对应ADC通道 */
ADC_ChannelConfTypeDef adc_channel;
adc_channel.Channel = ch; /* 设置ADCX对通道ch */
adc_channel.Rank = ADC_REGULAR_RANK_1; /* 设置采样序列 */
adc_channel.SamplingTime = ADC_SAMPLINGTIME_COMMON_1;
HAL_ADC_ConfigChannel( &hadc1, &adc_channel);
}
void get_adc_value(void)
{
uint32_t i,tmp;
for(i = 0;i < ADC_BUF_SIZE;i+=3)
{
tmp = i;
adc_channel_set(ADC_CHANNEL_4);
HAL_ADC_Start(&hadc1);
if(HAL_OK == HAL_ADC_PollForConversion(&hadc1,100))
adc_buf[tmp] =HAL_ADC_GetValue(&hadc1);
tmp++;
adc_channel_set(ADC_CHANNEL_4);
HAL_ADC_Start(&hadc1);
if(HAL_OK == HAL_ADC_PollForConversion(&hadc1,100))
adc_buf[tmp] =HAL_ADC_GetValue(&hadc1);
tmp++;
adc_channel_set(ADC_CHANNEL_7);
HAL_ADC_Start(&hadc1);
if(HAL_OK == HAL_ADC_PollForConversion(&hadc1,100))
adc_buf[tmp] =HAL_ADC_GetValue(&hadc1);
}
}
// 在main函数中的while去调用
int mian()
{
// ADC初始化函数
// .......
while(1){
get_adc_value();
// 其他逻辑操作
}
}
2、ADC理论部分
(待定)
参考1:STM32CubeMX | HAL库的ADC多通道数据采集(轮训、DMA、DMA+TIM)、读取内部传感器温度_tim+adc+dma采集-CSDN博客
参考2:ADC采样时间、采样周期、采样频率计算方法 – SymPny – 博客园
参考3:
STM32: ADC采样频率及相应时间的确定_adc采样频率计算公式-CSDN博客
作者:All right 1