#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.5SPS(4通道轮询吞吐率近似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 配置DMAUX:DMA0的请求源来自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. 配置DMAUX:DMA0的请求源来自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