你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【STM32U3评测】SPIDMA发送

[复制链接]
donatello1996 发布时间:2025-5-27 00:09
      SPIDMA发送/接收是非常常见的MCU应用,用于与高速SPI器件通信并提高效率,这个帖子是评测STM32U385RG的SPI1接口,并启用SPI1的DMA发送功能。

       在上一个帖子中提到,STM32U3的DMA外设名为GPDMA,在ST新出的系列中,任意支持DMA的外设都可以绑定到DMA的任意通道/流/节点上,不像F103和F4系列那样规定通道号,来看看SPI1DMA的配置:

8.JPG

Normal模式表示每次调用DMA发送函数只会发送一次,Circular模式表示只调用一次DMA发送函数就一直发;在外设所使用的DMA中一般配置外设地址非自增,内存地址可配置为自增或者非自增,内存地址非自增表示DMA发送的数据是同一个数据,也就是内存首地址的数据;源地址和目标地址的数据格式必须一致,都是8位或者16位;只要SPI使用了DMA进行发送或者接收,就必须要把SPI中断和DMA中断都打开,少打开一个都不会正常工作。

SPI初始化代码如下,在ST新系列中加入了一些不太常用的SPI机制,如CS片选引脚空闲等待节拍,Ready模式,因为在大部分场景中片选引脚都由GPIO进行控制,比较少用到SPI外设自带的NSS引脚功能,除非是双MCU通信:
  1. void SPI1_PA5_PA6_PA7_Init(void)
  2. {
  3.    
  4.   __HAL_RCC_GPIOA_CLK_ENABLE();
  5.     __HAL_RCC_SPI1_CLK_ENABLE();
  6.     __HAL_RCC_GPDMA1_CLK_ENABLE();
  7.    
  8.     RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
  9.     GPIO_InitTypeDef GPIO_InitStruct = {0};
  10.   SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0};
  11.    
  12.     PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SPI1;
  13.   PeriphClkInit.Spi1ClockSelection = RCC_SPI1CLKSOURCE_PCLK2;
  14.   HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
  15.    
  16.     GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
  17.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  18.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  19.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  20.     GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
  21.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  22.   hspi1.Instance = SPI1;
  23.   hspi1.Init.Mode = SPI_MODE_MASTER;
  24.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  25.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  26.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  27.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  28.   hspi1.Init.NSS = SPI_NSS_SOFT;
  29.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  30.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  31.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  32.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  33.   hspi1.Init.CRCPolynomial = 0x7;
  34.   hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  35.   hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  36.   hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  37.   hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  38.   hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  39.   hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  40.   hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
  41.   hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  42.   hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
  43.   hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;
  44.   HAL_SPI_Init(&hspi1);
  45.     __HAL_SPI_ENABLE(&hspi1);
  46.    
  47.     HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
  48.     HAL_NVIC_EnableIRQ(SPI1_IRQn);
  49.    
  50.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;
  51.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP1_GPDMA_CH0_TCF_TRG;
  52.   HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;
  53.   HAL_SPIEx_SetConfigAutonomousMode(&hspi1, &HAL_SPI_AutonomousMode_Cfg_Struct);

  54.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
  55.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
  56.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
  57.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
  58.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;
  59.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;
  60.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
  61.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
  62.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
  63.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
  64.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
  65.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
  66.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
  67.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
  68.     HAL_DMA_Init(&handle_GPDMA1_Channel1);

  69.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);

  70.     HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV);
  71.    
  72.     HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 0, 0);
  73.   HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);

  74. }

  75. void GPDMA1_Channel1_IRQHandler(void)
  76. {
  77.   HAL_DMA_IRQHandler(&handle_GPDMA1_Channel1);
  78. }

  79. void SPI1_IRQHandler(void)
  80. {
  81.   HAL_SPI_IRQHandler(&hspi1);
  82. }
复制代码


SPI发送或者发送+接收代码如下:
  1. uint8_t SPI1_PA5_PA6_PA7_Read_Write_Byte(uint8_t TxData)
  2. {
  3.     uint8_t Rxdata;
  4.     HAL_SPI_TransmitReceive(&hspi1 , &TxData , &Rxdata , 1 , 1000);      
  5.     return Rxdata;
  6. }

  7. void SPI1_PA5_PA7_Write_Byte(uint8_t TxData)
  8. {
  9.     HAL_SPI_Transmit(&hspi1 , &TxData , 1 , 1000);
  10. }
复制代码



SPI随时需要调整数据位数为16位或者改回8位:
  1. hspi1.Init.DataSize=SPI_DATASIZE_16BIT;
  2. HAL_SPI_Init(&hspi1);

  3. hspi1.Init.DataSize=SPI_DATASIZE_8BIT;
  4. HAL_SPI_Init(&hspi1);
复制代码

在实际应用中,常常需要修改DMA参数,最常见的就是内存地址自增或者非自增,数据是8位或者16位(Halfword半字)对齐:
  1. void SPI1_TX_DMA_Config_PID_MID_HalfWord_Normal()
  2. {
  3.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
  4.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
  5.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
  6.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
  7.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_FIXED;
  8.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;
  9.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
  10.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
  11.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
  12.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
  13.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
  14.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
  15.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
  16.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;

  17.   HAL_DMA_Init(&handle_GPDMA1_Channel1);
  18.    
  19.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
  20.    
  21. }

  22. void SPI1_TX_DMA_Config_PID_MIE_Byte_Normal()
  23. {
  24.     handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
  25.     handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
  26.     handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
  27.     handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
  28.     handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;
  29.     handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;
  30.     handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
  31.     handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
  32.     handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
  33.     handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
  34.     handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
  35.     handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
  36.     handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
  37.     handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;

  38.   HAL_DMA_Init(&handle_GPDMA1_Channel1);
  39.    
  40.     __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
  41.    
  42. }
复制代码

我这里用SPI1去驱动ST7789的屏幕,使用SPI1的DMA发送功能,因此使用DMA传输函数:
  1. HAL_SPI_Transmit_DMA(&hspi1 , pic , num1);
复制代码

SPI不使用DMA的波形如图:
IMG_20250522_224448.jpg
这里有个ST新系列的SPI波形问题,MOSI脚会在发送结束的时候拉低相当长的一段时间才会恢复高电平。

SPI使用DMA的波形如图:
IMG_20250526_224854.jpg

使用SPI引脚驱动ST7789液晶彩屏:
u3.JPG
IMG_20250526_235555.jpg

收藏 评论0 发布时间:2025-5-27 00:09

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版