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

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

[复制链接]
STMCU小助手 发布时间:2022-3-18 22:18
 在单片机中使用最多的通信接口基本就是串口了,说起串口就不得不提串口中最常用的一个函数就是打印函数printf()函数,在上位机上中这个函数直接从库函数中调用就可以了,那么在单片机中这个函数要怎么使用呢?能不能将这个函数和串口1对应起来,当然是有方法的。
: c" }3 M' V7 p7 J, ~
4 G2 i( r5 \8 j  下面就通过代码来演示一下如何在串口1上使用printf()函数的功能。9 R( N6 f$ q4 T0 W8 I0 a- m. E

( {3 S3 X% J3 ^3 j1 T* W) X
  1. void uart_init(u32 bound)
    * n9 w2 j* @0 I" O/ b9 x, V
  2. {
    & I* A- m& f8 F; N" h: L# R
  3.     //GPIO端口设置4 s2 j$ d+ t7 [; `" v7 P6 O8 h
  4.     GPIO_InitTypeDef GPIO_InitStructure;5 F  c; g+ ?! d% o# o( X) H
  5.     USART_InitTypeDef USART_InitStructure;
    6 u- E+ @0 d: ?2 O1 S
  6.     NVIC_InitTypeDef NVIC_InitStructure;
    4 }1 b# }4 j. G& p, p/ i. K6 s
  7. 6 d. l8 U. v/ F2 R2 s
  8.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);        //使能USART1,GPIOA时钟
    2 n9 @1 d- u0 q- s
  9. / X2 f8 p# E! M8 |. N2 u( k4 B
  10.     //USART1_TX   GPIOA.9
    * _# H; ]  l' n3 C( M
  11.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    5 \( V+ H* V3 ^0 m
  12.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    # e6 ?8 a  T0 H' L
  13.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出
    ) a0 o' K+ k! l5 Y
  14.     GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.97 Z/ F) {3 ]7 e" F' ?0 H

  15. # v) m! n* b3 G
  16.     //USART1_RX          GPIOA.10初始化
    9 c, v. }. M1 f, F2 X( w
  17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA106 I) e; }* W" H+ Z. L  Z
  18.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    8 y' W) M3 K: R' ^; b5 q2 v
  19.     GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
    : T: p( a$ m2 R4 Q

  20. ! l% @0 i/ C! A! v, `! y( Q- M0 Q. l
  21.     //Usart1 NVIC 配置
    1 I9 L7 \# z. R/ s
  22.     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    - R+ u  M  |" a& K
  23.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
    3 }0 @* N3 |$ N1 i6 [/ T
  24.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3. S; g: C* p( L8 r( E
  25.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能! E; s3 W) Q2 N  K9 ^! C$ c$ N
  26.     NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器
    & v& @" c( ^' ]5 K
  27.   l9 J/ v' i: G# z0 a: M0 ]
  28.     //USART 初始化设置
    $ E( F8 y6 y7 X/ T# l/ D
  29. ( t, s% J$ u% x# W5 a1 n
  30.     USART_InitStructure.USART_BaudRate = bound;//串口波特率7 f0 d5 Z* `8 ^
  31.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    ! H; X) O  [' z2 u1 v5 H
  32.     USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位4 S/ g5 ]' Q% t2 k( m- O% q
  33.     USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    3 p' [" Y" V' r  T1 m! m9 ?
  34.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    1 j- F6 Y' b( d0 G- W# u
  35.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收发模式2 p3 j* |, h0 O* v) p0 I5 y3 ^7 x; h0 [
  36. 3 Y. L* M+ j: M' o
  37.     USART_Init(USART1, &USART_InitStructure); //初始化串口12 S* Q: E5 C- C5 [% o, f& J4 i
  38.     USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
    . p2 l! h5 b/ v5 b
  39.     USART_Cmd(USART1, ENABLE);                    //使能串口1
    * r5 |6 m0 ^' I6 |7 [! e2 Q+ i! e
  40. }
复制代码

5 s. M. p7 M* N$ q. U" {  首先初始化串口1所使用到的端口,将发送引脚设置为推挽输出模式,将接收引脚设置为浮空输入模式。这里串口使用中断来接收数据,所以还需要设置中断优先级,通过NVIC(嵌套向量中断控制器)来设置串口中断的优先级,接下来设置串口的波特率,字长为8位,1位停止位,无奇偶校验位,无硬件数据流控制端口,串口模式设置为收发模式。最后使能串口,并开启中断功能。! x1 U1 ?6 N& m) f4 c% d

