GZZXB 发表于 2019-4-15 22:08

华大单片机的DMA为啥搞个块大小出来?一次最多只能16个数...

算是阉割版DMA吗? STM32多爽想传多少就传多少,华大最多只能一次传16吧,超过16就得用软件+中断实现了。

je段 发表于 2019-4-18 15:08

技术呢,出来帮忙解释下啊我只负责卖

smartpower 发表于 2019-4-18 21:13

1. 设置块数:(从 1 至 16 中选择)
2. 设置传输长度:(从 1 至 65536 中选择)
3. 总传输长度可达为16 * 65536 个字节。

operating 发表于 2019-4-28 16:31

应该可以多传的吧

martinhu 发表于 2019-4-29 09:40



BC块数:(从 1 至 16 中选择)
TC传输长度:(从 1 至 65536 中选择)
BC和TC合理使用,可以实现很多种传输组合

GZZXB 发表于 2019-5-2 13:11

martinhu 发表于 2019-4-29 09:40
BC块数:(从 1 至 16 中选择)
TC传输长度:(从 1 至 65536 中选择)
BC和TC合理使用,可以实现很多种 ...

需要软件+中断 才能实现。不像STM32设置好了,不用中断不用写代码处理。

martinhu 发表于 2019-5-5 08:48

GZZXB 发表于 2019-5-2 13:11
需要软件+中断 才能实现。不像STM32设置好了,不用中断不用写代码处理。

有硬件模式,只要有对应的触发事件就可以触发DMA传送,不需要非得软件+中断才可以

GZZXB 发表于 2019-5-5 09:03

martinhu 发表于 2019-5-5 08:48
有硬件模式,只要有对应的触发事件就可以触发DMA传送,不需要非得软件+中断才可以 ...

比如我扫描采集3个AD通道0 1 2,一共采集255*3个数据到buff。 不使用DMA中断,你能实现?

mtbf 发表于 2020-4-6 21:26

想ADC每1ms扫描采集10通道,每10ms处理一次数据,用DMA搬运数据,TC只能设置成0,即一次扫描采集完成,源地址重新装载。产生一次DMA中断。TC不能设置成9,即每10次扫描转换产生一次DMA中断。现在程序里是设置TC为0,目的地址不重新装载,记录到10次中断后,重新加载一次目的地址。这样设置中断比较频繁。
DMA控制器只能在TC减到0以后才能完成一次源地址,目的地址,BC,TC重新装载?

#define ADC_BC 10
#define ADC_TC 10

uint32_t ADC_Result_Array = {0};
uint16_t ADC_array_write_index = 0;
uint32_t u32AdcRestult_VSEN;
uint32_t u32AdcRestult_I1;
uint32_t u32AdcRestult_I2;
extern QXSemaphoresema_adc;

uint16_t current_prev_1, current_prev_2;
uint16_t current_flag_1, current_flag_2;

