This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] CC2652P:两个 SPI、一个使用 driverlib (UDMA、SSI)、一个使用 TI 驱动程序。 一旦 TI 驱动程序 SPI 使用 DMA、它就会停止工作。 问题出在哪里?

Guru**** 1824470 points
Other Parts Discussed in Thread: CC2652P, ADS131M06, SYSCONFIG
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/wireless-connectivity/bluetooth-group/bluetooth/f/bluetooth-forum/1006623/cc2652p-two-spis-one-using-driverlib-udma-ssi-one-using-ti-drivers-as-soon-as-the-ti-drivers-spi-uses-dma-it-stops-working-where-is-the-problem

器件型号:CC2652P
Thread 中讨论的其他部件:ADS131M06SysConfig

大家好!

我们正在使用 CC2652P 微控制器中提供的两个 SPI 外设同时读取两个 SPI ADC (2个 ADS131M06)并写入 SPI 闪存器件。 由于 ADC 所需的数据速率相当高(16kHz)、两个 ADC 的 SPI 配置如下:

  • SPI0使用 driverlib (udma.h 和 SSI.h)以及 DMA 通道3和4
  • 手动 CS 引脚配置(一个为高电平有效、一个为低电平有效)
  • 由零延迟定时器(HWI 优先级0)生成的16kHz 中断、绕过 RTOS  
  • 写入 FLASH_queue

然后、当 FLASH_queue 被填满时、一个较低优先级的闪存 RTOS 任务将向闪存写入大量数据(4228字节)。 闪存 SPI 配置如下:

  • SPI1使用带有 DMA 的 SysConfig 和 TI 驱动程序(SPI.h)
  • 手动 CS 引脚配置( 手动拉低并重新拉高)  
  • 将 flash_queue 的内容写入闪存

问题是、每当它使用 DMA 时-传输量超过10时立即自动执行:SPICC26X2DMA.c 第755行中的 transaction->count < hwAttrs->minDmaTransferSize -闪存任务永远不会 在785行的信标挂起时返回并 被抢占。  DMA 在第二个 SPI 实例中无法正常工作的原因可能是什么? 我已附上以下相关代码行。

如果有任何帮助、我们将不胜感激!

FLASH.c

// Simplified code of FLASH.c

// HWI Interrupt generated by low latency driver through Hwi_post()
void _Flash_Data_Ready_Notifier(UArg arg1) {
    Semaphore_post(flash_ready_semaphore);
}

void Flash_Execute(UArg arg0, UArg arg1) {
    bool request_busy = false;
    while(1) {
        // Binary semaphore to wait for data ready
        Semaphore_pend(flash_ready_semaphore, BIOS_WAIT_FOREVER);
        
        // ... configure 4228 bytes to transfer
        Flash_SPI_Transaction.count = length;
        GPIO_write(CONFIG_SPI_FLASH_CS, 0);
        SPI_transfer(_Flash_SPI_Handle, &Flash_SPI_Transaction);
        GPIO_write(CONFIG_SPI_FLASH_CS, 1);
    }
}

adc.c

GPTimerCC26XX_Handle gptHandle;
extern const GPTimerCC26XX_Config GPTimerCC26XX_config[];
void ADC_Timer_Zero_Latency_Callback(uintptr_t a0);
#endif

/* Lookup table definition for interfacing driverlib and register fields.
Used to simplify code and to easily look up register fields as several fields
are not symmetric across timer A and timer B registers (interrupts & dma)
*/
typedef struct GPTimerCC26XX_LUT
{
uint16_t map; /* Timer argument in driverlib (TIMER_A / TIMER_B) */
uint16_t shift; /* Bit shift for registers shared between GPT_A / GPT_B */
uint16_t offset; /* Byte offset for registers sequentially in memory map for GPT_A/GPT_B */
uint16_t interrupts[GPT_NUM_INTS]; /* Interrupt bitfields for GPTA/B. Order must match GPTimerCC26XX_Interrupt */
} const GPTimerCC26XX_LUT;

