TMS320F280049使用SPI作为从机通讯和ADC采样冲突问题

[复制链接]
18117|3
 楼主| qiuyuhai 发表于 2025-7-16 13:34 | 显示全部楼层 |阅读模式
我想请教一下各位大佬,遇到了下面这个问题,恳请提供建议和帮助,谢谢!描述和问题如下:
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);
}

评论

自定义指令集,自研内核架构,基于eclipse自研IDE,工具链,算法库。 根据自研QXS320F280049,做了600W和2KW数字电源方案,1.5KW电机方案,目前已在市场大量投产。 QXS320F290049应用于数字电源,光伏逆变器,充电桩,OBC,储能,变频器,伺服控制等 QXS320F2800137应用于三轮车、两轮车充电器OBC,小功率数字电源,模块电源,家电电机控制等 QXS320F28034应用于变频器 QXS320F28377D应用于电...  发表于 2025-7-23 13:48
星塔守护 发表于 2025-7-21 19:09 | 显示全部楼层

TMS320F280049使用SPI作为从机通讯和ADC采样冲突问题

建议你把程序里面所有的中断程序的执行时间确认清楚,主要是ADC中断(计算EPWM占空比?)、SPI DMA中断这两个的执行时间,再看进入中断的频率,就可以知道是否有占用了。从调试的角度,把进入中断的频率降低,看问题是否改善也可以
sunjd 发表于 2025-10-27 17:15 | 显示全部楼层

TMS320F280049使用SPI作为从机通讯和ADC采样冲突问题

根据您的描述和配置,问题很可能是由于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溢出问题。如果实施后仍有问题,可能需要考虑硬件层面的优化,



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

本版积分规则

1

主题

2

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部