硬汉嵌入式论坛

 找回密码
 立即注册
查看: 367|回复: 9
收起左侧

[SPI/QSPI] SPI DMA传输位数大于32时的配置问题

[复制链接]

2

主题

7

回帖

13

积分

新手上路

积分
13
发表于 2025-6-23 19:48:01 | 显示全部楼层 |阅读模式
佬们,最近我需要使用ADS8698进行采样,这款18bitADC的特点是每次采样都需要SPI的tx连续发送34bit(16bit控制位+18bit任意值),才能接收rx返回的18bit数据(全程HAL库)。当然,我们很容易想到:
HAL_SPI_TransmitReceive(&hspi3, (uint8_t*)g_spiTxBuf3, (uint8_t*)g_spiRxBuf3, g_spiLen_tx3, 1000)
将控制命令分割成8bit(SPI_DATASIZE_8BIT),传5次,凑40bit进行发送即可。然而参考硬汉哥的例程,若要利用TIM12的TRGO触发DMAMUX进行DMA传输,问题来了:

1. 首先考虑硬件NSS还是软件NSS。实测后发现软件NSS无法自动拉低拉高CS脚,所以要么尝试自己控制CS,要么使用硬件NSS。
2. 使用硬件NSS,发现8bit传输的情况下每次传完8bit后CS都会自动拉低拉高一次,而SPI3最大传输位数是32,满足不了ADC时序,控制命令没传完CS就又拉高了。
3. 使用软件NSS,尝试在HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)函数中控制CS引脚,想法是SPI传输完成后先拉高CS,再读取数据,再拉低CS,开启下一次传输。结果CS的波形对不上MISO和MOSI的波形,有十分明显的延迟。
DAC8563的DMA例程中,传24bit只需将DataSize设置为24,后面再补8bit即可,但我这个超32位了,想请教一下如何控制CS,和SPI的FifoThreshold有关系吗?或者说这种情况就用不了DMA?附上配置:

/************************************** DMA配置 ******************************************/

