当前位置: 首页 > news >正文

FreeRTOS下STM32双缓冲ADC数据采集与处理

目录

1. CubeMX工程配置ADC

ADC配置:

DMA配置:

2. 代码编写

业务逻辑:

具体实现逻辑:

逻辑图

流程图

详细说明

1. 系统初始化流程

2. ADC转换完成中断处理

3. 线程A (默认任务)处理流程

4. 线程B (数据处理任务)处理流程

5. 关键设计特点

6. 数据流向

7. 错误处理

代码


1. CubeMX工程配置ADC

ADC配置:

  • Clock Prescaler

  • Resolution:几位有效数据(ADC寄存器有16位,这里12表示只有12位有效)

  • Data Alignment:数据对齐方向(Right alignment表示向右对齐,即16位的寄存器,有效数据向右对齐,只有右边的低12位有效,高4位是无效数据),虽然CPU读取时还是按照2字节读取,但是只取出其中12位的有效数据。

  • Scan Conversion Mode 是控制:ADC 是否按照配置的通道序列,对多个通道进行依次转换。(我们本次就一个通道,所以未开启)

    • 如果是 Enable,ADC 将按照在规则通道(Regular Channels)中配置的通道序列,依次对多个通道进行转换。 每次触发(无论是软件触发还是硬件触发),ADC 都会按照顺序对所有配置的通道进行一次完整的转换序列。适用于需要同时采集多个模拟信号的情况,例如多传感器数据采集、数据监测等。

      • 如果同时启用了 Continuous Conversion Mode(连续转换模式), ADC 会在完成一次完整的通道序列转换后,立即开始下一次序列的转换,形成一个连续的循环。如果没有启用 Continuous Conversion Mode(连续转换模式),ADC 在完成一次通道序列转换后停止,等待下一个触发事件。

      • 如果同时启用了 Discontinuous Conversion Mode(非连续转换模式),会将通道序列分成若干组,每次触发事件只转换一组通道。

    • 如果是 Disable,ADC 仅对配置的一个通道进行转换,没有通道序列的概念。每次触发只转换一个通道,简单高效。适用于只需要采集一个模拟信号的简单应用,例如单一传感器的读取。

  • Continuous Conversion Mode(连续转换模式)是控制:是否持续的对某一个通道不停地转换,你会在 DR 里面一直看到数据更新,EOC 标志位一直会产生。于是你可以通过轮询或者中断的方式一直来取 ADC 的数据。

  • Discontinuous Conversion Mode(非连续转换模式)是控制:是否 ADC 将进入 非连续转换模式(Discontinuous Conversion Mode)。

    • 在非连续转换模式下(Enable),ADC 会将配置的通道序列分成若干个小组,每个小组的大小由 Discontinuous Number(非连续数目)参数确定,范围是 1 到 8。(应确保 Scan Conversion Mode 也是 Enable 的)

      • ADC 会在每个触发事件(比如软件或硬件触发)下,仅转换一个小组的通道,然后停止,等待下一个触发事件。每次非连续转换都需要新的触发事件。这种模式适用于需要在多个触发事件下分批次采样的情况。

        • 例如,在实时控制系统中,可能希望在每个控制周期内只采样部分通道(而不是 全部通道),以减少 CPU 负担。比如说,如果你配置了 6 个通道的序列,且将 Discontinuous Number 设置为 2,那么 ADC 会将这 6 个通道分成 3 组,每组 2 个通道。每次触发事件会启动一组(2 个通道)的转换,需要 3 次触发事件才能完成所有通道的转换。

  • DMA Continuous Requests:

    • enabled 每次ADC转换完了数据都会发出DMA请求,让DMA来搬运数据。

    • Disabled 状态下,即使开启了 Continuous Conversion Mode,不停地对一个通道转换,也只会发出一次DMA请求。

  • End Of Conversion Selection:

    • EOC flag at the end of single channel conversions:每一次转换完成后产生中断

    • EOC flag at the end of all conversions:所有转换完成后再产生中断

DMA配置:

2. 代码编写

业务逻辑:

  • ADC+DMA搬运ADC采集的数据到数据缓冲区buffer1或buffer2,数据搬运完成后,通过ADC转换完成回调函数发送queue1通知线程A控制更换另一个缓冲区作为下次接收的数据缓冲区,同时使用queue2通知线程B去刚搬运好数据的缓冲区中处理ADC转换数据。在线程A和线程B之间创建一个互斥锁,保证这两个线程互斥运行。

