0

我正在使用带有原型的 SAMD21 xPlained pro 来获取信号,使用配置为收集 5500 个样本的内部 ADC 和 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);
}
4

2 回答 2

0

我遇到了一个非常相似的问题。尽管我插入了 USB,但我的 SPI 工作正常,但是当我从特定的 USB-CDC 命令启动它时,它开始出现此问题。我正在使用 asf 3.34 和 as 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;
    }
}

whereSPI_done()检查寄存器transfer_tx_is_done并由中断设置(有时有效[我说它很脏])

于 2017-03-27T15:14:41.757 回答
0

我不是 USB 方面的专家,这可能与您的情况无关,但我也遇到了USB-CDC 的问题以及UC3A3芯片偶尔冻结的问题,我发现了两个不同的原因:

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

    我在进入和退出我得到的每个中断(包括USB)时设置测试位,如果它们太接近而无法重叠,就会发生奇怪的事情,比如冻结、输出信号抖动(比所有ISR一起更大)、同步和确认即使USB具有最高优先级,也会出现错误等。如果我重新计时我使用的所有东西,那么中断不会同时触发,所有工作都很好。

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

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

    我使用我的MCU进行全双工同步批量传输,并获得 3 层FIFO(主机上 2层, MCU上 1 层)来保持同步(连续 24/7进出)~640KByte/s对于每个新版本的~640KByte/sWindows(w2k -> Xp --> w7

    我在 W7 中发现的最新一件事(他们添加了新的“功能”)是主机端的一些USB控制器(如英特尔)要么自行冻结,要么冻结数据传输的不同优先级(基于管道或方向)和发送/接收比率1:1在某些机器上不再起作用,由于FIFO阻塞导致MCU端冻结1ms长达几秒钟。解决方法是完全填充接收FIFO的MCU(或增加MCU FIFO大小,这在我的情况下是不可能的,因为它已经占用了几乎所有内存)无论如何都应该完成,但在我的情况下,我在RT中工作并且前面没有很多数据包,所以我发现我需要发送至少 3 倍的数据包然后接收,直到FIFO随着时间的推移完全填充(并且每次主机发送冻结,这对于某些机器来说一直是)不要失去同步,这些机器上的所有半满FIFO机制不再起作用。

因此,如果您的USB传输与主机同步或相反,如果问题也存在,则值得检查不同的操作系统(如Xp )。如果不是,您很有可能遇到我正在处理的类似问题,因此您可以尝试解决方法...

于 2017-04-05T08:26:02.550 回答