本帖最后由 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 数据填充到另一个数据缓冲区。
欢迎大家留言讨论
|