| 
 
| 本帖最后由 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[DATA_LENGTH];
 
 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 数据填充到另一个数据缓冲区。
 
 欢迎大家留言讨论
  
 | 
 
×本帖子中包含更多资源您需要 登录 才可以下载或查看,没有账号?注册 
  |