Повышение производительности с DMA для SPI6 - PullRequest
1 голос
/ 17 мая 2019

Я использую библиотеки STM32H7 и HAL. На моей плате SPI6 используется для связи с внешним ЦАП (DAC8734). Связь работает отлично (с DMA). Цель состоит в том, чтобы обновлять ЦАП каждые 8 ​​мкс для имитации сигнала переменного тока. Для этого я использую базовый таймер TIM15. Таймер вызывает внутри своего прерывания функцию передачи DMA. После завершения передачи буфер будет увеличен в DMA_Interrupt_Handler, поскольку я не могу непрерывно отправлять данные в ЦАП (требуется ЦАП и триггер высокого / низкого уровня на линии CS для обновления своего канала). Есть ли способ повысить мою производительность?

здесь код для TIM15:

__HAL_RCC_DMA1_CLK_ENABLE();
__HAL_RCC_DMA2_CLK_ENABLE();
__HAL_RCC_BDMA_CLK_ENABLE();

TIM_ClockConfigTypeDef SClockSourceConfigDMA;
TIM_SlaveConfigTypeDef sSlaveConfigDMA;
TIM_MasterConfigTypeDef sMasterConfigDMA;
TIM_IC_InitTypeDef sConfigICDMA;

htim15.Instance = TIM15;                    //TIM15 must be synchron to TIM5         --> 40 MHz, Baseclock is 200 Mhz
htim15.Init.Prescaler = 300;//300;//15;         //Max. for good sin: Pre = 50 & Per = 16 & DIV4
htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
htim15.Init.Period = 5;//4;                     //Period = 5 & Prescaler = 100 für 200 kHz -->
htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim15) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

SClockSourceConfigDMA.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim15, &SClockSourceConfigDMA) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_Init(&htim15) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

sSlaveConfigDMA.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfigDMA.InputTrigger = TIM_TS_ITR2;
if (HAL_TIM_SlaveConfigSynchronization(&htim15, &sSlaveConfigDMA) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

sMasterConfigDMA.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfigDMA.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim15, &sMasterConfigDMA) != 
HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