void ADS8698_SelectMode_DMA(uint16_t mode, uint32_t _ulFreq)
{
               
                g_spiLen_tx3 = 0;
                g_spiLen_rx3 = 0;
                g_spiTxBuf3[g_spiLen_tx3++] = (uint8_t)(mode >> 8);
                g_spiTxBuf3[g_spiLen_tx3++] = (uint8_t)(mode & 0xFF);
                g_spiTxBuf3[g_spiLen_tx3++] = 0x00;
                g_spiTxBuf3[g_spiLen_tx3++] = 0x00;
                g_spiTxBuf3[g_spiLen_tx3++] = 0x00;
               
                if (g_spiLen_tx3 > SPI_BUFFER_SIZE)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
               
                spi3_reinit_flag = 1;
                /* 复位SPI配置 */
                hspi3.Instance                                                   = SPI3;
                hspi3.Init.Mode                                           = SPI_MODE_MASTER;
                hspi3.Init.Direction                                   = SPI_DIRECTION_2LINES;
                hspi3.Init.DataSize                                   = SPI_DATASIZE_8BIT;
                hspi3.Init.CLKPolarity                                   = SPI_POLARITY_LOW;
                hspi3.Init.CLKPhase                                   = SPI_PHASE_2EDGE;
                hspi3.Init.NSS                                                   = SPI_NSS_HARD_OUTPUT;
                hspi3.Init.BaudRatePrescaler                   = SPI_BAUDRATEPRESCALER_32;
                hspi3.Init.FirstBit                                   = SPI_FIRSTBIT_MSB;
                hspi3.Init.TIMode                                           = SPI_TIMODE_DISABLE;
                hspi3.Init.CRCCalculation                           = SPI_CRCCALCULATION_DISABLE;
                hspi3.Init.CRCPolynomial                           = 0x0;
                hspi3.Init.NSSPMode                                   = SPI_NSS_PULSE_ENABLE;
                hspi3.Init.NSSPolarity                                   = SPI_NSS_POLARITY_LOW;
                hspi3.Init.FifoThreshold                           = SPI_FIFO_THRESHOLD_16DATA;
                hspi3.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
                hspi3.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
                hspi3.Init.MasterSSIdleness                   = SPI_MASTER_SS_IDLENESS_01CYCLE;
                hspi3.Init.MasterInterDataIdleness           = SPI_MASTER_INTERDATA_IDLENESS_02CYCLE;
                hspi3.Init.MasterReceiverAutoSusp           = SPI_MASTER_RX_AUTOSUSP_DISABLE;
                hspi3.Init.MasterKeepIOState                   = SPI_MASTER_KEEP_IO_STATE_DISABLE;
                hspi3.Init.IOSwap                                           = SPI_IO_SWAP_DISABLE;
               
                if (HAL_SPI_DeInit(&hspi3) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
                if (HAL_SPI_Init(&hspi3) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
               
                /* DMA配置 */
                DMA2_CLK_ENABLE();
               
                hdma_spi3_tx.Instance                                   = SPI3_TX_DMA_STREAM;
                hdma_spi3_tx.Init.Request                           = DMA_REQUEST_SPI3_TX;
                hdma_spi3_tx.Init.Direction                   = DMA_MEMORY_TO_PERIPH;
                hdma_spi3_tx.Init.PeriphInc                   = DMA_PINC_DISABLE;
                hdma_spi3_tx.Init.MemInc                           = DMA_MINC_ENABLE;
                hdma_spi3_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
                hdma_spi3_tx.Init.MemDataAlignment           = DMA_MDATAALIGN_BYTE;
                hdma_spi3_tx.Init.Mode                                   = DMA_CIRCULAR;
                hdma_spi3_tx.Init.Priority                           = DMA_PRIORITY_LOW;
                hdma_spi3_tx.Init.FIFOMode                           = DMA_FIFOMODE_ENABLE;
                hdma_spi3_tx.Init.FIFOThreshold           = DMA_FIFO_THRESHOLD_FULL;
                hdma_spi3_tx.Init.MemBurst                           = DMA_MBURST_SINGLE;
                hdma_spi3_tx.Init.PeriphBurst                   = DMA_PBURST_SINGLE;
               
                if (HAL_DMA_DeInit(&hdma_spi3_tx) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
                if (HAL_DMA_Init(&hdma_spi3_tx) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
               
                __HAL_LINKDMA(&hspi3, hdmatx, hdma_spi3_tx);
               
                /* 同步触发配置 */
                sDmamux_SyncParams.SyncSignalID  = HAL_DMAMUX1_SYNC_TIM12_TRGO;
                sDmamux_SyncParams.SyncPolarity  = HAL_DMAMUX_SYNC_RISING;
                sDmamux_SyncParams.SyncEnable    = ENABLE;
                sDmamux_SyncParams.EventEnable   = ENABLE;
                sDmamux_SyncParams.RequestNumber = 1;
                if (HAL_DMAEx_ConfigMuxSync(&hdma_spi3_tx, &sDmamux_SyncParams) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
               
                TIM12_Config(_ulFreq);
               
                hdma_spi3_rx.Instance                                   = SPI3_RX_DMA_STREAM;
                hdma_spi3_rx.Init.Request                           = DMA_REQUEST_SPI3_RX;
                hdma_spi3_rx.Init.Direction                   = DMA_PERIPH_TO_MEMORY;
                hdma_spi3_rx.Init.PeriphInc                   = DMA_PINC_DISABLE;
                hdma_spi3_rx.Init.MemInc                           = DMA_MINC_ENABLE;
                hdma_spi3_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
                hdma_spi3_rx.Init.MemDataAlignment           = DMA_MDATAALIGN_BYTE;
                hdma_spi3_rx.Init.Mode                                   = DMA_CIRCULAR;
                hdma_spi3_rx.Init.Priority                           = DMA_PRIORITY_LOW;
                hdma_spi3_rx.Init.FIFOMode                           = DMA_FIFOMODE_ENABLE;
                hdma_spi3_rx.Init.FIFOThreshold           = DMA_FIFO_THRESHOLD_FULL;
                hdma_spi3_rx.Init.MemBurst                           = DMA_MBURST_SINGLE;
                hdma_spi3_rx.Init.PeriphBurst                   = DMA_PBURST_SINGLE;
               
                if (HAL_DMA_DeInit(&hdma_spi3_rx) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
                if (HAL_DMA_Init(&hdma_spi3_rx) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
               
                __HAL_LINKDMA(&hspi3, hdmarx, hdma_spi3_rx);
               
                /* 开SPI DMA中断 */
                HAL_NVIC_SetPriority(SPI3_DMA_TX_IRQn, 1, 0);
                HAL_NVIC_DisableIRQ(SPI3_DMA_TX_IRQn);
                HAL_NVIC_SetPriority(SPI3_DMA_RX_IRQn, 1, 0);
                HAL_NVIC_EnableIRQ(SPI3_DMA_RX_IRQn);
                HAL_NVIC_SetPriority(SPI3_IRQn, 1, 0);
                HAL_NVIC_EnableIRQ(SPI3_IRQn);
               
                //CS2_0();
               
                /* 启动DMA */
                if (HAL_SPI_TransmitReceive_DMA(&hspi3, (uint8_t*)g_spiTxBuf3, (uint8_t*)g_spiRxBuf3, g_spiLen_tx3) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
}


/************************************** SPI配置 ******************************************/

if (i == 3)
{
                hspi3.Instance                                  = SPI3;
                hspi3.Init.Mode                          = SPI_MODE_MASTER;
                hspi3.Init.Direction                  = SPI_DIRECTION_2LINES;
                hspi3.Init.DataSize                  = SPI_DATASIZE_8BIT;
                hspi3.Init.CLKPolarity                  = SPI_POLARITY_LOW;
                hspi3.Init.CLKPhase                  = SPI_PHASE_2EDGE;
                hspi3.Init.NSS                                  = SPI_NSS_SOFT;
                hspi3.Init.BaudRatePrescaler = _BaudRatePrescaler;
                hspi3.Init.FirstBit                  = SPI_FIRSTBIT_MSB;
                hspi3.Init.TIMode                          = SPI_TIMODE_DISABLE;
                hspi3.Init.CRCCalculation          = SPI_CRCCALCULATION_DISABLE;
                hspi3.Init.CRCPolynomial          = 0x0;
                hspi3.Init.NSSPMode                  = SPI_NSS_PULSE_ENABLE;
                hspi3.Init.NSSPolarity                  = SPI_NSS_POLARITY_LOW;
                hspi3.Init.FifoThreshold          = SPI_FIFO_THRESHOLD_16DATA;
                hspi3.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
                hspi3.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
                hspi3.Init.MasterSSIdleness                   = SPI_MASTER_SS_IDLENESS_01CYCLE;
                hspi3.Init.MasterInterDataIdleness    = SPI_MASTER_INTERDATA_IDLENESS_02CYCLE;
                hspi3.Init.MasterReceiverAutoSusp           = SPI_MASTER_RX_AUTOSUSP_DISABLE;
                hspi3.Init.MasterKeepIOState                   = SPI_MASTER_KEEP_IO_STATE_ENABLE;
                hspi3.Init.IOSwap                                           = SPI_IO_SWAP_DISABLE;
               
                if (HAL_SPI_DeInit(&hspi3) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
                if (HAL_SPI_Init(&hspi3) != HAL_OK)
                {
                        Error_Handler(__FILE__, __LINE__);
                }
}



if (_hspi->Instance == SPI3)
{
                //SPI3_NSS_CLK_ENABLE();
                //SPI3_SCK_CLK_ENABLE();
                //SPI3_MISO_CLK_ENABLE();
                //SPI3_MOSI_CLK_ENABLE();
                SPI3_CLK_ENABLE();
               
                /* SPI3 NSS */
                GPIO_InitStruct.Pin = SPI3_NSS_PIN;
                if (spi3_reinit_flag)
                {
                        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
                }
                else
                {
                        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
                }
               
                GPIO_InitStruct.Pull = GPIO_NOPULL;
                GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
                GPIO_InitStruct.Alternate = SPI3_NSS_AF;
                HAL_GPIO_Init(SPI3_NSS_GPIO, &GPIO_InitStruct);
               
                /* SPI3 SCK */
                GPIO_InitStruct.Pin = SPI3_SCK_PIN;
                GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
                GPIO_InitStruct.Pull = GPIO_NOPULL;
                GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
                GPIO_InitStruct.Alternate = SPI3_SCK_AF;
                HAL_GPIO_Init(SPI3_SCK_GPIO, &GPIO_InitStruct);
               
                /* SPI3 MISO */
                GPIO_InitStruct.Pin = SPI3_MISO_PIN;
                GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
                GPIO_InitStruct.Pull = GPIO_NOPULL;
                GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
                GPIO_InitStruct.Alternate = SPI3_MISO_AF;
                HAL_GPIO_Init(SPI3_MISO_GPIO, &GPIO_InitStruct);
               
                /* SPI3 MOSI */
                GPIO_InitStruct.Pin = SPI3_MOSI_PIN;
                GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
                GPIO_InitStruct.Pull = GPIO_NOPULL;
                GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
                GPIO_InitStruct.Alternate = SPI3_MOSI_AF;
                HAL_GPIO_Init(SPI3_MOSI_GPIO, &GPIO_InitStruct);
}



/************************************** 这个就是硬汉哥的例程了 ******************************************/
void TIM12_Config(uint32_t _ulFreq)
{
        uint16_t usPeriod;
        uint16_t usPrescaler;
        uint32_t uiTIMxCLK;

        __HAL_RCC_TIM12_CLK_ENABLE();
        /*-----------------------------------------------------------------------

          System Clock source       = PLL (HSI)
          SYSCLK(Hz)                = 400000000 (CPU Clock)
          HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)
          AHB Prescaler             = 2
          D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)
          D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)
          D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)
          D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)

          因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz; 不含这个总线下的LPTIM1
          因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;
          APB4上面的TIMxCLK没有分频,所以就是100MHz;

          APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1
          APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17

          APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5
        ----------------------------------------------------------------------- */
        uiTIMxCLK = SystemCoreClock / 2;

        if (_ulFreq < 100)
        {
                usPrescaler = 10000 - 1;                                        /* 分频比 = 10000 */
                usPeriod =  (uiTIMxCLK / 10000) / _ulFreq  - 1;                /* 自动重装的值 */
        }
        else if (_ulFreq < 3000)
        {
                usPrescaler = 100 - 1;                                        /* 分频比 = 100 */
                usPeriod =  (uiTIMxCLK / 100) / _ulFreq  - 1;                /* 自动重装的值 */
        }
        else        /* 大于4K的频率,无需分频 */
        {
                usPrescaler = 0;                                        /* 分频比 = 1 */
                usPeriod = uiTIMxCLK / _ulFreq - 1;        /* 自动重装的值 */
        }

        /* TIM12配置 */
        htim12.Instance                           = TIM12;
        htim12.Init.Prescaler                   = usPrescaler;
        htim12.Init.CounterMode           = TIM_COUNTERMODE_UP;
        htim12.Init.Period                           = usPeriod;
        htim12.Init.ClockDivision           = TIM_CLOCKDIVISION_DIV1;
        htim12.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
        if (HAL_TIM_Base_DeInit(&htim12) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);
        }
        if (HAL_TIM_Base_Init(&htim12) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);
        }

        sConfigOC.OCMode     = TIM_OCMODE_PWM1;
        sConfigOC.Pulse      = usPeriod / 2;
        sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
        sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
        if (HAL_TIM_OC_ConfigChannel(&htim12, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);
        }

        /* 启动OC1 */
        if (HAL_TIM_OC_Start(&htim12, TIM_CHANNEL_1) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);
        }

        /* TIM12的TRGO用于触发DMAMUX的请求发生器 */
        sMasterConfig.MasterOutputTrigger  = TIM_TRGO_OC1REF;
        sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
        sMasterConfig.MasterSlaveMode      = TIM_MASTERSLAVEMODE_DISABLE;
        if (HAL_TIMEx_MasterConfigSynchronization(&htim12, &sMasterConfig) != HAL_OK)
        {
                Error_Handler(__FILE__, __LINE__);
        }
}


回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2025-6-24 08:04:20 | 显示全部楼层
全程CS低电平正常不,这种情况不知道满足需求不
回复

使用道具 举报

2

主题

7

回帖

13

积分

新手上路

积分
13
 楼主| 发表于 2025-6-24 09:00:41 | 显示全部楼层
eric2013 发表于 2025-6-24 08:04
全程CS低电平正常不,这种情况不知道满足需求不

全程CS低电平不行,数据手册要求输出18bit数据(完成一次SPI传输)后CS要拉高,他这个烦人之处就在于发送和接收要连续传34bit
回复

使用道具 举报

1

主题

9

回帖

12

积分

新手上路

积分
12
发表于 2025-6-24 09:59:08 | 显示全部楼层
硬件CS固定每byte都高低跳变一次确实很烦,要是能自定义一次多少位就好了,很多ic都是一帧数据多个字节,CS按帧来的。
回复

使用道具 举报

2

主题

7

回帖

13

积分

新手上路

积分
13
 楼主| 发表于 2025-6-24 10:07:42 | 显示全部楼层
supertankhzt 发表于 2025-6-24 09:59
硬件CS固定每byte都高低跳变一次确实很烦,要是能自定义一次多少位就好了,很多ic都是一帧数据多个字节,CS ...

是的,这个好像自定义不了一次多少位
回复

使用道具 举报

5

主题

162

回帖

177

积分

初级会员

积分
177
发表于 2025-6-24 16:45:56 | 显示全部楼层
用DMA的另一个通道控制GPIO作为CS,计算号每一帧的时间,合理安排GPIO的电平,然后先启动CS的这个DMA,再启动SPI的DMA
回复

使用道具 举报

7

主题

25

回帖

46

积分

新手上路

积分
46
发表于 2025-6-25 08:18:59 | 显示全部楼层
传4个字节,然后再配置成IO,手动拉2个bit呢
回复

使用道具 举报

1万

主题

7万

回帖

11万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
117512
QQ
发表于 2025-6-25 09:07:56 | 显示全部楼层
zyf02468 发表于 2025-6-24 09:00
全程CS低电平不行,数据手册要求输出18bit数据(完成一次SPI传输)后CS要拉高,他这个烦人之处就在于发送 ...

那确实不方便了。

可以考虑外置逻辑芯片控制下CS
回复

使用道具 举报

2

主题

7

回帖

13

积分

新手上路

积分
13
 楼主| 发表于 2025-6-25 17:29:09 | 显示全部楼层
2859932063 发表于 2025-6-24 16:45
用DMA的另一个通道控制GPIO作为CS,计算号每一帧的时间,合理安排GPIO的电平,然后先启动CS的这个DMA,再启 ...

感觉可以考虑,等我研究一下
回复

使用道具 举报

2

主题

7

回帖

13

积分

新手上路

积分
13
 楼主| 发表于 2025-6-25 17:29:58 | 显示全部楼层
xinhaic 发表于 2025-6-25 08:18
传4个字节,然后再配置成IO,手动拉2个bit呢

那不就失去DMA的意义了吗
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2025-8-12 06:52 , Processed in 0.045286 second(s), 25 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表