本帖最后由 吐息间丶时光中 于 2017-2-21 00:01 编辑
+ ^5 u6 Z/ ]" F2 F8 {: f4 i0 Y0 ` q4 V+ k
小白是STM32初学者,遇到的一些问题在大神们面前太小儿科,还请大神不吝赐教 。
9 w/ z+ T7 E4 G6 R& K3 E: S) U9 r7 ?9 H8 j1 ?# A9 D# }
【问题一:TE置位自动发送一个空闲帧】) T; f, D0 O9 l1 b Y6 Z
前几天看刘凯老师的STM32培训视频第18集学习USART这一模块,在MDK-ARM5.17上进行软件仿真过程遇到一个坎硬是过不去:按照参考手册配置完寄存器后,软件仿真调试时(无论是单步一条一条的运行程序,还是全速运行程序),TXE一直处于低电平(也就导致串口窗口收不到数据)。
% B# p4 H: {8 ` 代码如下(与刘凯老师视频上的一致):
# c1 _+ J8 U- n) l7 E* y- #include"stm32f10x.h"
- k" ]! } G& ?0 i/ k! h6 x0 d7 J: y - ' q! d; _; ]* h0 e: C
- #define RCC_APB2ENR_Addr (RCC_BASE + 0x18)
0 N! g3 k( ]$ F - 6 M- N! _$ x- {9 l4 w
- #define BitBind(Addr,BitNum) *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\1 [3 h& m* w$ T
- ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))
! U1 Q( m& z) Y1 m% n0 m
: @' y8 ]8 N) e& `- #define RCC_APB2ENR(n) BitBind(RCC_APB2ENR_Addr,n)0 }, _( B* m( C6 Y1 b& d) D
& ^* L) x2 `5 Q. a: A/ v7 ]+ O, I8 O- /**********************************************************************! z* c4 u& B& K. W! F. V5 o
- * Function Name : MAIN$ |, [ X2 C; M4 `
- * Description : main program
4 [" V) b9 s$ k5 Z' l8 i - * Input : None4 h& _" N1 W8 b* i1 [9 i5 K
- * Output : None
$ Z+ d* E# M8 w: A - * Return : None. l( V& a) i" ~( i3 Q7 @4 A
- **********************************************************************/
, n2 i$ F4 A* s; M - int main(void)
8 U+ s8 G; w* o, g - {/***UART寄存器方式编程***// O1 a3 l9 G! K- Z. c9 c: B
- uint32_t BAUD=9600; //波特率9600bps/ l6 H j8 \! B7 Z+ W8 t
- uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00; //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)
A; c, h2 m8 G6 p# ]$ @1 v - float USARTDIV=0.0; //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时). K8 _, Z. \6 i8 a s( @
- 7 q7 E" T7 e- `. t2 r
- //寄存器配置:USART使能、字长、停止位、波特率、发送使能
* v) G& y) M7 u( r/ E - RCC_APB2ENR(14)=1; //USART1时钟使能6 _0 q+ Y5 s0 T; I% L! F
- // RCC_APB2ENR(2)=1; //GPIOA时钟使能(软件仿真不开启无影响)
8 B3 ~! A2 {. j V+ f - // GPIOA->CRH |= (3<<4); //软件仿真选择通用推挽输出也有效果(但不建议)
, E: `7 A, j% Z5 V - GPIOA->CRH |= (11<<4); //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式; X( ], W# v, I
- GPIOA->CRH &= ~(1<<6); //PA9复用功能推挽输出(最大速度50MHz)
/ e9 Q- N, [( c# j - * A* a Y _) {# @
- USART1->CR1 |= (1<<13); //USART使能 r: Y- e( P4 R W" g
- USART1->CR1 &= ~(1<<12); //1个起始位、8个数据位3 w# e8 z; p' r1 ~
- USART1->CR1 &= ~(1<<10); //禁止校验$ s- w% Y. d( H
- USART1->CR2 &= ~(7<<11); //1个停止位、禁止CK引脚(即异步)+ Z$ x% G" U, F1 M* y
- //波特率算法
% Y- b, a' \1 p4 q# Y5 f% H - USARTDIV = (72000000)/(16.0*BAUD); //注:整数除法会截掉右边的小数部分
! f2 J7 x, l1 z7 f( s/ r+ ?, u' t- b6 G - DIV_Mantissa = USARTDIV; //取整/ Q2 { H5 L# B
- DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5; //小数部分四舍五入并取整
" R# }3 @( t4 u% J! I1 S G - USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction; //也即:USART1->BRR = 0x1D4C; 7 B, t& ~7 K, Y. _8 a; \
5 K# b. U. Z; j/ ~0 J2 H7 q- USART1->CR1 |= (1<<3); //发送使能
1 l3 z6 i* k) _7 J - USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)3 P* v. B1 o3 m, [
-
" X. q: y! J2 V& V$ S- V; C* E& F9 M - return 0;7 p* ] b. o0 D/ c: P/ g
- }' m, E6 X$ B5 o
复制代码 调试界面:; V+ m5 ?1 S. N. M" y5 @
3 j, \4 t I' b, Y1 J
4 K V1 | ~5 `" Q1 v e. ^
起初以为是寄存器配置出错,所以把参考手册相关章节又看了一遍,后来发现并没有配置失误,于是又去查看了固件库里的USART配置函数,发现与自己的配置确实无异,就这样来来去去地鼓捣了两天……后来,在某一次调试时,小白将那全速运行按钮点了两次,奇迹出现——数据显示出来了!
) K* ], W$ L1 h6 X' [; C 那么问题是出现在这吗——因为刘凯老师视频里用的Keil版本是Ver4,而我的是Ver5,所以不同?而且网上也有很多吐槽Ver5有很多bug。怀着这种心态,刚开始那会小白也相信是这样。但小白的性格有点追求小小的完美,于是对自己刚发现的持半信半疑的态度,打算再看一看参考手册……或许是功夫不负有心人吧(实际是自己总是看视频,动脑太少 ),最终总算有了一个较满意的发现:将上面代码中的以下两句
+ n+ q, F2 W5 C0 ~. b! Z* f- USART1->CR1 |= (1<<3); //发送使能
! ?9 M L. ^3 K; E - USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
复制代码 进行如下更改:* q# n5 m# [" U5 D$ ?6 r
- USART1->CR1 |= (1<<3); //发送使能- x: C" ?1 r9 B' a5 x* G
- while((USART1->SR & 0x40) != 0x40); //置位TE将使得USART在第一个数据帧前发送一空闲帧
) E5 I, s* l: q: Y6 s* d - USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)
$ G7 K: G! O% E& a7 o) U - while((USART1->SR & 0x40) != 0x40);
" o' D3 B3 n4 j0 I2 V7 \8 t - USART1->CR1 &= ~(1<<3); //发送失能
复制代码 经过以上更改后,再次在MDK-ARM5.17里进行软件仿真时,仅点击一次全速运行即可完成任务。如果是点击单步运行,仅在语句“while((USART1->SR & 0x40) != 0x40);”处单步一次无法单步到下一条语句(应该是MDK仿真发送完一个数据设定有一定的时间段),此处可以将光标定位在下一条语句后点击调试按钮“Run to Cursor Line”(Ctrl+F10)即可。6 t4 v& A" B" v2 B/ o m
1 d7 j4 I3 U4 [' V更正后的完整代码如下:
1 [5 U8 R0 ~: S+ i% n- a- #include"stm32f10x.h"5 t) a9 w6 k4 P6 F/ X
, e1 _) N7 a. [+ K( o4 b: X- #define RCC_APB2ENR_Addr (RCC_BASE + 0x18)8 B( b0 Q5 v) s% p
7 P. n0 u& _( D4 D8 u- #define BitBind(Addr,BitNum) *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\
% G0 Z2 j0 \1 I% w9 L2 s, V - ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))
4 d4 ]. W0 J# @3 S2 W# ^
7 o9 ]% g- H6 L2 z6 B. X' n& l- #define RCC_APB2ENR(n) BitBind(RCC_APB2ENR_Addr,n)
& n; r0 v. y! \- |" e J - . `& W$ i. F- G4 O$ V5 P' L
- /**********************************************************************' u5 \2 X# C8 E% `
- * Function Name : MAIN# H9 G9 ]; u) v# p$ E5 i: K3 [
- * Description : main program2 K/ Z$ q$ d0 p, c
- * Input : None$ I7 E0 M3 m& A+ w# g
- * Output : None; J/ p6 Y/ q4 r& F* `! x7 K
- * Return : None. H4 k" F8 T# i* `+ `7 |
- **********************************************************************/, B) g, K# A6 R) N( k" u+ u
- int main(void)
$ F, `% ~3 v g' g$ w' p; o' b7 V - {/***UART寄存器方式编程***/7 Y5 M/ v. X3 e. x% u7 B$ T
- uint32_t BAUD=9600; //波特率9600bps' F$ w8 m' y3 e4 U
- uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00; //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)5 H( s' g `$ f u1 D
- float USARTDIV=0.0; //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)
4 {5 T5 w: S; i, J -
8 {6 ^6 X' |% P- Q3 j - //寄存器配置:USART使能、字长、停止位、波特率、发送使能, t* d( Z. d( [0 e
- RCC_APB2ENR(14)=1; //USART1时钟使能" Q# Z5 r+ t4 x
- // RCC_APB2ENR(2)=1; //GPIOA时钟使能(软件仿真不开启无影响)5 J" o) ]6 C1 F- B/ v# n
- // GPIOA->CRH |= (3<<4); //软件仿真选择通用推挽输出也有效果(但不建议)
: x/ B/ D" `! }! \; w2 { - GPIOA->CRH |= (11<<4); //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式0 b6 t9 ?8 Y$ P2 Y1 o
- GPIOA->CRH &= ~(1<<6); //PA9复用功能推挽输出(最大速度50MHz). |7 b2 m3 O4 _; D2 |3 C$ A
-
7 s/ Z |2 h, w( \ - USART1->CR1 |= (1<<13); //USART使能
, Z" C' ]/ U4 O* e3 ~ - USART1->CR1 &= ~(1<<12); //1个起始位、8个数据位+ b/ r6 C* x: v1 n7 B
- USART1->CR1 &= ~(1<<10); //禁止校验
4 Y5 t& b Z! F2 Y. O - USART1->CR2 &= ~(7<<11); //1个停止位、禁止CK引脚(即异步)
1 U' V9 C+ y0 o' L9 C C - //波特率算法
7 X2 x4 `* M+ E. m* S1 Y2 M - USARTDIV = (72000000)/(16.0*BAUD); //注:整数除法会截掉右边的小数部分! h: F% {; T6 T
- DIV_Mantissa = USARTDIV; //取整/ C, o6 c, ^" _& H& f% l
- DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5; //小数部分四舍五入并取整9 F* X0 }. P4 l( m; ], N
- USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction; //也即:USART1->BRR = 0x1D4C;
& }6 I% V+ u' j# D& t6 L7 x - 3 \' y( f0 C1 A% Q4 L6 j9 {/ X
- USART1->CR1 |= (1<<3); //发送使能" C0 v# c; h: ]! Q6 d/ T
- while((USART1->SR & 0x40) != 0x40); //置位TE将使得USART在第一个数据帧前发送一空闲帧
4 j: X1 ]' l' u+ P - USART1->DR = 0x66; //注意:DR只有9位有效位(调试时,串口窗口选择Mixed HEX ASCII Mode——混合十六进制和ASCII模式)" E* c2 n( m1 S$ ` S3 r" i
- while((USART1->SR & 0x40) != 0x40);6 s6 P; p& |6 _; s
- USART1->CR1 &= ~(1<<3); //发送失能: ^( o% a% L2 p: k! }: n
-
0 w" |2 C0 R4 m, @7 Z - return 0;
( |- U+ f7 _/ m. @ \" T - }
/ R4 A# y8 _/ v
复制代码 问题的根源也就是置位TE将使得USART在第一个数据帧前发送一空闲帧(参考手册有说明。截图如下),由于版本不同导致MDK仿真发送完一个数据设定的时间段或许有所区别,所以在MDK-ARM5.17上缺少了以上检测语句就达不到刘凯老师视频上的调试效果。
5 C- b; _! X* C0 D) }, P+ K2 k- D
+ A1 \' G i% T! W7 }
; V% h+ O4 D) C" t& r 同理,该视频刘凯老师给的第二个带有for语句的例子,也必须加上如上检测语句:
. _" p/ y- _1 O+ c. S# d- #include"stm32f10x.h"
% W+ t9 E8 c% V) Z3 c \# k) R - 9 X8 Q% _5 ^. M: y1 m6 v; y
- #define RCC_APB2ENR_Addr (RCC_BASE + 0x18)' R1 |+ Q, M; _
- 0 S/ e* D2 l/ f4 {4 C$ j
- #define BitBind(Addr,BitNum) *((__IO uint32_t *)((Addr&0xF0000000)+ 0x2000000 +\
' W; ?/ B! I L! e" L* w - ((Addr&0x000FFFFF)<<5)+ (BitNum<<2)))9 ^1 Z8 Q7 p0 y' U, q: q
- 9 j; I+ F# D, v
- #define RCC_APB2ENR(n) BitBind(RCC_APB2ENR_Addr,n)6 A7 u' A/ n# @+ j: _( q% O7 D1 U8 A
1 Q6 I/ g. N8 O' g0 f" [- /**********************************************************************% n" a& a1 v. H5 {' b
- * Function Name : MAIN
; b; w* L5 u r8 \ - * Description : main program
& @! S- w( h) z. U$ F, @: ^6 ~ - * Input : None) _$ Q4 ^) W/ k+ ^( z6 t& X) p
- * Output : None/ n- S m1 I t Z1 X1 S3 X/ \1 P
- * Return : None! n& ], {2 \) D' G- d/ _& F& u
- **********************************************************************/
6 P3 }6 s. x* E+ e/ i, ~/ y) b - int main(void)
1 E" n# Q& \% P' `' B% [6 }. L - {/***UART寄存器方式编程***/4 ?4 } b# c: g- C
- uint8_t num=0x00,data=0x00;
6 T$ R: H6 _; H" T( Q0 u* Y# r; t - uint32_t BAUD=9600; //波特率9600bps. E; g8 \3 h) H( Y' c& r6 E
- uint16_t DIV_Mantissa=0x00,DIV_Fraction=0x00; //未经显示初始化的自动变量的值为未定义值(外部变量与静态变量将被初始化为0)* f: `7 Z2 h% ?$ E
- float USARTDIV=0.0; //浮点型(如果单精度能解决,尽量用单精度,双精度算术运算特别费时)5 D% {) G% {. D2 j$ K F
-
) d7 C% j$ }8 i! v( | - //寄存器配置:USART使能、字长、停止位、波特率、发送使能
$ M7 Y) e4 N. t$ M2 K7 w) \! }- O- b/ { - RCC_APB2ENR(14)=1; //USART1时钟使能3 m1 x2 B6 m/ |4 g% Y
- // RCC_APB2ENR(2)=1; //GPIOA时钟使能(软件仿真不开启无影响)9 Y+ a1 m' g4 f+ }4 n4 B+ }
- // GPIOA->CRH |= (3<<4); //软件仿真选择通用推挽输出也有效果(但不建议)
3 t$ v" X9 @1 L4 Y8 I - GPIOA->CRH |= (11<<4); //PA9复用功能推挽输出(最大速度50MHz)【注意:此时实际是复用功能开漏输出模式】,PA10默认浮空输入模式$ I$ Q* B* A; V; a( N7 d
- GPIOA->CRH &= ~(1<<6); //PA9复用功能推挽输出(最大速度50MHz)* y9 N5 g6 d8 {* H/ J
-
`. y% V4 n$ N# J1 ?; _ - USART1->CR1 |= (1<<13); //USART使能; C2 n6 \0 |; h5 T4 d
- USART1->CR1 &= ~(1<<12); //1个起始位、8个数据位
. o- U, b9 b4 `1 G* A. \9 `9 \2 W - USART1->CR1 &= ~(1<<10); //禁止校验
& k- V! b' ^; v S2 Y - USART1->CR2 &= ~(7<<11); //1个停止位、禁止CK引脚(即异步)
, V8 _" e/ T1 i1 R$ y. e+ I7 I - //波特率算法
* `# K7 E9 Y4 W( e - USARTDIV = (72000000)/(16.0*BAUD); //注:整数除法会截掉右边的小数部分, K+ c, }0 A5 J
- DIV_Mantissa = USARTDIV; //取整! H( g( ~5 ^" A: V
- DIV_Fraction = (USARTDIV-DIV_Mantissa)*16+0.5; //小数部分四舍五入并取整/ ~! c8 x1 H; z) c. d1 R. E
- USART1->BRR = (DIV_Mantissa<<4)+DIV_Fraction; //也即:USART1->BRR = 0x1D4C; 5 V" Y5 t% q2 C ^: @ G/ l7 s
, e( Q* y( w t, R$ [6 n: G, C# V- data = '@';0 W7 Q$ J% q9 n/ [# ]* K& I3 Z- U+ |
- USART1->CR1 |= (1<<3); //发送使能3 ~3 v u! R- q3 [- l
- while((USART1->SR & 0x40) != 0x40); //置位TE将使得USART在第一个数据帧前发送一空闲帧9 Z! ]( @1 n' ?- e; r
- for( ; num < 64 ; num++) //发送64个数据 40H~7FH(ASCII码表)! e7 k# Q8 [8 F, T# R: y7 q* I
- {: @6 K/ H5 O; ^6 i: T
- USART1->DR = data;
& Z% Q4 m; [# y. i& Y - while((USART1->SR & 0x40) != 0x40);
# L+ _% z7 e- S1 R7 N - data++;
6 ], w: E5 p* M& W% W8 j - }//跳出for循环时data为0x80
$ ?1 B+ V8 ^: G3 l+ O4 @ -
+ Z9 J, j" s) L. b. @ - return 0;
5 r) f' j) \' [5 ^6 o9 E0 j - }
3 V" `, X) O. z5 e( t* X5 k, b
复制代码 经过小白测试,如果不加将导致第二个数据“41H”无法发送。% {- g9 B* S: p* T- C% y
如下图所示:
3 S3 G& S; E2 E8 D
[. j7 m( I" D0 h" n
1 A5 x* i& z% W9 R* B8 a v1 U4 { G+ ]5 `1 G: j8 n
5 b' S* p' c9 m- R4 U/ S
【问题二:使用printf函数】
: c1 a3 \" r( D0 e 用fputc重定向stdio库的printf函数后:文件流→串口USART1。使用printf函数将数据输出,如果没有提前进行代码配置(关于如何配置各论坛上有类似贴),或者将下图的“USE MicroLIB”勾选:
2 ?6 W, [0 R' ?) n
; G% B& X# T2 q3 O& H4 f* {. z
将导致:在Keil上进行软件仿真时,点击全速运行至少要点击三次(或者将光标定位在主函数里的某条语句,点击“Run to Cursor Line”也至少要点击三次);若是将程序烧进硬件跑,程序短时间未见能工作(未测试时间过久些是否会工作),串口助手收不到输出的数据。# W* I) A' X* S' r1 y$ J
1 p& g* I1 R7 m1 k------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------9 {" [5 l; A; @! |! y9 Q
( _3 q C( c* @2 i7 l9 a: [
问题很简单,但困扰了小白好几天,写下来希望其他初学者遇到相同问题时有所参考。9 _4 J: U5 ~* E+ [# s
32初学之路,还请各位大神大大多多指教。7 T. V2 X3 d& V+ R0 }- e. X
9 z, T8 M7 N/ `
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
5 a* l! u& d- v L3 j. R& T1 m2 X
后面的发送才正常。
初学过程,还请多赐教