上一帖评测了STM32U385的SPIDMA发送功能用作ST7789液晶屏刷图,这次来评测一下SPIDMA接收实现读取SPI FLASH数据并显示到屏幕上,原理差不多。
) A0 p# d' @ v$ e1 b5 j 看CubeMX配置:/ }5 p/ x- c, E8 ~* d8 [3 H+ N
0 U( a' v5 O0 c! }( t2 c, C
( ~5 K$ j7 D: q6 x3 D2 s4 r7 @
可以看到,配置跟SPIDMA发送基本大同小异,有不同的地方是,SPIDMA接收是不跟SPIDMA发送用同一个通道的,比如我这里就用新的通道即通道2,然后就是跟SPIDMA发送颠倒过来,是外设到内存,内存地址自增(如果内存地址非自增的话,那就只有内存首地址会收到源源不断的数据)。SPI的接收机制是,主机拉低片选并对从机发送【从机开始接收】指令之后,CLK脚会一直产生脉冲时钟信号给到从机,从机收到脉冲就会源源不断发给主机,只要主机的时钟信号不停就表示主机一直在接收状态,而SPIDMA接收呢则是在此基础上把SPI外设给DMA外设相应通道接管,因此CPU也是需要等待DMA外设完成接收产生中断的,所以代码必须要有中断,并且是DMA通道产生的中断:- HAL_NVIC_SetPriority(GPDMA1_Channel2_IRQn, 0, 0);- E- t5 {: q1 ]
- HAL_NVIC_EnableIRQ(GPDMA1_Channel2_IRQn);- D( I0 S! w' N, |5 L6 t, k
0 N9 v1 \% H) T8 T. Q3 e9 C- j- void GPDMA1_Channel2_IRQHandler(void)$ w6 t6 _6 B3 k& ]% p2 u7 W+ r
- {
: L2 X3 D, [1 v! b |6 N - HAL_DMA_IRQHandler(&handle_GPDMA1_Channel2);4 u( v r$ j' T5 S" i; ?: Z7 A: z
- }
复制代码 2 ^8 Q/ _! Q1 v; W* V. H
然后是DMA配置函数,跟上文说的一致,外设到内存,内存地址自增:
, W7 S7 e) f7 x1 j- void SPI1_RX_DMA_Config_PID_MIE_Byte_Normal()
0 y5 ]: x6 T9 _ x7 l - {
0 q; u$ Q0 B! ?$ ?! |4 ]. S6 V( R - handle_GPDMA1_Channel2.Instance = GPDMA1_Channel2;" m0 g- W. v& B4 w1 A( u
- handle_GPDMA1_Channel2.Init.Request = GPDMA1_REQUEST_SPI1_RX;
( l( D/ F& E3 r8 i2 X( C$ F - handle_GPDMA1_Channel2.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;, k$ ]5 Z5 V3 i3 w, ^
- handle_GPDMA1_Channel2.Init.Direction = DMA_PERIPH_TO_MEMORY;1 N' ~4 ]" }8 `: N& n3 }, Y. q
- handle_GPDMA1_Channel2.Init.SrcInc = DMA_SINC_FIXED;+ f& J. y. ~# r$ y
- handle_GPDMA1_Channel2.Init.DestInc = DMA_DINC_INCREMENTED;
. C* k+ q, d' v7 }" L" a) A, [ - handle_GPDMA1_Channel2.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
/ C( x; z; y; c/ B4 a1 O6 N4 J - handle_GPDMA1_Channel2.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
4 G' v' R1 f* ^# B6 J. } - handle_GPDMA1_Channel2.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
& A" N! v; ?# n6 n$ } - handle_GPDMA1_Channel2.Init.SrcBurstLength = 1;3 S0 I" j- r2 w. Q
- handle_GPDMA1_Channel2.Init.DestBurstLength = 1;
+ [! n! e3 ?7 V0 w) C - handle_GPDMA1_Channel2.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
8 _3 I! \4 \7 m2 h# }! I7 } - handle_GPDMA1_Channel2.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;: n/ w/ K9 \2 N
- handle_GPDMA1_Channel2.Init.Mode = DMA_NORMAL;% P% y* }! Y* a4 j f `
- HAL_DMA_Init(&handle_GPDMA1_Channel2);
5 O R7 l( }) Q5 f' H w
j4 @6 C7 I0 \, e/ x- __HAL_LINKDMA(&hspi1 , hdmarx , handle_GPDMA1_Channel2);" H0 T, W# d5 Q5 m
- * Q# Q7 J* x+ g& e6 ?; i Q
- HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel2 , DMA_CHANNEL_NPRIV);
/ ?" M$ p! C0 I - }
复制代码 - O% r( f; I* U2 J% v1 M3 U) v
7 L* M; o3 B( c7 u8 o+ y对于SPI FLASH从机而言,无论主机是否使用DMA接收,都是通过主机的指令行事,因此代码区别只在主机发完【从机开始接收】这个指令给从机之后的接收方式不同而已:
8 l. l" K5 T4 x- void W25QXX_Read(uint8_t* pBuffer , uint32_t addr , uint16_t num)
. z; d$ F# c9 a; [: A9 p4 | - {
0 N6 J: `9 u- ^3 {# R7 p5 t% a - uint16_t i; 8 i. ?7 [; { ?9 S
- XX25QXX_CS_LOW;* z% `5 D% E% p# M
-
; F4 |2 L3 d6 i {3 R: O - SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);4 y. ~4 T6 x9 H
- if(XX25QXX_TYPE == W25Q256) $ }: }7 v0 d: A* I$ z% C/ }: s; K8 o
- {% d$ V; [9 c# L
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24));
! { |8 N7 m7 L5 O9 j( T$ r2 } - }
{% G( `5 o" c - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));! ^# y; J# k5 \& L6 ]7 n
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8));9 f- Q6 _: q/ f+ q) z0 S
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr);
$ y0 c1 W" `# N) K( ?& o -
, a, Z7 Y3 O( r+ p) Y% w - for(i = 0 ; i < num ; i ++)
) O7 I- N: H" O& f$ W. U6 [ - { 7 B* \3 d# X# ?
- pBuffer[i]=SPI1_PA5_PA6_PA7_Read_Write_Byte(0xff);; O+ @5 t! H J1 Y. c( q
- }1 F0 T" f. N" r5 p, c( H# |
- 2 o) N% h( R' H' H
- XX25QXX_CS_HIGH; . t0 h9 U7 Q8 t2 L z# k% o
- }
v$ G( v1 Z7 @& L - 2 `1 s% G$ H a$ @( ~! V
- void XX25QXX_Read_DMA(uint8_t* pBuffer , uint32_t addr , uint16_t num)
+ O+ ?' F" {& z* [$ y' q* y9 W - { ! y5 K# V4 o, N$ L" y7 ~3 V
- uint16_t i; * {7 A8 f: }% C
- XX25QXX_CS_LOW;4 I) ^, @5 X' ]2 w) p1 ]
- SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);+ g9 w6 {$ u; Z+ p9 u
- if(XX25QXX_TYPE == W25Q256)
6 v, D$ K/ d8 z( ^9 S, V. P - {
$ b, g4 M) g! o3 X - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24));
7 Q: B8 H9 v- |8 [# H3 n - }
6 ?4 @7 d8 e1 x! U: F% q+ q- n - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));
0 }5 v- V C5 m0 b5 x - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8)); / K" G8 |- Q. }- d! `
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr);
/ d5 Q W* J i* S* P2 T: ~( p - 1 x& {4 t: i6 ^! p. N/ i, h' `
- SPI1_RX_DMA_Config_PID_MIE_Byte_Normal();; u4 h1 K7 h6 d% s! z
- HAL_SPI_Receive_DMA(&hspi1 , pBuffer , num);
8 A- l8 m+ {1 L1 v w - while(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_BUSY_RX);
+ o& ?6 P4 Z7 ]7 f) Q6 Q' @ - + X! U* b, l2 ]% O/ }! c
- XX25QXX_CS_HIGH; % ]1 f% x) X2 a W) N
- } : ?7 A* j& B& B5 k. J; Y
复制代码 ! ^" c0 W" Q. @; A% x
我这里用SPIDMA接收所实现的功能是从SPI FLASH读出图片数据并显示到SPITFTLCD液晶屏上,而MCU的RAM大小以及DMA通道大小是有限制的,所以必须要采用分块读取的方式实现,将320*480分辨率,16位色的屏幕,平均分成6块,每块就是320*480*2/6=51200大小:
, t# m& j/ T4 U* i- uint8_t datatemp[51200];5 e0 K# P, \( C
- 9 L0 U, z5 {' s/ Q8 ?5 ]
- for(j = 0 ; j < 6 ; j ++)8 I7 [2 g' E( B& V' I! y
- {
2 m3 Z/ a- m. S0 B. L - W25QXX_Write((uint8_t*)(gImage_u3_logo + j * 51200 ) , WRITE_READ_ADDR + j * 51200 , 51200);
* R0 O4 \/ x+ n6 h, X a8 g, v - XX25QXX_Read_DMA(datatemp , WRITE_READ_ADDR + j * 51200 , 51200);8 A p) o: H# ?# M9 I9 V/ B
-
2 C9 H' B# x- D5 W* l - SPITFTLCD_ST7789_ShowPicture_DMA(0 , j * 80 , SPITFTLCD_ST7789_W , 80 , datatemp);1 b1 a9 Z. _. E
- }
复制代码 : g0 M8 ~* Z: U' A& q+ h
演示效果:
! T/ k* j1 Y2 k% v h
1 g1 |+ F" J4 b! I
/ i% P, \) Y8 ^/ g* c% o: G2 v- e9 e$ j" U, i9 e
|