3 n$ P3 i4 N) ?' p4 q  下面编写串口中断函数,用来接收数据
8 k" G  q* r6 M4 L; J  g, E- T( k5 h0 `' W! {6 q# r9 N3 @3 q
  1. u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
    5 J, Q4 J% G& H
  2. //接收状态
    ) I8 U5 P5 X) G  {; n3 k( b7 E6 K! Y; ~5 s; V
  3. //bit15,        接收完成标志
    1 V* U9 l& |4 n5 d$ `# T" d
  4. //bit14,        接收到0x0d. K9 G, o  p+ h: z. i1 R, ~, v
  5. //bit13~0,        接收到的有效字节数目2 I# Z- e; c, _, [! X$ u1 W' d9 Y
  6. u16 USART_RX_STA = 0;     //接收状态标记  U2 X: T% o0 v9 N

  7. ' W+ ?% q: ~/ U6 i+ e
  8. void USART1_IRQHandler(void)                        //串口1中断服务程序  }9 }+ _" G+ C4 }0 j+ F5 Q% L+ Z
  9. {
    % I. ?7 {9 k+ @& Y
  10.     u8 Res;* j- M( k, \% e9 c. D
  11.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    7 f  G9 v* B) R/ I! Z& @7 s
  12.     {& k2 w6 V9 m9 U4 u5 g! O/ k6 \- o
  13.         Res = USART_ReceiveData(USART1);        //读取接收到的数据) j4 N( k$ \# h' l/ b

  14. % r" M! }* m# w% w1 E' C
  15.         if((USART_RX_STA & 0x8000) == 0) //接收未完成* J( @2 B8 N: Y9 [# V" |7 j+ I
  16.         {
    , {. j: E/ _6 @# s8 M
  17.             if(USART_RX_STA & 0x4000) //接收到了0x0d
    / q" h; x/ E. R. d/ H
  18.             {
    # {6 b: K5 T2 f' ?* L- f8 D& G% Y. {
  19.                 if(Res != 0x0a)USART_RX_STA = 0; //接收错误,重新开始
    8 ?6 G; A9 P, y/ [
  20.                 else USART_RX_STA |= 0x8000;        //接收完成了* E" d* P5 x- }* u
  21.             }
    2 G6 ^" g; w/ \% R( |
  22.             else //还没收到0X0D
    5 {3 h1 w! m$ n7 o2 k! N4 `- ]
  23.             {
    $ {& y. m; l' n  }
  24.                 if(Res == 0x0d)USART_RX_STA |= 0x4000;+ C% q, y$ X5 Y1 `
  25.                 else' j$ q" E5 K0 v7 `# M
  26.                 {
    , ~$ c; A6 d- q+ H4 P& _
  27.                     USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res ;
    , m4 r  ^5 h  d3 ^* i! }
  28.                     USART_RX_STA++;9 }% H4 d7 ?$ X4 ]: s% x; [0 t
  29.                     if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收6 X& o' j1 O8 m
  30.                 }& N3 o# h6 [, s& e; Q" l" I
  31.             }
    " ]) y5 i  v/ Q: M' P% l; L- d) l
  32.         }$ l, C1 f0 N* g8 M7 b! ~. N  U, L
  33.     }
    5 ]& r, i: U8 y) |
  34. }
复制代码
: q7 [9 T! N$ G4 }; r! B+ g
  接收数据时通过USART_RX_STA变量的最高两位表示接收数据的状态,接收数据以回车换行作为结束位,回车换行对应的数据为0x0D、0x0A,只有这两个数据挨着接收到之后才表示一组数据成功的接收到了,这里使用USART_RX_STA最高两位表示是否接收到了这两个结束标志。
/ ^- A$ I( X- S0 h4 L# g, |+ E
  串口设置好之后,接下来给串口1添加支持printf()函数的代码。
) T- q# }/ f' ^$ y( E  Q$ v: [/ y4 d
  1. #if 1" i% e6 ^9 }5 H- s/ F: i
  2. #pragma import(__use_no_semihosting)
    6 s$ P0 j8 s( v4 }* c
  3. //标准库需要的支持函数5 ~6 p7 g- b6 D- x( }8 K; x
  4. struct __FILE
    * A$ C  }+ J; D! ~1 {, s
  5. {
    7 z5 P* V; M! F
  6.     int handle;
    3 D2 _. e4 R" P4 E$ r2 A6 v# X* ]
  7. };: \( b) f$ ~. ?; h/ Z
  8. FILE __stdout;  N7 ?3 J$ Q# i$ T
  9. //定义_sys_exit()以避免使用半主机模式; L. }( S. K; k2 j3 t; w
  10. _sys_exit(int x)
    / g8 ?, ]1 ^# V4 V# P
  11. {
    ; P8 J9 S4 E% F* I7 [3 f
  12.     x = x;9 J" M& R6 [- Z$ x  J# p# r/ Z
  13. }
    . J+ c4 e, n+ F5 C% Z' J8 V
  14. //重定义fputc函数
    4 l  `- @# Z/ ]; X* g. n8 l
  15. int fputc(int ch, FILE *f)
    + f+ `) q5 O$ S2 l
  16. {
    4 u4 _# C0 b; {: c( y
  17.     while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕7 H4 T# r& m6 y9 X
  18.     USART1->DR = (u8) ch;3 ~. S8 F$ U' d, k
  19.     return ch;
    $ E' d0 H/ z/ T6 g2 `
  20. }6 F. @: m# A: V* w. K
  21. #endif
复制代码

