你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F103串口1 printf函数的实现

[复制链接]
STMCU小助手 发布时间:2022-3-18 22:18
 在单片机中使用最多的通信接口基本就是串口了,说起串口就不得不提串口中最常用的一个函数就是打印函数printf()函数,在上位机上中这个函数直接从库函数中调用就可以了,那么在单片机中这个函数要怎么使用呢?能不能将这个函数和串口1对应起来,当然是有方法的。
( o3 J# I1 m6 d. R5 P! i
) @+ T9 a2 B+ ~  下面就通过代码来演示一下如何在串口1上使用printf()函数的功能。+ c/ s3 J6 c8 i" K

) Q6 q8 C9 F$ D- U0 Z/ B$ B
  1. void uart_init(u32 bound)2 M7 U6 d/ u  x2 L9 m
  2. {$ t( a) v; I3 l  O; U  W
  3.     //GPIO端口设置
    / F4 c! q' l$ Z8 @0 Y
  4.     GPIO_InitTypeDef GPIO_InitStructure;
    & _: u9 w# g9 X. A! q
  5.     USART_InitTypeDef USART_InitStructure;; a% p9 w% @4 G' w) i
  6.     NVIC_InitTypeDef NVIC_InitStructure;
    + m/ x; `) U$ H: S8 T, O# K

  7. 1 i% N9 a' i8 ~1 K' b4 O, t' V% \" H
  8.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);        //使能USART1,GPIOA时钟
    6 ~, ]$ Y  j; E0 |: Y2 t' p! p  q
  9. / M) e) ]+ S' t) L, j, V
  10.     //USART1_TX   GPIOA.93 J& |, s0 r9 r, F7 R! ]+ C' i. ^
  11.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9- l0 I: l7 H9 I6 L0 A  {8 j
  12.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;8 Y8 f. O8 x4 y$ t0 L0 Q
  13.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出5 j& q, N7 I; L
  14.     GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.92 p8 p% M9 t& q/ o& }
  15. ) x- f: G* r6 A3 s$ h2 B
  16.     //USART1_RX          GPIOA.10初始化
    / c9 P* o# h0 g1 w4 u
  17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
    6 D: G2 j1 ^0 Y7 p; S4 w) Y
  18.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入# P4 |9 n9 T* _
  19.     GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
    ; `) s2 |& G0 c8 s
  20. ; }  N! G; @' g( S2 S2 L
  21.     //Usart1 NVIC 配置
    8 @0 h/ C1 o  j4 a: R* F
  22.     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    : j6 ?# f& `# \, M' @% N
  23.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3" Y+ X, E8 L0 ?, I( S
  24.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
    ( A, Q6 V4 R( ^6 W2 b0 @  {
  25.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能- Y) h! t. T. i, `9 y' z
  26.     NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器8 H" x6 ]: a) Y. s# [6 A

  27. # m3 s1 D$ i) H8 F* Y
  28.     //USART 初始化设置% p3 ?( ]5 L. K4 ~) J5 {
  29. 6 R3 q$ V8 {7 ~1 |4 J
  30.     USART_InitStructure.USART_BaudRate = bound;//串口波特率! ~7 p6 @/ I8 d  a& F) ~
  31.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式8 g" `  T* W2 k+ P
  32.     USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位+ }3 J( s: b/ d0 x% w
  33.     USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位  v5 u6 X0 P, H, |5 T7 b/ Y
  34.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制+ ]* h7 h9 A2 R  V! q' _5 S
  35.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收发模式
    # h5 v; B) P8 x) l  i$ g

  36. ! b. @6 s/ q2 G  S. @+ p
  37.     USART_Init(USART1, &USART_InitStructure); //初始化串口1
    1 p% S3 I3 I; p0 D% H! q1 T! z
  38.     USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
    5 f3 f( g6 I3 d+ h3 y
  39.     USART_Cmd(USART1, ENABLE);                    //使能串口1
    2 Q: M; _" k9 _
  40. }
复制代码
4 x9 Z% J, h9 _
  首先初始化串口1所使用到的端口,将发送引脚设置为推挽输出模式,将接收引脚设置为浮空输入模式。这里串口使用中断来接收数据,所以还需要设置中断优先级,通过NVIC(嵌套向量中断控制器)来设置串口中断的优先级,接下来设置串口的波特率,字长为8位,1位停止位,无奇偶校验位,无硬件数据流控制端口,串口模式设置为收发模式。最后使能串口,并开启中断功能。
5 Z7 V1 d. z3 U1 h: U5 }8 u2 b$ a& H
4 \" L6 p7 I$ \& h4 Y  下面编写串口中断函数,用来接收数据
8 c! [& Q- t, k1 M$ o; _' D' y  l3 z$ Q: Y$ {. [
  1. u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.( `! ?" w) {8 u( o1 U
  2. //接收状态
    ! T/ u7 M6 ]" O+ C% t, @
  3. //bit15,        接收完成标志
    0 `3 B8 G/ T) h4 q9 p
  4. //bit14,        接收到0x0d9 ?& ^. A( O( ]
  5. //bit13~0,        接收到的有效字节数目
    " m0 h$ D1 X1 I- S5 ]
  6. u16 USART_RX_STA = 0;     //接收状态标记! r8 c+ ?% y2 ~9 D) y9 A

  7. 7 A: N7 L: @5 E' |3 Q6 c
  8. void USART1_IRQHandler(void)                        //串口1中断服务程序. |5 f- d- E) D3 a# R1 Y8 k+ i
  9. {1 y$ R4 }1 T" j+ D. f" a* v
  10.     u8 Res;! _3 F( f; P  k$ U; O
  11.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)2 l, k. d- |3 L7 U' a
  12.     {
    : E, N: D/ B, [) x2 O: G
  13.         Res = USART_ReceiveData(USART1);        //读取接收到的数据
    , H3 a, ]+ [. a6 F. G
  14. 4 }5 G1 b6 A3 r* N
  15.         if((USART_RX_STA & 0x8000) == 0) //接收未完成& A6 p! v- k( m; e" r8 \5 F( l. W
  16.         {! K. d- m0 {9 X. ^
  17.             if(USART_RX_STA & 0x4000) //接收到了0x0d5 V! I  i. v& P  k2 c6 H. A
  18.             {* H& z3 k0 v" j  @5 t% h4 D
  19.                 if(Res != 0x0a)USART_RX_STA = 0; //接收错误,重新开始4 g* t: v4 m' Y
  20.                 else USART_RX_STA |= 0x8000;        //接收完成了# |: o4 g7 o" Y# i4 o1 I! w, o
  21.             }
    " f3 B+ o* M. J. m6 R/ G8 N! j) f
  22.             else //还没收到0X0D
    ; y8 O4 c* @6 K
  23.             {
    : g- F& f  X) Q- \
  24.                 if(Res == 0x0d)USART_RX_STA |= 0x4000;
    ' k/ w! M  v! C1 W/ e/ }4 M
  25.                 else9 a0 y) _: h) r/ }& z& y& m* K
  26.                 {
    ' ?8 k. r$ C" _. ^) F5 i0 w2 s  q% K
  27.                     USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res ;2 z9 N9 s3 i* C
  28.                     USART_RX_STA++;
    , U' c; J9 h  U8 T& {
  29.                     if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收' X: S5 u4 j  C+ D1 J7 z5 t
  30.                 }1 c6 z) H. [- j' K- M' K! Y" D- @
  31.             }% {0 F8 X: J& k- ~8 I; b' G
  32.         }
    9 {4 q3 G6 R$ v* E8 _, y
  33.     }
    % ~, q4 \; r- Z; x. V
  34. }
