上一帖评测了STM32U385的SPIDMA发送功能用作ST7789液晶屏刷图,这次来评测一下SPIDMA接收实现读取SPI FLASH数据并显示到屏幕上,原理差不多。 Z6 z4 L. O" d, R, i/ k" P
看CubeMX配置:' b* ~" J; e d; @0 Z& v& Q
' f- W% t* D+ d8 ~' n2 X
7 a& l1 S+ C q 可以看到,配置跟SPIDMA发送基本大同小异,有不同的地方是,SPIDMA接收是不跟SPIDMA发送用同一个通道的,比如我这里就用新的通道即通道2,然后就是跟SPIDMA发送颠倒过来,是外设到内存,内存地址自增(如果内存地址非自增的话,那就只有内存首地址会收到源源不断的数据)。SPI的接收机制是,主机拉低片选并对从机发送【从机开始接收】指令之后,CLK脚会一直产生脉冲时钟信号给到从机,从机收到脉冲就会源源不断发给主机,只要主机的时钟信号不停就表示主机一直在接收状态,而SPIDMA接收呢则是在此基础上把SPI外设给DMA外设相应通道接管,因此CPU也是需要等待DMA外设完成接收产生中断的,所以代码必须要有中断,并且是DMA通道产生的中断:- HAL_NVIC_SetPriority(GPDMA1_Channel2_IRQn, 0, 0);* |+ m6 E+ R% w
- HAL_NVIC_EnableIRQ(GPDMA1_Channel2_IRQn);6 ]# N7 `$ y# Z9 M
- h' a7 a U+ l# c8 O6 O3 }1 A* ~
- void GPDMA1_Channel2_IRQHandler(void)) R0 H# |9 Q1 b# E: \' X* L# S& P& O
- {
1 y; ]# i% V; P* ]1 h, c( U1 f - HAL_DMA_IRQHandler(&handle_GPDMA1_Channel2);+ @5 L9 Q0 L" n
- }
复制代码
+ x* G: y6 Y+ p" C, y然后是DMA配置函数,跟上文说的一致,外设到内存,内存地址自增:7 I; x& T9 r) ?% ^4 H# Y
- void SPI1_RX_DMA_Config_PID_MIE_Byte_Normal()
" w+ R- O a" z$ I& t - {
( P8 w0 _, H3 {5 J1 d1 U/ j( J - handle_GPDMA1_Channel2.Instance = GPDMA1_Channel2;0 `- P% T4 C6 r5 X
- handle_GPDMA1_Channel2.Init.Request = GPDMA1_REQUEST_SPI1_RX;
0 L5 B3 u, u; y @& g" ^ - handle_GPDMA1_Channel2.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
; G4 k$ x3 H9 ^) d4 ]! d5 Y9 k - handle_GPDMA1_Channel2.Init.Direction = DMA_PERIPH_TO_MEMORY;
1 u% F9 G. I3 D$ v - handle_GPDMA1_Channel2.Init.SrcInc = DMA_SINC_FIXED;
- Y3 P1 I& R6 j7 ~4 d - handle_GPDMA1_Channel2.Init.DestInc = DMA_DINC_INCREMENTED; e4 R# M( {; h- P1 o
- handle_GPDMA1_Channel2.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
) u. h& C: d2 @+ G - handle_GPDMA1_Channel2.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;- s/ R& r: @/ J( X8 r6 [) r
- handle_GPDMA1_Channel2.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;3 s4 J. Y- n1 O% d0 m$ V
- handle_GPDMA1_Channel2.Init.SrcBurstLength = 1;5 L# x M6 m4 S8 ?/ ?
- handle_GPDMA1_Channel2.Init.DestBurstLength = 1;6 M% z& k& R" K) \& }
- handle_GPDMA1_Channel2.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
0 u' Y) i+ }6 j, D - handle_GPDMA1_Channel2.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;% n; h( T/ E: Y+ d; K
- handle_GPDMA1_Channel2.Init.Mode = DMA_NORMAL;8 e) _# U9 F& o9 Q
- HAL_DMA_Init(&handle_GPDMA1_Channel2);/ G# o. ]% }1 f9 h' [3 e
4 Z/ }! |% X* {% _- __HAL_LINKDMA(&hspi1 , hdmarx , handle_GPDMA1_Channel2);6 |$ u% u$ K4 L/ [& f5 v3 r
- , G% x' \# G" z2 L; @/ T: Z. m
- HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel2 , DMA_CHANNEL_NPRIV);2 a" [' a6 ?! |/ Y2 }- a+ N
- }
复制代码
3 C. l b) C" C: }/ H1 b$ U- l; G3 P
对于SPI FLASH从机而言,无论主机是否使用DMA接收,都是通过主机的指令行事,因此代码区别只在主机发完【从机开始接收】这个指令给从机之后的接收方式不同而已:9 C0 K* j; u. F
- void W25QXX_Read(uint8_t* pBuffer , uint32_t addr , uint16_t num)
1 w; s& g$ F" m4 E3 ^4 u7 B - { / p" {- i' f# H e5 q
- uint16_t i; - K. T5 \; C- Z4 L0 k
- XX25QXX_CS_LOW;
$ [4 z; P- P6 C( U3 A -
4 k/ @( a0 q2 J T6 P. H - SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);
- {- p' }7 X. b5 L' J; v - if(XX25QXX_TYPE == W25Q256) " e* P3 C- I; H9 W
- {$ j. J/ l- l3 n, d$ `
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24)); - V$ Q( {6 h7 h) P( Q) k( R: L
- }1 y0 w8 A3 z! A5 A- A( r
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));
' t2 I3 M5 M( _; N2 l0 L - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8));2 d$ }2 |2 x" r
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr);# t+ I2 A( q1 a% M; e7 u. T
-
8 I3 Z6 \# d0 A* r" y: I k - for(i = 0 ; i < num ; i ++)
/ X7 X* H2 @9 @- w$ j, } - { 5 c+ L* M0 r' j9 Y) m' @
- pBuffer[i]=SPI1_PA5_PA6_PA7_Read_Write_Byte(0xff);# k( P- p" R, S* ~
- }9 J+ W) J. z) h1 S' c9 A+ N
- ! G' i9 u( b) O
- XX25QXX_CS_HIGH;
+ Q( H) X Y" h( M* C" Q - }
% y" P: l. x0 [6 k- l - # g& p6 z N( v' x7 t
- void XX25QXX_Read_DMA(uint8_t* pBuffer , uint32_t addr , uint16_t num)
0 Y+ [) q( @; V9 b0 D - { * V) c6 ?, g$ i1 A3 A
- uint16_t i; & a% y4 _/ `& o! t2 J" J
- XX25QXX_CS_LOW;
! X; _7 c8 @# L+ g7 _ - SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);
* c. T$ C+ x6 o, Y1 ?4 X - if(XX25QXX_TYPE == W25Q256)
% s- S$ w' X0 z1 F6 [1 \9 y) F' I - {
" A8 x$ H$ h7 X0 c) ] - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24)); 3 z5 d. j7 J' w9 z
- }" J/ r7 f8 {; e
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));
6 }: D) A9 i8 M! C - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8)); / v2 k7 Z, ]* [( ^# A0 L1 e; l G
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr);
- {8 W% l- ?; i5 X3 o- ` - ) h* w* d' J0 q( Z) H; z; c
- SPI1_RX_DMA_Config_PID_MIE_Byte_Normal();
1 i+ {: U- T* A7 X - HAL_SPI_Receive_DMA(&hspi1 , pBuffer , num);
* o) s0 V& p% b- p# W - while(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_BUSY_RX);; Q$ x% w# ~: x @7 v$ |
- 6 O9 U# R# M& H, }* n
- XX25QXX_CS_HIGH; 6 [- l" n! \9 [6 L/ w
- }
9 W$ y; \# Y- }0 Z$ p0 |- `) u
复制代码 # {8 _, M. B/ ~" E
我这里用SPIDMA接收所实现的功能是从SPI FLASH读出图片数据并显示到SPITFTLCD液晶屏上,而MCU的RAM大小以及DMA通道大小是有限制的,所以必须要采用分块读取的方式实现,将320*480分辨率,16位色的屏幕,平均分成6块,每块就是320*480*2/6=51200大小:
% V7 a/ k3 @! ?) H3 m* z( ^- uint8_t datatemp[51200];0 ]" \4 D/ P" h) j/ w2 ?+ ~
- 4 m5 Y: a0 a1 `$ p, F {
- for(j = 0 ; j < 6 ; j ++)" {4 |) k! P# i! V- v; b
- {' T3 L$ f4 W9 n: L! _5 s
- W25QXX_Write((uint8_t*)(gImage_u3_logo + j * 51200 ) , WRITE_READ_ADDR + j * 51200 , 51200);
( C7 N& g/ c6 c0 A0 g - XX25QXX_Read_DMA(datatemp , WRITE_READ_ADDR + j * 51200 , 51200);
, ]7 v9 g, C8 P t" }2 N' J3 H -
+ |. S7 I7 ?3 S! \9 m" Z - SPITFTLCD_ST7789_ShowPicture_DMA(0 , j * 80 , SPITFTLCD_ST7789_W , 80 , datatemp);
9 D7 x* T8 R- I0 b - }
复制代码
2 C! W; c: M9 y5 ^演示效果:5 [* f" K7 C1 \9 U& w5 a9 X
8 O8 L! `' p7 w( H8 M/ I& o
* I' b: E- q3 U' V0 @, [% ?
4 L% g; P! `' K6 G! T4 I
|