在上新建工程的基础上添加使用SPI功能(新建工程见STM32使用LL库建立工程)
7 D. D/ W5 a; I4 h0 A/ B# q$ m% @) J, P8 Y' t8 v0 r
一、CubeMX的配置 ]5 I/ F. X* z0 W/ l+ \ h; f
1、非SPI的LCD控制IO口的配置
) g/ `8 K' V4 h: z( m& Y+ a; O查看原理图可以看到,有5个引脚接在了MCU上,其中有两个SPI引脚,三个LCD控制引脚
9 W! p8 M5 W8 h
6 g' D! J3 n% w% X8 F
9 X$ u1 a$ X( P1 l) f# c其引脚对照表如下,所以我们先将PC6、PC7、PB15配置为通用IO推挽输出, ?4 D7 D. Q) {0 n; h+ b! t! I
( T& N9 Q5 H* t# j% m* U6 O) l! M
/ c3 p( s$ s# o/ ~* g7 @
8 M1 _2 U: @$ Q1 a" s
6 W; l9 v1 H) J4 k
+ b% u( v" L! m: q! @* x
2、SPI的的配置
0 A# P( w4 n( K- _ W+ u( H) T& cMCU只需要通过SPI向LCD控制器发送命令/数据即可,所以只使用 SPI2 的 SCK 和 MOSI 引脚,将SPI2配置为主设备只发送模式,接下来开始配置SPI2接口:
# \ n( `/ p9 C5 D, `$ ~- j# a9 y6 N; u, D* Y* o1 y0 w0 ~& h
首先将SPI2配置为只发送主设备模式
# @' b3 v) h* s; U7 l4 r5 y4 n ]) I" j' G
2 S" t+ \4 F- F, m3 q9 ]+ k* P+ H% r' A0 r& d4 q$ y% F
可以看到,SPI2_SCK引脚默认为PB10,我们需要将其修改为PB13) J, p _2 \9 C6 v1 p
7 s7 _3 O0 l- b: X( ?1 T. c2 L
: r9 |% D7 @ A: l9 W
1 R' g4 a, L( h' m, U( o
然后对SPI的参数进行配置
& w* f ?4 U. I1 t8 Z! J+ s( x( U9 i: g
- _+ l+ C* B) L4 B! `# |# c
, A- [0 G5 i% O2 A: P- Q+ x' x8 O
然后重要的是工程设置里,将HAL改成LL
% q. S" k* |( l7 y, P
: y! w" C$ C7 E; m0 Z
" g) [- W# M t) ]9 |
* e( }" [1 o" M6 @
然后便可以生成代码3 l" w$ }$ G* N7 o' ]
1 s: B# E. |" C/ \/ U) G9 w
5 ]5 N/ G- ^ f! y, ~( H3 d! H1 m/ v9 X2 w& I* {
二、用户代码修改
: g9 g% k* \, T( W1 V将SPI初始化代码修改为如下,放入新建的spi.c文件0 f0 k. m& K5 H2 Y1 P) J( R5 m
& S& G0 n Z8 _0 z5 A z
- //SPI2初始化代码,配置成主设备只发送模式* {- C/ ?+ V! M4 d; A
- void SPI2_Init(void)
- s# m, Q# v w) k5 K - {
# i' r* }) N9 l7 r1 l" B - LL_SPI_InitTypeDef SPI_InitStruct;
0 E4 x4 l% x, Q, G - LL_GPIO_InitTypeDef GPIO_InitStruct;! z0 g8 D% i p
! X3 c/ @1 t+ B' e) `- LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);//使能GPIOB时钟2 G- b4 p3 U+ G: T' b
- LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);//使能GPIOC时钟
4 i* }# d; Q% L% r - LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2); //使能SPI2时钟9 J8 i0 q* |1 Y' d0 j7 N
- , y( [, n7 |" y, K
- // **LCD控制GPIO配置 LCD_PWR->PB15 LCD_WR_RS PC6 LCD_RST->PC7 */
4 J6 I# J8 p) f - GPIO_InitStruct.Pin = LL_GPIO_PIN_15;
+ ?3 `( [/ D: h* G - GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;! t& a8 f* r1 y: i. L0 L' d4 I
- GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
3 I8 j ]' p3 }- [* \ - GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;& b( o8 T" c/ ]7 \
- GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
1 C* f; P Q) z- V5 g - LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
2 V9 N: R! {' x8 W% m7 q - LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_15);# N" \" D! y5 p
; `2 `) O- c) E' p8 U; q' E7 |- GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7;3 n5 C; a' ~6 M; I* N) T
- LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
* b0 f. F2 ^; [- A* N- V! b - LL_GPIO_ResetOutputPin(GPIOC, LL_GPIO_PIN_6|LL_GPIO_PIN_7);: Y4 D% M+ N% [3 ?$ V/ a" C
- . g7 Y1 j) a5 b% E/ i
- // **SPI2 GPIO Configuration PC3-> SPI2_MOSI PB13-> SPI2_SCK */0 d7 ?& y( I) s2 l
- GPIO_InitStruct.Pin = LL_GPIO_PIN_13;8 v" |" A) b/ N0 e4 @6 G% E
- GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
0 U1 a' T( o. D; ]2 Z3 R6 `- V7 ` - GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;8 w! o7 e4 Y5 {$ F2 k
- GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;' d ~2 Q8 Z2 u" o
- GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
- a, N. Z- B7 c+ B1 b: _6 ~ - GPIO_InitStruct.Alternate = LL_GPIO_AF_5;
5 s: P- }, @0 ]' p( C1 {' `2 W - LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
2 L" C" q, H6 m6 Y% W& Y1 r4 k( l; `3 w - 3 r$ K( M# v* y, c
- GPIO_InitStruct.Pin = LL_GPIO_PIN_3;3 d: _& x7 Z1 }
- LL_GPIO_Init(GPIOC, &GPIO_InitStruct);, S$ u' X; U t+ V# Y# t& ]
" b0 D9 q" S3 B1 j- /* SPI2 parameter configuration*/
6 ?- B K! i+ x; j- }. m! j - SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX; //全双工
) ]& u( a- T- f - SPI_InitStruct.Mode = LL_SPI_MODE_MASTER; //主设备模式
9 B6 [% y8 I* D2 ]; r# H" V - SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT; //8位数据宽度
; y$ P* b$ H/ P3 c9 h G - SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_HIGH; //串行同步时钟的空闲状态为高电平0 r4 Q; J2 `' Y5 d% y# _. L
- SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE; //串行同步时钟的第二个跳变沿数据被采样
`9 ^( [& o! _) O& K/ L - SPI_InitStruct.NSS = LL_SPI_NSS_SOFT; //NSS信号由软件(使用SSI位)管理:内部NSS信号由SSI位控制
1 r# E$ y8 j( ]' X# ~ - SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2;//定义波特率预分频的值:波特率预分频值为256
) |+ `8 p% W# v3 l - SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST; //数据传输从MSB位开始' Q0 Z- \8 ?) c! g: e& f! k* N
- SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;//关闭硬件CRC校验0 s7 o+ b" L% C M" _ D2 y5 R& U
- SPI_InitStruct.CRCPoly = 7; //CRC值计算的多项式' @6 g0 x) }9 b
- LL_SPI_Init(SPI2, &SPI_InitStruct);5 p; E' Z5 r' ^( Z% K
- $ S: E O$ g, B+ y
- LL_SPI_SetStandard(SPI2, LL_SPI_PROTOCOL_MOTOROLA); //SPI帧格式 为 SPI Motorola模式# |, p( B) b7 N; o
- LL_SPI_DisableNSSPulseMgt(SPI2); //关闭 NSS脉冲模式# O% e$ `7 G0 S6 j& L2 p+ x$ q% b
4 M, o9 X) T9 a e+ C- LL_SPI_Enable(SPI2); //使能SPI2
# n4 g, w1 M) @% }% D# D - }9 e2 b3 T% }+ e$ k
$ N( s) ]- w& i- a& O) M
复制代码 / O$ {' B5 h, ]( E
然后编写SPI2_WriteByte(u8 *TxData,u16 size)函数( @: z: c" x. a* b; j. ^
. l" O/ x& l3 Y0 \- /**
! W/ W! \3 _* I* w9 U5 N; }! b. j& ` - * @brief SPI3 写入一个字节/ X$ T8 e1 K) c$ A9 L
- * @param TxData 要写入的字节( t6 b7 y; K- m) h w" b
- * @param size 写入字节大小
2 w$ ?' G) q8 C; q4 R - * @return u8 0:写入成功,其他:写入失败
$ X# A* E( z! S4 W) w8 y8 k - */ |+ @; s7 h J7 p9 g
- u8 SPI2_WriteByte(u8 *TxData,u16 size)9 t9 {4 i* g+ c4 x
- {6 m* A& x# i- n2 v6 w0 I
- u8 retry=0;
" c t8 Y( }( @ - u8 n=0;
* }' f: I! D. k* }4 F7 A# Y - for(n=0;n<size;n++)6 _! o5 w& }' a
- {; D: z/ r1 ^3 ~9 \' y
- while (LL_SPI_IsActiveFlag_TXE(SPI2) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
( ^6 M$ M9 i; T! J) V8 \ - {5 m* L( B1 ~( B
- retry++;, j6 B6 P) s9 J; M3 U0 ~& R
- if(retry>200)return retry;
. l: N2 d) Z( p, C$ f( g - }
9 X* m# E' t0 Q# P9 h+ N, V - LL_SPI_TransmitData8(SPI2, TxData[n]); //通过外设SPIx发送一个数据
& z z7 J! v1 { - }6 |, d: t$ M0 ^6 l& S
- . Z" k7 Q; N$ W$ L& d% L
- return 0;
: L I' @9 \ c1 q! ?; \% m - }
复制代码 8 s$ p, c* C( r2 U( M
三、LCD代码移植
, B$ B4 U. f8 `( t- h7 tBearPi和正点原子的Pandora开发板使用的LCD是同一款,且模式配置也一样,所以既然有现成的代码,我们不妨copy一下,将lcd.c和lcd.h复制过来8 {8 O7 [7 T$ v6 J
然后修改lcd.h里的LCD控制GPIO代码+ h4 b& L; q; ~$ \: l2 g! {
4 K2 w/ p% ] Z8 E O- /*6 z, X2 r# h) ^; @
- LCD_PWR: PB150 S& ?. V: r, n% w" w( I) B
- LCD_RST: PC7, N8 [& v/ e2 R M9 p4 s% H! M( k
- LCD_DC: PC6 5 @. [: }/ U) K/ X; Y) \2 ~; V
- *// P* G" W0 H9 U& y3 n: W! f
- #define LCD_PWR(n) (n?LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_15):LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_15))& {+ D+ d8 {$ b0 K8 d
- #define LCD_RST(n) (n?LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_7 ):LL_GPIO_ResetOutputPin(GPIOC, LL_GPIO_PIN_7))- ]" i/ u& P" V0 m: {
- #define LCD_DC(n) (n?LL_GPIO_SetOutputPin(GPIOC, LL_GPIO_PIN_6 ):LL_GPIO_ResetOutputPin(GPIOC, LL_GPIO_PIN_6))
( O2 q( m' }. b' }2 {3 z
复制代码
5 q" j" Q8 Y% ?3 ~" A0 O8 p将lcd.c里的LCD_Gpio_Init()代码删去,然后在LCD_Init()开头添加如下代码:
* j* I' w) \6 q' i
: |) f4 E! |; h" ` o0 K; p- SPI2_Init();
) K+ ~) M+ @3 F" ?& { -
# x7 T/ T- R7 W/ d0 o" |( d - LCD_PWR(0); //关闭显示
3 K0 F4 D* k+ B: O# w
. F/ S4 I1 z& N- LCD_RST(0);9 u* {: `1 O5 [. r+ V: J
- delay_ms(120);
3 l7 I {+ g% t7 Q - LCD_RST(1); //复位
复制代码 ! S7 n# S; C# O
在主函数里写入
# \. k$ G9 C* Y' o2 |
. j3 F5 g6 y8 y+ [& ^8 D6 R- LCD_Init(); //初始化LCD4 Z) u2 w0 [2 r' Q5 c
- 9 H$ N+ s5 ]" O6 s/ i
- POINT_COLOR = RED;1 w Q4 v) n2 t" \2 `6 P
- LCD_ShowString(0, 100, 240, 32, 32, "BearPi STM32L4");
复制代码
! \2 N+ q0 T% P: f, |编译链接下载到开发板里,测试结果如下
! i( K& H4 p( H9 B% l
5 f7 L0 t- ?( I4 P5 t$ S! k
0 v/ n$ K4 j! f' q
+ c9 ^; J" O; z0 a: F; a表明测试成功
9 b# j! u) T8 z9 s6 k' ?) `4 N I————————————————
; ]" P4 y7 e: v# Z! r" @; A1 X转载:Willliam_william% U( F3 [. y2 Y9 d% ~9 p5 }: b
1 Q, A' }; X* g1 n3 n! `
% A! o- k/ P; }* k/ P1 c
|