/* Lookup table definition for interfacing driverlib and register fields. */
static const GPTimerCC26XX_LUT GPT_LUT[GPT_PARTS_COUNT] =
{
{
.map = TIMER_A,
.shift = 0,
.offset = 0,
.interrupts ={ GPT_MIS_TATOMIS, GPT_MIS_CAMMIS, GPT_MIS_CAEMIS, GPT_MIS_TAMMIS },
},
{
.map = TIMER_B,
.shift = 8,
.offset = 4,
.interrupts ={ GPT_MIS_TBTOMIS, GPT_MIS_CBMMIS, GPT_MIS_CBEMIS, GPT_MIS_TBMMIS },
},
};

void ADC_Timer_Zero_Latency_Callback(uintptr_t a0) {
GPTimerCC26XX_Handle handle = (GPTimerCC26XX_Handle)gptHandle;
GPTimerCC26XX_HWAttrs const *hwAttrs = gptHandle->hwAttrs;
uint32_t timer = GPT_LUT[handle->timerPart].map;
uint32_t interrupts = HWREG(hwAttrs->baseAddr + GPT_O_MIS);
uint32_t interruptClr = timer & interrupts;
HWREG(hwAttrs->baseAddr + GPT_O_ICLR) = interruptClr;

ADS131_Read();
}


void ADC_Init() {
ADS131_Init(...);

struct ADS131_Config_t ads131_config = {...};
ADS131_Setup(&ads131_config);

// Timer Interrupt of ADS131 ADC at 16 kHz using Hardware Timer 0
Timer_Params_init(&adc_timer_params);
adc_timer_params.period = 16 kHz
adc_timer_params.periodUnits = Timer_PERIOD_COUNTS;
adc_timer_params.timerMode = Timer_CONTINUOUS_CALLBACK;
adc_timer_params.timerCallback = ADC_Timer_Callback;
adc_timer_handle = Timer_open(CONFIG_TIMER_0, &adc_timer_params);

TimerCC26XX_HWAttrs *timerHwAttrs = (TimerCC26XX_HWAttrs *)adc_timer_handle->hwAttrs;
gptHandle = (GPTimerCC26XX_Handle)&GPTimerCC26XX_config[timerHwAttrs->gpTimerUnit];

GPTimerCC26XX_HWAttrs const *gptHwAttrs = gptHandle->hwAttrs;
GPTimerCC26XX_Object *gptObject = gptHandle->object;

/* Construct RTOS HWI */
HwiP_Struct *pHwi = &gptObject->hwi[gptHandle->timerPart];
HwiP_Params hp;
HwiP_Params_init(&hp);
hp.arg = (uintptr_t)gptHandle;
hp.enableInt = true;
hp.priority = 0;

GPTimerCC26XX_unregisterInterrupt(gptHandle);
HwiP_construct(pHwi, gptHwAttrs->intNum, ADC_Timer_Zero_Latency_Callback, &hp);

HwiP_enableInterrupt(gptHwAttrs->intNum);
uint32_t ui32Base = gptHwAttrs->baseAddr;
/* Enable interrupts in timer unit */
TimerIntEnable(ui32Base, TIMER_TIMA_TIMEOUT);
}
ADS131.c
/* The control table used by the uDMA controller, primary and alternate for 31 channels */
tDMAControlTable DMAControlTable[64] __attribute__ ((aligned (1024)));