void App_DmaCfg(void);
void App_Timer0Cfg(uint16_t u16Period);
void Adc_IRQHandler(void)
{
    if(TRUE == Adc_GetIrqStatus(AdcMskIrqSqr))
    {
      Adc_ClrIrqStatus(AdcMskIrqSqr);
//      u16AdcRestult_VSEN = Adc_GetSqrResult(AdcSQRCH0MUX);
//      u16AdcRestult_I1 = Adc_GetSqrResult(AdcSQRCH1MUX);;
//      u16AdcRestult_I1_buf = Adc_GetSqrResult(AdcSQRCH1MUX);
//      u16AdcRestult_I1_buf = Adc_GetSqrResult(AdcSQRCH2MUX);
//      u16AdcRestult_I1_buf = Adc_GetSqrResult(AdcSQRCH3MUX);
//      u16AdcRestult_I1_buf = Adc_GetSqrResult(AdcSQRCH4MUX);
//      u16AdcRestult_I2 = Adc_GetSqrResult(AdcSQRCH5MUX);
//      u16AdcRestult_I2_buf = Adc_GetSqrResult(AdcSQRCH5MUX);
//      u16AdcRestult_I2_buf = Adc_GetSqrResult(AdcSQRCH6MUX);
//      u16AdcRestult_I2_buf = Adc_GetSqrResult(AdcSQRCH7MUX);
//      u16AdcRestult_I2_buf = Adc_GetSqrResult(AdcSQRCH8MUX);
//      (void)QXSemaphore_signal(&sema_adc);
    }
}
void Dmac_IRQHandler(void)
{   
    if(DmaTransferComplete == Dma_GetStat(DmaCh0))
    {
      //等待传输完成
      //(void)QXSemaphore_signal(&sema_adc);
      Dma_ClrStat(DmaCh0);
      if(ADC_array_write_index < 9){
            //M0P_DMAC->DSTADR0 = (uint32_t)&ADC_Result_Array;
            ADC_array_write_index++;
      }else{
            ADC_array_write_index = 0;
            M0P_DMAC->DSTADR0 = (uint32_t)&ADC_Result_Array;
            IO_SET(LED_1, 1);
            (void)QXSemaphore_signal(&sema_adc);
      }
    }
    if(DmaTransferComplete == Dma_GetStat(DmaCh1))
    {
      //等待传输完成
      //IO_SET(LED_1, 1);
      //(void)QXSemaphore_signal(&sema_adc);
      Dma_ClrStat(DmaCh1);
      //IO_SET(LED_1, 0);
    }
    if((DmaAccSCRErr == Dma_GetStat(DmaCh0)) || (DmaAccDestErr == Dma_GetStat(DmaCh0)))
    {
      Dma_ClrStat(DmaCh0);
    }
   
}
void Tim0_IRQHandler(void)
{
    //Timer0 模式0 溢出中断
    if(TRUE == Bt_GetIntFlag(TIM0, BtUevIrq))
    {      
      Bt_ClearIntFlag(TIM0,BtUevIrq); //中断标志清零      
      //IO_SET(LED_1, 1);
    }
}
void App_Timer0Cfg(uint16_t u16Period)
{
    uint16_t                  u16ArrValue;
    uint16_t                  u16CntValue;
    stc_bt_mode0_cfg_t   stcBtBaseCfg;
    stc_bt_m23_adc_trig_cfg_tstcBtTrigAdc;
    DDL_ZERO_STRUCT(stcBtBaseCfg);
    DDL_ZERO_STRUCT(stcBtTrigAdc);
    Sysctrl_SetPeripheralGate(SysctrlPeripheralBaseTim, TRUE); //Base Timer外设时钟使能
   
    stcBtBaseCfg.enWorkMode = BtWorkMode0;                  //定时器模式
    stcBtBaseCfg.enCT       = BtTimer;                      //定时器功能,计数时钟为内部PCLK
    stcBtBaseCfg.enPRS      = BtPCLKDiv8;               //PCLK/256
    stcBtBaseCfg.enCntMode= Bt16bitArrMode;               //自动重载16位计数器/定时器
    stcBtBaseCfg.bEnTog   = FALSE;
    stcBtBaseCfg.bEnGate    = FALSE;
    stcBtBaseCfg.enGateP    = BtGatePositive;
    Bt_Mode0_Init(TIM0, &stcBtBaseCfg);                     //TIM0 的模式0功能初始化
   
    u16ArrValue = 0x10000 - u16Period;
    Bt_M0_ARRSet(TIM0, u16ArrValue);                        //设置重载值(ARR = 0x10000 - 周期)
   
    u16CntValue = 0x10000 - u16Period;
    Bt_M0_Cnt16Set(TIM0, u16CntValue);                      //设置计数初值
   
    stcBtTrigAdc.bEnTrigADC    = TRUE;                     //使能ADC触发全局控制
    stcBtTrigAdc.bEnUevTrigADC = TRUE;                     //Uev更新触发ADC
    Bt_M23_TrigADC_Cfg(TIM0, &stcBtTrigAdc);               //触发ADC配置
   
    Bt_ClearIntFlag(TIM0,BtUevIrq);                         //清中断标志   
    //Bt_Mode0_EnableIrq(TIM0);                               //使能TIM0中断(模式0时只有一个中断)
    //EnableNvic(TIM0_IRQn, IrqLevel3, TRUE);               //TIM0中断使能
}
void aio_init(void)
{
    stc_adc_cfg_t stcAdcCfg;
    stc_adc_sqr_cfg_t stcAdcSqrCfg;
    DDL_ZERO_STRUCT(stcAdcCfg);
    DDL_ZERO_STRUCT(stcAdcSqrCfg);
   
   
    Gpio_SetAnalogMode(GpioPortB, GpioPin0);
    Gpio_SetAnalogMode(GpioPortB, GpioPin1);
    Gpio_SetAnalogMode(GpioPortB, GpioPin11);
   
    if (Ok != Sysctrl_SetPeripheralGate(SysctrlPeripheralAdcBgr, TRUE))
    {
      Q_onAssert("adc_err", 999);
    }
    Bgr_BgrEnable();      ///< 开启BGR
    //ADC配置   
      
    stcAdcCfg.enAdcMode         = AdcScanMode;            ///<采样模式-扫描
    stcAdcCfg.enAdcClkDiv       = AdcMskClkDiv8;            ///<采样分频-8
    stcAdcCfg.enAdcSampCycleSel = AdcMskSampCycle12Clk;      ///<采样周期数-8
    stcAdcCfg.enAdcRefVolSel    = AdcMskRefVolSelInBgr2p5;   ///<参考电压选择-2.5V
    stcAdcCfg.enAdcOpBuf      = AdcMskBufDisable;         ///<OP BUF配置-
    stcAdcCfg.enInRef         = AdcMskInRefEnable;       ///<内部参考电压使能-关
    stcAdcCfg.enAdcAlign      = AdcAlignRight;               ///<转换结果对齐方式-右
    Adc_Init(&stcAdcCfg);
   
    stcAdcSqrCfg.bSqrDmaTrig = TRUE;
    stcAdcSqrCfg.enResultAcc = AdcResultAccDisable;
    stcAdcSqrCfg.u8SqrCnt    = 10;
    Adc_SqrModeCfg(&stcAdcSqrCfg);
   
    Adc_CfgSqrChannel(AdcSQRCH0MUX, AdcExInputCH8);
    Adc_CfgSqrChannel(AdcSQRCH9MUX, AdcExInputCH8);
    Adc_CfgSqrChannel(AdcSQRCH1MUX, AdcExInputCH9);
    Adc_CfgSqrChannel(AdcSQRCH2MUX, AdcExInputCH9);
    Adc_CfgSqrChannel(AdcSQRCH3MUX, AdcExInputCH9);
    Adc_CfgSqrChannel(AdcSQRCH4MUX, AdcExInputCH9);
    Adc_CfgSqrChannel(AdcSQRCH5MUX, AdcExInputCH18);
    Adc_CfgSqrChannel(AdcSQRCH6MUX, AdcExInputCH18);
    Adc_CfgSqrChannel(AdcSQRCH7MUX, AdcExInputCH18);
    Adc_CfgSqrChannel(AdcSQRCH8MUX, AdcExInputCH18);
   
    Adc_ClrIrqStatus(AdcMskIrqSqr);
    Adc_SqrExtTrigCfg(AdcMskTrigTimer0, TRUE);         //Timer0触发扫描转换   
   
    App_DmaCfg();
    //Adc_EnableIrq();
    //EnableNvic(ADC_IRQn, IrqLevel3, TRUE);
   
    delay10us(5);
    App_Timer0Cfg(3000);
}
void App_DmaCfg(void)
{
    stc_dma_cfg_t         stcDmaCfg;   
   
    DDL_ZERO_STRUCT(stcDmaCfg);                     //结构体变量 初始值清零
   
    // 使能 DMA时钟
    Sysctrl_SetPeripheralGate(SysctrlPeripheralDma,TRUE);
   
    stcDmaCfg.enMode =DmaMskBlock;                            //选择块传输
    stcDmaCfg.u16BlockSize = 10;                           //块传输个数
    stcDmaCfg.u16TransferCnt = 1;                           //Block模式,一次传输数据大小为 10,传输1次
    stcDmaCfg.enTransferWidth = DmaMsk32Bit;                  //传输数据的宽度,此处选择字(32Bit)宽度
    stcDmaCfg.enSrcAddrMode = DmaMskSrcAddrInc;               //源地址自增
    stcDmaCfg.enDstAddrMode = DmaMskDstAddrInc;               //目的地址自增
    stcDmaCfg.enDestAddrReloadCtl = DmaMskDstAddrReloadDisable; //不重新加载传输目的地址
    stcDmaCfg.enSrcAddrReloadCtl = DmaMskSrcAddrReloadEnable;   //使能重新加载传输源地址
    stcDmaCfg.enSrcBcTcReloadCtl = DmaMskBcTcReloadEnable;      //使能重新加载BC/TC值
    stcDmaCfg.u32SrcAddress = (uint32_t)&(M0P_ADC->SQRRESULT0);//指定传输源地址
    stcDmaCfg.u32DstAddress = (uint32_t)&ADC_Result_Array;   //指定传输目的地址
    stcDmaCfg.enTransferMode = DmaMskContinuousTransfer;      //DMAC 在传输完成时不清除 CONFA:ENS 位。这个功能允许连续传输而不需要 CPU 干预。
    stcDmaCfg.enRequestNum = DmaADCSQRTrig;                     //设置为ADC SQR触发
   
    Dma_InitChannel(DmaCh0,&stcDmaCfg);       //初始化DMA通道0
   
//    stcDmaCfg.enMode =DmaMskBlock;                            //选择块传输
//    stcDmaCfg.u16BlockSize = ADC_BC;                           //块传输个数
//    stcDmaCfg.u16TransferCnt = ADC_TC;                           //Block模式,一次传输数据大小为 3,传输三次
//    stcDmaCfg.enTransferWidth = DmaMsk32Bit;                  //传输数据的宽度,此处选择字(32Bit)宽度
//    stcDmaCfg.enSrcAddrMode = DmaMskSrcAddrInc;               //源地址自增
//    stcDmaCfg.enDstAddrMode = DmaMskDstAddrInc;               //目的地址自增
//    stcDmaCfg.enDestAddrReloadCtl = DmaMskDstAddrReloadEnable; //使能重新加载传输目的地址
//    stcDmaCfg.enSrcAddrReloadCtl = DmaMskSrcAddrReloadEnable;   //使能重新加载传输源地址
//    stcDmaCfg.enSrcBcTcReloadCtl = DmaMskBcTcReloadEnable;      //使能重新加载BC/TC值
//    stcDmaCfg.u32SrcAddress = (uint32_t)&ADC_Result_Array;//指定传输源地址
//    stcDmaCfg.u32DstAddress = (uint32_t)&ADC_Result_buf_Array;   //指定传输目的地址
//    stcDmaCfg.enTransferMode = DmaMskContinuousTransfer;      //DMAC 在传输完成时不清除 CONFA:ENS 位。这个功能允许连续传输而不需要 CPU 干预。
//    stcDmaCfg.enRequestNum = DmaADCSQRTrig;                     //设置为ADC SQR触发
//    Dma_InitChannel(DmaCh1,&stcDmaCfg);
    //使能DMA,使能DMA0
    Dma_Enable();
    Dma_EnableChannel(DmaCh0);
    Dma_EnableChannelIrq(DmaCh0);
    Dma_EnableChannelErrIrq(DmaCh0);
    //Dma_EnableChannel(DmaCh1);
    //Dma_EnableChannelIrq(DmaCh1);
   
    EnableNvic(DMAC_IRQn, IrqLevel2, TRUE);
}

