野火电子论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 236|回复: 3

【求助】STM32F103 通过ADC DMA读取电压数据总是错位

[复制链接]
发表于 2023-12-29 14:25:44 | 显示全部楼层 |阅读模式
26火花
本帖最后由 pengtian 于 2023-12-29 14:29 编辑

单片机型号:STM32F103C8T6



自己搞的智能小车项目上,需要监控电压和电流,其中的PA5、PA6、PA7分别负责读取基准电压(ref3030输出)、采样电阻电压(INA199-测电流用)、总电源电压分压后(分压后给到adc引脚)

所以就准备通过ADC + DMA的方式读取这三路adc数据


通过下面的代码读取adc数据,但问题是数组通过printf输出后,打印的数据除了第一次正确, 后续都是错误的

main.c  : main.c中初始化电源组件Battery_Manager_init,电源组件中会负责adc初始化,并通过freertos的task定时读取
  1. int main(void) {
  2.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

  3. /** ----- 通用组件初始化 ---- **/
  4.     // 延时初始化
  5.     Delay_Init(72);
  6.     // 串口通信初始化
  7.     Messenger_init();

  8. /** ----- 功能模块初始化 ---- **/
  9.     //pca9685初始化
  10.     PCA9685_init();
  11.     //等待pca9685初始化完成
  12.     Delay_ms(10);

  13.     //电源模块
  14.     Battery_Manager_init();

  15. //不相关模块初始化

  16.     xTaskCreate(startTask, "START_TASK",
  17.                 300, NULL, 1, &startTaskHandle);    /*创建起始任务*/
  18.     vTaskStartScheduler();    /*开启任务调度*/
  19. }