void ADS131_Read() {
ADS131_Data_Ready_Callback(_ADS131_SPI_RX_Buffer_1, _ADS131_SPI_RX_Buffer_2);

// Perform ADC 0 (active low) readout by writing a zero frame. The ADC data will be in RX_Buffer[1] ... RX_Buffer[N - 2]
_ads131_active_adc = !_ads131_active_adc;
HWREGB(GPIO_BASE + GPIO_O_DOUT3_0 + IOID_8) = _ads131_active_adc;

if(!(uDMAChannelIsEnabled (UDMA0_BASE, UDMA_CHAN_SSI0_RX))) {
uDMAChannelTransferSet(
UDMA0_BASE,
UDMA_CHAN_SSI0_TX | UDMA_PRI_SELECT,
UDMA_MODE_BASIC,
_ads131_active_adc ? _ADS131_SPI_TX_Buffer_1 : _ADS131_SPI_TX_Buffer_2,
(void *)(SSI0_BASE + SSI_O_DR),
2 * ADS131_FRAME_NUM_WORDS * _ADS131_FRAME_NUM_BYTES_PER_WORD
);
uDMAChannelEnable(UDMA0_BASE, UDMA_CHAN_SSI0_TX);
}
if(!(uDMAChannelIsEnabled (UDMA0_BASE, UDMA_CHAN_SSI0_TX))) {
uDMAChannelTransferSet(
UDMA0_BASE,
UDMA_CHAN_SSI0_RX | UDMA_PRI_SELECT,
UDMA_MODE_BASIC,
(uint32_t *)(SSI0_BASE + SSI_O_DR),
_ads131_active_adc ? _ADS131_SPI_RX_Buffer_1 : _ADS131_SPI_RX_Buffer_2,
2 * ADS131_FRAME_NUM_WORDS * _ADS131_FRAME_NUM_BYTES_PER_WORD
);
uDMAChannelEnable(UDMA0_BASE, UDMA_CHAN_SSI0_RX);
}
return 0;
}

void ADS131_Setup(ADS131_Config_t *config) {
...
}

static inline void _ADS131_SPI_Transfer_1(uint16_t input_data) {
uint_fast8_t i;
_ADS131_SPI_TX_Buffer_1[0] = input_data;
for(i = 0; i < 2 * ADS131_FRAME_NUM_WORDS * _ADS131_FRAME_NUM_BYTES_PER_WORD; ++i) {
//SSIDataPut(SSI0_BASE, _ADS131_SPI_TX_Buffer_1[i]);
//SSIDataGet(SSI0_BASE, (uint32_t *)(_ADS131_SPI_RX_Buffer_1 + i));
SSIDataPut(SSI0_BASE, _ADS131_SPI_TX_Buffer_1[i]);
SSIDataGet(SSI0_BASE, (uint32_t *)(_ADS131_SPI_RX_Buffer_1 + i));
}

}

void SPIDMA_init()
{
unsigned int key;

/* Disable interrupts when transfer*/
key = HwiP_disable();

/* power up and enable clock for DMA. */
PRCMPeripheralRunEnable(PRCM_PERIPH_UDMA); // UDMA
PRCMPeripheralSleepEnable(PRCM_PERIPH_UDMA);
PRCMPeripheralDeepSleepEnable(PRCM_PERIPH_UDMA);
PRCMLoadSet();
while(!PRCMLoadGet());

/* Set the base for the channel control table. */
uDMAControlBaseSet (UDMA0_BASE, &DMAControlTable);
/* Enable DMA. */
uDMAEnable(UDMA0_BASE);

/* DMA settings for SPI RX */
// Put the attributes in a known state for the uDMA SSI0RX channel. Disable the attr by default
uDMAChannelAttributeDisable(UDMA0_BASE, UDMA_CHAN_SSI0_RX, \
UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | \
UDMA_ATTR_HIGH_PRIORITY | \
UDMA_ATTR_REQMASK);
uDMAChannelAttributeEnable(UDMA0_BASE, UDMA_CHAN_SSI0_RX, UDMA_ATTR_USEBURST);

// Configure the control parameters for the primary & alternate control structure for SSI0RX

/* source address - no increment
* destination address - increment is 16-bit
* arbitration size - 4 // fixed at 4
* transfer data size - 16 bit
*/
uDMAChannelControlSet ( UDMA0_BASE, \
UDMA_CHAN_SSI0_RX | UDMA_PRI_SELECT, \
UDMA_SIZE_32 | UDMA_SRC_INC_NONE | UDMA_DST_INC_32 | \
UDMA_ARB_4 | UDMA_NEXT_USEBURST);

/* DMA settings for SPI TX */
// Put the attributes in a known state for the uDMA SSI0RX TX channel. Disable the attr by default
uDMAChannelAttributeDisable(UDMA0_BASE,
UDMA_CHAN_SSI0_TX, \
UDMA_ATTR_ALTSELECT | UDMA_ATTR_USEBURST | \
UDMA_ATTR_HIGH_PRIORITY | \
UDMA_ATTR_REQMASK);

uDMAChannelAttributeEnable(UDMA0_BASE, UDMA_CHAN_SSI0_TX, UDMA_ATTR_USEBURST);

// Configure the control parameters for the primary & alternate control structure for SSI0TX

/* source address - no increment
* destination address - no increment
* arbitration size - 4 // fixed at 4
* transfer data size - 16 bit
*/
uDMAChannelControlSet ( UDMA0_BASE, \
UDMA_CHAN_SSI0_TX | UDMA_PRI_SELECT, \
UDMA_SIZE_32 | UDMA_SRC_INC_32 | UDMA_DST_INC_NONE| \
UDMA_ARB_4 | UDMA_NEXT_USEBURST);

/* Re-enable interrupts */
HwiP_restore(key);

/* enable the spi module for dma */
SSIDisable(SSI0_BASE); //disable operation of the SSI to make sure the CR1:SSE is cleared
SSIDMAEnable(SSI0_BASE, SSI_DMA_TX | SSI_DMA_RX);
}

