上一帖评测了STM32U385的SPIDMA发送功能用作ST7789液晶屏刷图,这次来评测一下SPIDMA接收实现读取SPI FLASH数据并显示到屏幕上,原理差不多。, m2 q6 s- b4 R7 _* J
看CubeMX配置:2 O) v! |' y5 O. H) e+ u. z/ x5 I
) {' l ]1 A9 l! y- Q; h9 g0 ^7 D6 y1 p ! J, |: k$ o. ~( ?# g
可以看到,配置跟SPIDMA发送基本大同小异,有不同的地方是,SPIDMA接收是不跟SPIDMA发送用同一个通道的,比如我这里就用新的通道即通道2,然后就是跟SPIDMA发送颠倒过来,是外设到内存,内存地址自增(如果内存地址非自增的话,那就只有内存首地址会收到源源不断的数据)。SPI的接收机制是,主机拉低片选并对从机发送【从机开始接收】指令之后,CLK脚会一直产生脉冲时钟信号给到从机,从机收到脉冲就会源源不断发给主机,只要主机的时钟信号不停就表示主机一直在接收状态,而SPIDMA接收呢则是在此基础上把SPI外设给DMA外设相应通道接管,因此CPU也是需要等待DMA外设完成接收产生中断的,所以代码必须要有中断,并且是DMA通道产生的中断:- HAL_NVIC_SetPriority(GPDMA1_Channel2_IRQn, 0, 0);
6 T2 d+ a2 x, u i4 {1 |3 W - HAL_NVIC_EnableIRQ(GPDMA1_Channel2_IRQn);
2 V' G$ ?6 a) S0 R p8 d' Z( u% W0 l
( i6 o# O5 X$ I7 @7 _- void GPDMA1_Channel2_IRQHandler(void)( W9 d- a5 {3 w/ t! W
- {( \( u$ ^# [6 }" B7 w
- HAL_DMA_IRQHandler(&handle_GPDMA1_Channel2);
/ O0 O3 Y1 s2 }3 _1 G- T: Z( |& I - }
复制代码 # W3 R7 b4 r" F5 u" r5 ^
然后是DMA配置函数,跟上文说的一致,外设到内存,内存地址自增:
% B7 x' C3 O. z Z( ~- void SPI1_RX_DMA_Config_PID_MIE_Byte_Normal()
; F6 u L; w; p# \* B( ~' c5 t - {2 B; H! B5 G% k
- handle_GPDMA1_Channel2.Instance = GPDMA1_Channel2;5 z5 e+ m7 ]2 |, Y
- handle_GPDMA1_Channel2.Init.Request = GPDMA1_REQUEST_SPI1_RX;$ X" [% Y+ U4 z
- handle_GPDMA1_Channel2.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;+ S0 m% z6 F7 U0 ^& e
- handle_GPDMA1_Channel2.Init.Direction = DMA_PERIPH_TO_MEMORY;+ s. j' E* P$ t9 F
- handle_GPDMA1_Channel2.Init.SrcInc = DMA_SINC_FIXED;
4 T& s( K( i* Q+ |7 n - handle_GPDMA1_Channel2.Init.DestInc = DMA_DINC_INCREMENTED;5 U- q& V4 ^; T6 a6 j% U3 _0 j1 W
- handle_GPDMA1_Channel2.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;4 E3 R! Q* ]8 c. K, r
- handle_GPDMA1_Channel2.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
2 f6 o) {2 r1 o; D0 C- { - handle_GPDMA1_Channel2.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
4 j" B. B% i4 ~4 t. F - handle_GPDMA1_Channel2.Init.SrcBurstLength = 1;3 i0 _, i$ Z, I4 x0 u3 K
- handle_GPDMA1_Channel2.Init.DestBurstLength = 1;
5 j9 i8 T' F/ s& g$ d. q& D - handle_GPDMA1_Channel2.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
1 R% H4 _$ n% _0 o4 l% k; ? - handle_GPDMA1_Channel2.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;8 h2 r1 b/ e7 j x K) w
- handle_GPDMA1_Channel2.Init.Mode = DMA_NORMAL;3 Z0 l& N6 w* f8 m2 Z* @
- HAL_DMA_Init(&handle_GPDMA1_Channel2);" r0 I1 Q5 ?# ?9 X
- % }( b, d" ~0 R2 c: g
- __HAL_LINKDMA(&hspi1 , hdmarx , handle_GPDMA1_Channel2);, }9 {) |9 J7 A: T6 D- m6 o
, Q1 n6 k5 y: u1 ^9 ~- HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel2 , DMA_CHANNEL_NPRIV);
& i) J0 B3 {+ Z8 H4 ]' B; O3 E - }
复制代码 1 w& {2 ~: b# ~
( f. O; g, M ^3 D7 R, I, X) l
对于SPI FLASH从机而言,无论主机是否使用DMA接收,都是通过主机的指令行事,因此代码区别只在主机发完【从机开始接收】这个指令给从机之后的接收方式不同而已:
2 P9 y5 n8 x1 b- void W25QXX_Read(uint8_t* pBuffer , uint32_t addr , uint16_t num)
, U" w0 T: O+ ?1 J; S; X* U8 a( A4 d - {
& A2 d2 P) d" _! J# R: X7 p - uint16_t i;
1 a C9 u8 b& E. d% P - XX25QXX_CS_LOW;, u) I. e% l1 t$ u1 G! [9 K
- ! W" g" Q( N R+ B9 y" g2 m
- SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);' y8 @6 s4 g- n9 L
- if(XX25QXX_TYPE == W25Q256)
5 m4 \" j5 J, M! n }% ]9 S - {% X& Q# e3 h8 L( e- T- G: N6 C( s
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24));
4 w0 f3 a: _, ~) }- ?# E8 H - }
& |) [/ K) a/ K, c) r! s4 l# |. Z - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));7 H- j/ c6 [( z2 v
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8));. r) D5 }6 p( t8 a) E9 y: j
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr);8 q. T2 V+ t; a) w- u
-
" o. W: _! G3 q f9 j6 @9 g - for(i = 0 ; i < num ; i ++)
+ J* K& r! v) u8 c# v+ K3 I% T) Q - { # o9 u, C1 v# y, A* m' ?1 M! y4 y6 Q
- pBuffer[i]=SPI1_PA5_PA6_PA7_Read_Write_Byte(0xff);
' ~* p) R: u) V8 S, H - }4 D7 z+ y4 b) K/ K
- 4 b9 G: p3 c$ S8 y
- XX25QXX_CS_HIGH; # p9 d e7 O! \6 N
- }
/ d) C: ]% \3 A7 p - / }; }! @7 W1 ? C
- void XX25QXX_Read_DMA(uint8_t* pBuffer , uint32_t addr , uint16_t num)
- p) j" z b, Z1 c' r1 V - {
0 K% _) ]3 D1 c9 Q, L2 N - uint16_t i; 1 `" _ ~4 Z& I, Z4 T
- XX25QXX_CS_LOW;
& T: O4 C5 [5 Z: B - SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);
/ Q4 i+ U6 O* w9 z6 } - if(XX25QXX_TYPE == W25Q256)0 l( F- g8 [: u4 h3 E
- {
8 O8 \3 M; Z/ y; j0 ~6 T9 ^& x - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24));
9 o1 r0 P0 l. r2 J% X- K7 h - }0 H% d/ j/ M; e( c& k3 |) u
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));
1 o; A3 p7 @# ? - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8));
1 X% {: F0 M" X5 [/ |, G - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr); 6 o$ q( A9 s0 d0 ?- _0 R
- 4 L5 R/ K, T6 a3 u
- SPI1_RX_DMA_Config_PID_MIE_Byte_Normal();. ?# i# X% R n2 O! N3 R
- HAL_SPI_Receive_DMA(&hspi1 , pBuffer , num);
9 n, m# T$ \6 {# [ ^% K5 T+ U: @, T - while(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_BUSY_RX);
. ~! g$ j* t* K0 m0 N l: o( ]; C* R - 3 }! Y9 H4 y Z& ~
- XX25QXX_CS_HIGH; 0 C1 t# b& Z% b/ R. M4 `$ D! x
- } 5 }7 Z9 z0 v5 V/ }% \0 U! ~
复制代码
- d) n0 }& H. O) t) x% V4 P5 m我这里用SPIDMA接收所实现的功能是从SPI FLASH读出图片数据并显示到SPITFTLCD液晶屏上,而MCU的RAM大小以及DMA通道大小是有限制的,所以必须要采用分块读取的方式实现,将320*480分辨率,16位色的屏幕,平均分成6块,每块就是320*480*2/6=51200大小:
# S8 T1 X- h8 @7 B' o- uint8_t datatemp[51200];
4 \+ L9 U8 s C0 s$ B( h - 8 n* ]0 I( k9 ~3 s* f5 }
- for(j = 0 ; j < 6 ; j ++)
) Z' q$ Y5 D1 h7 h! i; b - {5 k% z8 m- A h2 O4 ^
- W25QXX_Write((uint8_t*)(gImage_u3_logo + j * 51200 ) , WRITE_READ_ADDR + j * 51200 , 51200);( g% W4 Y, w+ A3 j5 G" N
- XX25QXX_Read_DMA(datatemp , WRITE_READ_ADDR + j * 51200 , 51200);
: C* @/ O( Y$ ?2 s - - p& z; F _7 D# V; Z
- SPITFTLCD_ST7789_ShowPicture_DMA(0 , j * 80 , SPITFTLCD_ST7789_W , 80 , datatemp);" e% E4 M9 @. H9 c& M; M5 T
- }
复制代码
8 ?6 w3 S% Z( a7 N% R演示效果:
- H- Z8 @$ V1 ?& k
+ U( P/ M/ b% [- C$ Q9 f
4 i6 v3 I6 u/ ?& s6 L) h" j
0 _/ E' R& h" g S
|