mtbf 发表于 2020-4-9 22:21

不好意思,有技术支持能确认下吗?

liaotian001 发表于 2020-4-13 10:58

1. 设置块数:(从 1 至 16 中选择)
2. 设置传输长度:(从 1 至 65536 中选择)
3. 总传输长度可达为16 * 65536 个字节。
HC32L136 使用了DMA,可以实现想传多少就传多少。以上传输模式对于客户而言是透明的,在总线架构上面为了满足系统对于实时性的需求,每次传16个数据,就会让总线,DMA进行一次仲裁,如果CPU无操作需求,DMA继续传输数据,也可以传大量数据,也不需要软件处理。
友商想传多少就传多少,会屏蔽CPU对于实时操作的能力,只有等DMA把所有数据传输完成之后,CPU才能继续响应中断,这对于系统而言,基本属于无实时性可言。
每种总线架构,有每种架构的需求,每个设计者考虑的东西也不一样,彼此都有优先级考虑的。我们考虑的是,DMA传输可能比友商慢一点点,但是整个系统对于实时响应性能强,DMA不会卡死CPU。

mtbf 发表于 2020-4-13 21:00

liaotian001 发表于 2020-4-13 10:58
1. 设置块数:(从 1 至 16 中选择)
2. 设置传输长度:(从 1 至 65536 中选择)
3. 总传输长度可达为16 ...