复制代码
# A7 w) R9 G& p: D- W" L
  接收数据时通过USART_RX_STA变量的最高两位表示接收数据的状态,接收数据以回车换行作为结束位,回车换行对应的数据为0x0D、0x0A,只有这两个数据挨着接收到之后才表示一组数据成功的接收到了,这里使用USART_RX_STA最高两位表示是否接收到了这两个结束标志。4 T  Z) x% F8 ^' F5 B$ [
! a# @4 @! y, r1 M) E/ d. {4 ]
  串口设置好之后,接下来给串口1添加支持printf()函数的代码。
/ b/ b! x* S& M  V6 \( q/ {$ j5 D6 G6 l3 d: b& H2 a
  1. #if 1
    / {* @2 o8 E) D+ R4 C, W
  2. #pragma import(__use_no_semihosting)
    3 _0 E4 G9 l& b5 s
  3. //标准库需要的支持函数
    3 x3 o$ y/ V# _6 R8 z# K) w/ ]* s
  4. struct __FILE9 j7 M% |- u; ~
  5. {
    ' p* c! Q& W" c  W/ D) ~
  6.     int handle;
    : d9 O9 E. [  ]6 u2 O4 @
  7. };
    $ W' J# R. h1 W1 b9 {6 O
  8. FILE __stdout;
    + h1 i; T  Y2 V/ f3 _
  9. //定义_sys_exit()以避免使用半主机模式0 i/ ~2 `# ?9 @4 X) f, Z4 {$ `7 s
  10. _sys_exit(int x)! R/ r# L3 v: m. H8 a
  11. {
    ' J4 s* n! ]3 J7 y
  12.     x = x;4 A7 F) q; _: V* G' Q. v
  13. }
    $ g" y- |% y. J, Q
  14. //重定义fputc函数- p5 O# E5 d" V, W$ J+ S5 R
  15. int fputc(int ch, FILE *f)4 P9 O  K2 j6 X# `5 w
  16. {
    6 Z2 ]- y0 Q) u: V* c2 S) F! k: A2 W- A8 o
  17.     while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕
    / `8 w) ?3 w* @  f9 m
  18.     USART1->DR = (u8) ch;
    / k2 i( T# ~, n9 l: R2 ]3 B2 b
  19.     return ch;
    ( l( ^$ f7 ?0 S) B  W6 w. ~. i/ b, W
  20. }
    - z, ~0 h% [+ z6 C6 E
  21. #endif
复制代码
  L( J5 \, _" M0 x/ g0 ?6 R
  上面的两个函数是固定模式不需要修改,自己需要修改的是fputc()函数,在这个函数中将寄存器设置为串口1的寄存器,这样printf()函数在打印数据的时候,就会通过串口1将数据发送出去,如何这里的寄存器设置为了其他串口的寄存器,那么使用printf()函数打印的时候就会通过其他串口打印出去。重定义函数fputc()设置好之后,就可在程序中直接使用printf()函数打印了。( @' _! {: K2 \. |( j$ h9 T

; u  o/ M' P, t8 N  e
  1. int main(void)
    9 w! ?% t& V  e) d; o8 j  }
  2. {
    2 X6 {, h7 _. i  w9 m
  3.     u8 t;# A+ @9 l4 V9 p$ D0 e
  4.     u8 len;
    : g( s; t( b% }
  5.     u16 times=0;7 A9 u, {2 X+ e  x5 u
  6.     delay_init();       //延时函数初始化
    # x. d' V# k% o# u0 Y
  7.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    6 Z; M$ c6 _6 h; D! m
  8.     uart_init(115200);3 p3 R5 Q* k+ _0 ]
  9.     LED_Init();
    - \) s- \  p6 \4 f2 r
  10.     while(1)
    $ m  z7 A$ f- ]" Z( \, M
  11.     {( x% d1 ]5 K/ k! |
  12.                 if(USART_RX_STA&0x8000)
    . s6 J7 G! W# y1 m1 e6 A  ^
  13.                 {
    7 k- k  c8 X& Z; }, x- D. B5 T
  14.                     LED1=!LED1;+ [' o. ]) q/ s( f* s9 Q! E& F
  15.                         len=USART_RX_STA&0x3fff;                //获取本次接收数据长度& t+ r3 k! X* Y
  16.                         printf("\r\n您发送的消息为:\r\n");' X7 n+ P& o' v# V9 A1 h/ Z+ Y
  17.                         for(t=0;t<len;t++)% D2 [1 q/ N0 L4 n' z# d* [
  18.                         {
    % P1 B4 V: z$ X/ B
  19.                                 USART_SendData(USART1,USART_RX_BUF[t]);//向串口1发送数据
    - }9 F, {* R$ o$ |" D- {
  20.                                 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送完成
    ) ?4 j1 A9 x, N7 x4 w
  21.                         }
    . O2 Y$ |, i$ J  I' J, n
  22.                         printf("\r\n");
    + U+ m, ~( L+ s
  23.                         USART_RX_STA=0;7 Q9 _0 N# \- p3 m
  24.                 }; U. a" j, l0 J5 S: ]  }$ ]! \
  25.                 else
    3 e( C! d8 S9 Z3 O& A
  26.                 {
    : @3 X% ?/ ?7 |6 I" u
  27.                         times++;
    2 E8 n- a% C' j- A1 N
  28.                         if(times%5000==0)
    # s: E& J0 `' a# T! F
  29.                         {  d# m( ]: {- B9 R" `3 t* }
  30.                                 printf("串口实验\r\n");0 `6 H! I& V6 J  W: g% Q2 G
  31.                         }
    # x7 B' v) X1 |8 W* {
  32.                         if(times%300==0)% o  J2 F; `; F
  33.                                 printf("请输入数据,以回车键结束\r\n");1 {8 U0 L) _7 T  w8 k
  34.                         if(times%30==0)
    , u2 h. o% ?- @2 o: M; P
  35.                                 LED0=!LED0;
    * n- Y9 S: ~1 E: J& ]: @/ U
  36.                         delay_ms(10);8 O+ o8 ~; ^/ H: I) s7 F" D
  37.                
    ; X+ `( d' t* y8 H5 Z; |. Y
  38.                 }& \1 k+ ]- d8 Z+ a% O# c! e
  39.     }# v% Q* X; `9 g4 n1 O3 ?
  40. }
复制代码
+ n* H5 t, j% M2 Q% d% p7 Q9 E7 W* s* a9 Z
  在主函数中通过printf()打印提示语句,提示用户输入数据。当用户输入数据并以回车换行结束后,串口接收到数据就会将接收到的数据直接打印出来。
' w7 H! @. b) L8 E) L# O
. E9 p* J' Q: j WK40RVM]K$KH$EBIBO{W2X6.png 9 D- t5 w0 f6 G

/ n7 Q  a+ V4 S( [: a3 a) p1 `  这样在单片机中,通过串口1就可以输出printf()函数打印的数据了。1 f  `4 H6 F  T, ?) Q
* e/ T4 C  d) f9 a( L! U) h8 ~

, J3 n0 e! ]# e; f9 {
$ \9 b, c+ b8 _! {1 K; l
赞 收藏 评论0 发布时间:2022-3-18 22:18

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版