sConfigICDMA.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigICDMA.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigICDMA.ICPrescaler = TIM_ICPSC_DIV1;
sConfigICDMA.ICFilter = 1;
if (HAL_TIM_IC_ConfigChannel(&htim15, &sConfigICDMA, TIM_CHANNEL_1) != 
HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_ConfigChannel(&htim15, &sConfigICDMA, TIM_CHANNEL_2) != 
HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_ConfigChannel(&htim15, &sConfigICDMA, TIM_CHANNEL_3) != 
HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

if (HAL_TIM_IC_ConfigChannel(&htim15, &sConfigICDMA, TIM_CHANNEL_4) != 
HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

__HAL_TIM_ENABLE_IT(&htim15, TIM_IT_UPDATE);
__HAL_TIM_ENABLE_IT(&htim15, TIM_IT_CC1);
__HAL_TIM_ENABLE_IT(&htim15, TIM_IT_CC2);
__HAL_TIM_ENABLE_IT(&htim15, TIM_IT_CC3);
__HAL_TIM_ENABLE_IT(&htim15, TIM_IT_CC4);

SystemCoreClockUpdate();
}

вот код для DMA:

//Setting the configuration for the DMA tx --> this is the configuration for         SPI6 as Trigger
hdma_spi6_tx_init.Instance                  = BDMA_Channel2;                        //Choose BDMA, for SPI6 is connected to DMAMUX2
//hdma_spi6_tx_init.DMAmuxChannel->CCR      = 0b1100;                               //Selects SPI6 for DMAMUX2
hdma_spi6_tx_init.Init.Request              = BDMA_REQUEST_SPI6_TX;                 //BDMA (DMAUX2) for TX of SPI6
hdma_spi6_tx_init.Init.Direction            = DMA_MEMORY_TO_PERIPH;                 //Transfering from Memory to Peripherie (2, S.632)
hdma_spi6_tx_init.Init.PeriphInc            = DMA_PINC_ENABLE;                      //Incrementing the address register todo: maybe enable
hdma_spi6_tx_init.Init.MemInc               = DMA_MINC_ENABLE;                      //Incrementing the memory address register
hdma_spi6_tx_init.Init.PeriphDataAlignment  = DMA_PDATAALIGN_BYTE;                  //Data size: Byte, because SPI6 is transferring 8-Bit at the time
hdma_spi6_tx_init.Init.MemDataAlignment     = DMA_MDATAALIGN_BYTE;                  //Memory data size: Byte, because thats the size of the other registers
hdma_spi6_tx_init.Init.Mode                 = DMA_NORMAL;                           //Peripheral flow control mode (S.632)
hdma_spi6_tx_init.Init.Priority             = DMA_PRIORITY_VERY_HIGH;                   //High Priority for transfer
hdma_spi6_tx_init.Init.FIFOMode             = DMA_FIFOMODE_ENABLE;                  //Direct mode for transfer (todo:FIFO enable)
hdma_spi6_tx_init.Init.FIFOThreshold        = DMA_FIFO_THRESHOLD_FULL;              //Wait for full FIFO
hdma_spi6_tx_init.Init.MemBurst             = DMA_MBURST_SINGLE;                    //One byte sized burst for memory
hdma_spi6_tx_init.Init.PeriphBurst          = DMA_PBURST_SINGLE;                    //One byte sized burst for peripheral

//Setting the configuration for the BDMA (S.653 + S.663)
bdma_spi6_init.CPAR  = BDMA_REQUEST_SPI6_TX;            //Peripheral register address for SPI6
bdma_spi6_init.CMAR  = (uint8_t *) Crrct_Size_Buffer;   //Memory register address
bdma_spi6_init.CNDTR = 0xFFFF;//0x1F2;                  //Total number of data to transfer
bdma_spi6_init.CCR  |= 0x3098;
//  Bits for CCR           (0 << 15) ||             //Double-buffer mode off
//                         (0 << 14) ||             //Memory-to-memory mode off
//                         (1 << 13) ||             //Priority level high
//                         (1 << 12) ||             //Priority level high
//                         (0 << 11) ||             //Memory size: 8-Bit
//                         (0 << 10) ||             //Memory size: 8-Bit
//                         (0 <<  9) ||             //Peripheral size: 8-Bit
//                         (0 <<  8) ||             //Peripheral size: 8-Bit
//                         (1 <<  7) ||             //Peripheral as destination, enable Memory increment mode
//                         (0 <<  6) ||             //Memory as source, disable Peripheral increment mode
//                         (0 <<  5) ||             //Circular mode disabled
//                         (1 <<  4) ||             //Read from Memory
//                         (1 <<  3) ||             //Enable transfer error interrupt
//                         (0 <<  2) ||             //Disable half transfer interrupt
//                         (0 <<  1) ||             //Disable transfer complete interrupt
//                         (0 <<  0);

if (HAL_DMA_Init(&hdma_spi6_tx_init) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

__HAL_LINKDMA( hspi, hdmatx, hdma_spi6_tx_init);

Внутри TIM15_IRQHandler я вызываю передачу DMA:

    SCB_CleanDCache_by_Addr( (uint8_t *) Crrct_Size_Buffer, sizeof(Crrct_Size_Buffer)/sizeof(Crrct_Size_Buffer[0]));    //Clear memory space for TxBuffer

    HAL_SPI_Transmit_DMA(&hspi6, (uint8_t *) Crrct_Size_Buffer, 3); 

После передачи вызывается обработчик IRQ BDMA:

Crrct_Size_Buffer[0] = Crrct_Size_Buffer[IRQ_Counter[0]+3];                                                         
Crrct_Size_Buffer[1] = Crrct_Size_Buffer[IRQ_Counter[0]+4];
Crrct_Size_Buffer[2] = Crrct_Size_Buffer[IRQ_Counter[0]+5];

if(IRQ_Counter[0] < (NumberOfSamples-1)*3 )                                                                         
{
    IRQ_Counter[0] = IRQ_Counter[0] + 3;
}
else
{
    IRQ_Counter[0] = 0;
}

HAL_GPIO_WritePin(DAC_LDAC_GPIO_Port,DAC_LDAC_Pin, GPIO_PIN_SET);                                                   //LDAC high/low to update the command register
HAL_GPIO_WritePin(DAC_LDAC_GPIO_Port,DAC_LDAC_Pin, GPIO_PIN_RESET);

HAL_DMA_IRQHandler(&hdma_spi6_tx_init);

Моя проблема сейчас в том, что я не получаю никакого увеличения производительности. Я предполагаю, что это потому, что я вручную увеличиваю свой Crrct_Size_Buffer, но я не могу просто отправить все данные сразу, из-за ЦАП (для которого нужен триггер высокого / низкого уровня). У кого-нибудь есть идеи как повысить производительность? Если вам нужна дополнительная информация, пожалуйста, не стесняйтесь спрашивать. Извините за мой плохой английский я не родной :) 1015 *

Спасибо за вашу помощь!

1 Ответ

0 голосов
/ 18 мая 2019

Похоже, что SPI-контроллер STM32H7 способен самостоятельно генерировать импульсы CS после каждого 24-битного кадра. Ключевыми параметрами являются поле DSIZE в SPI->CFG1 и биты управления выбором ведомого в SPI->CFG2.

Настройка DSIZE сообщает контроллеру количество бит в одном кадре. Установка 23 (на один размер меньше фактического размера кадра) имеет два эффекта.

  • При записи полного 32-битного значения uint32_t в TXDR (регистр передаваемых данных) старшие 8 бит будут игнорироваться, а 24 бита будут смещены на вывод MOSI.
  • CS может быть импульсным после каждого 24-битного кадра.

Из раздела Справочного руководства Управление выводами Slave Select (SS)

Аппаратное управление SS (SSM = 0)

Разрешение выхода SS (SSOE = 1)

...

c) Когда SSOM=1, SP=000 и MIDI>1, импульс SS неактивен между данными кадры и остаются неактивными в течение определенного количества тактов SPI, определяемых MIDI значение уменьшается на единицу (от 1 до 14).

Если бы в FIFO передачи SPI было больше кадров данных, он начал бы смещать следующий, но их больше нет, поэтому он будет ждать следующей записи в TXDR.

Теперь записи 32-битного регистра каждые 8 ​​мкс достаточно для запуска всей последовательности передачи. Его можно автоматизировать с помощью таймера и канала DMA, не требуя программных прерываний для каждой передачи.

Настройте канал DMA для копирования 32 бит из буфера памяти (который должен быть дополнен до 32 бит) в регистр передачи SPI. Передача DMA инициируется событием обновления таймера, а не SPI. Установите таймер на 125 кГц и сгенерируйте запрос DMA на события обновления (бит UDE в регистре DIER).

...