问题描述: 之前一直使用的单片机是LPC2109,对其SPI很熟悉。基本就是原本拿来稍作修改就用。# X, J7 I" C8 [& O0 ^/ |
由于某种原因需要使用STM32,然后设备的驱动是之前写好的,只修改了一些硬件控制端口,由于硬件驱动使用到了SPI接口,而我是把SPI接口提供了出来,本来以为简单修改SPI配置到对应单片机就行了。简单看了STM3的SPI配置,轻车熟路改代码,瞬间体现了良好的接口有哈。
8 O8 n- ~# V* W! M编译,生成目标文件,下载运行。- b4 M& k6 F7 b+ s% P
并没有出现预想的结果。由于之前的设备驱动是能用的,所以排除设备驱动问题。
! y; ^5 e' k- A5 R& e开始以为是由于对STM32端口配置的不熟悉导致的、看手册,看别人代码,没发现问题。
- u* p* \* |$ n6 K/ F' _debug........
) k1 m1 |- ?/ V% |2 D0 ~问题定在SPI代码上。查看配置,一样啊。郁闷!!!7 R+ Z8 A( Q5 }6 G3 \2 Z2 C
把自己配置考到别人能用的代码中,可以使用。更加郁闷!!!!
5 ?% U) _2 A# W8 F2 P! Fdebug看寄存器。对比能运行代码寄存器状态。发现运行到一段代码的时候寄存器不同
1 p/ m6 T, F+ X& k HSPI_CR 0x00439 a) u" f$ e: p4 u3 y2 }/ J9 j
SPI_CR 0x0002+ a& P! D& I! H+ d7 G1 v
看datasheet.OVR置位。问题应该就在这了。可是为什么呢?????? ! s/ N; I# e- ?6 c! b5 p
溢出错误(OVR)
, N6 }! d5 Z1 @" h 溢出错误表示连续传输多个数据时,后一个数据覆盖了前一个数据而产生的错误。9 T2 q m% J) D( R2 d4 t5 r
状态标志SPIF表示的是数据传输正在进行中,它对数据的传输有较大的影响。主器件的SPIF有效由数据寄存器的空标志SPTE=0产生,而从器件的SPIF有效则只能由收到的第一个SCK的跳变产生,且又由于从器件的SPIF和主器件发出的SCK是异步的,因此从器件的传输标志SPIF从相对于主器件的传输标志SPIF主有一定的滞后。如图4所示,在主器件连续发送两个数据的时候将有可能导致从器件的传输标志和主器件下一个数据的传输标志相重叠(图4中虚线和阴影部分),第一个收到的数据必然被覆盖,第二个数据的收/发也必然出错,产生溢出错误
" s8 k6 {8 @' l+ y; G
' ~& \1 W. {( I2 E5 I! ~" e图4溢出错误: F Q; Z" V1 i6 \
通过对从器件的波形分析发现,counter=8后的第一个时钟周期,数据最后一位的传输已经完成。在数据已经收/发完毕的情况下,counter=8状态的长短对数据的正确性没有影响,因此可以缩短counter=8的状态,以避免前一个SPIF和后一个SPIF相重叠。这样,从硬件上避免了这一阶段的溢出错误。
: k& ^( B. ?) b1 v7 } 但是,如果从器件工作速度不够快或者软件正在处理其他事情,在SPI接口接收到的数据尚未被读取的情况下,又接收到一个新的数据,溢出错误还是会发生的。此时,SPI接口保护前一个数据不被覆盖,舍弃新收到的数据,置溢出标志OVR=1;另外发出中断信号(如果该中断允许),通知从器件及时读取数据。
* d6 e# ?4 V" R23.4.7 错误标志位$ B/ I6 }! N6 w! r5 A
I2S 单元有2个错误标志位。
6 U* F8 R( Z- C6 n" ]% A下溢标志位(UDR)
2 Z% T+ D; \' P$ m在从发送模式下,如果数据传输的第一个时钟边沿到达时,新的数据仍然没有写入SPI_DR寄存
# h7 c1 M2 x0 D8 U1 {/ _ D! V器,该标志位会被置’1’ 。在寄存器SPI_I2SCFGR的I2SMOD 位置’1’ 后,该标志位才有效。如果# d% [8 J& n: J7 b- a" F* N
寄存器SPI_CR2的ERRIE位为’1’ ,就会产生中断。
* j X& u, I2 ]9 c通过对寄存器SPI_SR进行读操作来清除该标志位。" h: O* n N# F) C C( T
上溢标志位(OVR)
% E* r& P+ [( j8 p$ Q1 Y: Y4 F" @如果还没有读出前一个接收到的数据时,又接收到新的数据,即产生上溢,该标志位置’1’ ,如/ o2 ~# }1 o& a4 e
果寄存器SPI_CR2的ERRIE位为’1’ ,则产生中断指示发生了错误。
+ F) Z6 Y% q2 d! I这时,接收缓存的内容,不会刷新为从发送设备送来的新数据。对寄存器SPI_DR的读操作返回
* T1 f2 E* d& ^$ g+ z, P最后一个正确接收到的数据。其他所有在上溢发生后由发送设备发出的16位数据都会丢失。
6 A; y6 U& ?% ?/ d3 a6 {通过先读寄存器SPI_SR再读寄存器SPI_DR,来清除该标志位。 - void SPI_write_byte(u8 data)7 J3 e& O+ ^6 D& v8 I+ O$ x0 [
- {
. b" Y6 N/ L$ h - S0SPDR = data;
! { I( I' G X7 f1 Y - while ((S0SPSR & 0x80) == 0);, K$ J1 X" A; I0 C8 ~- p
- }
5 G" c9 n1 ?4 e, O1 G+ R - , `+ H: K4 B- J5 }; H- b
- u8 SPI_read_byte(void)
3 Q8 y$ j& y% X O% K- \) J - {
# J1 {, a7 r4 p2 T- J - S0SPDR = 0xff;! f$ c5 R$ I0 B: a- [' Q. L
- while((S0SPSR & 0x80) == 0);
$ x8 q3 w5 X- ?2 g: Y5 F3 [+ L& k3 u - return (S0SPDR);
2 l$ s {5 ~, x - }
复制代码
# u! N- A& ^/ ~' V整个工程修改的代码如下(注释代码为不能正常工作的): - /*---------------------------------------------------------------------------*/0 Z+ b6 B4 Z: q3 G. K& J0 A
- // void SPI_write_byte(u8 data)& y5 h5 m9 Q0 g4 P% b3 \
- // {6 \. P4 O0 d, N3 k
- // while (!(SPI1->SR & (1 << 1)));* B- `# y. A2 P$ h
- // SPI1->DR = data;8 r( E8 G+ e* m; s" S* E5 @
- // }
; M5 S. q; B6 k/ f
* F* Z# F( ]% l- // u8 SPI_read_byte(void)3 U. D" b6 x+ S( D
- // {& _( [1 a/ m1 ] y4 [; E o# v
- // while (!(SPI1->SR & 1));
- l' k8 z+ ^/ ]# m: {, F - // return SPI1->DR;
9 }0 D4 b2 N) p1 \# a9 f, ? - // }
0 t! j# l/ y. P8 U7 @1 P - $ }4 r, C8 @+ U$ M. v. H
- u8 spi_rw(u8 data)* X1 k3 H; I+ H6 w% b' D! Y
- {- P& W z9 L$ m! Q/ u6 C! b
- while (!(SPI1->SR & (1 << 1)));
. n. ^0 x9 P* e% e- {% K/ a - SPI1->DR = data;
& ?1 \5 ?% H/ n: _ - while (!(SPI1->SR & 1));$ ?* J8 u& L6 o5 f" S I K( J k0 u
- return SPI1->DR;. I3 T r/ F$ I7 M+ B( i2 |: p
- }
" Y' g& z+ @: H, a" H4 a& I9 ]; h - /*---------------------------------------------------------------------------*/, J. M i6 L4 Z) p
- // SPI_write_byte(op | (address & ADDR_MASK));4 \2 R3 W8 s' K j
- // SPI_write_byte(data);. Q8 Z) G6 R: p# L8 X5 v/ q$ ]
- spi_rw(op | (address & ADDR_MASK));
: N) A; v1 m' Z3 l - spi_rw(data);
/ k# k5 b& s0 D! r - /*---------------------------------------------------------------------------*/
7 ]6 ]( e) v, ?# s# s D- D9 h - // SPI_write_byte(RBM);
0 C5 U7 C5 x4 j% w3 K; d - spi_rw(RBM);
! _& Y8 v& }: k" d) N6 n% u4 J - // *data = SPI_read_byte();
. Q6 {2 |! s$ X1 Y$ A% H/ X - *data = spi_rw(0xff);
- P( |' q2 o# x8 d - /*---------------------------------------------------------------------------*/# a: h! _: ~ ^9 h3 C
- // SPI_write_byte(WBM);
3 L$ P- ]+ n7 i# W G4 R4 k$ N - spi_rw(WBM);' f0 G- ?- j3 j7 u
- // SPI_write_byte(*data);
7 R4 r9 r+ D$ H: L7 f3 S# x - spi_rw(*data);
. a) a& F+ r0 I - /*---------------------------------------------------------------------------*/
复制代码 ! G/ A5 y& \+ d5 O$ J4 `
看完基本就明白问题所在了... ! z7 ?# o1 C0 ?1 @7 o! i
分析问题: 我是按照LPC的SPI配置的,而现在的是STM32,问题关键就在于STM32的接受缓冲空和发送缓冲非空的标志是不同的。而LPC单片机是相同的。仔细分析我写的代码,实际上每次执行都缺少了对状态的判断,从而导致了数据的溢出。 0 ^$ y+ q+ t1 L) X3 [
解决问题: 修改代码如下,问题解决。 - u8 SPI_write_byte(u8 data)
s3 [$ ^7 t) ~( C8 ]; F1 h - {$ m3 T1 H' k* g* f' z4 B7 }" c
- while (!(SPI1->SR & (1 << 1)));
& e7 n; q0 J- _& M) H. I& @" H. p+ m, k - SPI1->DR = data;4 P$ c$ v. j8 w# c9 s4 u8 |
- while (!(SPI1->SR & 1));( w+ ?7 a! h7 h" A! j7 y8 Q+ Q# A
- return SPI1->DR;
$ S* k- ]/ {8 f' u( r# j - }% b) g& |6 p$ b1 [4 m
( L' Q$ H& U5 U/ A; ^- O- u8 SPI_read_byte(void)/ a- w/ B, r) E' R j
- {! m: h6 b1 Q3 F3 |
- while (!(SPI1->SR & (1 << 1)));
/ S5 Z: N6 B5 k - SPI1->DR = 0xff;
" s; t' A! R5 O& M - while (!(SPI1->SR & 1));. q4 x8 ~, Z# y3 g' s1 ?0 N
- return SPI1->DR;4 v4 R m& [ p+ v0 Y0 x* _5 b
- }
复制代码总结: 问题出在思维的定势,先入为主的思想导致了错误的思维,也体现了对问题的分析能力,以及编码的随意性。哎血的教训啊。。。
7 d( I+ X' G! x, g# r |
感谢分享!解决了我对一段驱动代码的困惑。