具体实现逻辑:

  • 在FreeRTOS下,一开始默认使用ADC+DMA搬运ADC采集的数据到数据缓冲区buffer1,配置ADC转换完成回调函数,在ADC转换完成回调函数中发送queue1给线程A。

  • 线程A阻塞等待receive queue1的数据,接收到数据后,使用queue_peek查看queue2中的数据是否被线程B接收走,如果队列为空,则阻塞等待获取互斥锁,拿到互斥锁后,HAL_ADC_Start_DMA启动ADC转换和DMA搬运到另一个buffer,使用一个全局变量DMA_pointer来标志当前使用的buffer(为0则目前dma搬运的目标buffer是buffer1,为1则目前dma搬运的目标buffer是buffer2),根据DMA_pointer的值来判断发送queue2的值是BUFFER1_READY还是BUFFER2_READY。

  • 线程B使用queue_peek阻塞查看queue2是否有数据,如果queue_peek返回pdTRUE,则阻塞获取互斥锁,然后receive queue2的数据,处理数据,然后释放互斥锁。

逻辑图

流程图

详细说明

1. 系统初始化流程
  1. 分配两个数据缓冲区(buffer1和buffer2)

  2. 创建两个队列:

    1. queue1 (ADC_Conv_Cplt_Notice_xQueue): 用于ADC转换完成通知

    2. queue2 (ADC_data_need_cal_Notice_xQueue): 用于数据就绪通知

  3. 创建互斥锁(ADC_Mutex)确保线程间同步

  4. 初始化DMA_pointer为0,表示当前使用buffer1

  5. 启动ADC DMA传输,将数据直接搬运到buffer1

    2. ADC转换完成中断处理
    • 当ADC转换完成时,HAL库会调用HAL_ADC_ConvCpltCallback函数

    • 在中断上下文中,向queue1发送通知(DMA_ADC_CPLT_INT)

    • 必要时触发任务切换,确保高优先级任务及时运行

      3. 线程A (默认任务)处理流程
      1. 阻塞等待queue1的通知

      2. 收到通知后,检查queue2是否为空(确保前一次数据已被处理)

      3. 获取互斥锁(防止与线程B同时访问共享资源)

      4. 根据DMA_pointer值切换DMA目标缓冲区:

        1. 如果当前使用buffer1,则切换到buffer2

        2. 如果当前使用buffer2,则切换到buffer1

      5. 通过queue2发送通知,告知线程B哪个缓冲区数据就绪

      6. 释放互斥锁

        4. 线程B (数据处理任务)处理流程
        1. 使用queue_peek检查queue2是否有数据(不移除消息)

        2. 发现数据后,获取互斥锁

        3. 从queue2接收数据,确定哪个缓冲区数据就绪

        4. 处理相应缓冲区中的数据

        5. 释放互斥锁

          5. 关键设计特点
          1. 双缓冲机制: 使用两个缓冲区交替工作,实现数据采集和处理的并行执行

          2. 线程同步: 通过互斥锁确保对共享资源(缓冲区和DMA_pointer)的互斥访问

          3. 通知机制: 使用两个队列实现任务间通信,解耦数据采集和数据处理

          4. 阻塞等待: 任务在等待资源时主动让出CPU,提高系统效率

            6. 数据流向
            ADC -> DMA -> buffer1/buffer2 -> 线程B处理

            数据通过DMA直接搬运到内存缓冲区,减少了CPU干预,提高了系统效率。

            7. 错误处理

            代码中包含了对队列操作、内存分配和互斥锁操作的错误检查,确保系统稳定性。

            这种设计实现了ADC数据采集和处理的流水线操作,充分利用了DMA和双缓冲技术的优势,提高了系统的实时性和效率。

            代码

            /* USER CODE BEGIN Header */
            /********************************************************************************* File Name          : freertos.c* Description        : Code for freertos applications******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
            /* USER CODE END Header *//* Includes ------------------------------------------------------------------*/
            #include "FreeRTOS.h"
            #include "task.h"
            #include "main.h"
            #include "cmsis_os.h"/* Private includes ----------------------------------------------------------*/
            /* USER CODE BEGIN Includes */
            #include <stdlib.h>
            #include <string.h>
            #include "queue.h"
            #include "semphr.h"
            /* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
            /* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
            /* USER CODE BEGIN PD */
            #define BUFFER_SIZE 1uint32_t* buffer1 = NULL;
            uint32_t* buffer2 = NULL;
            /* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
            /* USER CODE BEGIN PM */
            #define DMA_ADC_CPLT_INT    0xA1
            #define BUFFER1_READY       0x01
            #define BUFFER2_READY       0x02
            /* USER CODE END PM *//* Private variables ---------------------------------------------------------*/
            /* USER CODE BEGIN Variables */extern ADC_HandleTypeDef hadc1;         // ADC handle
            extern DMA_HandleTypeDef hdma_adc1;     // ADC's DMA handleQueueHandle_t ADC_Conv_Cplt_Notice_xQueue = NULL;       // ADC_Conv_Cplt_Notice_xQueue
            QueueHandle_t ADC_data_need_cal_Notice_xQueue = NULL;   // ADC data need to calculate notice queue
            uint32_t DMA_pointer = 0;                               // 0: buffer1   1: buffer2osMutexId_t ADC_Mutex = NULL;              // define ADC_Mutex/* USER CODE END Variables */
            /* Definitions for defaultTask */
            osThreadId_t defaultTaskHandle;
            const osThreadAttr_t defaultTask_attributes = {.name = "defaultTask",.stack_size = 128 * 4,.priority = (osPriority_t) osPriorityNormal,
            };/* Private function prototypes -----------------------------------------------*/
            /* USER CODE BEGIN FunctionPrototypes */
            osThreadId_t adc_output_TaskHandle;
            const osThreadAttr_t adc_output_Task_attributes = {.name = "adc_output_Task",.stack_size = 128 * 4,.priority = (osPriority_t)osPriorityNormal,
            };void adc_output_Task(void* argument);/* USER CODE END FunctionPrototypes */void StartDefaultTask(void *argument);void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) *//*** @brief  FreeRTOS initialization* @param  None* @retval None*/
            void MX_FREERTOS_Init(void) {/* USER CODE BEGIN Init *//* USER CODE END Init *//* USER CODE BEGIN RTOS_MUTEX *//* add mutexes, ... *//* USER CODE END RTOS_MUTEX *//* USER CODE BEGIN RTOS_SEMAPHORES *//* add semaphores, ... *//* USER CODE END RTOS_SEMAPHORES *//* USER CODE BEGIN RTOS_TIMERS *//* start timers, add new ones, ... *//* USER CODE END RTOS_TIMERS *//* USER CODE BEGIN RTOS_QUEUES *//* add queues, ... *//* USER CODE END RTOS_QUEUES *//* Create the thread(s) *//* creation of defaultTask */defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);/* USER CODE BEGIN RTOS_THREADS */adc_output_TaskHandle = osThreadNew(adc_output_Task, NULL, &adc_output_Task_attributes);/* add threads, ... *//* USER CODE END RTOS_THREADS *//* USER CODE BEGIN RTOS_EVENTS *//* add events, ... *//* USER CODE END RTOS_EVENTS */}/* USER CODE BEGIN Header_StartDefaultTask */
            /*** @brief  Function implementing the defaultTask thread.* @param  argument: Not used* @retval None*/
            /* USER CODE END Header_StartDefaultTask */
            void StartDefaultTask(void *argument)
            {/* USER CODE BEGIN StartDefaultTask */// 0:buffer1    || 1:buffer2DMA_pointer = 0;  // creat ADC_Conv_Cplt_Notice_xQueueADC_Conv_Cplt_Notice_xQueue = xQueueCreate(10, 4);if (NULL == ADC_Conv_Cplt_Notice_xQueue){printf("ADC_Conv_Cplt_Notice_xQueue creat failed! at [%d] tick\r\n", HAL_GetTick());return;}// creat ADC_data_need_cal_Notice_xQueueADC_data_need_cal_Notice_xQueue = xQueueCreate(10, 4);if (NULL == ADC_data_need_cal_Notice_xQueue){printf("ADC_data_need_cal_Notice_xQueue creat failed! at [%d] tick\r\n", HAL_GetTick());return;}// creat ADC_Mutex in default modeADC_Mutex = xSemaphoreCreateMutex();if (ADC_Mutex == NULL) {printf("ADC_Mutex creat failed! at [%d] tick\r\n",HAL_GetTick());}// creat data receive bufferbuffer1 = (uint32_t*)malloc(sizeof(uint32_t) * BUFFER_SIZE);buffer2 = (uint32_t*)malloc(sizeof(uint32_t) * BUFFER_SIZE);if (NULL == buffer1){printf("buffer1 malloc failed!\r\n");return;}if (NULL == buffer2){printf("buffer2 malloc failed!\r\n");return;}printf("buffer1,buffer2 malloc successfully!\r\n");// set buffer default valuememset(buffer1, 0xFF, (sizeof(uint32_t) * BUFFER_SIZE));memset(buffer2, 0xFF, (sizeof(uint32_t) * BUFFER_SIZE));// Enables ADC DMA request after last transfer (Single-ADC mode) and enables ADC peripheralif (HAL_OK != HAL_ADC_Start_DMA(&hadc1, buffer1, BUFFER_SIZE)){// print error infoprintf("HAL_ADC_Start_DMA call [%d] tick\r\n", HAL_GetTick());}#if 1   // UnitTest Queue send and receive// receive queue dataBaseType_t queue_ret = pdTRUE;uint32_t ADC_Conv_Cplt_Pattern = 0xff;uint32_t ADC_data_need_cal_Pattern = BUFFER1_READY;BaseType_t buffer_is_handled_Pattern = pdTRUE;uint32_t tmp = 0;#endif  // End of UnitTest Queue send and receive/* Infinite loop */for(;;){osDelay(1);printf("default task at [%d] tick\r\n", HAL_GetTick());queue_ret = xQueueReceive(ADC_Conv_Cplt_Notice_xQueue, &ADC_Conv_Cplt_Pattern, portMAX_DELAY);if (pdTRUE != queue_ret){printf("xQueueReceive ADC_Conv_Cplt_Notice_xQueue failed! at [%d] tick\r\n", HAL_GetTick());}// check if the buffer data is calculated by adc_output_Taskwhile (pdTRUE == xQueuePeek(ADC_data_need_cal_Notice_xQueue, &tmp, 0)){// task switchtaskYIELD();}// take ADC_Mutexif (pdTRUE != xSemaphoreTake(ADC_Mutex, portMAX_DELAY)){printf("ADC_Mutex take error! at [%d] tick\r\n", HAL_GetTick());}// change the DMA_receive_buffer and DMA_pointerif (0 == DMA_pointer){// print receive dataprintf("buffer1 data = [%ld] at [%d] tick\r\n", buffer1[0], HAL_GetTick());HAL_ADC_Start_DMA(&hadc1, buffer2, BUFFER_SIZE);    // start ADC, DMA move data to buffer2DMA_pointer = 1;                                    // DMA_pointer point buffer2ADC_data_need_cal_Pattern = BUFFER1_READY;          // buffer1 data ready// Send queue to notice adc_output_Task to cal ADC dataif (pdTRUE != xQueueSend(ADC_data_need_cal_Notice_xQueue, &ADC_data_need_cal_Pattern, portMAX_DELAY)){printf("ADC_data_need_cal_Notice_xQueue QueueSend error! [%d] tick",HAL_GetTick());}}else{// print receive dataprintf("buffer2 data = [%ld] at [%d] tick\r\n", buffer2[0], HAL_GetTick());HAL_ADC_Start_DMA(&hadc1, buffer1, BUFFER_SIZE);    // start ADC, DMA move data to buffer1DMA_pointer = 0;                                    // DMA_pointer point buffer1ADC_data_need_cal_Pattern = BUFFER2_READY;          // buffer2 data ready// Send queue to notice adc_output_Task to cal ADC dataif (pdTRUE != xQueueSend(ADC_data_need_cal_Notice_xQueue, &ADC_data_need_cal_Pattern, portMAX_DELAY)){printf("ADC_data_need_cal_Notice_xQueue QueueSend error! [%d] tick", HAL_GetTick());}}// release ADC_Mutexif (pdTRUE != xSemaphoreGive(ADC_Mutex)){printf("ADC_Mutex give error! at [%d] tick\r\n", HAL_GetTick());}}/* USER CODE END StartDefaultTask */
            }/* Private application code --------------------------------------------------*/
            /* USER CODE BEGIN Application */void adc_output_Task(void* argument)
            {printf("adc_output_thread [%d] tick\r\n", HAL_GetTick());uint32_t ADC_data_need_cal_Pattern = BUFFER1_READY;BaseType_t queue_ret = pdTRUE;uint32_t tmp = 0;for (;;){osDelay(1);// receive queue noticeif (NULL != ADC_data_need_cal_Notice_xQueue){// queue_peek check if there's new data in queueif (pdTRUE != xQueuePeek(ADC_data_need_cal_Notice_xQueue, &tmp, portMAX_DELAY)){printf("xQueuePeek ADC_data_need_cal_Notice_xQueue error! at [%d] tick\r\n", HAL_GetTick());}// take ADC_Mutexif (pdTRUE != xSemaphoreTake(ADC_Mutex, portMAX_DELAY)) {printf("ADC_Mutex take error! at [%d] tick\r\n", HAL_GetTick());}// queue receivequeue_ret = xQueueReceive(ADC_data_need_cal_Notice_xQueue, &ADC_data_need_cal_Pattern, portMAX_DELAY);if (pdTRUE != queue_ret){printf("xQueueReceive ADC_Conv_Cplt_Notice_xQueue failed! at [%d] tick\r\n", HAL_GetTick());}// handle the dataif (BUFFER1_READY == ADC_data_need_cal_Pattern){printf("output thread buffer1 data = [%d] at [%d] tick\r\n", buffer1[0], HAL_GetTick());}else if (BUFFER2_READY == ADC_data_need_cal_Pattern){printf("output thread buffer1 data = [%d] at [%d] tick\r\n", buffer2[0], HAL_GetTick());}// release ADC_Mutexif (pdTRUE != xSemaphoreGive(ADC_Mutex)) {printf("ADC_Mutex release error! at [%d] tick\r\n", HAL_GetTick());}}}
            }/* ADC Conversion Compelet Callback */
            void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
            {/* Prevent unused argument(s) compilation warning */UNUSED(hadc);/* NOTE : This function Should not be modified, when the callback is needed,the HAL_ADC_ConvCpltCallback could be implemented in the user file*/printf("HAL_ADC_ConvCpltCallback at [%d] tick\r\n", HAL_GetTick());// send Queueuint32_t dma_pattern_cplt           = DMA_ADC_CPLT_INT;BaseType_t queue_ret                = pdTRUE;           // return value of xQueueSendFromISRBaseType_t xHigherPriorityTaskWoken = pdFALSE;          queue_ret = xQueueSendFromISR(ADC_Conv_Cplt_Notice_xQueue, &dma_pattern_cplt, &xHigherPriorityTaskWoken);// check the return valueif (pdPASS != queue_ret){printf("xQueueSend ADC_Conv_Cplt_Notice_xQueue failed! at [%d] tick\r\n", HAL_GetTick());}/* xQueueSendFromISR() will set** pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task* to unblock, and the unblocked task has a priority higher than the currently* running task. */if (pdTRUE == xHigherPriorityTaskWoken){// Set a PendSV to request a context switch after this ISR CallbacktaskYIELD();}
            }void HAL_ADC_ErrorCallback(ADC_HandleTypeDef* hadc)
            {/* Prevent unused argument(s) compilation warning */UNUSED(hadc);/* NOTE : This function Should not be modified, when the callback is needed,the HAL_ADC_ErrorCallback could be implemented in the user file*/printf("ADC convert error at [%d] tick\r\n",HAL_GetTick());
            }/* USER CODE END Application */

            http://www.dtcms.com/a/424726.html

            相关文章:

          5. 主页导航网站建设定制网站首页菜单栏
          6. 校园二手用品网站建设的项目章程南宁网站建设推广优化
          7. Docker 容器与镜像
          8. 网站ico如何修改有什么做任务得佣金的网站
          9. 网站建设背景公司营销策划方案案例
          10. 住房和城乡建设部网站主页公司网站建设一条龙
          11. 5. 软件工程基础知识
          12. C++进阶(2)——多态
          13. 营销网站建站开发什么是交换链接
          14. 校园风险管理网站建设方案wordpress使用php版本号
          15. html头部
          16. 韩国网站域名分类常州seo第一人
          17. 建设部网站官网办事厅音乐网站素材
          18. 人工智能-机器学习day4
          19. 网站建设和维护要点重庆cms建站模板
          20. 做外汇需要了解的网站网页制作有什么软件
          21. 做棋牌网站建设云南网站开发公司找哪家
          22. 文案网站策划书织梦网站系统删除
          23. Linux开发工具(一)
          24. 虚拟资源站码支付wordpress合江县住房建设规划局网站
          25. 国企网站建设标准房地产市场发展趋势
          26. 网站 用户粘度无人区高清免费看完整版
          27. 做数据可视化的网站汕头网站开发找哪里
          28. 【MySQL】深分页的性能优化,游标方案和覆盖索引+延迟回表方案
          29. 进入深圳市住房和建设局网站胶州市城乡建设局网站
          30. 进口倾角传感器代理与水平监测传感器厂家的选择指南
          31. 自定义手机网站建设图片分类展示网站源码
          32. 基于python数据挖据的教学监控系统的设计与应用
          33. 网络舆情监测系统:洞察网络舆论的利器
          34. AI 超级智能体全栈项目阶段三:自定义 Advisor 与结构化输出实现以及对话记忆持久化开发