2023-12-12 07:33 PM
2023-12-13 08:04 AM
I'd configure two timers as a pair master - slave:reset, to make them run synchronously. Than use master as trigger to SPI read from ADC, likely you have configured SPI as master in this link. Another timer does same things, triggers SPI write to DAC. I'd route update_event from master timer as external signal for SPI-ADC, and CC1 for another timer. Changing CC1 you can easily shift phase adc-dac sequence.
2024-01-04 06:49 PM
Thanks for the reply.
The solution which is suggested is similar to what is currently implemented, except that the DAC SPI is running in DMA transfer mode.
Adding some details... A continuous output waveform ( ~100 kHz) is required from the DAC, that is where the issue lies. Using the DAC in SPI DMA mode, a continuous waveform can be generated, but only if the DMA is configured in "Circular" mode. Otherwise, the SPI transfers are interrupted and a gap appears in the waveform. Unforunately the ADC and DAC lose sync when the DAC SPI DMA is in "Circular" mode.
Is there perhaps a way to operate both the ADC SPI and DAC SPI in DMA "Circular" mode and synchronize the transfers?
Thanks
2024-01-04 08:08 PM
"Unforunately the ADC and DAC lose sync when the DAC SPI DMA is in "Circular" mode."
Not clear what is "sync" , is it delay time in between dac conversion and adc conversion or is it phase shift between outputed waveform and adc data? Regarding first it can't be lost if you configured two timers as suggested.
I have a few projects that run in sync. Continuous DMA mode always, what is important how you start all system.
1. Make DMA buffer for DAC equal to DMA buffer of the ADC. Or at least proportional by integer numbers x2, x3 etc.
2. Configure slave-reset timer, than master timer . and here is the trick: call SPI config in the middle of master timer config subfunction, here is the example:
static void TIM2_Config(void)
{
__HAL_RCC_TIM2_CLK_ENABLE();
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0x0000;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 219; //179; // 0xBB
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
// htim2.Init.RepetitionCounter = 0;
// htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
TIM2->CR1 |= (TIM_CR1_ARPE);
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
// HAL_TIM_PWM_MspInit(htim);
// Dma_Tmr(&htim2);
SPI2_Init();
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; //TIM_TRGO_OC2REF;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
// LDAC
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 84;
// na 4 pixela bol'she 4em CS, gdeto na 40 nsec, nado min = 20nsec
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
// NSS-CS
sConfigOC.Pulse = 80;
sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
}
void dac_stop(void)
{
TIM2->CR1 &= ~(TIM_CR1_CEN);
//?? CNTR reset ?
__HAL_TIM_DISABLE_DMA(&htim2, TIM_DMA_UPDATE);
//LDAC
if (HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
//CS
if (HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
}
void dac_start(void)
{
TIM2->CR1 |= (TIM_CR1_CEN);
__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_UPDATE);
//CS
if (HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
//LDAC
if (HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
}
SPI:
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct = { 0};
static DMA_HandleTypeDef hdma_tim;
if(hspi->Instance == SPI2) {
// SPI2-Tx
__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_SPI2_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/**SPI2 GPIO Configuration
PB12 ------> SPI2_NSS
PB13 ------> SPI2_SCK
PB15 ------> SPI2_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
// GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
hdma_tim.Instance = DMA1_Stream7;
hdma_tim.Init.Channel = DMA_CHANNEL_3;
hdma_tim.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_tim.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_tim.Init.MemInc = DMA_MINC_ENABLE;
hdma_tim.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_tim.Init.MemDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_tim.Init.Mode = DMA_CIRCULAR;
hdma_tim.Init.Priority = DMA_PRIORITY_VERY_HIGH;
hdma_tim.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
TIM_HandleTypeDef *htim = &htim2;
__HAL_LINKDMA(htim, hdma[TIM_DMA_ID_UPDATE], hdma_tim);
HAL_DMA_Init(htim->hdma[TIM_DMA_ID_UPDATE]);
// HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 0, 0);
// HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn);
}
}
void SPI2_Init(void)
{
hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_16BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_HARD_OUTPUT;
// hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}
__HAL_SPI_ENABLE(&hspi2);
/*
addr = 0x40003800;
temp = (*(uint32_t*)addr);
Serial.print(F("\n\tReg: "));
Serial.print(addr, HEX);
prnt_binf(temp);
*/
}
than
TIM2_Config();
Serial.print(F("\tdone."));
htim2.hdma[TIM_DMA_ID_UPDATE]->XferCpltCallback = TransferComplete;
htim2.hdma[TIM_DMA_ID_UPDATE]->XferErrorCallback = TransferError ;
htim2.hdma[TIM_DMA_ID_UPDATE]->XferHalfCpltCallback = TransferHalfComplete;
Serial.print(F("\n\thal_dma_start_it..."));
delay(100);
if (HAL_DMA_Start_IT(htim2.hdma[TIM_DMA_ID_UPDATE], (uint32_t) out, SPI2_Write, (2 *OUT_BUFF)) != HAL_OK)
{
Error_Handler();
}
Serial.print(F("\tdone."));
Serial.print(F("\n\tdac start..."));
delay(100);
dac_start();
Serial.print(F("\n\tSetup done."));
As you can see DAC in use has LDAC pin, to trigger conversion by external GPIO, max5717.