How to swap DMA/DAC buffer only when a circular dma/dac has completed - STM32H7A/B
Edit: I've pretty much solved my problem.
The issue of course was that I was setting the Complete flag in the first cycle.
By changing:
if(DAC_Complete){
swap_buffer();
DAC_Complete=0;
}
to:
DAC_Complete=0;
while(DAC_Complete){
swap_buffer();
}
Things are more or less synchronised but now there is a wait period which is not wanted and also there still seems to be a little glitching.
So my new question is:
Is there an equivalent callback for the start of DAC like HAL_DAC_CH1_COMPLETE_CB_ID?
A sort of HAL_DAC_CH1_BEGIN_CB_ID
My intention is to swap a DMA buffer at the exact time a DMA Cyclical buffer ends writing it's sequence to the DAC. I want a seamless transition from writing the last DAC value out to the writing a new sequence (buffer) to the DAC... The buffer needs to be swapped at unpredictable times and has varying lengths.
In other words I want to commit the swap once the DAC has written the last value of n cycles and immediately after that.
viz the function - swap_buffer:
a. Executes at unpredictable times.
b. Changes the length and values of the DMA stream that is written to the DAC:
(stops the timer and DMA). Currently implemented in:
HAL_TIM_Base_Start(...) HAL_DAC_Stop_DMA (...)
before altering the DMA stream to DAC and restarting the timer
HAL_TIM_Base_Start (...) HAL_DAC_Start_DMA(...newvalues...)
c. The intention is to swap the DMA stream once it has finished writing it's last entry to the DAC. (reached the end of the cyclical buffer)
Currently DAC_REFRESH_PERIOD = 64U
Sys_ClockSpeed = 240Mhz
DAC_REFRESH_PRESCALER = 0
I have tried using:
uint16_t buffer[2] ={0};
uint8_t frame_id=0;
uint32_t buffer_len=0;
volatile uint8_t DAC_Complete = 1;
void DAC1_Ch1_Complete(DAC_HandleTypeDef *def) {
DAC_Complete = 1;
HAL_GPIO_TogglePin(DEBUG_PIN_GPIO_Port, DEBUG_PIN_Pin);
}
int main(){
... //Nothing out of the ordinary
MX_DMA_Init();
MX_DAC1_Init();
MX_TIM1_Init();
...
HAL_DAC_RegisterCallback(&hdac1, HAL_DAC_CH1_COMPLETE_CB_ID, &DAC1_Ch1_Complete);
...
while(1) {
...
//various functions with unpredicatable cycles...
...
buffer[frame_id] = ...; //buffer populated previously in code...
buffer_len= ...; //buffer size determinded previously in code...
DAC_Complete=0;
while(DAC_Complete){
swap_buffer();
DAC_Complete=0;
}
/* old version
if(DAC_Complete){
swap_buffer();
DAC_Complete=0;
}
*/
}
}
void swap_buffer()
{
frame_id = frame_id + 1 % 2;
HAL_TIM_Base_Stop(&htim1);
HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1);
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*) buffer[frame_id], buffer_len, DAC_ALIGN_12B_R);
HAL_TIM_Base_Start(&htim1);
}
}The stream consistently repeats until the swap_buffer function executes. This glitches the DAC pulse.
When observing the DAC output under an oscilloscope. the function seems to cut off a pending DMA cycle
Current result...
Intended result:
Another Edit: (to remove the delay I placed the swap routine into the callback.. This presents other problems. Chiefly the main routine could request 2 swaps whilst the DMA is being
uint16_t buffer[2] ={0};
uint8_t frame_id=0;
uint32_t buffer_len=0;
volatile uint8_t swapbuffer= 0;
void DAC1_Ch1_Complete(DAC_HandleTypeDef *def) {
HAL_GPIO_TogglePin(DEBUG_PIN_GPIO_Port, DEBUG_PIN_Pin);
//new version :
if (swapbuffer) {
HAL_TIM_Base_Stop(&htim1);
HAL_DAC_Stop_DMA(&hdac1, DAC_CHANNEL_1);
HAL_DAC_Start_DMA(&hdac1, DAC_CHANNEL_1, (uint32_t*) buffer[frame_id], buffer_len,
DAC_ALIGN_12B_R);
HAL_TIM_Base_Start(&htim1);
}
}
int main() {
... //Nothing out of the ordinary
MX_DMA_Init();
MX_DAC1_Init();
MX_TIM1_Init();
...
HAL_DAC_RegisterCallback(&hdac1, HAL_DAC_CH1_COMPLETE_CB_ID, &DAC1_Ch1_Complete);
...
while(1) {
...
//various functions with unpredicatable cycles...
...
buffer[frame_id] = ...; //buffer populated previously in code...
buffer_len= ...; //buffer size determinded previously in code...
swap_buffer();
}
void swap_buffer()
{
frame_id = frame_id + 1 % 2;
swapbuffer=true;
}
}Additional configuration
void HAL_DAC_MspInit(DAC_HandleTypeDef* dacHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
HAL_DMA_MuxSyncConfigTypeDef pSyncConfig= {0};
if(dacHandle->Instance==DAC1)
{/* USER CODE END DAC1_MspInit 0 */
/* DAC1 clock enable */
__HAL_RCC_DAC1_CLK_ENABLE();
/* DAC1 DMA Init */
/* DAC1_CH1 Init */
hdma_dac1_ch1.Instance = DMA1_Stream0;
hdma_dac1_ch1.Init.Request = DMA_REQUEST_DAC1;
hdma_dac1_ch1.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_dac1_ch1.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_dac1_ch1.Init.MemInc = DMA_MINC_ENABLE;
hdma_dac1_ch1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
hdma_dac1_ch1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
hdma_dac1_ch1.Init.Mode = DMA_CIRCULAR;
hdma_dac1_ch1.Init.Priority = DMA_PRIORITY_MEDIUM;
hdma_dac1_ch1.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_dac1_ch1) != HAL_OK)
{
Error_Handler();
}
pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_EXTI0;
pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_NO_EVENT;
pSyncConfig.SyncEnable = DISABLE;
pSyncConfig.EventEnable = ENABLE;
pSyncConfig.RequestNumber = 1;
if (HAL_DMAEx_ConfigMuxSync(&hdma_dac1_ch1, &pSyncConfig) != HAL_OK)
{
Error_Handler();
}
__HAL_LINKDMA(dacHandle,DMA_Handle1,hdma_dac1_ch1);
/* DAC1 interrupt Init */
HAL_NVIC_SetPriority(TIM1_DAC_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM1_DAC_IRQn);
/* USER CODE BEGIN DAC1_MspInit 1 */
/* USER CODE END DAC1_MspInit 1 */
}void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
...
if(tim_baseHandle->Instance==TIM1)
{
/* USER CODE BEGIN TIM1_MspInit 0 */
/* USER CODE END TIM1_MspInit 0 */
/* TIM1 clock enable */
__HAL_RCC_TIM1_CLK_ENABLE();
/* TIM1 interrupt Init */
HAL_NVIC_SetPriority(TIM1_DAC_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM1_DAC_IRQn);
/* USER CODE BEGIN TIM1_MspInit 1 */
/* USER CODE END TIM1_MspInit 1 */
}
}/* TIM1 init function */
void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim6.Instance = TIM1;
htim6.Init.Prescaler = 0;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = DAC1_REFRESH_PERIOD;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
}/* DAC1 init function */
void MX_DAC1_Init(void)
{
/* USER CODE BEGIN DAC1_Init 0 */
/* USER CODE END DAC1_Init 0 */
DAC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN DAC1_Init 1 */
/* USER CODE END DAC1_Init 1 */
/** DAC Initialization
*/
hdac1.Instance = DAC1;
if (HAL_DAC_Init(&hdac1) != HAL_OK)
{
Error_Handler();
}
/** DAC channel OUT1 config
*/
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_T1_TRGO;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_DISABLE;
sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_ENABLE;
sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN DAC1_Init 2 */
/* USER CODE END DAC1_Init 2 */
}