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 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);
}