SPIDMA发送/接收是非常常见的MCU应用,用于与高速SPI器件通信并提高效率,这个帖子是评测STM32U385RG的SPI1接口,并启用SPI1的DMA发送功能。
在上一个帖子中提到,STM32U3的DMA外设名为GPDMA,在ST新出的系列中,任意支持DMA的外设都可以绑定到DMA的任意通道/流/节点上,不像F103和F4系列那样规定通道号,来看看SPI1DMA的配置:
Normal模式表示每次调用DMA发送函数只会发送一次,Circular模式表示只调用一次DMA发送函数就一直发;在外设所使用的DMA中一般配置外设地址非自增,内存地址可配置为自增或者非自增,内存地址非自增表示DMA发送的数据是同一个数据,也就是内存首地址的数据;源地址和目标地址的数据格式必须一致,都是8位或者16位;只要SPI使用了DMA进行发送或者接收,就必须要把SPI中断和DMA中断都打开,少打开一个都不会正常工作。
SPI初始化代码如下,在ST新系列中加入了一些不太常用的SPI机制,如CS片选引脚空闲等待节拍,Ready模式,因为在大部分场景中片选引脚都由GPIO进行控制,比较少用到SPI外设自带的NSS引脚功能,除非是双MCU通信:
- void SPI1_PA5_PA6_PA7_Init(void)
- {
-
- __HAL_RCC_GPIOA_CLK_ENABLE();
- __HAL_RCC_SPI1_CLK_ENABLE();
- __HAL_RCC_GPDMA1_CLK_ENABLE();
-
- RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
- GPIO_InitTypeDef GPIO_InitStruct = {0};
- SPI_AutonomousModeConfTypeDef HAL_SPI_AutonomousMode_Cfg_Struct = {0};
-
- PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SPI1;
- PeriphClkInit.Spi1ClockSelection = RCC_SPI1CLKSOURCE_PCLK2;
- HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);
-
- GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
- GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
- hspi1.Instance = SPI1;
- hspi1.Init.Mode = SPI_MODE_MASTER;
- hspi1.Init.Direction = SPI_DIRECTION_2LINES;
- hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
- hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
- hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
- hspi1.Init.NSS = SPI_NSS_SOFT;
- hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
- hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
- hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
- hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
- hspi1.Init.CRCPolynomial = 0x7;
- hspi1.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
- hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
- hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
- hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
- hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
- hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
- hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_ENABLE;
- hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
- hspi1.Init.ReadyMasterManagement = SPI_RDY_MASTER_MANAGEMENT_INTERNALLY;
- hspi1.Init.ReadyPolarity = SPI_RDY_POLARITY_HIGH;
- HAL_SPI_Init(&hspi1);
- __HAL_SPI_ENABLE(&hspi1);
-
- HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(SPI1_IRQn);
-
- HAL_SPI_AutonomousMode_Cfg_Struct.TriggerState = SPI_AUTO_MODE_DISABLE;
- HAL_SPI_AutonomousMode_Cfg_Struct.TriggerSelection = SPI_GRP1_GPDMA_CH0_TCF_TRG;
- HAL_SPI_AutonomousMode_Cfg_Struct.TriggerPolarity = SPI_TRIG_POLARITY_RISING;
- HAL_SPIEx_SetConfigAutonomousMode(&hspi1, &HAL_SPI_AutonomousMode_Cfg_Struct);
- handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
- handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
- handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
- handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
- handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;
- handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;
- handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
- handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
- handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
- handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
- handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
- handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
- handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
- handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
- HAL_DMA_Init(&handle_GPDMA1_Channel1);
- __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
- HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel1, DMA_CHANNEL_NPRIV);
-
- HAL_NVIC_SetPriority(GPDMA1_Channel1_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(GPDMA1_Channel1_IRQn);
- }
- void GPDMA1_Channel1_IRQHandler(void)
- {
- HAL_DMA_IRQHandler(&handle_GPDMA1_Channel1);
- }
- void SPI1_IRQHandler(void)
- {
- HAL_SPI_IRQHandler(&hspi1);
- }
复制代码
SPI发送或者发送+接收代码如下:
- uint8_t SPI1_PA5_PA6_PA7_Read_Write_Byte(uint8_t TxData)
- {
- uint8_t Rxdata;
- HAL_SPI_TransmitReceive(&hspi1 , &TxData , &Rxdata , 1 , 1000);
- return Rxdata;
- }
- void SPI1_PA5_PA7_Write_Byte(uint8_t TxData)
- {
- HAL_SPI_Transmit(&hspi1 , &TxData , 1 , 1000);
- }
复制代码
SPI随时需要调整数据位数为16位或者改回8位:
- hspi1.Init.DataSize=SPI_DATASIZE_16BIT;
- HAL_SPI_Init(&hspi1);
- hspi1.Init.DataSize=SPI_DATASIZE_8BIT;
- HAL_SPI_Init(&hspi1);
复制代码
在实际应用中,常常需要修改DMA参数,最常见的就是内存地址自增或者非自增,数据是8位或者16位(Halfword半字)对齐:
- void SPI1_TX_DMA_Config_PID_MID_HalfWord_Normal()
- {
- handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
- handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
- handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
- handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
- handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_FIXED;
- handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;
- handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_HALFWORD;
- handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_HALFWORD;
- handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
- handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
- handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
- handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
- handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
- handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
-
- HAL_DMA_Init(&handle_GPDMA1_Channel1);
-
- __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
-
- }
- void SPI1_TX_DMA_Config_PID_MIE_Byte_Normal()
- {
- handle_GPDMA1_Channel1.Instance = GPDMA1_Channel1;
- handle_GPDMA1_Channel1.Init.Request = GPDMA1_REQUEST_SPI1_TX;
- handle_GPDMA1_Channel1.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
- handle_GPDMA1_Channel1.Init.Direction = DMA_MEMORY_TO_PERIPH;
- handle_GPDMA1_Channel1.Init.SrcInc = DMA_SINC_INCREMENTED;
- handle_GPDMA1_Channel1.Init.DestInc = DMA_DINC_FIXED;
- handle_GPDMA1_Channel1.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
- handle_GPDMA1_Channel1.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
- handle_GPDMA1_Channel1.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
- handle_GPDMA1_Channel1.Init.SrcBurstLength = 1;
- handle_GPDMA1_Channel1.Init.DestBurstLength = 1;
- handle_GPDMA1_Channel1.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
- handle_GPDMA1_Channel1.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
- handle_GPDMA1_Channel1.Init.Mode = DMA_NORMAL;
-
- HAL_DMA_Init(&handle_GPDMA1_Channel1);
-
- __HAL_LINKDMA(&hspi1 , hdmatx , handle_GPDMA1_Channel1);
-
- }
复制代码
我这里用SPI1去驱动ST7789的屏幕,使用SPI1的DMA发送功能,因此使用DMA传输函数:
- HAL_SPI_Transmit_DMA(&hspi1 , pic , num1);
复制代码
SPI不使用DMA的波形如图:
这里有个ST新系列的SPI波形问题,MOSI脚会在发送结束的时候拉低相当长的一段时间才会恢复高电平。
SPI使用DMA的波形如图:
使用SPI引脚驱动ST7789液晶彩屏:
|