Libby@ 发表于 2024-5-8 17:10

32位SAM系列 DMA Link自环,多通道ADC转换,MCC配置,快速配置,少量代码,省时省力

本帖最后由 Libby@ 于 2024-5-9 09:14 编辑

#技术资源#
32位SAM系列 DMA Link自环,多通道ADC转换,MCC配置,快速配置,少量代码,省时省力

       当使用 MCU 自带的 ADC 扫描多个 ADC 通道时,MCU 需要从 ADC 寄存器读取转换值,这会导致 MCU 被占用。如果 MCU 在新的转换完成之前无法读取结果,则新通道的结果会覆盖之前的结果,可能导致数据混乱。为了解决这个问题,可以使用 DMA。当 ADC 有新数据时,DMA 可以直接将数据传输到 SRAM,无需 MCU 或任何干预。这样可以减轻 MCU 的负载。通过访问 SRAM,可以随时处理 ADC 转换值,而且数据是最新的通道转换值,不会导致数据混乱。

      下面以32位SAM系列微控制器SAMD21J18A为例,借助DMA Link自环功能实现多通道ADC转换,并结合Microchip Code Configurator(MCC)进行快速配置。这样可以最大限度地减少编写代码的工作量。Microchip Code Configurator(MCC)是一个强大的工具,提供了一个直观的界面,可以通过几个简单的步骤快速配置项目。

(1) 在MPLAB X IDE新建工程



(2) 选择芯片型号



(3) 选择编译器



(4) 点击完成后,自动打开MCC, Clock默认已配置,可根据需要更改。



(5) 配置ADC,选择要使用的ADC通道,并设置采样率、参考电压等参数,本次 ADC 对两个通道进行采样,将对¼ VDDCORE(1.2V*¼)和¼ VDD(3.3*¼)进行连续采样,Number of inputs to scan 设置为1,但实际是2个通道,如果是一个通道,就设置成0。从¼ Scaled I/O Supply开始,设置成连续转换Free Run。



(6) 配置DMA,首先启用Link 功能Use Linked list Mode,ADC_RESRDY作为DMA触发信号,一旦ADC转换准备好,数据直接存储到SRAM中。设置源和目标地址传输模式以及传输数据大小等参数。ADC是12位的,所以设置Beat Size 为16-bit。



(7) 配置 USART 进行调试打印,我用了SAMD21Xplianed pro板子,SERCOM3作为串口输出,设置波特率及TX和RX。MCC自带工具STDIO,直连到SERCOM3。



(8) 配置完成后,单击“Generate”按钮,MCC可以生成相应的初始化代码,生成的代码可以直接集成到项目中,极大地减少了编写代码的工作量。


(9) 添加代码,可查看生成的 ADC、DMA 和其他外设的初始化代,并且复制相关函数到main.c中。ADC转换结果的值到SRAM,只要加4个函数。

//读取DMA图形化配置内容
ADCtoSRAM=DMAC_ChannelSettingsGet(DMAC_CHANNEL_0);

//建立DMA自环,配置DMA 源地址(SRCADDR)、目标地址(DSTADDR)和下一个要执行的地址(DESCADDDR),并且下个地址指向自己。
    DMAC_LinkedListDescriptorSetup(&(DMA_Descriptor_ADCtoSRAM), ADCtoSRAM,(constvoid*) &(ADC_REGS->ADC_RESULT), (constvoid*) Destination_adc,
            sizeof (Destination_adc), &(DMA_Descriptor_ADCtoSRAM));

//启动DMA传输
    DMAC_ChannelLinkedListTransfer(DMAC_CHANNEL_0, &(DMA_Descriptor_ADCtoSRAM));
//ADC使能
    ADC_Enable();

以下是main.c:
/*******************************************************************************
Main Source File

Company:
    Microchip Technology Inc.

File Name:
    main.c

Summary:
    This file contains the "main" function for a project.

Description:
    This file contains the "main" function for a project.The
    "main" function calls the "SYS_Initialize" function to initialize the state
    machines of all modules in the system
*******************************************************************************/

// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************

#include <stddef.h>                     // Defines NULL
#include <stdbool.h>                  // Defines true
#include <stdlib.h>                     // Defines EXIT_FAILURE
#include "definitions.h"                // SYS function prototypes