// Public accessing features of ADS131
void ADS131_Init(uint8_t spi_instance) {
// ******************************************************************
// Hardware initialization
// ******************************************************************
unsigned int key;

/* Disable interrupts when transfer */
key = HwiP_disable();

/* power up and enable clock for SPI. */
PRCMPeripheralRunEnable(PRCM_PERIPH_SSI0); // SSI0
PRCMPeripheralSleepEnable(PRCM_PERIPH_SSI0);
PRCMPeripheralDeepSleepEnable(PRCM_PERIPH_SSI0);
PRCMLoadSet();
while(!PRCMLoadGet());

HwiP_restore(key);

/* Configure IOs for SSI0: Base, RX, TX, CS, CLK */
IOCPinTypeSsiMaster(SSI0_BASE, IOID_11, IOID_10, IOID_UNUSED, IOID_12);

/* Configure SSI0 */
//disable operation of the SSI to make sure the CR1:SSE is cleared
SSIDisable(SSI0_BASE);

SSIConfigSetExpClk(SSI0_BASE, \
SysCtrlClockGet(), \
SSI_FRF_MOTO_MODE_1, \
SSI_MODE_MASTER, \
8000000, \
8);

SSIIntDisable(SSI0_BASE, SSI_TXFF | SSI_RXFF | SSI_RXTO | SSI_RXOR);
/* Configure DMA driver */
SPIDMA_init();

// enable the SPI module
SSIEnable(SSI0_BASE);

GPIO_setConfig(CONFIG_SPI_ADC_CS, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_HIGH);
GPIO_write(CONFIG_SPI_ADC_CS, 1);
}

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    我们不建议使用 driverlib 函数、这可能会在访问硬件资源时导致一些冲突。

    在这种情况下、我将验证某些问题是否可能是由 DMA 访问中的某些冲突引起的。 例如、确保两个不同的 uDMA 实例用于所使用的两个 DMA。

    此致、

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    尊敬的 Clement:

    感谢你的帮助。 我已经解决了由 DMA 控制表冲突引起的问题。 在 TI 驱动程序初始化期间  

    UDMACC26XX_initHw (UDMACC26XX_Handle handle)

    这将把 DMA 控制表设置为  

    uDMAControlBaseSet (hwAttrs->baseAddr、(void *) UDMACC26XX_CONFIG_BASE);

    但是、我自己的 driverlib DMA 初始化最终也会设置 DMA 控制表:

    • tDMAControlTable DMAControlTable[64]__attribute__((对齐(1024)));
    • uDMAControlBaseSet (UDMA0_BASE、&DMAControlTable);

    通过在我自己的驱动程序中删除初始化并保持 TI 驱动程序初始化、DMA 和 SPI 都可以正常工作!

    谢谢。