Files
CHJ/user/MCU/lhl_adc_dma.c
2026-03-20 21:19:53 +08:00

396 lines
19 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "../main/SystemInclude.h"
/*********************************************************************************************************************************************/
/*DMA 中断方式 */
/*********************************************************************************************************************************************/
DMA_HandleTypeDef DMA_Handle_ADC0,DMA_Handle_ADC1;
/**------------------------------------------------------------------------
* @brief 初始化ADC的DMA传输单通道循环模式
* @note 配置DMA将ADC转换结果自动搬运到指定内存缓冲区使用循环模式
* 并使能DMA主中断。适用于连续采样场景。
* @param ADCx: ADC实例 (ADC_0 / ADC_1)
* @param dest_addr: 目标内存缓冲区地址(需保持有效)
* @param dest_count: 缓冲区长度(即重复传输次数,循环模式中每次传输后索引循环)
* @example DMA_ADC_Init(ADC_0, (u32*)adc_buffer, 32);
**/
void DMA_ADC_Init(ADC_ID_t ADCx ,u32 *dest_addr , u8 dest_count)
{
DMA_HandleTypeDef DMA_Handle_ADC;
//DMA_Handle_ADC.Channel = DMA_CHANNEL_ADC0;
DMA_Handle_ADC.Mode = DMA_DIRECT_MODE; // 直接 Mode
DMA_Handle_ADC.Request = DMA_HARDWARE_REQUEST;
DMA_Handle_ADC.Init.Direction = DMA_PERIPH_TO_MEMORY; // 外设->内存
//DMA_Handle_ADC.Init.Src_Address = (uint32_t)&(MainADC->ADC_DATA_0);
DMA_Handle_ADC.Init.Dest_Address = (uint32_t)dest_addr;
DMA_Handle_ADC.Init.Data_Width = DMA_DATA_WIDTH_4B;
DMA_Handle_ADC.Init.Data_Size = 1;
DMA_Handle_ADC.Init.Repetition = dest_count;
DMA_Handle_ADC.Init.Trans_Mode = DMA_CYCLIC_TRANSMISSION; // 循环模式
if(ADCx == ADC_0)
{
//连接到DMA
LHL_ADC_DMACmd(ADC_0, ENABLE);
DMA_DMAMUX_CFG(DMA_CHANNEL_ADC_0 ,REQUEST_SOURCE_ADC0);
DMA_Handle_ADC0 = DMA_Handle_ADC;
DMA_Handle_ADC0.Channel = DMA_CHANNEL_ADC_0;
DMA_Handle_ADC0.Init.Src_Address = (uint32_t)&(MainADC->ADC_DATA_0);
if (LHL_DMA_Init(&DMA_Handle_ADC0) != LHL_OK)
{
while(1);
}
LHL_DMA_ITConfig(&DMA_Handle_ADC0, DMA_IT_MAJOR, ENABLE);// 主中断
// LHL_DMA_Start(&DMA_Handle_ADC0);
}
else if(ADCx == ADC_1)
{
//连接到DMA
LHL_ADC_DMACmd(ADC_1, ENABLE);
DMA_DMAMUX_CFG(DMA_CHANNEL_ADC_1 ,REQUEST_SOURCE_ADC1);
DMA_Handle_ADC1 = DMA_Handle_ADC;
DMA_Handle_ADC1.Channel = DMA_CHANNEL_ADC_1;
DMA_Handle_ADC1.Init.Src_Address = (uint32_t)&(MainADC->ADC_DATA_1);
if (LHL_DMA_Init(&DMA_Handle_ADC1) != LHL_OK)
{
while(1);
}
LHL_DMA_ITConfig(&DMA_Handle_ADC1, DMA_IT_MAJOR, ENABLE);// 主中断
// LHL_DMA_Start(&DMA_Handle_ADC1);
}
}
/**------------------------------------------------------------------------
* @brief 启动ADC的DMA传输
* @note 启动DMA通道并同时启动ADC转换软件触发模式下立即开始
* @param ADCx: ADC实例 (ADC_0 / ADC_1)
* @example DMA_StartADC(ADC_0);
**/
void DMA_StartADC(ADC_ID_t ADCx)
{
if(ADCx == ADC_0) { LHL_DMA_Start(&DMA_Handle_ADC0); LHL_ADC_Start(ADC_0); }
else { LHL_DMA_Start(&DMA_Handle_ADC1); LHL_ADC_Start(ADC_1); }
}
/**------------------------------------------------------------------------
* @brief 停止ADC的DMA传输
* @param ADCx: ADC实例 (ADC_0 / ADC_1)
* @example DMA_StopADC(ADC_0);
**/
void DMA_StopADC(ADC_ID_t ADCx)
{
if(ADCx == ADC_0) { LHL_DMA_Stop(&DMA_Handle_ADC0); LHL_ADC_Stop(ADC_0); }
else { LHL_DMA_Stop(&DMA_Handle_ADC1); LHL_ADC_Stop(ADC_1); }
}
/*********************************************************************************************************************************************/
/*DMA 序列方式 */
/*********************************************************************************************************************************************/
/* 运行逻辑启动ADC0后
* ==》ADC转换完毕触发DMA0搬运ADC0的数据
* ==》DMA0搬运完毕触发DMA1根据序列器配置ADC0的下一个转换通道
* ==》DMA1配置完毕ADC0下一个通道自动启动转换
* ==》待所有ADC通道转换完毕则停止并触发DMA1主中断 */
//DMA_ADC序列方式采集与获取=========================================================================
DMA_HandleTypeDef DMA_Handle_ADCstatus ,DMA_Handle_ADCcontrol;
//ADC_Data_t adcBuffer[8]; /* 单ADC序列器缓存区 */
/**------------------------------------------------------------------------
* @brief 初始化ADC0单通道序列器DMA模式
* @note 配置ADC0以序列器方式自动切换多个输入通道使用双DMA协作
* - DMA0负责搬运ADC状态和数据
* - DMA1负责更新ADC通道配置寄存器。
* 序列器模式需传入序列配置结构体,包含通道列表、采样率、增益等参数。
* @param seq_config: 序列配置结构体指针,包含通道序列、采样率、增益、缓冲区等
* @example DMA_ADC0_SingleChannel_SEQ_Init(&my_seq_config);
**/
void DMA_ADC0_SingleChannel_SEQ_Init(__SeqConfig_TypeDef* seq_config)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_SingleCSInitTypeDef ADC_CSInitStructure; /* 单ADC序列器初始化结构体 */
ADC_Config_t ADC_Config[seq_config->SeqCount] __ALIGN(32); /* 单ADC序列器参数必须32位对齐 */
DMAMUX_InitTypeDef DMAMUX_InitStructure;
/* 1. 初始化内部基准 */
ADC_REF_Init(REF_INTERNAL_2P5V,REF_INTERNAL_2P5V);
/* 2.1 初始化ADC0和ADC1通道序列器首先配置ADC通用参数固定模式单次转换。
* 然后序列器模式专用结构体配置:多个顺序通道,序列器循环模式,序列器触发源 */
ADC_InitStructure.FS = seq_config->SPS;// 输出速率12.5SPS4通道轮询吞吐率近似1SPS
ADC_InitStructure.PGA = seq_config->Gain;
ADC_InitStructure.Code = ADC_CODE_BIPOLAR;
ADC_InitStructure.ReferenceSelect = ADC_REF_REFP_to_REFN;
ADC_InitStructure.REF_BUFP = ENABLE;
ADC_InitStructure.REF_BUFM = ENABLE;
ADC_InitStructure.REF_Precharge = DISABLE;
ADC_InitStructure.Reference = 2500.0f;
ADC_CSInitStructure.ADC_ID = ADC_0; // ADC0
ADC_CSInitStructure.ADC_CFG = ADC_Config; // 序列配置
ADC_CSInitStructure.Active_Channels = seq_config->SeqCount; // 序列通道数量
ADC_CSInitStructure.Cycle_Mode = DISABLE; // 循环使能:软件触发一次,无限循环;硬件触发一次,循环序列后等待下次触发。
ADC_CSInitStructure.Sequencer_Trigger = ADC_TRIGGER_SOFTWARE; // 循环禁用:软件触发一次,循环序列后停止;硬件触发一次,依次采集一个通道。
for (int i = 0; i < seq_config->SeqCount; i++) { //SeqCount =4: ADC0的序列转换通道0->1->2->3->0->1->...
ADC_CSInitStructure.AINP_Channel[i] = seq_config->SeqChannel[i].ainp_channel;
ADC_CSInitStructure.AINM_Channel[i] = seq_config->SeqChannel[i].ainm_channel;
}
LHL_ADC_SingleChannelSequencerInit(&ADC_InitStructure, &ADC_CSInitStructure);
/* 2.2 通道序列器模式必须使能相应ADC的DMA功能 */
LHL_ADC_DMACmd(ADC_0, ENABLE); // 使能ADC0的DMA功能
/* 3.1 配置DMAUXDMA0的请求源来自ADC0 */
DMA_DMAMUX_CFG(DMA_CHANNEL_ADC_STA ,REQUEST_SOURCE_ADC0);
/* 3.2 配置DMA0用于读取ADC0数据和状态 */
DMA_Handle_ADCstatus.Channel = DMA_CHANNEL_ADC_STA;
DMA_Handle_ADCstatus.Mode = DMA_CHAINING_MODE;
DMA_Handle_ADCstatus.Request = DMA_HARDWARE_REQUEST;
DMA_Handle_ADCstatus.Init.Direction = DMA_PERIPH_TO_MEMORY;
DMA_Handle_ADCstatus.Init.Src_Address = (uint32_t)(&MainADC->ADC_STATUS_0); // 源地址读取ADC0数据
DMA_Handle_ADCstatus.Init.Dest_Address = (uint32_t)seq_config->adcBuffer;
DMA_Handle_ADCstatus.Init.Data_Width = DMA_DATA_WIDTH_4B;
DMA_Handle_ADCstatus.Init.Data_Size = 2; // 状态和数据共2个寄存器
DMA_Handle_ADCstatus.Init.Repetition = seq_config->SeqCount;
DMA_Handle_ADCstatus.Init.Trans_Mode = DMA_CYCLIC_TRANSMISSION;
DMA_Handle_ADCstatus.Init.Chaining = (DMA_CHAINING_t)DMA_CHANNEL_ADC_CTL; //理论上应该是 TO_DMA_CHANNEL_3 // 完成后自动触发DMA1
if (LHL_DMA_Init(&DMA_Handle_ADCstatus) != LHL_OK)
{
while(1); // DMA Init Error
}
/* 4.1 配置DMA1用于配置ADC0的通道寄存器 */
DMA_Handle_ADCcontrol.Channel = DMA_CHANNEL_ADC_CTL; // 句柄更改->hDMA1
DMA_Handle_ADCcontrol.Mode = DMA_DIRECT_MODE;
DMA_Handle_ADCcontrol.Request = DMA_HARDWARE_REQUEST;
DMA_Handle_ADCcontrol.Init.Direction = DMA_MEMORY_TO_PERIPH;
DMA_Handle_ADCcontrol.Init.Src_Address = (uint32_t)ADC_Config; // 源地址读取ADC0通道序列器的配置参数
DMA_Handle_ADCcontrol.Init.Dest_Address = (uint32_t)(&pADC->ADC_CONTROL_0); // 目的地址是ADC0寄存器
DMA_Handle_ADCcontrol.Init.Data_Width = DMA_DATA_WIDTH_4B;
DMA_Handle_ADCcontrol.Init.Data_Size = 2;
DMA_Handle_ADCcontrol.Init.Repetition = seq_config->SeqCount;
DMA_Handle_ADCcontrol.Init.Trans_Mode = DMA_CYCLIC_TRANSMISSION;
if (LHL_DMA_Init(&DMA_Handle_ADCcontrol) != LHL_OK)
{
while(1); // DMA Init Error
}
/* 4.2 使能DMA中断 */
LHL_DMA_ITConfig(&DMA_Handle_ADCcontrol, DMA_IT_MAJOR, ENABLE);
// NVIC_EnableIRQ(DMA1_CH1_IRQn); //ADCcontrol的中断
// /* 5. 显式启动DMA*/
// LHL_DMA_Start(&DMA_Handle_ADCstatus);
// memset(&(adcBuffer),0,sizeof(adcBuffer));
}
/**------------------------------------------------------------------------
* @brief 启动ADC序列器DMA传输
* @note 启动DMA0状态搬运并启动ADC转换序列器将按配置自动轮换通道。
* @param ADCx: ADC实例 (仅支持ADC_0函数内部已固定为ADC0)
* @example StartDMA_SEQ_ADC(ADC_0);
**/
void StartDMA_SEQ_ADC(ADC_ID_t ADCx)
{
if(ADCx == ADC_0) { LHL_DMA_Start(&DMA_Handle_ADCstatus); LHL_ADC_Start(ADC_0); }
else { LHL_DMA_Start(&DMA_Handle_ADCstatus); LHL_ADC_Start(ADC_1); }
}
/**------------------------------------------------------------------------
* @brief 停止ADC序列器DMA传输
* @param ADCx: ADC实例 (ADC_0 / ADC_1)
* @note 停止DMA通道并停止ADC转换。
* @example StopDMA_SEQ_ADC(ADC_0);
**/
void StopDMA_SEQ_ADC(ADC_ID_t ADCx)
{
if(ADCx == ADC_0) { LHL_DMA_Stop(&DMA_Handle_ADC0); LHL_ADC_Stop(ADC_0); }
else { LHL_DMA_Stop(&DMA_Handle_ADC1); LHL_ADC_Stop(ADC_1); }
}
//============================================================
#if 0
/* 运行逻辑: 启动ADC0在同步模式下ADC1同时启动
* ==》ADC转换完毕触发DMA0搬运ADC0和ADC1的数据。
* ==》DMA0搬运完毕触发DMA1搬运ADC0和ADC1的配置参数启动下一组ADC转换通道直到所有序列通道转换完成 */
typedef struct{ /* 双ADC的双缓存区 */
ADC_DualData_t BufferA[SEQUENCER_COUNT];
ADC_DualData_t BufferB[SEQUENCER_COUNT];
} ADC_DualData_Buffer_t;
ADC_DualData_Buffer_t adcDualBuffer; /* 双ADC的数据缓冲区 */
#define TCD_COUNT 2 /* 双缓冲用到2个描述符 */
void DMA_ADC0_1_DualChannel_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_DualCSInitTypeDef ADC_DualCSInitStructure; /* 双ADC序列器初始化结构体 */
ADC_DualConfig_t ADC_DualConfig[SEQUENCER_COUNT] __ALIGN(128); /* 双ADC序列器参数必须128位对齐 */
DMAMUX_InitTypeDef DMAMUX_InitStructure;
DMA_HandleTypeDef DMA_Handle_ADCstatus ,DMA_Handle_ADCcontrol;/* 共用到2路DMA */
DMA_InitTypeDef DMA_InitStructure[TCD_COUNT]; /* TCD描述符需要对应数量的DMA参数结构体 */
DMA_DES_N_TypeDef TCD_Quene[TCD_COUNT] __ALIGN(32); /* DMA TCD描述符必须32位对齐*/
/* 1.1 初始化内部基准 */
ADC_REF_Init(REF_INTERNAL_2P5V,REF_INTERNAL_OFF);
/* 1.2 初始化ADC0和ADC1通道序列器首先配置ADC通用参数模式必须为单次转换。
* 然后序列器模式专用结构体配置:多个通道号,序列器循环模式,以及序列器触发源 */
ADC_InitStructure.FS = SPS_12P5; // 输出速率
ADC_InitStructure.PGA = GAIN1;
ADC_InitStructure.Code = ADC_CODE_BIPOLAR;
ADC_InitStructure.ReferenceSelect = ADC_REF_REFP_to_REFN;
ADC_InitStructure.REF_BUFP = ENABLE;
ADC_InitStructure.REF_BUFM = ENABLE;
ADC_InitStructure.REF_Precharge = DISABLE;
ADC_InitStructure.Reference = 2500.0f;
ADC_DualCSInitStructure.ADC_CFGs = ADC_DualConfig; // 双通道序列器参数表
ADC_DualCSInitStructure.Active_Channels = SEQUENCER_COUNT; // 序列通道共4个
ADC_DualCSInitStructure.Cycle_Mode = DISABLE; // 循环使能:软件触发一次,无限循环;硬件触发一次,循环序列后等待下次触发。
ADC_DualCSInitStructure.Sequencer_Trigger = ADC_TRIGGER_SOFTWARE; // 循环禁用:软件触发一次,循环序列后停止;硬件触发一次,依次采集一个通道。
for (int i = 0; i < SEQUENCER_COUNT; i++) {
ADC_DualCSInitStructure.AINP0_Channel[i] = ADC0seq_configs[i].ainp_channel;
ADC_DualCSInitStructure.AINM0_Channel[i] = ADC0seq_configs[i].ainm_channel;
ADC_DualCSInitStructure.AINP1_Channel[i] = ADC1seq_configs[i].ainp_channel;
ADC_DualCSInitStructure.AINM1_Channel[i] = ADC1seq_configs[i].ainm_channel;
}
LHL_ADC_DualChannelSequencerInit(&ADC_InitStructure, &ADC_DualCSInitStructure); // 初始化ADC双序列器功能
/* 1.3 通道序列器模式必须使能同步模式和相应ADC的DMA功能 */
LHL_ADC_SetSync(ENABLE); // 必须使用同步模式
LHL_ADC_DMACmd(ADC_0, ENABLE); // 必须使能ADC0和ADC1的DMA功能
LHL_ADC_DMACmd(ADC_1, ENABLE);
/* 2. 配置DMAUXDMA0的请求源来自ADC0 */
DMA_DMAMUX_CFG(DMA_CHANNEL_0 ,REQUEST_SOURCE_ADC0);
/* 3. 配置DMA用于读取ADC0和ADC1的数据和状态 */
DMA_Handle_ADCstatus.Channel = DMA_CHANNEL_0; // 句柄hDMA0
DMA_Handle_ADCstatus.Mode = DMA_SCATTER_GATHER_MODE; // 分散聚合模式
DMA_Handle_ADCstatus.Request = DMA_HARDWARE_REQUEST; // 硬件触发
DMA_Handle_ADCstatus.TCD_Count = TCD_COUNT; // 2个TCD用于双缓冲
DMA_Handle_ADCstatus.TCD_List = TCD_Quene; // TCD链表
DMA_Handle_ADCstatus.TCD_Init = DMA_InitStructure; // TCD初始化队列
DMA_InitStructure[0].Direction = DMA_PERIPH_TO_MEMORY; // TCD0用于ADC0和ADC1的数据读取
DMA_InitStructure[0].Src_Address = (uint32_t)(&pADC->IO_CONTROL_IOUT);
DMA_InitStructure[0].Data_Width = DMA_DATA_WIDTH_16B;
DMA_InitStructure[0].Dest_Address = (uint32_t)&adcDualBuffer.BufferA[0];// 指向缓存区A
DMA_InitStructure[0].Data_Size = 2;
DMA_InitStructure[0].Repetition = SEQUENCER_COUNT;
DMA_InitStructure[0].Trans_Mode = DMA_INTERVAL_TRANSMISSION; // 间隔传输用于读写多个不相邻的寄存器
DMA_InitStructure[0].Src_Interval_Factor = 3;
DMA_InitStructure[0].Dest_Interval_Factor = 1;
DMA_InitStructure[0].Chaining = TO_DMA_CHANNEL_1; // 链式触发DMA1用于ADC序列通道切换
DMA_InitStructure[0].TCD_Address = (int32_t)&TCD_Quene[1]; // 完成后加载TCD1用于数据缓冲区切换
DMA_InitStructure[0].Auto_Start = DISABLE;
DMA_InitStructure[0].INT_Major = DISABLE;
DMA_InitStructure[0].INT_Half = DISABLE;
DMA_InitStructure[1].Direction = DMA_PERIPH_TO_MEMORY; // TCD1与TCD0基本一致
DMA_InitStructure[1].Src_Address = (uint32_t)(&pADC->IO_CONTROL_IOUT);
DMA_InitStructure[1].Data_Width = DMA_DATA_WIDTH_16B;
DMA_InitStructure[1].Dest_Address = (uint32_t)&adcDualBuffer.BufferB[0];// 指向缓存区B
DMA_InitStructure[1].Data_Size = 2;
DMA_InitStructure[1].Repetition = 4;
DMA_InitStructure[1].Trans_Mode = DMA_INTERVAL_TRANSMISSION;
DMA_InitStructure[1].Src_Interval_Factor = 3;
DMA_InitStructure[1].Dest_Interval_Factor = 1;
DMA_InitStructure[1].Chaining = TO_DMA_CHANNEL_1;
DMA_InitStructure[1].TCD_Address = (int32_t)&TCD_Quene[0]; // 完成后回到TCD0
DMA_InitStructure[1].Auto_Start = DISABLE;
DMA_InitStructure[1].INT_Major = DISABLE;
DMA_InitStructure[1].INT_Half = DISABLE;
if (LHL_DMA_Init(&DMA_Handle_ADCstatus) != LHL_OK)
{
while(1); // DMA Init Error
}
/* 4. 配置DMA1用于配置ADC0和ADC1的各个设置寄存器 */
DMA_Handle_ADCcontrol.Channel = DMA_CHANNEL_1; // 注意句柄更改->hDMA1
DMA_Handle_ADCcontrol.Mode = DMA_DIRECT_MODE;
DMA_Handle_ADCcontrol.Request = DMA_HARDWARE_REQUEST;
DMA_Handle_ADCcontrol.Init.Direction = DMA_MEMORY_TO_PERIPH;
DMA_Handle_ADCcontrol.Init.Src_Address = (uint32_t)&ADC_DualConfig[0]; // 源地址保存了ADC双通道序列器的配置参数列表
DMA_Handle_ADCcontrol.Init.Data_Width = DMA_DATA_WIDTH_16B;
DMA_Handle_ADCcontrol.Init.Dest_Address = (uint32_t)(&pADC->INTERRUPT_CONTROL_0); // 目的地址是ADC1 CHANNEL_CFG寄存器
DMA_Handle_ADCcontrol.Init.Data_Size = 2;
DMA_Handle_ADCcontrol.Init.Repetition = SEQUENCER_COUNT; // 主循环次数等于序列器通道数
DMA_Handle_ADCcontrol.Init.Trans_Mode = DMA_INTERVAL_TRANSMISSION;
DMA_Handle_ADCcontrol.Init.Src_Interval_Factor = 1;
DMA_Handle_ADCcontrol.Init.Dest_Interval_Factor = 3;
if (LHL_DMA_Init(&DMA_Handle_ADCcontrol) != LHL_OK)
{
while(1); // DMA Init Error
}
/* 5. 配置完毕使能DMA1中断 */
LHL_DMA_ITConfig(&DMA_Handle_ADCcontrol, DMA_IT_MAJOR, ENABLE); // 开启DMA1主中断
NVIC_EnableIRQ(DMA1_CH1_IRQn);
/* 6. 显式启动DMA0 */
LHL_DMA_Start(&DMA_Handle_ADCstatus);
adcFlag = 0;
memset(&adcDualBuffer,0,sizeof(adcDualBuffer));
}
//读ADC数据---------------------------------------------------------------
uint32_t ADC_ReadDualData(ADC_ID_t adcx ,uint8_t SeqChannelNum)//根据序列通道0-8读取
{
volatile uint32_t reg_data;
volatile uint32_t uCurrentBuffer = LHL_DMA_GetDestAddress(DMA_CHANNEL_0); // 获取DMA0当前指向的Buffer 状态寄存器参数搬运通道DMA_CHANNEL_0
if (adcFlag > 0)
{
if (uCurrentBuffer < (uint32_t)adcDualBuffer.BufferB) // 通过获取DMA0的目的地址判断数据存放在哪个Buffer
{
//The dual ADC data is stored in BufferB;
if(adcx == ADC_0) reg_data = adcDualBuffer.BufferB[SeqChannelNum].ADC0_Data ;
else reg_data = adcDualBuffer.BufferB[SeqChannelNum].ADC1_Data ;
}
else
{
//The dual ADC data is stored in BufferA
if(adcx == ADC_0) reg_data = adcDualBuffer.BufferA[SeqChannelNum].ADC0_Data ;
else reg_data = adcDualBuffer.BufferA[SeqChannelNum].ADC1_Data ;
}
reg_data = ((((reg_data & 0xFFFFFF) | ((reg_data & 0x800000) ? 0xFF000000 : 0)) >> 8) + 32768) & 0xFFFF;
adcFlag = 0;
}
return reg_data ;
}
#endif