STM32之串口通信之printf重定向

当我们在学习一款CPU时,最经典的实验莫过于流水灯了,掌握流水灯的话就基本等于学会操作I/O口了,那么在学会I/O之后,相对于我们来说会把学习串口的操作放在第二位。在程序运行的时候我们可以点亮一个LED来显示代码的执行状态,但有时候我们还想把某些中间量后者其他程序状态信息打印出来显示在计算机上,那么这时候串口的作用就可显而知了。

下面我们来看一下什么是串口
串行接口简称串口,也称串行通信接口或串行通讯接口(通常指COM接口),是采用串行通信方式的扩展接口。串行接口 (Serial Interface) 是指数据一位一位地顺序传送,其特点是通信线路 简单,只要一对传输线就可以实现双向通信(可以直接利用电话线作为传输线),从而大大降低了成本,特别适用于远距离通信,但传送速度较慢
与并口的区别: 串口形容一下就是一条车道,而并口就是有8个车道同一时刻能传送8位(一个字节)数据。但是并不是说并口快,由于8位通道之间的互相干扰(串扰),传输时速度就受到了限制,传输容易出错。串口没有互相干扰。并口同时发送的数据量大,但要比串口慢。[3] 串口硬盘就是这样被人们重视的。
串口通信串口通信(Serial Communication),是指外设和计算机间,通过数据信号线、地线等,按位进行传输数据的一种通讯方式。 
串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。
串口通信协议:
在串口通信中,常用的协议包括RS-232、RS-422和RS-485。 
•RS-232:标准串口,最常用的一种串行通讯接口。有三种类型(A,B和C),它们分别采用不同的电压来表示on和off。最被广泛使用的是RS-232C,它将mark(on)比特的电压定义为-3V到-12V之间,而将space(off)的电压定义到+3V到+12V之间。传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其驱动器负载为3~7kΩ。所以RS-232适合本地设备之间的通信。 
•RS-422:最大传输距离为1219米,最大传输速率为10Mb/s。其平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能达到最大传输距离。只有在很短的距离下才能获得最高速率传输。一般100米长的双绞线上所能获得的最大传输速率仅为1Mb/s。 
•RS-485:从RS-422基础上发展而来的,最大传输距离约为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能使用规定最长的电缆长度。只有在很短的距离下才能获得最高速率传输。一般100米长双绞线最大传输速率仅为1Mb/s。

数据传输通信方式:
(1)单工通信:
单工通信(Simplex Communication)是指消息只能单方向传输的工作方式。
在单工通信中,通信的信道是单向的,发送端与接收端也是固定的,即发送端只能发送信息,不能接收信息;接收端只能接收信息,不能发送信息。基于这种情况,数据信号从一端传送到另外一端,信号流是单方向的。

(2)半双工通信:

半双工通信(Half-duplex Communication)可以实现双向的通信,但不能在两个方向上同时进行,必须轮流交替地进行。
在这种工作方式下,发送端可以转变为接收端;相应地,接收端也可以转变为发送端。但是在同一个时刻,信息只能在一个方向上传输。因此,也可以将半双工通信理解为一种切换方向的单工通信。
(3)全双工通信:

全双工通信(Full duplex Communication)是指在通信的任意时刻,线路上存在A到B和B到A的双向信号传输。 全双工通信允许数据同时在两个方向上传输,又称为双向同时通信,即通信的双方可以同时发送和接收数据。在全双工方式下,通信系统的每一端都设置了发送器和接收器,因此,能控制数据同时在两个方向上传送。全双工方式无需进行方向的切换,因此,没有切换操作所产生的时间延迟,这对那些不能有时间延误的交互式应用(例如远程监测和控制系统)十分有利。这种方式要求通讯双方均有发送器和接收器,同时,需要2根数据线传送数据信号。(可能还需要控制线和状态线,以及地线)。
同步方式:

同步通信:是一种比特同步通信技术,要求发收双方具有同频同相的同步时钟信号,只需在传送报文的最前面附加特定的同步字符,使发收双方建立同步,此后便在同步时钟的控制下逐位发送/接收。如:SPI总线,I2C总线。 
异步通信:指两个互不同步的设备通过计时机制或其他技术进行数据传输。也就是说,双方不需要共同的时钟。发送方可以随时传输数据,而接收方必须在信息到达时准备好接收。如:串口(USART)。