复制代码
void startTask(void *arg) {
    // 进入临界区
    taskENTER_CRITICAL();
    // 打印剩余堆栈大小
    printf("Free heap before starting: %d bytes\n", xPortGetFreeHeapSize());

    xTaskCreate(Battery_DMA_DATA_Task, "Battery_DMA_DATA_Task",
//。。。。不相关模块task创建

    // 打印剩余堆栈大小
    printf("Free heap after starting: %d bytes\n", xPortGetFreeHeapSize());
    // 删除开始任务
    vTaskDelete(startTaskHandle);
    // 退出临界区
    taskEXIT_CRITICAL();
}


bap_adc.c中,初始化具体adc和DMA

  1. #include "bsp_adc.h"

  2. #include "battery.h"

  3. #include "FreeRTOS.h"
  4. #include "task.h"
  5. #include "queue.h"

  6. #include "delay.h"

  7. //adc当前数据缓存
  8. __IO uint16_t ADC_ConvertedValue[ADC_Channel_Num]={0, 0, 0};
  9. float BAT_ConvertedValueLocal[ADC_Channel_Num];

  10. /**
  11.   * [url=home.php?mod=space&uid=41770]@brief[/url]  ADC GPIO 初始化
  12.   * @param  无
  13.   * @retval 无
  14.   */
  15. static void ADCx_GPIO_Config(void) {
  16.     GPIO_InitTypeDef GPIO_InitStructure;

  17.     // 打开 ADC IO端口时钟
  18.     ADC_GPIO_APBxClock_FUN(ADC_GPIO_CLK, ENABLE);

  19.     // 配置 ADC IO 引脚模式
  20.     GPIO_InitStructure.GPIO_Pin = ADC_PIN1 | ADC_PIN2 | ADC_PIN3;
  21.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;

  22.     // 初始化 ADC IO
  23.     GPIO_Init(ADC_PORT, &GPIO_InitStructure);
  24. }

  25. /**
  26.   * @brief  配置ADC工作模式
  27.   * @param  无
  28.   * @retval 无
  29.   */
  30. static void ADCx_Mode_Config(void) {
  31.     DMA_InitTypeDef DMA_InitStructure;
  32.     ADC_InitTypeDef ADC_InitStructure;

  33.     // 打开DMA时钟
  34.     RCC_AHBPeriphClockCmd(ADC_DMA_CLK, ENABLE);
  35.     // 打开ADC时钟
  36.     ADC_APBxClock_FUN(ADC_CLK, ENABLE);
  37.     // --------------------------------------------------

  38.     // 复位DMA控制器
  39.     DMA_DeInit(ADC_DMA_CHANNEL);
  40.     // 配置 DMA 初始化结构体
  41.     // 外设基址为:ADC 数据寄存器地址 //0x4001244C
  42.     DMA_InitStructure.DMA_PeripheralBaseAddr = (u32) (&(ADC_x->DR));
  43.     // 存储器地址
  44.     DMA_InitStructure.DMA_MemoryBaseAddr = (u32) ADC_ConvertedValue;
  45.     // 数据源来自外设
  46.     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  47.     // 缓冲区大小,应该等于数据目的地的大小
  48.      DMA_InitStructure.DMA_BufferSize = ADC_Channel_Num;
  49.     // 外设寄存器只有一个,地址不用递增
  50.     DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  51.     // 存储器地址递增
  52.     DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  53.     // 外设数据大小为半字,即两个字节
  54.     DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  55.     // 内存数据大小也为半字,跟外设数据大小相同
  56.     DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  57.     // 单次传输模式DMA_Mode_Normal
  58.     DMA_InitStructure.DMA_Mode = DMA_Mode_Circular/* DMA_Mode_Circular循环传输模式*/;
  59.     // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响
  60.     DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//DMA_Priority_High;
  61.     // 禁止存储器到存储器模式,因为是从外设到存储器
  62.     DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  63.     // 初始化DMA
  64.     DMA_Init(ADC_DMA_CHANNEL, &DMA_InitStructure);
  65.     // 使能 DMA 通道
  66.     DMA_Cmd(ADC_DMA_CHANNEL, ENABLE);
  67.     // --------------------------------------------------

  68.     // ADC 模式配置
  69.     // 只使用一个ADC,属于单模式
  70.     ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  71.     // 扫描模式
  72.     ADC_InitStructure.ADC_ScanConvMode = ENABLE;
  73.     // 连续转换模式
  74.     ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
  75.     // 不用外部触发转换,软件开启即可
  76.     ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  77.     // 转换结果右对齐
  78.     ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  79.     // 转换通道个数
  80.     ADC_InitStructure.ADC_NbrOfChannel = ADC_Channel_Num;
  81.     // 初始化ADC
  82.     ADC_Init(ADC_x, &ADC_InitStructure);
  83.     // 配置ADC时钟CLK2的6分频,即12MHz
  84.     RCC_ADCCLKConfig(RCC_PCLK2_Div8);

  85.     // 配置ADC 通道的转换顺序和采样时间
  86.     ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL1, 1, ADC_SampleTime_55Cycles5);
  87.     ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL2, 2, ADC_SampleTime_55Cycles5);
  88.     ADC_RegularChannelConfig(ADC_x, ADC_CHANNEL3, 3, ADC_SampleTime_55Cycles5);

  89.     // 使能ADC DMA 请求
  90.     ADC_DMACmd(ADC_x, ENABLE);
  91.     // 开启ADC ,并开始转换
  92.     ADC_Cmd(ADC_x, ENABLE);
  93.     // 初始化ADC 校准寄存器
  94.     ADC_ResetCalibration(ADC_x);
  95.     // 等待校准寄存器初始化完成
  96.     while (ADC_GetResetCalibrationStatus(ADC_x));
  97.     // ADC开始校准
  98.     ADC_StartCalibration(ADC_x);
  99.     // 等待校准完成
  100.     while (ADC_GetCalibrationStatus(ADC_x));
  101.     // --------------------------------------------------
  102.    // 由于没有采用外部触发,所以使用软件触发ADC转换
  103.    ADC_SoftwareStartConvCmd(ADC_x, ENABLE);
  104. }

  105. /**
  106.   * @brief  ADC初始化
  107.   * @param  无
  108.   * @retval 无
  109.   */
  110. void ADCx_Init(void) {
  111.     ADCx_GPIO_Config();
  112.     ADCx_Mode_Config();
  113. }

  114. /*********************************************END OF FILE**********************/
复制代码


