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

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

[复制链接]
STMCU小助手 发布时间:2022-3-18 22:18
 在单片机中使用最多的通信接口基本就是串口了,说起串口就不得不提串口中最常用的一个函数就是打印函数printf()函数,在上位机上中这个函数直接从库函数中调用就可以了,那么在单片机中这个函数要怎么使用呢?能不能将这个函数和串口1对应起来,当然是有方法的。" b; [) B, g. k6 W4 P4 \7 M' ?
* k/ q! q& Q# H) m% A  p
  下面就通过代码来演示一下如何在串口1上使用printf()函数的功能。
, X$ h4 J% B9 f4 X! ~* i. |6 t7 {9 @
  1. void uart_init(u32 bound)
    5 j0 ~4 k; M' a. t
  2. {
    , G0 `- z, A  D9 }4 l2 E
  3.     //GPIO端口设置" l8 P& h$ o# W8 c; }  g; W" ?3 Y* A
  4.     GPIO_InitTypeDef GPIO_InitStructure;8 w. l1 T# v  S: S
  5.     USART_InitTypeDef USART_InitStructure;
    * a2 h( ^5 N- x7 r* I
  6.     NVIC_InitTypeDef NVIC_InitStructure;
    7 P5 X# u. L7 Y! \

  7. 5 T; ^3 \7 e9 Q
  8.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);        //使能USART1,GPIOA时钟  M# V$ {# d0 {, N: _' W
  9. + g* V3 Y7 ]; A" a. A6 Y
  10.     //USART1_TX   GPIOA.9
    4 K* N, b* {, B$ |3 w" A# f0 j
  11.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    & c, S. Q$ ]" ]9 l/ J8 k) K
  12.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    # u: d+ n$ ~" r: v. w! |4 x
  13.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出+ V" h0 d: A5 k+ Z* G
  14.     GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.94 s9 Q; z9 k0 v! |& X' \) U; E) L9 r

  15. . a7 M; v2 y5 ]7 K( J9 d
  16.     //USART1_RX          GPIOA.10初始化
    * n' }! w6 h# s8 ]* @5 W
  17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
    # k* d% u2 ?- A: c, l7 m3 C2 L
  18.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    3 p/ ]# Q  q& u! O1 S  z
  19.     GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
    ; Z# z' c1 b  k, I% R4 u8 w
  20. 5 C* W5 e- ?  @3 R6 d) j  E
  21.     //Usart1 NVIC 配置
    0 p' y+ \# _& t! q: I. J* h
  22.     NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    $ n1 o3 m0 {5 D1 }
  23.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级35 ?6 s7 E0 a& a9 N% e
  24.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
    9 L4 ?9 n7 R! z/ n& O* p9 I( o% [
  25.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能+ I: {# w$ D: I3 w
  26.     NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器0 W6 k5 |! }8 K1 t# k8 x3 N) I7 Q
  27. ( }5 ~$ }4 }: C) r, ^7 m6 a
  28.     //USART 初始化设置
    3 Z* n: }$ r3 Q0 C8 \& m
  29. " k; O4 W3 p. c. t; @* R6 Y
  30.     USART_InitStructure.USART_BaudRate = bound;//串口波特率
    % Z5 ~+ i5 r3 n6 z7 G( z) H  y- F6 T
  31.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式0 h+ L2 [8 t# x6 Q, l  }9 o
  32.     USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    / _" P2 j! R, Z/ [6 d4 d$ n  y
  33.     USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位8 V& F8 ^' P" A" M. a
  34.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    " f9 x: }" r" ~- s0 H! F" x
  35.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收发模式$ \8 @: l3 h& X
  36.   F- E; Q" G6 g* V  ~
  37.     USART_Init(USART1, &USART_InitStructure); //初始化串口13 t' p" F( n& J2 M$ Z* V& K4 {, i  U
  38.     USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断5 \* m/ C  X' c/ \
  39.     USART_Cmd(USART1, ENABLE);                    //使能串口1
    7 a' o* ~' B8 _8 Q* N2 [  X( }
  40. }
复制代码

' |+ G/ `8 s* [% x4 T% l% z! I  首先初始化串口1所使用到的端口,将发送引脚设置为推挽输出模式,将接收引脚设置为浮空输入模式。这里串口使用中断来接收数据,所以还需要设置中断优先级,通过NVIC(嵌套向量中断控制器)来设置串口中断的优先级,接下来设置串口的波特率,字长为8位,1位停止位,无奇偶校验位,无硬件数据流控制端口,串口模式设置为收发模式。最后使能串口,并开启中断功能。! g" B4 {% b- _; r7 V/ A# i2 G

, I, k8 J3 A4 {$ A% G3 y7 d9 ?  下面编写串口中断函数,用来接收数据2 X2 Z3 x& t) ?  [! V6 {, X
9 a. M! S( C6 i1 T) x8 `# d
  1. u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.; x2 I) t2 Z) L2 F2 X* l- N* E$ c
  2. //接收状态- F8 f3 R) N" k' K
  3. //bit15,        接收完成标志
    ; Z5 P( K( P5 q; j
  4. //bit14,        接收到0x0d& X. b* Q7 p7 G! u0 G7 p" ?
  5. //bit13~0,        接收到的有效字节数目
    4 }' I, V, I& V# X' p
  6. u16 USART_RX_STA = 0;     //接收状态标记
    ; X% F( x9 B, d

  7. 1 K) I7 l8 L, k2 i. T, X
  8. void USART1_IRQHandler(void)                        //串口1中断服务程序
    3 I+ [; a2 \  u% c
  9. {# u) A* {; N; Q1 ~( h
  10.     u8 Res;* p+ s2 s7 ^0 Q. f4 H! A+ s
  11.     if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    & I8 y* Y. f) a1 x  g" Q
  12.     {% ^7 c4 Z0 E* g: Q" a
  13.         Res = USART_ReceiveData(USART1);        //读取接收到的数据9 q+ M  |2 S1 a6 P$ ?9 D$ T. n

  14. % F8 u8 k( H: l& z5 U% h
  15.         if((USART_RX_STA & 0x8000) == 0) //接收未完成, v" o$ l# t$ u9 o5 \
  16.         {1 W! R$ W; _( e$ k: N/ m) y* j
  17.             if(USART_RX_STA & 0x4000) //接收到了0x0d$ G9 I7 C! W% z0 P& O
  18.             {8 m" N0 e1 h. V+ L
  19.                 if(Res != 0x0a)USART_RX_STA = 0; //接收错误,重新开始- j; S' S  F+ V& F. k5 r
  20.                 else USART_RX_STA |= 0x8000;        //接收完成了4 W; m9 s3 o. l; Q; _7 [
  21.             }4 a+ X! O3 m( Z3 r& y7 ?
  22.             else //还没收到0X0D
    7 H# b, Q# i0 O
  23.             {
    3 x9 D3 D  _# o6 s; ~2 Q/ Z3 A
  24.                 if(Res == 0x0d)USART_RX_STA |= 0x4000;3 ?$ _2 ~, v: J0 u
  25.                 else% X3 s  ]2 |. e; D
  26.                 {
    4 h) [2 r: @5 b- F2 ~
  27.                     USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res ;7 A; M" i4 ]2 C/ V$ B: h) Z; e
  28.                     USART_RX_STA++;2 F8 P8 a. k6 p+ K
  29.                     if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收
    ( i! j. \4 ~1 I0 g4 l# _( P
  30.                 }
    ! T5 H/ f2 H4 J2 z; h
  31.             }2 m. O1 q* b7 G. E& c$ d
  32.         }2 B5 ?' w4 Q- ^1 z
  33.     }
    : P: D3 M' F# k1 g- C% |% j# ^& z
  34. }
复制代码

* ^+ \4 W7 U, W, N0 J  接收数据时通过USART_RX_STA变量的最高两位表示接收数据的状态,接收数据以回车换行作为结束位,回车换行对应的数据为0x0D、0x0A,只有这两个数据挨着接收到之后才表示一组数据成功的接收到了,这里使用USART_RX_STA最高两位表示是否接收到了这两个结束标志。8 s, b/ S$ U1 V% C
4 d  s$ p& Y) y5 N" X2 c4 v: F+ y
  串口设置好之后,接下来给串口1添加支持printf()函数的代码。/ m/ g9 D: E, J% q7 k

& m" h" Y4 a' A/ |6 ?( g/ R
  1. #if 1
    6 S. Q9 J9 q6 d
  2. #pragma import(__use_no_semihosting)
    % b, S6 T  i9 X5 K; ?  C; C
  3. //标准库需要的支持函数1 V8 r5 @# K# _  F4 `# I
  4. struct __FILE, U% e- S* L6 f, C1 L- j: B
  5. {. ~$ ]: }* j' m' T$ y9 C
  6.     int handle;/ s* s3 B6 `3 R9 q9 q  a
  7. };
    % u3 v& \' X. y' A, v& K$ r
  8. FILE __stdout;5 i/ f/ |8 N% `3 h/ {
  9. //定义_sys_exit()以避免使用半主机模式
    $ ?2 X( Y: F/ f
  10. _sys_exit(int x)
    ) W, {9 l; D- S# C+ U2 j5 A! B* n0 f
  11. {
    6 \+ g. x& S: D, k' o8 v; U6 B
  12.     x = x;
    / l1 b! K- W4 S' A4 a
  13. }
    " y# E! y2 H" {+ N
  14. //重定义fputc函数  x* s8 Z+ Z" R2 m& x4 R" _2 j- t9 C
  15. int fputc(int ch, FILE *f)
    - _8 o/ ^& {- T
  16. {
    2 I4 R/ `" b; Z0 N9 j8 _
  17.     while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕
    8 d2 p/ i( A7 f: l4 T
  18.     USART1->DR = (u8) ch;9 d' B9 {5 ?1 K! T" R) I
  19.     return ch;
    + V  U9 M4 g! ?: P7 [; n5 y
  20. }
    & `- o- c4 h/ v
  21. #endif
复制代码
1 u; i0 k5 M% P& r) I
  上面的两个函数是固定模式不需要修改,自己需要修改的是fputc()函数,在这个函数中将寄存器设置为串口1的寄存器,这样printf()函数在打印数据的时候,就会通过串口1将数据发送出去,如何这里的寄存器设置为了其他串口的寄存器,那么使用printf()函数打印的时候就会通过其他串口打印出去。重定义函数fputc()设置好之后,就可在程序中直接使用printf()函数打印了。$ J, ?9 b  R* E

+ U) j% l  ~. b  r0 K  r
  1. int main(void)" s% F5 p6 R$ A
  2. {( w1 z% ^, [3 L/ p7 n0 n. o, `
  3.     u8 t;
    4 W/ K  j. y; r' A8 n
  4.     u8 len;& G4 ?5 q4 A' x# A; j3 B9 K# R, I
  5.     u16 times=0;' l1 i, x; m4 T3 r
  6.     delay_init();       //延时函数初始化) U2 _9 L; ?8 L
  7.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);3 `' j/ ~: S: i- `6 x
  8.     uart_init(115200);& _/ n4 I- g* U% Q4 T4 d7 B
  9.     LED_Init();, i/ D: ]6 {, ?1 q: `! ?0 d- c, R
  10.     while(1)
    , s' r7 H! B8 c
  11.     {
    1 x" Q8 F" b# `" ~, `; I
  12.                 if(USART_RX_STA&0x8000)
    8 T/ q+ I) R" C
  13.                 {- |& k. X  ?3 E( I6 C# x( k1 i
  14.                     LED1=!LED1;
    2 P- H1 d5 p. g: z( y. L% r; U3 l
  15.                         len=USART_RX_STA&0x3fff;                //获取本次接收数据长度
    + K6 N" J% d% A- F8 k
  16.                         printf("\r\n您发送的消息为:\r\n");
    ; y) w9 F" q4 Y0 m+ i/ `3 V8 i, E
  17.                         for(t=0;t<len;t++)0 a# X! z# W. E/ x5 ^: N' J
  18.                         {
    " b3 y0 ^6 h; E+ s3 a* _, S
  19.                                 USART_SendData(USART1,USART_RX_BUF[t]);//向串口1发送数据' ]' R7 B2 ?0 E, ]1 O
  20.                                 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送完成 & C/ _7 u: n* r+ b
  21.                         }
    8 x9 s& d% L) \- P" P8 c- I- y
  22.                         printf("\r\n");7 ?; D9 O7 ?1 S) g& a  O
  23.                         USART_RX_STA=0;$ y8 j" i- ]) f; e0 ^6 g
  24.                 }
    : ~  j# x& p, x' ]
  25.                 else
    # M3 q" O5 n* o. Q
  26.                 {
    , u0 p1 F) ?  S: t! F7 c
  27.                         times++;
    $ k! t. {9 d0 e" `; O
  28.                         if(times%5000==0)9 L1 K; C0 @  d* B5 ~
  29.                         {- Z" ~  @5 |6 C+ l" _% E
  30.                                 printf("串口实验\r\n");
    ; x" b6 A% y+ F# S5 p
  31.                         }, e' H" M# O( ]: B) a4 o$ n
  32.                         if(times%300==0)
    ; O0 {9 n7 l, g/ G! l1 ~1 M# ]
  33.                                 printf("请输入数据,以回车键结束\r\n");9 E# r' j4 I1 Q$ W# D0 ?) P
  34.                         if(times%30==0)0 \8 C! {7 y" G* d" Y
  35.                                 LED0=!LED0;
    % b2 \% Z% A* i
  36.                         delay_ms(10);
    5 C8 G! J2 {; j6 w5 B6 v0 K9 j) J
  37.                
    2 Z  P2 I& \" `! w, ^7 H
  38.                 }1 D! _0 n! R' ]- r, K( L) E$ u
  39.     }
    4 @" J/ i. o2 `, A$ b
  40. }
复制代码

, `- v+ T/ @: h  @  在主函数中通过printf()打印提示语句,提示用户输入数据。当用户输入数据并以回车换行结束后,串口接收到数据就会将接收到的数据直接打印出来。: y1 k6 M( g0 Z( e0 R
7 ?$ i0 H( u3 g- j0 T1 `! {
WK40RVM]K$KH$EBIBO{W2X6.png
0 j6 \3 b6 _4 L& w2 y+ G4 o1 `
  这样在单片机中,通过串口1就可以输出printf()函数打印的数据了。
4 j" j  @9 Y) p( k0 h: u- o+ H3 v1 q# R2 O6 F! O

8 d: t4 Y* D* `" k6 o( t: w' X7 ?6 _2 G5 t2 s$ c0 R
赞 收藏 评论0 发布时间:2022-3-18 22:18

举报

0个回答

所属标签

相似技术帖

官网相关资源

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