打印
[应用相关]

STM32单片机双ADC同步转换和DMA传输数据到Buffer并通过串口发送数据的方法

[复制链接]
32|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
磨砂|  楼主 | 2025-1-13 07:42 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
      作者将在本文中通过实际例演示STM32单片机双ADC同步转换机制、通过DMA把采集到的数据发送到DMA Buffer中去,最后在ADC传输完成事件中断回调函数HAL_ADC_ConvCpltCallback()里,通过串口把转换的工程值发送到串口助手。

        可以参考本文作者的其他文章: 细说STM32单片机ADC规则组多通道输入和DMA传输数据到Buffer并通过串口发送数据的方法-CSDN博客  https://wenchm.blog.csdn.net/article/details/144060957

一、 工程配置
1、工程描述
        开发板的底板上有2个可调电位器的模拟信号输入到PA0和PA1引脚。

        使用ADC1和ADC2同步采集两个通道的信号,双重ADC同步采集时,不能采集同一个通道,所以使用ADC1_IN1采集可调电位器的模拟信号输入到PA0,使用ADC2_IN2采集另一个可调电位器的模拟信号输入到PA1,多重ADC模式只能采用DMA方式传输数据。

2、 时钟、DEBUG、Timer3、USART2
        同参考文章。

3、 NVIC
        只需开启DMA1  Channel1 global interupt。抢占式优先级设置为1。

4、 ADC1
(1) ADC1 Mode
  ADC1_IN1,single_ended;
(2) ADCs_Common_Settings组
Mode:选择Dual regular simultaneous mode only;
DMAAccess Mode:DMA访问模式,选择DMA access mode Enabled;
Delay between 2 sampling phases:1;两次采样之间的间隔。这个参数用于交替模式时,设置交替采样的间隔时间,如1 Cycles表示1个ADCCLK周期。本例是同步模式,因此此参数无影响。
(3)ADC_Settings组
Clock Prescaler:选择同步时钟4分频;
Resolution:12位;
Data Alignment:右对齐(Right alignment);
Scan Conversion Mode:Disabled;因为只有一个通道,所以参数Scan Conversion Mode(扫描转换模式)设置为Disabled。
End of Conversion Selection:end of single conversion;
Continuous Conversion Mode:Enabled;
Discontinuous Conversion Mode:Disabled;
DMA Continuous Requests:Enabled;多重ADC只能使用DMA方式传输数据,所以参数DMA Continuous Requests(DMA连续请求)设置为Enabled。
(4)ADC_Regular_ConversionMode
Enable Regular Conversions:Enabled;
Number of Conversion:1;
External Trigger Conversion Source:Timer 3 Trigger out envent;用于设置启动ADC转换的外部触发信号源,列表中列出了所有可选的信号源,是一些定时器的Trigger Out event或Capture Compare event,这里选择Timer 3 Trigger Out event,也就是定时器TIM3的TRGO信号。
External Trigger Conversion Edge:上跳沿;用于设置触发转换的跳变沿,可选上跳沿、下跳沿或双边都触发。这里选择上跳沿,因为TRGO是一个短时正脉冲信号。
Rank1:Channel1,采样时间(Sampling Time)=24.5,无偏移;
5、 ADC2
(1) ADC2 Mode
  ADC2_IN2,single_ended;
(2) ADCs_Common_Settings组
Mode:选择Dual regular simultaneous mode only,也就是ADC1和ADC2规则同步转换模式。
DMAAccess Mode:DMA访问模式,选择DMA access mode Enabled;
Delay between 2 sampling phases:1;
(3)ADC_Settings组
Clock Prescaler:选择同步时钟4分频;
Resolution:12位;
Data Alignment:右对齐(Right alignment);
Scan Conversion Mode:Disabled;
End of Conversion Selection:end of single conversion;
Continuous Conversion Mode:Enabled;
Discontinuous Conversion Mode:Disabled;
DMA Continuous Requests:Enabled;
(4)ADC_Regular_ConversionMode
Enable Regular Conversions:Enabled;
Number of Conversion:1;
Rank1:Channel2,采样时间(Sampling Time)=24.5,无偏移;
       ADC2的参数(如时钟分频系数、分辨率、数据采样时间等)都应该与ADC1保持一致,以保证两个ADC能同步采集。ADC2的设置里没有触发源选项,在双ADC同步模式下,ADC2由ADC1的触发源触发。