好了,下载我 们再来看看STM32开发板上是如何实现串口通信的,本文以实现printf重定向为例来进行分析!
代码如下:
/*
	*函数名 :fputc
	*描述   :重定向C库函数printf到USART2
	* 输入  :无
  * 输出  :无
  * 调用  :由printf调用
  */
	
	int fputc(int ch,FILE *f)  
{    
    //while(USART_GetFlagStatus(USART2,USART_FLAG_TC) != SET);   
    USART_SendData(USART2,(unsigned char)ch);      
    while(USART_GetFlagStatus(USART2,USART_FLAG_TC) != SET);    
    return (ch);    
}  

这个代码中调用了两个ST库函数。USART_SendData()和USART_GetFlagStatus()。
重定向时,我们把fput()的形参ch,作为串口将要发送的数据,也就是说,当使用printf()时,先调用这个fputc()函数,然后使用ST库的串口发送函数USART_Send Data(),把数据转移到发送数据寄存器TDR,触发我们的串口向PC发送一个相应的数据。调用完 USART_SendData()后,要使用  while(USART_GetFlagStatus(USART2,USART_FLAG_TC) != SET)  语句不停地检查串口发送是否完成的 标志位TC,一直检测到标志为完成,才进入下一步操作,避免出错。在这段while循环检测的延时中,串口外设已经由发送控制器以及根据我们的配置把数据从移位寄存器一位一位的通过串口线TX发送出去了。
主代码:
/*
* 函数名:main
* 描述  :主函数
* 输入  :无
* 输出  :无
*
*/
#include "printf.h"
#include <stdio.h>

int main()
{ 
	int i;
	/* USART init 115200 8-N-1 */
	USART_init();
	
  printf("\r\n hahahhahahahahaha \r\n");

  printf("\r\n 欢迎使用奋斗stm32开发板 \r\n");
} 	

printf.c

/*
* 函数名:main
* 描述  :主函数
* 输入  :无
* 输出  :无
*
*/
#include "printf.h"
#include <stdio.h>

int main()
{ 
	int i;
	/* USART init 115200 8-N-1 */
	USART_init();
	
  printf("\r\n hahahhahahahahaha \r\n");

  printf("\r\n 欢迎使用奋斗stm32开发板 \r\n");
} 	
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "printf.h"
#include "misc.h"
#include "stm32f10x.h"

/*
	*函数名 :fputc
	*描述   :重定向C库函数printf到USART2
	* 输入  :无
  * 输出  :无
  * 调用  :由printf调用
  */
	
	int fputc(int ch,FILE *f)  
{    
    while(USART_GetFlagStatus(USART2,USART_FLAG_TC) != SET);   
    USART_SendData(USART2,(unsigned char)ch);      
    while(USART_GetFlagStatus(USART2,USART_FLAG_TC) != SET);    
    return (ch);    
}  






/*
* 函数名 :USART_init
* 描述   :USART GPIO 配置,工作模式配置。115200 8—N-1
* 输入   :无
* 输出   :无
* 调用   :外部调用
*/

void USART_init(void)
{
   GPIO_InitTypeDef GPIO_InitStructure;
	 USART_InitTypeDef USART_InitStructure;
	
	
   /*config USART2 clock */
	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);   
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);  
       /*USART2 GPIO config*/ 
     /* configure USART2 Tx (PA.02) as alternate function push-pull*/	
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP; //复用推挽输出    
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    
    GPIO_Init(GPIOA,&GPIO_InitStructure);   
    /* configure USART2 Rx (PA.03) as alternate function push-pull*/
    GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;    
    GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;  //复用开漏输入    
    GPIO_Init(GPIOA,&GPIO_InitStructure);   
       /*USART2 mode Config*/     
    USART_InitStructure.USART_BaudRate = 115200;  
    USART_InitStructure.USART_WordLength = USART_WordLength_9b;  
    USART_InitStructure.USART_StopBits = USART_StopBits_1;  
    USART_InitStructure.USART_Parity = USART_Parity_No;  
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;  
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;  
    USART_Init(USART1,&USART_InitStructure);  
    USART_Cmd(USART1,ENABLE); 
	}	


printf.h

#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x.h"
#include <stdio.h>

extern void USART_init(void);
extern int fputc(int ch, FILE *f);

猜你喜欢

转载自blog.csdn.net/panrenqiu/article/details/79819470