上一帖评测了STM32U385的SPIDMA发送功能用作ST7789液晶屏刷图,这次来评测一下SPIDMA接收实现读取SPI FLASH数据并显示到屏幕上,原理差不多。- a6 Q/ K0 b" [0 L1 J$ d
看CubeMX配置:6 [) w" M+ Y3 U3 ]/ F% [/ J1 c* y
; e. N ?% X1 u/ t1 Z% J6 T' k
- ], E! p9 C" f9 B& R. T' J
可以看到,配置跟SPIDMA发送基本大同小异,有不同的地方是,SPIDMA接收是不跟SPIDMA发送用同一个通道的,比如我这里就用新的通道即通道2,然后就是跟SPIDMA发送颠倒过来,是外设到内存,内存地址自增(如果内存地址非自增的话,那就只有内存首地址会收到源源不断的数据)。SPI的接收机制是,主机拉低片选并对从机发送【从机开始接收】指令之后,CLK脚会一直产生脉冲时钟信号给到从机,从机收到脉冲就会源源不断发给主机,只要主机的时钟信号不停就表示主机一直在接收状态,而SPIDMA接收呢则是在此基础上把SPI外设给DMA外设相应通道接管,因此CPU也是需要等待DMA外设完成接收产生中断的,所以代码必须要有中断,并且是DMA通道产生的中断:- HAL_NVIC_SetPriority(GPDMA1_Channel2_IRQn, 0, 0);
5 K3 p) M6 t4 b( e9 y: _0 J4 M - HAL_NVIC_EnableIRQ(GPDMA1_Channel2_IRQn);
& }4 [- R# c' `+ r% |
! A6 v) i* `( P7 [# F/ o- void GPDMA1_Channel2_IRQHandler(void)
3 ~5 }% F3 ?9 j, y" H4 x - {% N6 q2 f3 b% F
- HAL_DMA_IRQHandler(&handle_GPDMA1_Channel2);% p% _9 H. I8 O# O
- }
复制代码
- M( |7 c/ O. m然后是DMA配置函数,跟上文说的一致,外设到内存,内存地址自增:
' P T$ s' d+ }- void SPI1_RX_DMA_Config_PID_MIE_Byte_Normal()5 z2 d- u5 _7 _
- {
3 J# V) i* _7 u% L& _ - handle_GPDMA1_Channel2.Instance = GPDMA1_Channel2;
7 k& J& ~" c0 K - handle_GPDMA1_Channel2.Init.Request = GPDMA1_REQUEST_SPI1_RX;
S. K) n4 }; f5 d/ e - handle_GPDMA1_Channel2.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;) O+ p0 L; h7 `: b
- handle_GPDMA1_Channel2.Init.Direction = DMA_PERIPH_TO_MEMORY;
; ~% d4 l1 P, @" H8 C - handle_GPDMA1_Channel2.Init.SrcInc = DMA_SINC_FIXED;- R! q8 @3 E' W0 N
- handle_GPDMA1_Channel2.Init.DestInc = DMA_DINC_INCREMENTED;+ U! p( P0 w8 D0 u3 G8 p( u |& k
- handle_GPDMA1_Channel2.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
! _# J: A4 g7 U9 G* `, F - handle_GPDMA1_Channel2.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
$ f* J2 |, u9 v5 q! a" q - handle_GPDMA1_Channel2.Init.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
) r- o' K8 m& S5 H% P, V - handle_GPDMA1_Channel2.Init.SrcBurstLength = 1;
7 r& T7 K/ C+ }* o5 u - handle_GPDMA1_Channel2.Init.DestBurstLength = 1;
2 B8 w' ~' Q/ x1 ~" p7 L9 v - handle_GPDMA1_Channel2.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0 | DMA_DEST_ALLOCATED_PORT0;
2 G, a6 v7 r0 _ - handle_GPDMA1_Channel2.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
# \3 p5 u3 A* H$ n& |8 t) y - handle_GPDMA1_Channel2.Init.Mode = DMA_NORMAL;) p- e! [) _( S x
- HAL_DMA_Init(&handle_GPDMA1_Channel2);
0 k6 \ g5 Q l& i% ~6 X
1 l& s2 P- h3 g& a$ D- __HAL_LINKDMA(&hspi1 , hdmarx , handle_GPDMA1_Channel2);% s7 q9 [0 e& ?4 v8 T2 {
/ d s1 W& `; _- HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel2 , DMA_CHANNEL_NPRIV);9 H' Z$ w- g& {( v( g, W
- }
复制代码 , D7 f4 Z( R* k
p4 Y* m5 F/ u( W# u4 {对于SPI FLASH从机而言,无论主机是否使用DMA接收,都是通过主机的指令行事,因此代码区别只在主机发完【从机开始接收】这个指令给从机之后的接收方式不同而已:8 v+ e4 k3 V7 K/ F6 q9 x
- void W25QXX_Read(uint8_t* pBuffer , uint32_t addr , uint16_t num)
& z L- v5 j6 N - {
% M X8 W! a# p! \1 Y9 G& K: o - uint16_t i;
9 W" z* V, x5 x0 M- F - XX25QXX_CS_LOW;
! I: z0 u7 P0 F7 j: n - ( H, V3 o6 J0 ~; p9 {
- SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);
2 z7 ?0 ^' w; a% {* ~9 P - if(XX25QXX_TYPE == W25Q256)
) c4 A& e" o- W5 i# r2 L: V - {! R" r. @5 l; e; }- c3 y1 p
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24));
! U/ v' N# |8 f5 l2 C5 e - }5 K: |& c7 n( g4 _. a! ]
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));
: S. |3 F {+ {5 g4 q7 |6 |6 {6 K - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8));; v$ f* D. T C0 m5 J& s7 V
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr);
8 v4 w' i" `3 v# f - 9 E$ E+ h- A( I+ ]/ n2 s: k
- for(i = 0 ; i < num ; i ++)
+ M: t8 _ K, j: w H, L3 X - {
, M: r8 {# o0 n - pBuffer[i]=SPI1_PA5_PA6_PA7_Read_Write_Byte(0xff);& l/ Q, Y/ q0 P
- }# c: N3 w% y# m- n
- " v' }/ [' b0 O0 ^: D- F
- XX25QXX_CS_HIGH;
2 i2 O' H4 l# J% s - } / F/ ]# T+ L: R Y! q
) q) H2 r( e! T! I, r- void XX25QXX_Read_DMA(uint8_t* pBuffer , uint32_t addr , uint16_t num)% f) J$ b; g% p k2 ?1 l
- { 8 ^& y9 F; q8 ?. y2 h
- uint16_t i; . u6 H" i; d' `# }+ }
- XX25QXX_CS_LOW;
' w0 R. k# H8 `& ? ` - SPI1_PA5_PA6_PA7_Read_Write_Byte(W25X_ReadData);
* n8 I: w% _' B6 B, S) H5 ?* O$ ?7 ~3 M - if(XX25QXX_TYPE == W25Q256)
0 ?0 N% a3 ^( x3 M) \1 p - {9 V& p: n% f: B) L" u% T2 s
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>24)); 9 p) p1 x9 u- r* ~0 D
- }9 P; D3 S$ q3 w; Q# o3 V
- SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>16));
1 o) x! R6 Q3 ?- c$ @ - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)((addr)>>8));
+ u/ q7 M2 X" F1 f f) q' K - SPI1_PA5_PA6_PA7_Read_Write_Byte((uint8_t)addr);
i0 r) f) G1 J -
+ ^, v0 R5 T6 y, d - SPI1_RX_DMA_Config_PID_MIE_Byte_Normal();& m% w; Z, j6 \! n
- HAL_SPI_Receive_DMA(&hspi1 , pBuffer , num);
& q3 l c0 e: E0 h; o' ?: l, V - while(HAL_SPI_GetState(&hspi1) == HAL_SPI_STATE_BUSY_RX);
) W0 i1 m1 C& R$ V* ?' t -
! v& l9 u; t* a/ A - XX25QXX_CS_HIGH;
8 k1 g- C9 K/ i5 E: n - } 3 Q7 m# _% ?- m9 K; T3 n( I
复制代码 . C) J; G) {2 l! |3 d! Y1 y
我这里用SPIDMA接收所实现的功能是从SPI FLASH读出图片数据并显示到SPITFTLCD液晶屏上,而MCU的RAM大小以及DMA通道大小是有限制的,所以必须要采用分块读取的方式实现,将320*480分辨率,16位色的屏幕,平均分成6块,每块就是320*480*2/6=51200大小:+ F4 c/ Z! d' D2 ?* A! l
- uint8_t datatemp[51200];) k; E% c1 i' G/ r
- ) e0 r; U2 n$ F1 m8 k5 y2 |
- for(j = 0 ; j < 6 ; j ++)
: _$ V: b6 R) |+ |( S - {. B4 a& w! \! h% j
- W25QXX_Write((uint8_t*)(gImage_u3_logo + j * 51200 ) , WRITE_READ_ADDR + j * 51200 , 51200);2 }, ^7 k* _" }" T3 w
- XX25QXX_Read_DMA(datatemp , WRITE_READ_ADDR + j * 51200 , 51200); _. w: Q2 V; A
-
j- \- B4 z; t' k! l4 `2 S" Y/ X - SPITFTLCD_ST7789_ShowPicture_DMA(0 , j * 80 , SPITFTLCD_ST7789_W , 80 , datatemp);
3 z4 V' U( k# M8 n( O9 K - }
复制代码 5 p4 A' u' y/ v3 r- Q/ n- D
演示效果:
' f6 e$ K9 Q( {
+ ~- [9 a5 l' y9 Y" a4 Q
' v! r9 l- C1 l
. p4 d- X$ v1 N% H
|