本帖最后由 吐息间丶时光中 于 2017-2-21 00:01 编辑 . w- ?" t3 ?# U# f0 D
; _7 C' O( i2 I+ @* @1 S: V+ {小白是STM32初学者,遇到的一些问题在大神们面前太小儿科,还请大神不吝赐教 。
. @$ [/ D7 m3 H8 g/ J' o- H; g# r! L f: P2 r1 {& u Q8 q
【问题一:TE置位自动发送一个空闲帧】( l& q5 U9 u. y3 ?0 o9 m
前几天看刘凯老师的STM32培训视频第18集学习USART这一模块,在MDK-ARM5.17上进行软件仿真过程遇到一个坎硬是过不去:按照参考手册配置完寄存器后,软件仿真调试时(无论是单步一条一条的运行程序,还是全速运行程序),TXE一直处于低电平(也就导致串口窗口收不到数据)。# b/ H( H( ]4 w' F3 L
代码如下(与刘凯老师视频上的一致):
+ W' T* T7 @' x- #include"stm32f10x.h"
, f& x' I4 @% [- S% W3 b
. e0 x, N3 `* z( _2 C- #define RCC_APB2ENR_Addr (RCC_BASE + 0x18)( k% E' t* N% {0 ~
- * k9 ~$ N/ k6 ?+ b+ v. \
- #define BitBind(Addr,BitNum) *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\1 p: A' _) Z+ j9 Y/ A
- ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))
" L; d% d! N- p* y: X0 V - " w* I* [% e( d
- #define RCC_APB2ENR(n) BitBind(RCC_APB2ENR_Addr,n)
# a8 G. z0 z& D* @
: h! ?" ~9 f* R5 q7 m& D- /**********************************************************************
6 W: R7 s8 q9 \) i$ A - * Function Name : MAIN/ g5 T# n9 g3 R0 j. `# l+ C/ w
- * Description : main program
# K) i) Z% z. q - * Input : None: L- d1 z0 p* a
- * Output : None
- Y G; t/ E! c% k- j - * Return : None6 j* p( @# t) Q, B% X: L; l
- **********************************************************************/
. f' u4 w7 U# g% M% e& R - int main(void)( P9 V! M& [, X8 w
- {/***UART寄存器方式编程***/8 C- D) }9 S- v; y/ E- K) x" f+ g
- uint32_t BAUD=9600; //波特率9600bps$ s& x' T' S6 j0 [9 g
- uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00; //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0) q! v5 W, X% j+ t" i8 X
- float USARTDIV=0.0; //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
0 p1 x3 r8 ^2 l! |8 C$ `: D -
# S5 T& P" o% M - //寄存器配置:USART使能、字长、停止位、波特率、发送使能
" g9 U, ?8 a( Z - RCC_APB2ENR(14)=1; //USART1时钟使能
+ c+ f" K( w- c$ O6 l1 p' e1 e - // RCC_APB2ENR(2)=1; //GPIOA时钟使能(软件仿真不开启无影响); g5 @4 p5 @2 B7 W( c: z
- // GPIOA->CRH |= (3<<4); //软件仿真选择通用推挽输出也有效果(但不建议)7 I' O; W1 ?8 r' b
- GPIOA->CRH |= (11<<4); //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式6 x' L4 J/ E1 H8 x& S" b
- GPIOA->CRH &= ~(1<<6); //PA9复用功能推挽输出(最大速度50MHz)
" N' [# N5 c- s& x: G: v: E0 \, U -
: A& f( y. ~" U' X$ z4 I; c - USART1->CR1 |= (1<<13); //USART使能2 a/ b" E7 G& M* a* @$ j
- USART1->CR1 &= ~(1<<12); //1个起始位、8个数据位
8 z0 G- m/ d ` J - USART1->CR1 &= ~(1<<10); //禁止校验0 A4 Z8 Z B9 x' J- g
- USART1->CR2 &= ~(7<<11); //1个停止位、禁止CK引脚(即异步)
3 J* ]& J. I6 k; \3 l/ B - //波特率算法2 G1 [5 H6 a5 D) |- t
- USARTDIV = (72000000)/(16.0*BAUD); //注:整数除法会截掉右边的小数部分" [& H) s$ D2 X% d& y
- DIV_Mantissa = USARTDIV; //取整! d0 l9 \3 y* B6 a% a$ x
- DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5; //小数部分四舍五入并取整
4 f! s* J7 T6 \% I/ @ - USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction; //也即:USART1->BRR = 0x1D4C; u& J+ ]4 @* J( H6 B4 q) Q. N
- 0 |' ?, l0 }+ h
- USART1->CR1 |= (1<<3); //发送使能0 _0 p! w5 [: n0 v- T
- USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
t2 `8 L% T- l& ?2 g2 {9 y/ j - " s2 L& P" d2 @ x* |9 k; h- k6 @- U
- return 0;% @) ~1 `6 s5 |: U7 Y1 w
- }- ~( x! |2 Q0 f9 j( B, K
复制代码 调试界面:
; ]# D' i8 Z1 x2 f$ k
6 f1 ]0 F! u: \( i) ~" z! ]$ Y- J. r, B8 ^3 W$ P) |$ ?
起初以为是寄存器配置出错,所以把参考手册相关章节又看了一遍,后来发现并没有配置失误,于是又去查看了固件库里的USART配置函数,发现与自己的配置确实无异,就这样来来去去地鼓捣了两天……后来,在某一次调试时,小白将那全速运行按钮点了两次,奇迹出现——数据显示出来了!
! r& F" [$ a, O 那么问题是出现在这吗——因为刘凯老师视频里用的Keil版本是Ver4,而我的是Ver5,所以不同?而且网上也有很多吐槽Ver5有很多bug。怀着这种心态,刚开始那会小白也相信是这样。但小白的性格有点追求小小的完美,于是对自己刚发现的持半信半疑的态度,打算再看一看参考手册……或许是功夫不负有心人吧(实际是自己总是看视频,动脑太少 ),最终总算有了一个较满意的发现:将上面代码中的以下两句5 |' ]( {+ Q) y2 Y# d- C' i
- USART1->CR1 |= (1<<3); //发送使能9 R" _' I& ~- |- X0 Q7 ]3 Q
- USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
复制代码 进行如下更改:6 Q- D, h/ w+ _+ f
- USART1->CR1 |= (1<<3); //发送使能
2 I' q- s* r* l0 g9 T: Z3 O - while((USART1->SR & 0x40) != 0x40); //置位TE将使得USART在第一个数据帧前发送一空闲帧
8 O6 _- S" X- P0 T - USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)" \. y( ]6 J% p" p$ q* n
- while((USART1->SR & 0x40) != 0x40);
2 x; d* K" e% ~: D! m' V - USART1->CR1 &= ~(1<<3); //发送失能
复制代码 经过以上更改后,再次在MDK-ARM5.17里进行软件仿真时,仅点击一次全速运行即可完成任务。如果是点击单步运行,仅在语句“while((USART1->SR & 0x40) != 0x40);”处单步一次无法单步到下一条语句(应该是MDK仿真发送完一个数据设定有一定的时间段),此处可以将光标定位在下一条语句后点击调试按钮“Run to Cursor Line”(Ctrl+F10)即可。
; K0 H; c" \! `9 ~/ h/ t
, Z& Q' I6 m4 e6 N! R更正后的完整代码如下:
- d. P4 |! D6 R" Z0 X- #include"stm32f10x.h"* g; b' I" h$ @# w {
- ) y. F" a8 \! A0 ?
- #define RCC_APB2ENR_Addr (RCC_BASE + 0x18)' e1 s/ z. `' Z1 t* g1 G% F
- 8 |0 }8 f) ]& p' e7 v, G5 c d9 `8 S
- #define BitBind(Addr,BitNum) *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\' f3 D9 }4 M6 M; h
- ((Addr&0x000FFFFF)<<5)+ (BitNum<<2))), S4 M! J, Z: g
' {5 J/ }/ |8 Q, N6 F2 N% r- #define RCC_APB2ENR(n) BitBind(RCC_APB2ENR_Addr,n)
% S+ h$ j3 D! Z! F - 0 h; o, d4 Y* K) K. a# f/ |
- /**********************************************************************
0 S/ k1 ?# L0 b' v C# @ - * Function Name : MAIN
+ W. j- v; H+ {) @ - * Description : main program! b- h* F$ T& B5 {
- * Input : None
/ t1 d+ |3 y- p% |/ g8 R - * Output : None
( ]: P0 P- F9 |! D" v$ I - * Return : None4 q6 I/ ~0 v9 y! P& s# j
- **********************************************************************/
/ Q& F/ T: c: x% h5 C: U- B4 S - int main(void)4 ~) r( }& F3 h4 a/ T0 t
- {/***UART寄存器方式编程***/; P* q4 K4 Z9 g2 w/ V, u. p$ u
- uint32_t BAUD=9600; //波特率9600bps
+ n9 {- G" J2 @# Z: B/ Q. l - uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00; //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)6 ~0 A& A8 [5 j+ @; l! [$ R
- float USARTDIV=0.0; //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
$ e( p. C x3 T. w -
% K( @4 Q- D" @) P5 z - //寄存器配置:USART使能、字长、停止位、波特率、发送使能. ~; e6 L% {. U* t, ?9 p) h; e# R) q! v
- RCC_APB2ENR(14)=1; //USART1时钟使能( L2 R, X; I2 F- u$ ?* N- z
- // RCC_APB2ENR(2)=1; //GPIOA时钟使能(软件仿真不开启无影响). J1 y" S% F1 o7 O6 ]. H
- // GPIOA->CRH |= (3<<4); //软件仿真选择通用推挽输出也有效果(但不建议)
6 {% L. [% x8 o0 `3 {" } - GPIOA->CRH |= (11<<4); //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式: H, V4 k9 @3 D; B( T, j8 m& u5 L
- GPIOA->CRH &= ~(1<<6); //PA9复用功能推挽输出(最大速度50MHz)
2 ^+ j8 N: L& I: X - ) J) K( t; F* {% U q
- USART1->CR1 |= (1<<13); //USART使能
* T" `' v' i* E - USART1->CR1 &= ~(1<<12); //1个起始位、8个数据位
6 M" b3 W+ K, [! S8 r. f2 k8 u - USART1->CR1 &= ~(1<<10); //禁止校验
6 U: |5 q( f7 V" Q( Q - USART1->CR2 &= ~(7<<11); //1个停止位、禁止CK引脚(即异步)" U( H; y. l0 H: J: I: r9 }/ t f: O; X
- //波特率算法1 D% z0 y" g7 v) ^6 @' E' J
- USARTDIV = (72000000)/(16.0*BAUD); //注:整数除法会截掉右边的小数部分: q& \7 R: h, @9 l, K: G" {$ _
- DIV_Mantissa = USARTDIV; //取整% A* g: X; C$ u* L/ O( }# [2 J J4 L
- DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5; //小数部分四舍五入并取整
; P& e; g5 l% v9 I - USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction; //也即:USART1->BRR = 0x1D4C;
4 E _2 D- p# I2 Y1 M
/ R/ L: w4 F' _& T% J/ q) c1 j, u6 q- USART1->CR1 |= (1<<3); //发送使能
( [1 v5 w: m" Y3 ]2 z; _ - while((USART1->SR & 0x40) != 0x40); //置位TE将使得USART在第一个数据帧前发送一空闲帧' o9 s8 z' {' J0 y0 u
- USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
, L% G4 z/ y6 q9 C- @! u4 G" C/ @ - while((USART1->SR & 0x40) != 0x40);+ n1 J: d! p& [" w
- USART1->CR1 &= ~(1<<3); //发送失能! C3 N; Q B# b
- ) j" g4 |, z8 F; @* V) M( ]& ^
- return 0;# O+ c6 ^; E3 B/ d( ]4 i9 e& Y' l
- }
5 i( l2 S* [% u
复制代码 问题的根源也就是置位TE将使得USART在第一个数据帧前发送一空闲帧(参考手册有说明。截图如下),由于版本不同导致MDK仿真发送完一个数据设定的时间段或许有所区别,所以在MDK-ARM5.17上缺少了以上检测语句就达不到刘凯老师视频上的调试效果。
e+ R o8 P) \2 {5 Y7 N2 o- u
# `, n) W3 p* x
3 Y) g3 c! B- U/ M" ]2 M1 T7 k8 T 同理,该视频刘凯老师给的第二个带有for语句的例子,也必须加上如上检测语句:" H7 O6 P: `; H
- #include"stm32f10x.h"& ] g, c- d. V
- 3 u! k# Z( q2 q2 D( j
- #define RCC_APB2ENR_Addr (RCC_BASE + 0x18)
; K0 X8 U& Z$ O, G* h& [4 l0 S
v1 m+ \; j0 u+ m$ f- #define BitBind(Addr,BitNum) *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\
# D ?" Y O3 y( U1 { - ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))6 x5 O5 n5 [/ j% n7 P
( T6 v& c3 H" e, {" a* M- #define RCC_APB2ENR(n) BitBind(RCC_APB2ENR_Addr,n)0 m+ @3 `. Z6 w* o
- 6 @( `9 t) d/ h# @
- /**********************************************************************
8 t( d" l9 b2 ^6 N - * Function Name : MAIN7 z) t( Y9 I6 S% W. n6 E0 X+ |6 b
- * Description : main program
: R. s( m8 B. Y' v5 M! J$ r - * Input : None
$ A7 f& g! R' v; [+ a - * Output : None* ~8 G. x! c& \/ u( J. A4 P( S
- * Return : None
; J& O9 g. ?8 {1 f - **********************************************************************/
2 Q( K% o: C9 b9 \! q - int main(void)2 I$ `3 d+ N' [0 x, i& G: u: O7 [
- {/***UART寄存器方式编程***/, x2 D' c2 Q5 J* o- D& l9 W! ?
- uint8_t num=0x00,data=0x00;
: v; G1 w5 \3 U - uint32_t BAUD=9600; //波特率9600bps- T9 f0 A6 x7 u* L2 V
- uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00; //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)
. I% x0 T. ~ ?% W0 B+ Z: ~. ] - float USARTDIV=0.0; //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
( k% O% N2 C0 h4 v/ S8 B -
0 ^ B! W0 R* r" H: h - //寄存器配置:USART使能、字长、停止位、波特率、发送使能) q$ ]+ J x: P
- RCC_APB2ENR(14)=1; //USART1时钟使能& f7 X. Q5 l8 ]& x9 \
- // RCC_APB2ENR(2)=1; //GPIOA时钟使能(软件仿真不开启无影响)
/ I: g6 W6 R7 J) I3 s, \: _% V - // GPIOA->CRH |= (3<<4); //软件仿真选择通用推挽输出也有效果(但不建议)
2 X5 X' I( [1 S" X0 W: q7 W - GPIOA->CRH |= (11<<4); //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式; c: r: P+ S" P: i" V
- GPIOA->CRH &= ~(1<<6); //PA9复用功能推挽输出(最大速度50MHz), Y3 @% ^( k; `4 h
- " \; O* L: [& ]1 g8 N! k/ B
- USART1->CR1 |= (1<<13); //USART使能. r2 P8 @* Z6 x. T
- USART1->CR1 &= ~(1<<12); //1个起始位、8个数据位5 y' K, e8 k6 Q1 U' k1 @
- USART1->CR1 &= ~(1<<10); //禁止校验* J, @# J6 l& s' J6 p
- USART1->CR2 &= ~(7<<11); //1个停止位、禁止CK引脚(即异步)
2 n; [ ?, Z* j) ~3 ~# y; Z' F } - //波特率算法 q0 U! l" ~" }4 `
- USARTDIV = (72000000)/(16.0*BAUD); //注:整数除法会截掉右边的小数部分4 y4 v* c7 o" Y
- DIV_Mantissa = USARTDIV; //取整4 z0 V! _ M# V/ e3 \- [+ L* y
- DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5; //小数部分四舍五入并取整
& }1 s: _) k& B# U. V# X% u - USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction; //也即:USART1->BRR = 0x1D4C; & ?7 E" ` B. ^# M: S
- 7 z2 k* V( X3 q7 |, a9 d
- data = '@';7 P& ?7 V7 y7 `" i9 T
- USART1->CR1 |= (1<<3); //发送使能 j) `! }. l. I$ g+ E$ s0 \; _( I
- while((USART1->SR & 0x40) != 0x40); //置位TE将使得USART在第一个数据帧前发送一空闲帧
" Q; u' I& b& d - for( ; num < 64 ; num++) //发送64个数据 40H~7FH(ASCII码表)+ J% _/ W, `7 c
- {
1 P+ j# g: V- T% W - USART1->DR = data;
, l' n4 ~4 ]: @+ T; Y' ] - while((USART1->SR & 0x40) != 0x40);( u! B7 }* `: Q! X6 K0 d& ^( R
- data++;
% U: e' {1 H; u - }//跳出for循环时data为0x80; ^# D& g. @8 ?: Q2 S ?
-
# O. j* A3 `" X% D - return 0;# o, I4 ]7 T2 a: |3 U9 H
- }
8 P% T8 D: T3 | T M* ^" R K
复制代码 经过小白测试,如果不加将导致第二个数据“41H”无法发送。/ A# r0 w! m* B* m: C( C" Y$ a8 X
如下图所示:
+ B5 |: ^2 ^! H) p0 v
5 Q# G2 x" n5 k; V: \5 e" o
0 g+ @; s# m( j- O6 Z2 H! T6 m. Q/ R) m8 b6 y7 a' q
; d0 [0 H4 R4 k" ^" ?7 E$ a
【问题二:使用printf函数】
; E- z# B0 t, I3 l' M2 p 用fputc重定向stdio库的printf函数后:文件流→串口USART1。使用printf函数将数据输出,如果没有提前进行代码配置(关于如何配置各论坛上有类似贴),或者将下图的“USE MicroLIB”勾选:
( q4 O* D. X8 {! V2 `
8 b! a: q0 w8 J% T0 i将导致:在Keil上进行软件仿真时,点击全速运行至少要点击三次(或者将光标定位在主函数里的某条语句,点击“Run to Cursor Line”也至少要点击三次);若是将程序烧进硬件跑,程序短时间未见能工作(未测试时间过久些是否会工作),串口助手收不到输出的数据。
# k4 L' @5 ?6 w7 Q" r6 X2 z. B3 @+ D! A* C
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------3 i( Y% z8 J& I/ ]
* U! o- G" j' Q# M1 n# ] I 问题很简单,但困扰了小白好几天,写下来希望其他初学者遇到相同问题时有所参考。. e" J* b! \- B% v" M" Y
32初学之路,还请各位大神大大多多指教。& |. T3 Y! l: X6 } u, }
' L. N X' G ^% S9 A------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
后面的发送才正常。
初学过程,还请多赐教