DMA的几个要素
- 源地址
- 目标地址
- 传输长度
S32K344提供的DMA编程非常灵活,每个DMA通道的配置由TCD结构体控制。
TCD结构体
DMA传输可以分为一个主循环,多个次循环,每个DMA REQUEST完成一个副循环,当CITER计数为0,意味着主循环完成,触发相应的完成中断。
源地址和目的地址的偏移计算
完成一个副循环后:
现地址=最初地址+offset+minor offset。
两个offset都可以为0,minor offset在最后一个次循环不会生效,此时last adjustmnet address会被加到当前地址形成最终地址。
当一个主循环完成后,上一次主循环完成后的最终地址变成此时的最初参考地址。
minor offset可以使DMA传输两个不连续的地址。也可以通过last adjustmnet address使得传递后的地址回到最初地址。
参数解释
biter:设置的minor loop个数
citer:当前minor loop的个数,开始一个major loop时,会从把biter装载进来,当citer为0,触发DMA传输完成中断。
smod:
sszie:每次搬运的字节数
nbytes:一个副循环需要搬运的字节数
soff:搬运sszie后,源地址的增加量,可以为正负,也可以为0
minor offset:一个副循环完成后,地址的增量,正,负,0。
剩下的参数为两个寄存器
通道状态寄存器CHn_CSR
主要功能:允许硬件外设产生DMA请求(软件触发DMA不受控制),active位,done位
通带控制寄存器TCDn_CSR
主要功能:软件触发DMA,允许传输完成中断,允许传输完成后禁止硬件外设请求DMA
DMA通道的串联
假设此时CH3在运行,第一个minor loop完成后会设置CH1软件触发DMA位,使得CH1完成自己第一个minor loop,当CH3完成第二个minor loop,会再次设置CH1软件触发DMA位,使得CH1完成第二个minor loop。软件触发DMA位会被自动清处。当CH3完成第三个minor loop,此时major loop完成,CH1不会被触发,即使major loop没有link通道。
通道的link其实没有改变DMA的TCD结构体,会周而复始,循环往复。
通道串联的内部实现是通过软件启动来触发DMA请求的
DMA的scatter/gather
DMA的scatter/gather 聚散功能允许一个DMA通道使用多个TCD配置。聚散功能启用时,当1次主循环结束后DMA引擎自动从预先输入的地址中调入下一个新的TCD配置,无需软件介入。此功能允许DMA通过TCD数组1次传输多个物理上不连续的块,完成传输后只发起一次中断。
关键函数
在mcl_init初始化实现中,会把你在EB中的配置写入对应DMA通道的TCD寄存器中,EB是无法设置源地址和目的地址的。
Dma_Ip_LogicChannelInit
Static_Dma_Ip_SetLogicChannelTransferConfig
static Dma_Ip_ReturnType Static_Dma_Ip_SetLogicChannelTransferConfig(const uint32 LogicCh, const Dma_Ip_TransferConfigType * const TransferConfig)
{
Dma_Ip_LogicChannelTransferListType LocList[DMA_IP_LOC_TRANSFER_PARAM_LIST_DIMENSION];
const Dma_Ip_LogicChannelIdType * LocLogicChId;
uint32 LocHwVers;
uint32 LocHwInst;
uint32 LocHwCh;
Dma_Ip_TcdRegType * PxTcd = NULL_PTR;
DMA_IP_DEV_ASSERT(NULL_PTR != Dma_Ip_pxInit);
DMA_IP_DEV_ASSERT(NULL_PTR != Dma_Ip_pxInit->ppxLogicChannelConfigArray[LogicCh]);
DMA_IP_DEV_ASSERT(DMA_IP_NOF_CFG_LOGIC_CHANNELS > LogicCh);
DMA_IP_DEV_ASSERT(NULL_PTR != TransferConfig);
LocLogicChId = (const Dma_Ip_LogicChannelIdType *)&(Dma_Ip_pxInit->ppxLogicChannelConfigArray[LogicCh]->LogicChId);
LocHwVers = LocLogicChId->HwVersId;
LocHwInst = LocLogicChId->HwInstId;
LocHwCh = LocLogicChId->HwChId;
LocList[0U].Param = DMA_IP_CH_SET_SOURCE_ADDRESS;
LocList[0U].Value = (uint32)TransferConfig->Source.Addr;
LocList[1U].Param = DMA_IP_CH_SET_SOURCE_SIGNED_OFFSET;
LocList[1U].Value = (uint32)TransferConfig->Source.SignedOffset;
LocList[2U].Param = DMA_IP_CH_SET_SOURCE_SIGNED_LAST_ADDR_ADJ;
LocList[2U].Value = (uint32)TransferConfig->Source.LastAddrAdj;
LocList[3U].Param = DMA_IP_CH_SET_SOURCE_TRANSFER_SIZE;
LocList[3U].Value = (uint32)TransferConfig->Source.TransferSize;
LocList[4U].Param = DMA_IP_CH_SET_SOURCE_MODULO;
LocList[4U].Value = (uint32)TransferConfig->Source.Modulo;
LocList[5U].Param = DMA_IP_CH_SET_DESTINATION_ADDRESS;
LocList[5U].Value = (uint32)TransferConfig->Destination.Addr;
LocList[6U].Param = DMA_IP_CH_SET_DESTINATION_SIGNED_OFFSET;
LocList[6U].Value = (uint32)TransferConfig->Destination.SignedOffset;
LocList[7U].Param = DMA_IP_CH_SET_DESTINATION_SIGNED_LAST_ADDR_ADJ;
LocList[7U].Value = (uint32)TransferConfig->Destination.LastAddrAdj;
LocList[8U].Param = DMA_IP_CH_SET_DESTINATION_TRANSFER_SIZE;
LocList[8U].Value = (uint32)TransferConfig->Destination.TransferSize;
LocList[9U].Param = DMA_IP_CH_SET_DESTINATION_MODULO;
LocList[9U].Value = (uint32)TransferConfig->Destination.Modulo;
LocList[10U].Param = DMA_IP_CH_SET_MINORLOOP_EN_SRC_OFFSET;
LocList[10U].Value = (TransferConfig->MinorLoop.EnSrcOffset ? TRUE : FALSE);
LocList[11U].Param = DMA_IP_CH_SET_MINORLOOP_EN_DST_OFFSET;
LocList[11U].Value = (TransferConfig->MinorLoop.EnDstOffset ? TRUE : FALSE);
LocList[12U].Param = DMA_IP_CH_SET_MINORLOOP_SIGNED_OFFSET;
LocList[12U].Value = (uint32)TransferConfig->MinorLoop.Offset;
LocList[13U].Param = DMA_IP_CH_SET_MINORLOOP_EN_LINK;
LocList[13U].Value = (TransferConfig->MinorLoop.EnLink ? TRUE : FALSE);
LocList[14U].Param = DMA_IP_CH_SET_MINORLOOP_LOGIC_LINK_CH;
LocList[14U].Value = (uint32)TransferConfig->MinorLoop.LogicLinkCh;
LocList[15U].Param = DMA_IP_CH_SET_MINORLOOP_SIZE;
LocList[15U].Value = (uint32)TransferConfig->MinorLoop.Size;
LocList[16U].Param = DMA_IP_CH_SET_MAJORLOOP_EN_LINK;
LocList[16U].Value = (TransferConfig->MajorLoop.EnLink ? TRUE : FALSE);
LocList[17U].Param = DMA_IP_CH_SET_MAJORLOOP_LOGIC_LINK_CH;
LocList[17U].Value = (uint32)TransferConfig->MajorLoop.LogicLinkCh;
LocList[18U].Param = DMA_IP_CH_SET_MAJORLOOP_COUNT;
LocList[18U].Value = (uint32)TransferConfig->MajorLoop.Count;
LocList[19U].Param = DMA_IP_CH_SET_CONTROL_EN_MAJOR_INTERRUPT;
LocList[19U].Value = (TransferConfig->Control.EnMajorInt ? TRUE : FALSE);
LocList[20U].Param = DMA_IP_CH_SET_CONTROL_EN_HALF_MAJOR_INTERRUPT;
LocList[20U].Value = (TransferConfig->Control.EnHalfMajorInt ? TRUE : FALSE);
LocList[21U].Param = DMA_IP_CH_SET_CONTROL_DIS_AUTO_REQUEST;
LocList[21U].Value = (TransferConfig->Control.DisAutoHwRequest ? TRUE : FALSE);
#if (STD_ON == DMA_IP_END_OF_PACKET_SIGNAL_IS_AVAILABLE)
LocList[22U].Param = DMA_IP_CH_SET_CONTROL_EN_END_OF_PACKET_SIGNAL;
LocList[22U].Value = (TransferConfig->Control.EnEndOfPacketSignal == TRUE) ? 1U : 0U;
#else
LocList[22U].Param = DMA_IP_CH_SET_CONTROL_BANDWIDTH;
LocList[22U].Value = (uint32)TransferConfig->Control.BandwidthControl;
#endif /* #if (STD_ON == DMA_IP_END_OF_PACKET_SIGNAL_IS_AVAILABLE) */
#if (STD_ON == DMA_IP_TRANSFER_MODE_CONTROL_IS_AVAILABLE)
if(DMA_IP_HARDWARE_VERSION_4 == LocHwVers)
{
LocList[23U].Param = DMA_IP_CH_SET_CONTROL_TRANSFER_MODE;
LocList[23U].Value = (uint32)TransferConfig->Control.TransferModeControl;
}
else
{
LocList[23U].Param = DMA_IP_CH_SET_CONTROL_BANDWIDTH;
LocList[23U].Value = (uint32)TransferConfig->Control.BandwidthControl;
}
#else
LocList[23U].Param = DMA_IP_CH_SET_CONTROL_BANDWIDTH;
LocList[23U].Value = (uint32)TransferConfig->Control.BandwidthControl;
#endif /* STD_ON == DMA_IP_TRANSFER_MODE_CONTROL_IS_AVAILABLE */
/* Start bit is loaded last into the TCD */
LocList[24U].Param = DMA_IP_CH_SET_CONTROL_SOFTWARE_REQUEST;
LocList[24U].Value = (TransferConfig->Control.EnStart ? TRUE : FALSE);
#if (STD_ON == DMA_IP_HWV3_IS_AVAILABLE)
if(DMA_IP_HARDWARE_VERSION_3 == LocHwVers)
{
PxTcd = &DMA_IP_HWV3_TCD_TCD_VALUE(LocHwInst, LocHwCh);
}
#endif /* #if (STD_ON == DMA_IP_HWV3_IS_AVAILABLE) */
/* Done shall be cleared before a new transfer is configured. */
HwAccDmaCh_SetCommand((uint32)DMA_IP_CH_CLEAR_DONE, LocHwVers, LocHwInst, LocHwCh);
if(NULL_PTR != PxTcd)
{
***//关键调用,通过list.param的值来调用数组中的函数指针***
Static_Dma_Ip_SetLogicChannelTransferList(LogicCh, PxTcd, LocList, DMA_IP_LOC_TRANSFER_PARAM_LIST_DIMENSION);
}
return DMA_IP_STATUS_SUCCESS;
}
初始化后,此时在我们自己的程序主体想要设置源地址和目标地址
Dma_Ip_LogicChannelScatterGatherListType list[2U];
list[0].Param=DMA_IP_CH_SET_SOURCE_ADDRESS;
list[0].Value=(uint32)&mysrc;
list[1].Param=DMA_IP_CH_SET_DESTINATION_ADDRESS;
list[1].Value=(uint32)&mydes;
Dma_Ip_SetLogicChannelTransferList(1,list,2);
Dma_Ip_SetLogicChannelTransferList
Static_Dma_Ip_SetLogicChannelTransferList
{
for(LocParamIdx = 0U; LocParamIdx < ListDimension; LocParamIdx++)
{
#if (STD_ON == DMA_IP_TRANSFER_MODE_CONTROL_IS_AVAILABLE)
DMA_IP_DEV_ASSERT(DMA_IP_CH_SET_CONTROL_TRANSFER_MODE >= List[LocParamIdx].Param);
#else
DMA_IP_DEV_ASSERT(DMA_IP_CH_SET_CONTROL_BANDWIDTH >= List[LocParamIdx].Param);
#endif
HwAccDmaCh_SetTransferParam((uint32)List[LocParamIdx].Param, LocHwVers, PxTcd, List[LocParamIdx].Value);
}
}
void HwAccDmaCh_SetTransferParam(const uint32 Parameter, const uint32 LocHwVers, Dma_Ip_TcdRegType * pxLocTcd, const uint32 LocValue)
{
static void (* const fpHwAcc_DmaCh_SetTransferParam[])(const uint32 LocHwVers, Dma_Ip_TcdRegType * pxLocTcd, const uint32 LocValue) =
{
HwAccDmaCh_SetSource_Address,
HwAccDmaCh_SetSource_SignedOffset,
HwAccDmaCh_SetSource_LastAddrAdj,
HwAccDmaCh_SetSource_TransferSize,
HwAccDmaCh_SetSource_Modulo,
HwAccDmaCh_SetDestination_Address,
HwAccDmaCh_SetDestination_SignedOffset,
HwAccDmaCh_SetDestination_LastAddrAdj,
HwAccDmaCh_SetDestination_TransferSize,
HwAccDmaCh_SetDestination_Modulo,
HwAccDmaCh_SetMinorLoop_enSrcOffset,
HwAccDmaCh_SetMinorLoop_enDstOffset,
HwAccDmaCh_SetMinorLoop_Offset,
HwAccDmaCh_SetMinorLoop_EnLink,
HwAccDmaCh_SetMinorLoop_LogicLinkCh,
HwAccDmaCh_SetMinorLoop_Size,
HwAccDmaCh_SetMajorLoop_EnLink,
HwAccDmaCh_SetMajorLoop_LogicLinkCh,
HwAccDmaCh_SetMajorLoop_Count,
#if (STD_ON == DMA_IP_STORE_DST_ADDR_IS_AVAILABLE)
HwAccDmaCh_SetControl_StoreDestinationAddress,
#else
HwAccDmaCh_SetTransferDummyFunction,
#endif
HwAccDmaCh_SetControl_EnStart,
HwAccDmaCh_SetControl_EnMajor,
HwAccDmaCh_SetControl_EnHalfMajor,
HwAccDmaCh_SetControl_DisAutoHwRequest,
#if (STD_ON == DMA_IP_END_OF_PACKET_SIGNAL_IS_AVAILABLE)
HwAccDmaCh_SetControl_EnEndOfPacketSignal,
#else
HwAccDmaCh_SetTransferDummyFunction,
#endif
HwAccDmaCh_SetControl_BandwidthControl,
#if (STD_ON == DMA_IP_TRANSFER_MODE_CONTROL_IS_AVAILABLE)
HwAccDmaCh_SetControl_TransferModeControl,
#endif
};
//根据第
fpHwAcc_DmaCh_SetTransferParam[Parameter](LocHwVers, pxLocTcd, LocValue);
}
对于SCATTER/GATHER,都是通过mcl_init做一个基本的初始化,为了实现链表,第一个TCD结构体成员DLAS_SAG会包含下一个TCD结构体地址。
MCL_INIT初始化时有条件选择,普通的单通道以及scatter/gather
if(NULL_PTR != Dma_Ip_pxInit->ppxLogicChannelConfigArray[LogicCh]->pxTransferConfig)
{
/* Force Disable ScatterGather and Force Reset Address */
HwAccDmaCh_SetControl_EnScatterGatherProcessing(LocHwVers, PxTcd, FALSE);
HwAccDmaCh_SetControl_ScatterGatherAddress(LocHwVers, PxTcd, 0U);
Dma_Ip_SetHwChannelState(LocHwVers, LocHwInst, LocHwCh, DMA_IP_CH_TRANSFER_EVENT);
LocStatus = Static_Dma_Ip_SetLogicChannelTransferConfig(LocLogicChId->LogicChId, Dma_Ip_pxInit->ppxLogicChannelConfigArray[LogicCh]->pxTransferConfig);
}
else if(NULL_PTR != Dma_Ip_pxInit->ppxLogicChannelConfigArray[LogicCh]->ppxScatterGatherConfigArray)
{
/* Force Disable ScatterGather and Force Reset Address */
HwAccDmaCh_SetControl_EnScatterGatherProcessing(LocHwVers, PxTcd, FALSE);
HwAccDmaCh_SetControl_ScatterGatherAddress(LocHwVers, PxTcd, 0U);
Dma_Ip_SetHwChannelState(LocHwVers, LocHwInst, LocHwCh, DMA_IP_CH_SCATTERGATHER_EVENT);
//scather/gather调用这个函数进行初始化
LocStatus = Static_Dma_Ip_SetLogicChannelScatterGatherInit(LocLogicChId->LogicChId, 0U);
}
补充:DMA的scatter功能是每个TCD加载完后自动加载下一个TCD,每一个TCD是可以选择配置传输结束后是否触发中断,DMA加载下一个TCD后并不一定会DMA传输,如果想要加载后DMA自动启动传输,应该设置下一个TCD的start为1.
Dma_Ip_SetLogicChannelScatterGatherList用来设置DMA属性,比如设置START为1
Dma_Ip_SetLogicChannelScatterGatherConfig这个是将软件TCD写入硬件
因此在设置连续TCD时,后续的TCD应该设置为软件触发,才能使得DMA连续搬运。最后TCD加载后应该重新写入TCD,否则DMA不在运行(除非硬件触发)