6、DMA
        ADC1、ADC2各只有一个DMA请求,为ADC1的DMA请求配置DMA1 Channel1,为ADC2的DMA请求配置DMA1 Channel2,设置DMA传输属性参数,DMA传输方向自动设置为Peripheral To Memory(外设到存储器)在DMA Request Settings组中将Mode(工作模式)设置为Circular(循环模式),将外设和存储器的数据宽度都设置为Word,因为ADC转换结果数据寄存器是32位的。存储器设置为地址自增加。

(1)DMA Settings
为ADC1选择DMA1 Channel1,为ADC2选择DMA1 Channel2外设到内存,优先级中等;
Mode:Circular;
Data Width:Word;
(2) 使用DMA时是否开启外设的全局中断
        在使用ADC1的DMA方式传输时,即使不开启ADC1的全局中断,DMA传输功能也能正常工作,所以在NVIC设置部分关闭ADC1的全局中断。

(3)读最新出版的书籍,使用最新版本的软件
        在《STM32 Cube高效开发教程(基础篇)》 一书中,书中的目标MCU是STM32F407,书中的观点是:在多ADC同步DMA采集时,ADC2不得设置DMA模式,把DMA相关的设置全关掉,但IDE生成的驱动里,却要手动修改:

hade2.Init.DMAContinuousRequests=ENABLE;//初始代码是DISABLE,将其修改为ENABLE
         书中认为是CubeMX的bug,每次修改驱动重新生成代码时,必须再改回来。作者无法确认那本书当时创作时或当时的IDE是否有BUG,但是作者按照其想法配置了工程,生成并下载了代码,程序是不工作的。把ADC2也设置为开启DMA,则程序正常工作了。

        所以,给看到本文章的网友建议:读最新出版的书籍,使用最新版本的软件。

7、Project Manager Code Generater
        ☑Generate peripheral initialization as a pair of'c/h'fles per peripheral

二、软件设计
1、main.c
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN PV */
#define BATCH_DATA_LEN 1                                // Dual ADC captures 32-bit data stored at once
uint32_t dmaDataBuffer[BATCH_DATA_LEN];        // DMA Buffer
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
HAL_ADCEx_MultiModeStart_DMA(&hadc1, dmaDataBuffer, BATCH_DATA_LEN);        // Start ADC1
HAL_ADCEx_MultiModeStart_DMA(&hadc2, dmaDataBuffer, BATCH_DATA_LEN);        // Start ADC2
HAL_TIM_Base_Start(&htim3);        // Start TIM3
/* USER CODE END 2 */
/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
        uint32_t Volt;
        uint32_t adcValue=dmaDataBuffer[0];                        // Data for ADC2 and ADC1

        uint32_t ADC1_val = adcValue & 0x0000FFFF;        // The lower 16 bits are the data of ADC1
        Volt=3300*ADC1_val;
        Volt=Volt>>12;
        printf("ADC1 = %ld\r\n",Volt );

        uint32_t ADC2_val = adcValue & 0xFFFF0000;        // The higher 16 bits are the data of ADC2
        ADC2_val =  ADC2_val>>16;
        Volt=3300*ADC2_val;        // mV unit
        Volt=Volt>>12;                // divided by 2^12
        printf("ADC2 = %ld\r\n",Volt );
}

//串口打印
int __io_putchar(int ch)
{
        HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 0xFFFF);
        return ch;
}
/* USER CODE END 4 */

        上述代码定义了一个uint32_t类型的全局数组dmaDataBuffer用作DMA缓冲区,只有一个元素。因为在双ADC同步模式下,MCU自动将ADC1和ADC2一次转换的数据组合成一个32位数,高16位是ADC2的数据,低16位是ADC1的数据。

        main()函数使用函数HAL_ADCEx_MultiModeStart_DMA()以多重模式DMA传输方式启动了ADC1和ADC2,且都使用缓冲区dmaDataBuffer。

        重写了DMA流传输完成事件中断关联的回调函数HAL_ADC_ConvCpltCallback()。因为dmaDataBuffer的长度为1,所以完成一次转换就会调用一次这个回调函数。dmaDataBuffer[0]包含ADC1和ADC2一次转换的数据,高16位是ADC2的数据,低16位是ADC1的数据。这个回调函数实现的功能就是将ADC1和ADC2的转换结果数据读取出来,计算为mV后在串口助手上显示。

三、下载运行
        构建项目后,下载到开发板并运行测试,可以发现串口助手上每隔约500ms刷新显示一次ADC1和ADC2采集的值。调节开发板上的可调电阻,显示值跟随变化。



————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/wenchm/article/details/144082966

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

103

主题

4186

帖子

2

粉丝