使用USB时,Atmel SAM D21 DMA处于忙碌状态

时间:2017-03-13 07:23:04

标签: usb atmel dma asf

我正在使用带有原型的SAMD21 xPlained pro来获取信号,使用内部ADC和配置为收集5500个样本的DMAC。 然后通过目标USB将样本传输到PC应用程序。

我为此编写的固件正在运行,但我注意到DMA不时会陷入忙碌状态。 为了调试我通过USB取消了传输部件到PC。

我注意到DMA工作正常并将样本传输到内存,但如果我将USB电缆连接到PC(不通过USB传输数据),DMA会不时被卡住; 但是一旦断开(目标)USB电缆,DMA就会连续工作而不会陷入繁忙状态。

我怀疑它与USB和ADC的中断和优先级有关,它们都使用DMA。 我认为我应该将ADC采样设置为最高优先级,这样USB就不会导致DMA卡在忙碌状态,但我找不到如何在代码中配置(我使用的是ASF)。

任何想法为什么插入USB会导致DMA偶尔陷入忙碌状态? 我怀疑这个问题是否与优先级有关,是否有任何想法如何降低USB中断优先级?

代码:

void GSWatchMainProcess(void)
{
    static int i=0;

    GSPacket PacketToStream;

    switch (KnockKnockStateMachine)
    {
        case InitKnockKnock:
            KnockKnockStateMachine = KnockKnockStandby;
            break;

        case KnockKnockStandby:
            if (StreamADC && !RequestForAcknowledge) KnockKnockStateMachine = WakeupAphrodite;
            KnockKnockStateMachine = WakeupAphrodite;       //this line was added to skip waiting for a command from the PC
            break;

        case WakeupAphrodite:
            if (dma_is_busy(&example_resource))
            {
                KnockKnockStateMachine = AbortKnockKnock;
            }
            else
            {
                port_pin_set_output_level(PIN_PB09, true);
                port_pin_set_output_level(LED_0_PIN, false);
                transfer_is_done = false;
                if(dma_start_transfer_job(&example_resource))
                {
                    KnockKnockStateMachine = AbortKnockKnock;
                }
            }

            KnockKnockStateMachine = WaitForBurstToEnd;
            i=200000;       //this counter is used as work-around to reset the DMA when it get stuck after timeout
            break;

        case WaitForBurstToEnd:
            if (!dma_is_busy(&example_resource))
            {
                KnockKnockStateMachine = ProcessBurst;
            }
            if(!--i)        // work-around to reset DMA when it get stuck
            {
                KnockKnockStateMachine = AbortKnockKnock;
            }
            break;

        case ProcessBurst:
            PacketToStream.Type=Stream;
            PacketToStream.ContentLength=0;
            for (i = 0; i<(ADC_SAMPLES); i++)
            {
                PacketToStream.Content[PacketToStream.ContentLength++] = adc_result_buffer[i] / 256;
                PacketToStream.Content[PacketToStream.ContentLength++] = adc_result_buffer[i] % 256;
                if(PacketToStream.ContentLength>=PACKET_MAX_SIZE)
                {
                    //SendViaGSWatchLink(PacketToStream);
                    PacketToStream.ContentLength=0;
                }
            }
            //if(PacketToStream.ContentLength>0) SendViaGSWatchLink(PacketToStream);
            RequestForAcknowledge = true;
            KnockKnockStateMachine = KnockKnockStandby;
            break;

        case AbortKnockKnock:
            dma_abort_job(&example_resource);
            dma_free(&example_resource);
            port_pin_set_output_level(PIN_PB09, false);
            port_pin_set_output_level(LED_0_PIN, true);
            transfer_is_done = true;

            configure_dma_resource(&example_resource);
            setup_transfer_descriptor(&DMA_ADC_descriptor);
            dma_add_descriptor(&example_resource, &DMA_ADC_descriptor);
            dma_register_callback(&example_resource, transfer_done, DMA_CALLBACK_TRANSFER_DONE);
            dma_enable_callback(&example_resource, DMA_CALLBACK_TRANSFER_DONE);
            system_interrupt_enable_global();

            KnockKnockStateMachine = WakeupAphrodite;
            break;
    }
}




