Thread 中讨论的其他部件:ADS131M06、 SysConfig
大家好!
我们正在使用 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 ADS131void 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);}