你描述的是软件Block传输或硬件Burst传输情况,从ADC扫描转换结果寄存器 到内存不适合用。从ADC_SqrResult0 - 15传送一批数据到内存适合硬件Block传输。

liaotian001 发表于 2020-4-23 11:47

mtbf 发表于 2020-4-13 21:00
你描述的是软件Block传输或硬件Burst传输情况,从ADC扫描转换结果寄存器 到内存不适合用。从ADC_SqrResul ...

ADC_SqrResult0 - 15,一共16个数据,我们的DMA可以支持一次搬运完成的。硬件设计中,ADC采样完成最后一个buffer地址的采样,ADC 硬件 trigger DMA request, DMA就会完成一次16笔数据的搬运。

詹求实 发表于 2020-4-29 18:51

liaotian001 发表于 2020-4-23 11:47
ADC_SqrResult0 - 15,一共16个数据,我们的DMA可以支持一次搬运完成的。硬件设计中,ADC采样完成最后一 ...

DMA一次搬运的数据可以单独设置吗?

mtbf 发表于 2020-4-29 22:49

liaotian001 发表于 2020-4-23 11:47
ADC_SqrResult0 - 15,一共16个数据,我们的DMA可以支持一次搬运完成的。硬件设计中,ADC采样完成最后一 ...

