问答

汇集网友智慧,解决技术难题

21ic问答首页 - TMS320F280049使用SPI作为从机通讯和ADC采样冲突问题

DSP TI 技术交流 使用 从机 TMS320F280049

TMS320F280049使用SPI作为从机通讯和ADC采样冲突问题 赏300家园币

qiuyuhai2025-07-16
我想请教一下各位大佬,遇到了下面这个问题,恳请提供建议和帮助,谢谢!描述和问题如下:
1.使用了SPIA,作为从机,比特率为15M,FIFO的接收和发送的深度都为8,同步收发16位的200个数组元素的数组。
2.SPI启用了DMA的CH4(RX)、CH5(TX),都为循环模式,burst_size=8,transfer_size=25,都开启了循环模式。
3.ADC使用了EPWM的SOC触发采样,使用了三个ADC,ADC1,、ADC2、ADC3,分别采样128个数据。
4.ADC分别使用了CH1、CH2、CH3
5.问题现象:现在的工况是工作是开启ADC采样DMA发送完成触发中断后调用函数执行FFT以及触发CLA任务完成后再次开启采样,直道工作接收停止这个循环过程;SPI配置好和主机同步后就一直通过DMA循环接收,中间不停止不重启,现在是只要不启动工作,SPI的通讯收发是正常的,但是只要开启工作,ADC一直开始采样,SPI的接收FIFO就会溢出,清除标志位也一样很快溢出,调试看DMA的发送完成中断触发的时间变慢了,一溢出通讯就异常了,是因为DMA总线被ADC一直大量占用了吗,还是中断的优先级比其他的低导致SPI的中断和DMA的中断相应不及时导致的,试了降低比特率和FIFO的阈值都起不到效果, 恳请给出好的建议!!!
一下是程序的一些配置:
void ADC_init(){
//AD_PI initialization
// ADC Initialization: Write ADC configurations and power up the ADC
// Configures the ADC module's offset trim
ADC_setOffsetTrimAll(ADC_REFERENCE_EXTERNAL,ADC_REFERENCE_3_3V);
// Configures the analog-to-digital converter module prescaler.
ADC_setPrescaler(AD_PI_BASE, ADC_CLK_DIV_2_0);
// Sets the timing of the end-of-conversion pulse
ADC_setInterruptPulseMode(AD_PI_BASE, ADC_PULSE_END_OF_CONV);
// Powers up the analog-to-digital converter core.
ADC_enableConverter(AD_PI_BASE);
// Delay for 1ms to allow ADC time to power up
DEVICE_DELAY_US(500);
// SOC Configuration: Setup ADC EPWM channel and trigger settings
// Disables SOC burst mode.
ADC_disableBurstMode(AD_PI_BASE);
// Sets the priority mode of the SOCs.
ADC_setSOCPriority(AD_PI_BASE, ADC_PRI_ALL_ROUND_ROBIN);
// Start of Conversion 0 Configuration
// Configures a start-of-conversion (SOC) in the ADC and its interrupt SOC trigger.
// SOC number : 0
// Trigger : ADC_TRIGGER_EPWM3_SOCA
// Channel : ADC_CH_ADCIN0
// Sample Window : 16 SYSCLK cycles
// Interrupt Trigger: ADC_INT_SOC_TRIGGER_NONE
ADC_setupSOC(AD_PI_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM3_SOCA, ADC_CH_ADCIN0, 16U);
ADC_setInterruptSOCTrigger(AD_PI_BASE, ADC_SOC_NUMBER0, ADC_INT_SOC_TRIGGER_NONE);
// ADC Interrupt 1 Configuration
// SOC/EOC number : 0
// Interrupt Source: enabled
// Continuous Mode : enabled
ADC_setInterruptSource(AD_PI_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER0);
ADC_enableInterrupt(AD_PI_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(AD_PI_BASE, ADC_INT_NUMBER1);
ADC_enableContinuousMode(AD_PI_BASE, ADC_INT_NUMBER1);
//AD_SI initialization
// ADC Initialization: Write ADC configurations and power up the ADC
// Configures the ADC module's offset trim
ADC_setOffsetTrimAll(ADC_REFERENCE_EXTERNAL,ADC_REFERENCE_3_3V);
// Configures the analog-to-digital converter module prescaler.
ADC_setPrescaler(AD_SI_BASE, ADC_CLK_DIV_2_0);
// Sets the timing of the end-of-conversion pulse
ADC_setInterruptPulseMode(AD_SI_BASE, ADC_PULSE_END_OF_CONV);
// Powers up the analog-to-digital converter core.
ADC_enableConverter(AD_SI_BASE);
// Delay for 1ms to allow ADC time to power up
DEVICE_DELAY_US(500);
// SOC Configuration: Setup ADC EPWM channel and trigger settings
// Disables SOC burst mode.
ADC_disableBurstMode(AD_SI_BASE);
// Sets the priority mode of the SOCs.
ADC_setSOCPriority(AD_SI_BASE, ADC_PRI_ALL_ROUND_ROBIN);
// Start of Conversion 0 Configuration
// Configures a start-of-conversion (SOC) in the ADC and its interrupt SOC trigger.
// SOC number : 0
// Trigger : ADC_TRIGGER_EPWM3_SOCA
// Channel : ADC_CH_ADCIN4
// Sample Window : 16 SYSCLK cycles
// Interrupt Trigger: ADC_INT_SOC_TRIGGER_NONE
ADC_setupSOC(AD_SI_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM3_SOCA, ADC_CH_ADCIN4, 16U);
ADC_setInterruptSOCTrigger(AD_SI_BASE, ADC_SOC_NUMBER0, ADC_INT_SOC_TRIGGER_NONE);
// ADC Interrupt 1 Configuration
// SOC/EOC number : 0
// Interrupt Source: enabled
// Continuous Mode : enabled
ADC_setInterruptSource(AD_SI_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER0);
ADC_enableInterrupt(AD_SI_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(AD_SI_BASE, ADC_INT_NUMBER1);
ADC_enableContinuousMode(AD_SI_BASE, ADC_INT_NUMBER1);
//AD_SU initialization
// ADC Initialization: Write ADC configurations and power up the ADC
// Configures the ADC module's offset trim
ADC_setOffsetTrimAll(ADC_REFERENCE_EXTERNAL,ADC_REFERENCE_3_3V);
// Configures the analog-to-digital converter module prescaler.
ADC_setPrescaler(AD_SU_BASE, ADC_CLK_DIV_2_0);
// Sets the timing of the end-of-conversion pulse
ADC_setInterruptPulseMode(AD_SU_BASE, ADC_PULSE_END_OF_CONV);
// Powers up the analog-to-digital converter core.
ADC_enableConverter(AD_SU_BASE);
// Delay for 1ms to allow ADC time to power up
DEVICE_DELAY_US(500);
// SOC Configuration: Setup ADC EPWM channel and trigger settings
// Disables SOC burst mode.
ADC_disableBurstMode(AD_SU_BASE);
// Sets the priority mode of the SOCs.
ADC_setSOCPriority(AD_SU_BASE, ADC_PRI_ALL_ROUND_ROBIN);
// Start of Conversion 0 Configuration
// Configures a start-of-conversion (SOC) in the ADC and its interrupt SOC trigger.
// SOC number : 0
// Trigger : ADC_TRIGGER_EPWM3_SOCA
// Channel : ADC_CH_ADCIN0
// Sample Window : 16 SYSCLK cycles
// Interrupt Trigger: ADC_INT_SOC_TRIGGER_NONE
ADC_setupSOC(AD_SU_BASE, ADC_SOC_NUMBER0, ADC_TRIGGER_EPWM3_SOCA, ADC_CH_ADCIN0, 16U);
ADC_setInterruptSOCTrigger(AD_SU_BASE, ADC_SOC_NUMBER0, ADC_INT_SOC_TRIGGER_NONE);
// ADC Interrupt 1 Configuration
// SOC/EOC number : 0
// Interrupt Source: enabled
// Continuous Mode : enabled
ADC_setInterruptSource(AD_SU_BASE, ADC_INT_NUMBER1, ADC_SOC_NUMBER0);
ADC_enableInterrupt(AD_SU_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(AD_SU_BASE, ADC_INT_NUMBER1);
ADC_enableContinuousMode(AD_SU_BASE, ADC_INT_NUMBER1);
}
void FB_start(void)
{
EPWM_clearADCTriggerFlag(EPWM_AD_BASE, EPWM_SOC_A);
ADC_clearInterruptStatus(AD_PI_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(AD_SI_BASE, ADC_INT_NUMBER1);
ADC_clearInterruptStatus(AD_SU_BASE, ADC_INT_NUMBER1);
DMA_clearTriggerFlag(DMA_CH1_BASE);
DMA_clearTriggerFlag(DMA_CH2_BASE);
DMA_clearTriggerFlag(DMA_CH3_BASE);

// Clearing all pending interrupt flags & Start DMA
//DMA_clearTriggerFlag(DMA_CH1_BASE);
DMA_startChannel(DMA_CH1_BASE);
//DMA_clearTriggerFlag(DMA_CH2_BASE);
DMA_startChannel(DMA_CH2_BASE);
//DMA_clearTriggerFlag(DMA_CH3_BASE);
DMA_startChannel(DMA_CH3_BASE);
EPWM_enableADCTrigger(EPWM_AD_BASE, EPWM_SOC_A);
}
SPI配置:
SPi_To_N32_Init();
// //
// // Initialize DMA
// //
// DMA_initController();
DMA_triggerSoftReset(DMA_CH4_BASE);
DMA_triggerSoftReset(DMA_CH5_BASE);
//----------------------------------------------------------------------------------------------------------------------------------
// DMA channel 4 set up for SPI_RX
DMA_configAddresses(DMA_CH4_BASE, (uint16_t *)DSP_RxArray, (uint16_t *)(SCR_SPI_BASE + SPI_O_RXBUF));
// Perform enough 16-word bursts to fill the results buffer. Data will be
// transferred 32 bits at a time hence the address steps below.
DMA_configBurst(DMA_CH4_BASE, burst_size, 0, 1);
DMA_configTransfer(DMA_CH4_BASE, transfer_size, 0, 1);
DMA_configMode(DMA_CH4_BASE, DMA_TRIGGER_SPIARX, (DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE | DMA_CFG_SIZE_16BIT));
//
// Configure DMA Ch4 interrupts
//
DMA_setInterruptMode(DMA_CH4_BASE, DMA_INT_AT_END);
DMA_enableInterrupt(DMA_CH4_BASE);
DMA_enableTrigger(DMA_CH4_BASE);
// DMA channel 5 set up for SPI_TX
DMA_configAddresses(DMA_CH5_BASE, (uint16_t *)(SCR_SPI_BASE + SPI_O_TXBUF), (uint16_t *)DSP_TxArray);
// Perform enough 16-word bursts to fill the results buffer. Data will be
// transferred 32 bits at a time hence the address steps below.
DMA_configBurst(DMA_CH5_BASE, burst_size, 1, 0);
DMA_configTransfer(DMA_CH5_BASE, transfer_size, 1, 0);
DMA_configMode(DMA_CH5_BASE, DMA_TRIGGER_SPIATX, (DMA_CFG_ONESHOT_DISABLE | DMA_CFG_CONTINUOUS_ENABLE | DMA_CFG_SIZE_16BIT));
//
// Configure DMA Ch5 interrupts
//
DMA_setInterruptMode(DMA_CH5_BASE, DMA_INT_AT_END);
DMA_enableInterrupt(DMA_CH5_BASE);
DMA_enableTrigger(DMA_CH5_BASE);
//SCR_SPI initialization
SPI_disableModule(SCR_SPI_BASE);
SPI_clearInterruptStatus(SCR_SPI_BASE, SPI_INT_RXFF | SPI_INT_TXFF);
SPI_setFIFOInterruptLevel(SCR_SPI_BASE, SPI_FIFO_TX8, SPI_FIFO_RX8);
SPI_setConfig(SCR_SPI_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA1,
SPI_MODE_SLAVE, 15000000, 16);
SPI_disableLoopback(SCR_SPI_BASE);
SPI_enableFIFO(SCR_SPI_BASE);
SPI_setEmulationMode(SCR_SPI_BASE, SPI_EMULATION_STOP_MIDWAY);
SPI_enableInterrupt(SCR_SPI_BASE, SPI_INT_RXFF | SPI_INT_TXFF);
SPI_enableModule(SCR_SPI_BASE);
// SPI_FIFO
DMA_startChannel(DMA_CH4_BASE);
DMA_startChannel(DMA_CH5_BASE);
}
回答 +关注 5
21305人浏览 4人回答问题 分享 举报
4 个回答
  • 学习了
  • 星塔守护 发表于 2025-7-21 19:09
    建议你把程序里面所有的中断程序的执行时间确认清楚,主要是ADC中断(计算EPWM占空比?)、SPI DMA中断这两个 ...

    是的,还是要先梳理一下代码,然后分模块进行调试。
  • 根据您的描述和配置,问题很可能是由于DMA总线带宽竞争和中断响应延迟导致的问题根因分析
    1. DMA总线带宽竞争:ADC三个通道同时采样,每个通道128个数据,加上SPI的15Mbps高速传输,导致DMA总线过载
    2. 中断优先级冲突:ADC采样完成中断、SPI DMA中断、CLA任务可能产生优先级冲突
    3. 内存访问冲突:多个DMA通道同时访问内存造成瓶颈

    解决方案建议
    1. 优化DMA配置

    // 降低SPI DMA的burst size,减少单次传输占用总线时间
    DMA_configBurst(DMA_CH4_BASE, 4, 0, 1);  // 从8降到4
    DMA_configBurst(DMA_CH5_BASE, 4, 1, 0);
    // 调整transfer size,分更小的块传输
    DMA_configTransfer(DMA_CH4_BASE, 50, 0, 1);  // 25->50,但每次传输量减半
    DMA_configTransfer(DMA_CH5_BASE, 50, 1, 0);

    2. 调整中断优先级

    // 提高SPI相关中断优先级
    Interrupt_register(INT_DMA_CH4, &SPI_RX_DMA_ISR);
    Interrupt_register(INT_DMA_CH5, &SPI_TX_DMA_ISR);

    // 设置更高的优先级(数值越小优先级越高)
    Interrupt_setPriority(INT_DMA_CH4, 0);
    Interrupt_setPriority(INT_DMA_CH5, 1);

    // 降低ADC相关中断优先级
    Interrupt_setPriority(INT_DMA_CH1, 4);
    Interrupt_setPriority(INT_DMA_CH2, 5);
    Interrupt_setPriority(INT_DMA_CH3, 6);

    3. 优化ADC采样策略

    // 考虑交错ADC采样,减少同时进行的DMA传输
    void FB_start_optimized(void) {
        // 先启动一个ADC,延迟后再启动其他的
        DMA_startChannel(DMA_CH1_BASE);
        DEVICE_DELAY_US(10);  // 微小延迟
       
        DMA_startChannel(DMA_CH2_BASE);
        DEVICE_DELAY_US(10);
       
        DMA_startChannel(DMA_CH3_BASE);
       
        EPWM_enableADCTrigger(EPWM_AD_BASE, EPWM_SOC_A);
    }

    4. 优化SPI配置

    void optimize_SPI_config(void) {
        SPI_disableModule(SCR_SPI_BASE);
       
        // 降低FIFO阈值,更频繁但更小的DMA传输
        SPI_setFIFOInterruptLevel(SCR_SPI_BASE, SPI_FIFO_TX4, SPI_FIFO_RX4);
       
        // 如果可能,适当降低SPI比特率
        SPI_setConfig(SCR_SPI_BASE, DEVICE_LSPCLK_FREQ, SPI_PROT_POL0PHA1,
                      SPI_MODE_SLAVE, 10000000, 16);  // 15M->10M
       
        SPI_enableModule(SCR_SPI_BASE);
    }

    5. 添加流量控制机制

    // 在SPI接收中断中添加溢出保护
    __interrupt void SPI_RX_DMA_ISR(void) {
        if(SPI_getRxFIFOStatus(SCR_SPI_BASE) == SPI_FIFO_RXOVER) {
            // 紧急处理:暂停ADC采样,清空FIFO
            EPWM_disableADCTrigger(EPWM_AD_BASE, EPWM_SOC_A);
            SPI_clearRxFIFO(SCR_SPI_BASE);
            SPI_clearInter

            EPWM_enableADCTrigger(EPWM_AD_BASE, EPWM_SOC_A);
        }
        DMA_clearInterruptStatus(DMA_CH4_BASE);
    }

    6. 内存访问优化
    确保DMA缓冲区在内存中的位置不会产生冲突

    // 使用不同的内存段存放不同DMA通道的数据
    #pragma DATA_SECTION(DSP_RxArray, "DMARAM4");
    #pragma DATA_SECTION(DSP_TxArray, "DMARAM5");
    #pragma DATA_SECTION(AD_PI_Results, "DMARAM1");
    #pragma DATA_SECTION(AD_SI_Results, "DMARAM2");
    #pragma DATA_SECTION(AD_SU_Results, "DMARAM3");


    7. 监控和调试建议
    添加调试代码来确认问题


    // 在关键位置添加时间戳监控
    uint32_t get_time_stamp(void) {
        return CPU_TIMER2_TIM->TIMH;  // 使用一个定时器作为时间基准
    }

    // 在ISR中记录时间
    __interrupt void SPI_RX_DMA_ISR(void) {
        uint32_t enter_time = get_time_stamp();
        // ... ISR处理
        uint32_t exit_time = get_time_stamp();
        if((exit_time - enter_time) > MAX_ISR_TIME) {
            // 记录ISR执行时间过长
        }
    }


    推荐实施顺序
    首先调整中断优先级(方案2)
    2. 然后优化DMA burst size(方案1)
    3. 接着交错ADC启动(方案3)
    4. 最后如果问题依旧,考虑降低SPI速率(方案4)
    这种分层优化应该能显著改善SPI FIFO溢出问题。如果实施后仍有问题,可能需要考虑硬件层面的优化,



  • 建议你把程序里面所有的中断程序的执行时间确认清楚,主要是ADC中断(计算EPWM占空比?)、SPI DMA中断这两个的执行时间,再看进入中断的频率,就可以知道是否有占用了。从调试的角度,把进入中断的频率降低,看问题是否改善也可以

您需要登录后才可以回复 登录 | 注册