打印
[技术问答]

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

[复制链接]
4055|31
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
GZZXB|  楼主 | 2019-4-15 22:08 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
算是阉割版DMA吗? STM32多爽想传多少就传多少,华大最多只能一次传16吧,超过16就得用软件+中断实现了。

使用特权

评论回复
沙发
je段| | 2019-4-18 15:08 | 只看该作者
技术呢,出来帮忙解释下啊我只负责卖

使用特权

评论回复
评论
21ic小管家 2019-4-18 15:40 回复TA
快把你们公司的技术拽出来上论坛呦~会加大销量的~ 
板凳
smartpower| | 2019-4-18 21:13 | 只看该作者
1. 设置块数:(从 1 至 16 中选择)
2. 设置传输长度:(从 1 至 65536 中选择)
3. 总传输长度可达为  16 * 65536 个字节。

使用特权

评论回复
地板
operating| | 2019-4-28 16:31 | 只看该作者
应该可以多传的吧

使用特权

评论回复
5
martinhu| | 2019-4-29 09:40 | 只看该作者


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

使用特权

评论回复
6
GZZXB|  楼主 | 2019-5-2 13:11 | 只看该作者
martinhu 发表于 2019-4-29 09:40
BC块数:(从 1 至 16 中选择)
TC传输长度:(从 1 至 65536 中选择)
BC和TC合理使用,可以实现很多种 ...

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

使用特权

评论回复
7
martinhu| | 2019-5-5 08:48 | 只看该作者
GZZXB 发表于 2019-5-2 13:11
需要软件+中断 才能实现。不像STM32设置好了,不用中断不用写代码处理。

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

使用特权

评论回复
8
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-3 11:41 回复TA
@martinhu :测试了DMA,地址重新装载只能在全部完成后,不能触发一次 源地址重新装载。是哪里设置错误了吗? 
martinhu 2019-5-7 10:56 回复TA
如果DMA传送完成中断和ADT的AOS功能,你都不想用,按上述的设置方法,DMAC寄存器CONFBx的MSK位设置为0,那么DMA传送255次之后自然清除了该通道的使能位,你只需要在下次使用时候配置和使能就好了 
martinhu 2019-5-7 10:51 回复TA
能啊,BC设置为2(block数据为3),TC设置为254(传送255次),模式为硬件块传输,使用timer0~6其中之一触发ADC转换3个通道,ADC转换结束后触发DMA搬运,ADC转换255次(也就是DMA搬运255次)之后,可以使用DMA转换完成中断,结束你要的255*3次的采样操作。如果你实在不想要中断,那么用ADT0(timer4)触发ADC采样,ADT1(timer5)通过AOS的硬件计数采样次数,到255次之后timer5通过AOS停止timer4! 
9
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[ADC_BC*10] = {0};
uint16_t ADC_array_write_index = 0;
uint32_t u32AdcRestult_VSEN;
uint32_t u32AdcRestult_I1;
uint32_t u32AdcRestult_I2;
extern QXSemaphore  sema_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[0] = Adc_GetSqrResult(AdcSQRCH1MUX);
//        u16AdcRestult_I1_buf[1] = Adc_GetSqrResult(AdcSQRCH2MUX);
//        u16AdcRestult_I1_buf[2] = Adc_GetSqrResult(AdcSQRCH3MUX);
//        u16AdcRestult_I1_buf[3] = Adc_GetSqrResult(AdcSQRCH4MUX);
//        u16AdcRestult_I2 = Adc_GetSqrResult(AdcSQRCH5MUX);
//        u16AdcRestult_I2_buf[0] = Adc_GetSqrResult(AdcSQRCH5MUX);
//        u16AdcRestult_I2_buf[1] = Adc_GetSqrResult(AdcSQRCH6MUX);
//        u16AdcRestult_I2_buf[2] = Adc_GetSqrResult(AdcSQRCH7MUX);
//        u16AdcRestult_I2_buf[3] = 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*10];
            ADC_array_write_index++;
        }else{
            ADC_array_write_index = 0;
            M0P_DMAC->DSTADR0 = (uint32_t)&ADC_Result_Array[ADC_array_write_index];
            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_t  stcBtTrigAdc;
    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[0];   //指定传输目的地址
    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[0];//指定传输源地址
//    stcDmaCfg.u32DstAddress = (uint32_t)&ADC_Result_buf_Array[0];   //指定传输目的地址
//    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);
}

使用特权

评论回复
10
mtbf| | 2020-4-9 22:21 | 只看该作者
不好意思,有技术支持能确认下吗?

使用特权

评论回复
11
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。

使用特权

评论回复
12
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传输。

使用特权

评论回复
13
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笔数据的搬运。

使用特权

评论回复
14
詹求实| | 2020-4-29 18:51 | 只看该作者
liaotian001 发表于 2020-4-23 11:47
ADC_SqrResult0 - 15,一共16个数据,我们的DMA可以支持一次搬运完成的。硬件设计中,ADC采样完成最后一 ...

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

使用特权

评论回复
15
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。

使用特权

评论回复
16
舒伯特玫瑰| | 2020-4-30 17:55 | 只看该作者
不是必须中断吧,那样太费事了

使用特权

评论回复
17
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传输结果,启动就死机了

使用特权

评论回复
18
liaotian001| | 2020-5-8 09:35 | 只看该作者
lesheng002 发表于 2020-5-7 20:21
照这个说法,DMA传输影响MCU的工作了,难怪我设计的ADC检测,用DMA传输结果,启动就死机了 ...

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

使用特权

评论回复
19
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即使连续传输中间也是有间隔的

使用特权

评论回复
20
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

dma_hw_trigger_block.zip

2.13 MB

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

96

主题

331

帖子

10

粉丝