希望采用stm32F1采集6路ADC,用来DMA把数据采集到数组中,但是结果不正确,代码如下:
u16 adc_original_data[6] = {0};
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );//使能端口A时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE );//使能ADC1通道时钟
//初始化DMA
DMA_DeInit(DMA1_Channel1);//将DMA的通道1寄存器重设为缺省值
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)( ADC1_BASE+0x4c);//DMA外设ADC基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)adc_original_data;//DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,从外设读到内存
DMA_InitStructure.DMA_BufferSize = 6;//总接收发数据长度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//数据宽度为16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;// 当使用一个DMA通道时,优先级设置不影响
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//DMA通道x没有设置为内存到内存传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
// 使能 DMA 通道
DMA_Cmd(DMA1_Channel1 , ENABLE);
//PA1 作为模拟通道输入引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOA, &GPIO_InitStructure);
ADC_DeInit(ADC1);//复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
RCC_ADCCLKConfig(RCC_PCLK2_Div8);//设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE;//模数转换工作在多通道模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//模数转换工作在连续转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 6;//顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure);//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
// 配置ADC 通道的转换顺序和采样时间
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 4, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 5, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 6, ADC_SampleTime_239Cycles5);
ADC_Cmd(ADC1, ENABLE);//使能指定的ADC1
ADC_DMACmd(ADC1, ENABLE);//使能ADC DMA 请求
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使用软件触发ADC转换
}
给一个电位器两端接的3.3V和GND,用万用表测量中间电位0.9V,把通道一接在电位器中间那个脚上
用串口把结果打印在电脑上明显不对
这是main里打印的代码:
while(1)
{
for(i=0;i<=5;i++){
voltage[i] = (float)(adc_original_data[i]*(3.3/4096));
printf("通道%d的值 = %f \r\n",i+1,voltage[i]);
}
printf("\r\n");
printf("\r\n");
printf("\r\n");
delay_ms(10);
麻烦看看代码哪不对,感谢
不用DMA的功能,从ADC的寄存器里读取ADC的值是否正确?
根据问题描述和参考资料,可能存在以下几个可能导致ADC采集结果异常的问题:
参考电压问题:检查VDDA和VDD引脚是否正确连接,以及VREF+引脚是否大于2.5V,确保参考电压设置正确。
ADC通道转换模式问题:检查代码中是否正确选择了合适的ADC转换模式。根据参考资料中的说明,可以尝试使用单通道连续转换模式(CONT=1, SCAN=0)或多通道连续扫描转换模式(CONT=1, SCAN=1)来结合DMA进行数据传输。
DMA设置问题:确认DMA设置正确,包括DMA通道和传输方向,以确保数据能够正确传输到数组中。
ADC精度设置问题:检查是否正确配置了ADC的精度,确保精度设置与所需的采样精度一致。
电源电压问题:检查芯片的电源电压是否稳定,如果电源电压波动较大,可能会对ADC采集结果产生影响。
根据以上可能的问题,可以逐步排查,修改代码和配置,以解决ADC采集结果异常的问题。以下是一种可能的解决方案示例:
#include "stm32f1xx_hal.h"
// 定义ADC数据缓冲区
#define ADC_BUFFER_SIZE 6
uint16_t adcBuffer[ADC_BUFFER_SIZE];
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
// 初始化ADC和DMA
void ADC_DMA_Init(void)
{
// 使能ADC1时钟
__HAL_RCC_ADC1_CLK_ENABLE();
// 使能DMA1时钟
__HAL_RCC_DMA1_CLK_ENABLE();
// 配置ADC1
hadc1.Instance = ADC1;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE; // 开启扫描模式
hadc1.Init.ContinuousConvMode = ENABLE; // 开启连续转换模式
hadc1.Init.NbrOfConversion = ADC_BUFFER_SIZE; // 设置通道数量
hadc1.Init.DiscontinuousConvMode = DISABLE; // 关闭间断模式
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 手动触发转换
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT; // 数据右对齐
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; // 每次扫描转换结束后触发中断
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
// 配置ADC通道
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = ADC_CHANNEL_1; // 选择通道1
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5; // 设置采样时间
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
// 配置DMA
hdma_adc1.Instance = DMA1_Channel1; // 选择DMA1通道1
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; // 外设数据对齐为HALFWORD(16位)
hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; // 内存数据对齐为HALFWORD(16位)
hdma_adc1.Init.Mode = DMA_CIRCULAR; // 循环模式
hdma_adc1.Init.Priority = DMA_PRIORITY_HIGH; // 设置DMA优先级为高
if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
{
Error_Handler();
}
// 关联ADC和DMA
__HAL_LINKDMA(&hadc1, DMA_Handle, hdma_adc1);
// 使能DMA传输
if (HAL_DMA_Start(&hdma_adc1, (uint32_t)&ADC1->DR, (uint32_t)adcBuffer, ADC_BUFFER_SIZE) != HAL_OK)
{
Error_Handler();
}
// 启动ADC转换
if (HAL_ADC_Start(&hadc1) != HAL_OK)
{
Error_Handler();
}
}
// ADC DMA中断处理函数
void DMA1_Channel1_IRQHandler(void)
{
// 处理DMA中断请求
HAL_DMA_IRQHandler(&hdma_adc1);
}
// ADC转换完成中断回调函数
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
{
// 数据处理代码
// 处理前一半数据
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
// 数据处理代码
// 处理后一半数据
}
int main()
{
HAL_Init();
// 配置系统时钟、GPIO等
ADC_DMA_Init();
while (1)
{
// 等待数据处理完成
}
}
上述代码中,通过使用DMA和中断来实现ADC采集,并将结果保存到数组中。确保配置了正确的参考电压、ADC通道转换模式和相关DMA设置。在ADC转换完成后的中断回调函数中可以添加数据处理的代码,可根据需求将数据打印到串口或其他方式进行处理。根据具体情况,可以在主循环中等待数据处理完成后继续进行其他操作。
以上是一种解决问题的思路和示例代码,具体的解决方案还需要根据具体的硬件和软件环境进行调试和优化。如果还存在其他问题或者有其他细节需要说明,请提供更多的信息进行进一步分析和解答。
单独采集ADC结果正确的,实在是头秃