在battery.c中负责循环读取adc中的数据,并经过计算转化为电压数据。 目前是还在读取的阶段,数据就错乱了

  1. void Battery_DMA_DATA_Task(void *param) {
  2.     DMAData_t dmaData;
  3.     while (1) {
  4.         dmaData.base = ADC_ConvertedValue[0];
  5.         dmaData.cur = ADC_ConvertedValue[1];
  6.         dmaData.vol = ADC_ConvertedValue[2];
  7.         //_handleBatteryAdcData(ADC_ConvertedValue[0], ADC_ConvertedValue[1], ADC_ConvertedValue[2]);
  8.         printf("baseADCValue = %d, curADCValue=%d, batADCValue=%d\n", dmaData.base, dmaData.cur, dmaData.vol);
  9.         vTaskDelay(500);
  10.     }
  11. }
复制代码


对应的串口输出:500ms间隔读取一次数据并输出,可以看到除了第一次正确, 其他的都是错误的


14:23:30(266): Free heap before starting: 13296 bytes
Free heap after starting: 9824 bytes
baseADCValue = 3867, curADCValue=983, batADCValue=3691

14:23:30(786): baseADCValue = 3895, curADCValue=4079, batADCValue=3752

14:23:31(295): baseADCValue = 3647, curADCValue=934, batADCValue=4085

14:23:31(800): baseADCValue = 3924, curADCValue=3796, batADCValue=3680

14:23:32(306): baseADCValue = 936, curADCValue=3936, batADCValue=3651

14:23:32(799): baseADCValue = 3867, curADCValue=3930, batADCValue=908

14:23:33(305): baseADCValue = 4050, curADCValue=3994, batADCValue=1034

14:23:33(813): baseADCValue = 1009, curADCValue=951, batADCValue=3703

14:23:34(320): baseADCValue = 3766, curADCValue=3759, batADCValue=3904

14:23:34(825): baseADCValue = 914, curADCValue=4062, batADCValue=3752

14:23:35(333): baseADCValue = 3935, curADCValue=1002, batADCValue=3792

14:23:35(838): baseADCValue = 957, curADCValue=3962, batADCValue=3936

14:23:36(345): baseADCValue = 3872, curADCValue=972, batADCValue=1033


请大佬们帮忙看下为什么会出现这种数据错乱的情况,DMA和ADC的配置我都仔细核对过了,煎熬了好几天了,实在没办法,周围也没人能指导下,只有我自己玩这个

回复

使用道具 举报

 楼主| 发表于 2024-1-2 12:13:27 来自手机 | 显示全部楼层
有人吗?
回复

使用道具 举报

 楼主| 发表于 2024-1-2 13:49:36 | 显示全部楼层
Freertos引起了adc采样的数据错乱, 我将freertos关闭,直接在main的while循环中打印就没有问题,有谁知道是为什么么?
回复

使用道具 举报

 楼主| 发表于 2024-1-2 13:51:16 | 显示全部楼层
int main(void) {
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

/** ----- 通用组件初始化 ---- **/
    // 延时初始化
    Delay_Init(72);
    // 串口通信初始化
    Messenger_init();

/** ----- 功能模块初始化 ---- **/
    //pca9685初始化
    PCA9685_init();
    //等待pca9685初始化完成
    Delay_ms(10);

    //电源模块
    Battery_Manager_init();

//不相关模块初始化

这样打印没问题
        while (1)
        {        
   
                        BAT_ConvertedValueLocal[0] =(float) ADC_ConvertedValue[0]/4096*3.3;
                        BAT_ConvertedValueLocal[1] =(float) ADC_ConvertedValue[1]/4096*3.3;
                        BAT_ConvertedValueLocal[2] =(float) ADC_ConvertedValue[2]/4096*3.3;
               
                        printf("\r\n CH0 value = %f V \r\n",BAT_ConvertedValueLocal[0]);
                        printf("\r\n CH1 value = %f V \r\n",BAT_ConvertedValueLocal[1]);
                        printf("\r\n CH2 value = %f V \r\n",BAT_ConvertedValueLocal[2]);
               
                        printf("\r\n\r\n");
                        Delay_ms(1000);                 
        }


//    xTaskCreate(startTask, "START_TASK",
//                300, NULL, 1, &startTaskHandle);    /*创建起始任务*/
//    vTaskStartScheduler();    /*开启任务调度*/
}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

联系站长|手机版|野火电子官网|野火淘宝店铺|野火电子论坛 ( 粤ICP备14069197号 ) 大学生ARM嵌入式2群

GMT+8, 2024-5-2 20:47 , Processed in 0.137289 second(s), 23 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表