定时器触发ADC完成16通道扫描,10KHz,想做到10次扫描以后参数16X10数据,DMA搬运到内存,产生一次中断。现在只能做到每扫描一次产生中断,也就是16次中断,中断频率跟定时器一样,10KHz。

舒伯特玫瑰 发表于 2020-4-30 17:55

不是必须中断吧,那样太费事了

lesheng002 发表于 2020-5-7 20:21

本帖最后由 lesheng002 于 2020-5-7 20:24 编辑

liaotian001 发表于 2020-4-13 10:58
1. 设置块数:(从 1 至 16 中选择)
2. 设置传输长度:(从 1 至 65536 中选择)
3. 总传输长度可达为16 ...
照这个说法,DMA传输影响MCU的工作了,难怪我设计的ADC检测,用DMA传输结果,启动就死机了

liaotian001 发表于 2020-5-8 09:35

lesheng002 发表于 2020-5-7 20:21
照这个说法,DMA传输影响MCU的工作了,难怪我设计的ADC检测,用DMA传输结果,启动就死机了 ...

请仔细看一下我的说明,DMA传输数据,启动就死机,那我认为100%是你软件的问题了。

CharryW 发表于 2020-5-8 11:32

liaotian001 发表于 2020-4-13 10:58
1. 设置块数:(从 1 至 16 中选择)
2. 设置传输长度:(从 1 至 65536 中选择)
3. 总传输长度可达为16 ...

这也太不专业了,DMA和CPU都作为主机会保证CPU有至少一半的AHB总线带宽,DMA即使连续传输中间也是有间隔的

lesheng002 发表于 2020-5-8 11:58

本帖最后由 lesheng002 于 2020-5-8 12:00 编辑

liaotian001 发表于 2020-5-8 09:35
请仔细看一下我的说明,DMA传输数据,启动就死机,那我认为100%是你软件的问题了。 ...
我不清楚你们是怎么做的,从目前的情况看,说不清楚到底是哪里的问题,不一定是DMA传输造成死机问题,可能是ADC转换,可能 是DMA,我使用贵公司FAE提供的代码(用TIM2触发ADC转换,转换结果用DMA传输到RAM),这个基础上简单的加上系统滴答,加上一个蜂鸣器,系统滴答用于延时,没有初始化ADC转换,DMA传输,蜂鸣器就不停的滴滴叫,打开ADC部分,就不吭声了,你说是我的代码问题还是MCU有问题?我将代码附上,麻烦你看看, 芯片型号是HC32L176KATA
页: [1] 2
查看完整版本: 华大单片机的DMA为啥搞个块大小出来?一次最多只能16个数...