#define ADC_VREF      (1.65f)//参考电压
#define DATA_LENGTH    2   

static uint16_t Destination_adc;

volatile uint8_t DMA_IsTransferCompleted = 0;

dmac_descriptor_registers_t DMA_Descriptor_ADCtoSRAM;

DMAC_CHANNEL_CONFIG ADCtoSRAM;

float input_voltage;
// *****************************************************************************
// *****************************************************************************
// Section: Main Entry Point
// *****************************************************************************
// *****************************************************************************
//DMA中断

void DMA_TransferCompleted(DMAC_TRANSFER_EVENT event, uintptr_t contextHandle) {
    if (event == DMAC_TRANSFER_EVENT_COMPLETE) {
      DMA_IsTransferCompleted = 1;
    }
}

int main(void) {
    /* Initialize all modules */
    SYS_Initialize(NULL);

    DMAC_ChannelCallbackRegister(DMAC_CHANNEL_0, DMA_TransferCompleted, (uintptr_t) NULL); //DMA中断注册函数
    ADCtoSRAM = DMAC_ChannelSettingsGet(DMAC_CHANNEL_0); //读取DMA图形化配置内容
    //配置DMA 源地址(SRCADDR)、目标地址(DSTADDR)和下一个要执行的描述符的地址(DESCADDDR)
    //DMA自环,下一个地址指向自己,还是通道0
    DMAC_LinkedListDescriptorSetup(&(DMA_Descriptor_ADCtoSRAM), ADCtoSRAM,
            (const void*) &(ADC_REGS->ADC_RESULT),
            (const void*) Destination_adc,
            sizeof (Destination_adc), &(DMA_Descriptor_ADCtoSRAM));

    DMAC_ChannelLinkedListTransfer(DMAC_CHANNEL_0, &(DMA_Descriptor_ADCtoSRAM)); //启动DMA传输
    ADC_Enable(); //ADC使能
    while (true) {
      if (DMA_IsTransferCompleted) {
            DMA_IsTransferCompleted = 0;
            //参考电压是VDD/2,12位 ADC,输出到PC终端软件,波特率115200
            printf("\r\n ADC conversion of 2 inputs done \r\n");
            input_voltage = (float) Destination_adc[0] * ADC_VREF / 4095U;
            printf("\r\n : VDDCORE/4: %4d, ADC Input Voltage = %2.4f V \r\n\r\n",
                  Destination_adc[0], input_voltage);

            input_voltage = (float) Destination_adc[1] * ADC_VREF / 4095U;
            printf("\r\n :   VDD/4: %4d, ADC Input Voltage = %2.4f V \r\n\r\n",
                  Destination_adc[1], input_voltage);
      }
    }
    /* Execution should not come here during normal operation */

    return ( EXIT_FAILURE);
}


/*******************************************************************************
End of File
*/









       现在我们将进入应用程序部分,该应用程序不针对处理 ADC 转换的结果。只是将其打印在PC的终端窗口上,如下所示,显示电压VDDCORE/4 和 VDDIO/4。因为是12位ADC采样,参考电压1.65V,一般VDDCORE约1.2V,VDD约3.3V,所以实际监测出1/4的VDDCORE约0.3074V,1/4的VDD约0.8298V。


       本次实践中 ADC 对两个通道进行连续采样,结果将由 DMA传输到SRAM 。当DMA块传输完成后产生中断,需要为此中断注册一个回调函数,在应用程序的主循环中处理,也可以什么都不做。除此之外,启用DMA传输错误中断并注册回调函数,也很有用。

      我们也可以配置两个DMA通道,互相linked,ADC 数据读取到两个数据缓冲区,MCU可以处理其中一个数据缓冲区的内容,同时 DMA 将 ADC 数据填充到另一个数据缓冲区。

欢迎大家留言讨论{:smile:}

598330983 发表于 2024-5-8 22:53

这配置都有点复杂了,看看。

Libby@ 发表于 2024-5-9 09:16

598330983 发表于 2024-5-8 22:53
这配置都有点复杂了,看看。

图形化配置还好吧,欢迎留言

Interested 发表于 2024-5-16 13:42

DMA 真好用
页: [1]
查看完整版本: 32位SAM系列 DMA Link自环,多通道ADC转换,MCC配置,快速配置,少量代码,省时省力