5 S+ g6 |3 _3 n& K  [  上面的两个函数是固定模式不需要修改,自己需要修改的是fputc()函数,在这个函数中将寄存器设置为串口1的寄存器,这样printf()函数在打印数据的时候,就会通过串口1将数据发送出去,如何这里的寄存器设置为了其他串口的寄存器,那么使用printf()函数打印的时候就会通过其他串口打印出去。重定义函数fputc()设置好之后,就可在程序中直接使用printf()函数打印了。9 w3 |9 d; U! e5 n$ y' {( {. }
& e: {/ y7 M% D: O
  1. int main(void)
      D$ G3 _6 \+ a9 O4 e- D, k& }, o' G
  2. {
    4 P2 P# K9 U8 ~" u1 j
  3.     u8 t;
    ( E! s& ]7 Q9 ~3 x+ G2 ~$ x% b( S
  4.     u8 len;& @5 r" d- s6 J0 u. R4 X, g; F
  5.     u16 times=0;
    - p& r. N- ]+ P' l: B1 D5 N
  6.     delay_init();       //延时函数初始化
    ! V, v; }% A7 k2 u
  7.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);, j. @9 g) N5 O& p6 }2 u
  8.     uart_init(115200);6 [/ c: }8 Q2 ~% e+ m6 Q! p  {
  9.     LED_Init();4 [9 v, b2 t- G" p7 A! ?7 B+ s
  10.     while(1)2 _9 H; s5 d7 B2 Q# b/ D
  11.     {# V6 t/ o/ L1 l$ O: S$ a, H1 F
  12.                 if(USART_RX_STA&0x8000), W3 N0 P+ C* O7 `9 w. o
  13.                 {  Z+ Z4 l. z: R" `( I! q
  14.                     LED1=!LED1;
    . |# d4 C5 @2 e. ~
  15.                         len=USART_RX_STA&0x3fff;                //获取本次接收数据长度
    - [2 R, J8 w: f2 X0 L+ v
  16.                         printf("\r\n您发送的消息为:\r\n");
    5 \" ]: c- w9 ~: z+ @9 K; S* f6 K8 x
  17.                         for(t=0;t<len;t++)% v* L" o% z# V0 L. D5 {
  18.                         {
    7 L4 Q! @  `% V  g
  19.                                 USART_SendData(USART1,USART_RX_BUF[t]);//向串口1发送数据
    . Y, o/ t: o9 W& R
  20.                                 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送完成
    : l7 V' U$ V7 l1 Y7 A) O& Z
  21.                         }
    ' S# u$ J  `5 E! R
  22.                         printf("\r\n");
    # m( ]2 V6 z4 P4 T% v% B2 h
  23.                         USART_RX_STA=0;
    $ R2 {% H* `$ h# e5 c. o
  24.                 }* t. j* C4 b, l& Z& N) n( T
  25.                 else
    ' t2 \$ |# M( T* _; W! `0 q
  26.                 {8 o" G9 _0 z4 P/ v
  27.                         times++;, i  t7 m# p+ a, l8 }0 x
  28.                         if(times%5000==0)
    ) K( B3 e7 l$ h7 x+ F; o
  29.                         {
    / y$ h$ i$ L+ G) u5 Z$ p- Z( P) [
  30.                                 printf("串口实验\r\n");
    & X+ D" `6 D4 O- s0 |9 f( \6 Q
  31.                         }, F. k" Z/ B8 `3 o6 j
  32.                         if(times%300==0)
    . D# |+ `+ d. ?& w" k; t/ s. a' s! v
  33.                                 printf("请输入数据,以回车键结束\r\n");8 e! Y" f1 ?' p8 l# ~  V1 G* E9 a* q
  34.                         if(times%30==0)+ J6 Y5 q6 ^4 Z. a" Y2 \3 y. }3 Z
  35.                                 LED0=!LED0;5 ^8 k& i& b7 q0 ^* D0 R  {* ^
  36.                         delay_ms(10);# Q- u) G' E# R
  37.                 2 w) M, [1 M9 C0 F
  38.                 }
    8 Y7 @% ]8 o4 @
  39.     }
      _( Y) m9 V8 w  I
  40. }
复制代码

) r9 n. V+ A5 Y7 v3 D2 r  在主函数中通过printf()打印提示语句,提示用户输入数据。当用户输入数据并以回车换行结束后,串口接收到数据就会将接收到的数据直接打印出来。3 b& A3 x1 P( @' l4 R/ H0 V
7 G. Y. |: j! O: w7 }' U. [& k' j
WK40RVM]K$KH$EBIBO{W2X6.png - T1 |8 y: `" _5 S; e
  F+ W, l6 ?& d) ^/ r9 G% |. o
  这样在单片机中,通过串口1就可以输出printf()函数打印的数据了。  L* Z. R# z7 Z
7 t6 G2 ?& G& [$ ]

) z* a6 m. p3 T: |# ^
1 v1 |2 f6 `. J$ }+ K, D
收藏 评论0 发布时间:2022-3-18 22:18

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版