上一帖评测了STM32U385的SPIDMA发送功能用作ST7789液晶屏刷图,这次来评测一下SPIDMA接收实现读取SPI FLASH数据并显示到屏幕上,原理差不多。
看CubeMX配置:
可以看到,配置跟SPIDMA发送基本大同小异,有不同的地方是,SPIDMA接收是不跟SPIDMA发送用同一个通道的,比如我这里就用新的通道即通道2,然后就是跟SPIDMA发送颠倒过来,是外设到内存,内存地址自增(如果内存地址非自增的话,那就只有内存首地址会收到源源不断的数据)。SPI的接收机制是,主机拉低片选并对从机发送【从机开始接收】指令之后,CLK脚会一直产生脉冲时钟信号给到从机,从机收到脉冲就会源源不断发给主机,只要主机的时钟信号不停就表示主机一直在接收状态,而SPIDMA接收呢则是在此基础上把SPI外设给DMA外设相应通道接管,因此CPU也是需要等待DMA外设完成接收产生中断的,所以代码必须要有中断,并且是DMA通道产生的中断:- HAL_NVIC_SetPriority(GPDMA1_Channel2_IRQn, 0, 0);
- HAL_NVIC_EnableIRQ(GPDMA1_Channel2_IRQn);
- void GPDMA1_Channel2_IRQHandler(void)
- {
- HAL_DMA_IRQHandler(&handle_GPDMA1_Channel2);
- }
复制代码
然后是DMA配置函数,跟上文说的一致,外设到内存,内存地址自增:
- void SPI1_RX_DMA_Config_PID_MIE_Byte_Normal()
- {
- handle_GPDMA1_Channel2.Instance = GPDMA1_Channel2;
- handle_GPDMA1_Channel2.Init.Request = GPDMA1_REQUEST_SPI1_RX;
- handle_GPDMA1_Channel2.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
- handle_GPDMA1_Channel2.Init.Direction = DMA_PERIPH_TO_MEMORY;
- handle_GPDMA1_Channel2.Init.SrcInc = DMA_SINC_FIXED;
- handle_GPDMA1_Channel2.Init.DestInc = DMA_DINC_INCREMENTED;
- handle_GPDMA1_Channel2.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
- handle_GPDMA1_Channel2.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
- handle_GPDMA1_Channel2.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
- handle_GPDMA1_Channel2.Init.SrcBurstLength = 1;
- handle_GPDMA1_Channel2.Init.DestBurstLength = 1;
- handle_GPDMA1_Channel2.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
- handle_GPDMA1_Channel2.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
- handle_GPDMA1_Channel2.Init.Mode = DMA_NORMAL;
- HAL_DMA_Init(&handle_GPDMA1_Channel2);
- __HAL_LINKDMA(&hspi1 , hdmarx , handle_GPDMA1_Channel2);
- HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel2 , DMA_CHANNEL_NPRIV);
- }
复制代码
对于SPI FLASH从机而言,无论主机是否使用DMA接收,都是通过主机的指令行事,因此代码区别只在主机发完【从机开始接收】这个指令给从机之后的接收方式不同而已:
- void W25QXX_Read(uint8_t* pBuffer , uint32_t addr , uint16_t num)
- {
- uint16_t i;
- XX25QXX_CS_LOW;
-
- SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);
- if(XX25QXX_TYPE == W25Q256)
- {
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24));
- }
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8));
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr);
-
- for(i = 0 ; i < num ; i ++)
- {
- pBuffer[i]=SPI1_PA5_PA6_PA7_Read_Write_Byte(0xff);
- }
-
- XX25QXX_CS_HIGH;
- }
- void XX25QXX_Read_DMA(uint8_t* pBuffer , uint32_t addr , uint16_t num)
- {
- uint16_t i;
- XX25QXX_CS_LOW;
- SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);
- if(XX25QXX_TYPE == W25Q256)
- {
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24));
- }
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8));
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr);
-
- SPI1_RX_DMA_Config_PID_MIE_Byte_Normal();
- HAL_SPI_Receive_DMA(&hspi1 , pBuffer , num);
- while(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_BUSY_RX);
-
- XX25QXX_CS_HIGH;
- }
复制代码
我这里用SPIDMA接收所实现的功能是从SPI FLASH读出图片数据并显示到SPITFTLCD液晶屏上,而MCU的RAM大小以及DMA通道大小是有限制的,所以必须要采用分块读取的方式实现,将320*480分辨率,16位色的屏幕,平均分成6块,每块就是320*480*2/6=51200大小:
- uint8_t datatemp[51200];
- for(j = 0 ; j < 6 ; j ++)
- {
- W25QXX_Write((uint8_t*)(gImage_u3_logo + j * 51200 ) , WRITE_READ_ADDR + j * 51200 , 51200);
- XX25QXX_Read_DMA(datatemp , WRITE_READ_ADDR + j * 51200 , 51200);
-
- SPITFTLCD_ST7789_ShowPicture_DMA(0 , j * 80 , SPITFTLCD_ST7789_W , 80 , datatemp);
- }
复制代码
演示效果:
|