void transfer_done(struct dma_resource* const resource )
{
    transfer_is_done = true;
    port_pin_set_output_level(PIN_PB09, false);
    port_pin_set_output_level(LED_0_PIN, true);

}
void setup_transfer_descriptor(DmacDescriptor *descriptor)
{
    struct dma_descriptor_config descriptor_config;
    dma_descriptor_get_config_defaults(&descriptor_config);
    descriptor_config.beat_size = DMA_BEAT_SIZE_HWORD;
    descriptor_config.block_transfer_count = ADC_SAMPLES;
    descriptor_config.dst_increment_enable = true;
    descriptor_config.src_increment_enable = false;
    descriptor_config.source_address = (uint32_t)(&adc_instance.hw->RESULT.reg);
    //descriptor_config.destination_address = (uint32_t)adc_result_buffer + 2 * ADC_SAMPLES;
    descriptor_config.destination_address = (uint32_t)adc_result_buffer + sizeof(adc_result_buffer);
    dma_descriptor_create(descriptor, &descriptor_config);
}
void configure_dma_resource(struct dma_resource *resource)
{
    struct dma_resource_config config_dma;
    dma_get_config_defaults(&config_dma);
    config_dma.peripheral_trigger = ADC_DMAC_ID_RESRDY;
    config_dma.trigger_action = DMA_TRIGGER_ACTON_BEAT;
    config_dma.priority = DMA_PRIORITY_LEVEL_3;
    dma_allocate(resource, &config_dma);
}
void configure_adc(void)
{
    struct adc_config config_adc;
    adc_get_config_defaults(&config_adc);
    //  config_adc.gain_factor          = ADC_GAIN_FACTOR_DIV2;  //TODO: check if we need this feature
    config_adc.clock_prescaler      = ADC_CLOCK_PRESCALER_DIV32; //TODO: check whether it possible to work with 8
    config_adc.reference            = ADC_REFERENCE_INTVCC0; //ADC_REFERENCE_INT1V; //ADC_REFERENCE_INTVCC1;
    config_adc.positive_input       = ADC_POSITIVE_INPUT_PIN8;
    config_adc.negative_input       = ADC_NEGATIVE_INPUT_GND;
    config_adc.resolution           = ADC_RESOLUTION_12BIT;
    config_adc.sample_length        = 0;    //4
    config_adc.differential_mode    = false;
    config_adc.freerunning          = true;

    adc_init(&adc_instance, ADC, &config_adc);
    adc_enable(&adc_instance);
}

2 个答案:

答案 0 :(得分:0)

我遇到了一个非常相似的问题。我的SPI工作正常,虽然我插入了USB,但是当我从特定的USB-CDC命令启动它时就开始了这个问题。 我正在使用asf 3.34作为7和L21(非常相似)。 我的解决方法不干净但有效: 在开始DMA传输之后,我连续(while-loop)检查传输完成位(REG_DMAC_CHINTFLAG应该是0x2),当它是时,我将状态设置为ok。 在代码中:

transfer_tx_is_done=0;
dma_start_transfer_job(&res_2_Byte);

while (!transfer_tx_is_done) {
    /* Wait for transfer done */
    if (SPI_done())
    {
        res_2_Byte.job_status=STATUS_OK;
        break;
    }
}

其中SPI_done()检查寄存器,transfer_tx_is_done将由中断设置(有时工作[我说它很脏])

答案 1 :(得分:0)

我不是USB的专家,这可能与你的情况无关,但我也遇到了 USB-CDC 的问题,偶尔冻结 UC3A3 芯片我发现了2单独的原因:

  1. USB中断和任务必须有足够的空闲MCU时间

    我正在设置测试位进入和退出我得到的每个中断(包括 USB ),如果它们太接近重叠,奇怪的事情就像冻结,输出信号抖动(更大的方式)一样发生然后所有 ISR 一起),同步和确认错误等,即使 USB 具有最高优先级。如果我重新计算我使用的所有东西,那么中断不会在同一时间触发,一切正常。

    当心 GPIO 切换操作缓慢,因此您也需要考虑到这一点。

  2. 每个版本的主机操作系统(Windows)都有不同的时间

    我使用 MCU 进行全双工同步批量传输,并获得3层 FIFO (主机2个, MCU 1个)保持同步(连续24/7 ~640KByte/s in和~640KByte/s out)。对于每个新版本的Windows(w2k -> Xp --> w7),在线程或驱动程序服务的调度和重新计时都需要更改传输时间和超时,以便锁定更少的多线程用于 RT同步USB传输仍然按原样工作。

    我在W7中发现的最新内容(他们添加了新的&#34;功能&#34;)是主机端的某些 USB 控制器(如英特尔)要么自行冻结,要么不同的优先级数据传输(基于管道或方向)和发送/接收比率 1:1 不再适用于某些机器导致 MCU 端冻结{{1}由于 FIFO 被阻止,最多几秒钟。解决方法是完全填充 MCU 接收 FIFO (或增加 MCU FIFO 大小,这在我的情况下是不可能的因为它几乎已经占用所有内存了)无论如何应该这样做,但在我的情况下,我在 RT 工作并且没有很多数据包,所以我发现我需要发送至少3倍的数据包然后接收到 FIFO 的完全填充时间(以及每次主机发送冻结,这对于某些机器一直都是这样),只是不丢失同步和所有半满 FIFO

  3. 因此,如果您的 USB 转移与主机同步或相反,则值得检查不同的操作系统(例如 Xp )如果问题也在那里。如果没有,你很可能遇到类似的问题,所以你可以尝试解决方法......