STM32-串口通信

1  串行通信接口背景知识

        串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式,其通讯协议可分层为协议层和物理层。物理层规定通信协议中具有机械、电子功能的特性,从而确保原始数据在物理媒体的传播;协议层主要规定通讯逻辑,统一双方的数据打包、解包标准。通俗的讲物理层规定我们用嘴巴还是肢体交流,协议层规定我们用中文还是英文交流。

1.1 设备之间通信的方式

        一般情况下,设备之间的通信方式可以分成并行通信和串行通信两种。它们的区别是:

并行通信串行通信
传输原理数据各个位同时传输数据按位顺序传输
优点速度快占用引脚资源少
缺点占用引脚资源多速度相对较慢

1.2 串行通信的分类

1.2.1 按照数据传送方向

        单工数据传输只支持数据在一个方向上传输
        半双工允许数据在两个方向上传输。但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;它不需要独立的接收端和发送端,两者可以合并一起使用一个端口。
        全双工允许数据同时在两个方向上传输。因此,全双工通信是两个单工通信方式的结合,需要独立的接收端和发送端。

1.2.2 按照通信方式

        同步通信带时钟同步信号传输。比如:SPI,IIC通信接口。
        异步通信不带时钟同步信号。比如:UART(通用异步收发器),单总线。
        在同步通讯中,收发设备上方会使用一根信号线传输信号,在时钟信号的驱动下双方进行协调,同步数据。例如,通讯中通常双方会统一规定在时钟信号的上升沿或者下降沿对数据线进行采样。在异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些用于同步的信号位,或者将主题数据进行打包,以数据帧的格式传输数据。通讯中还需要双方规约好数据的传输速率(也就是波特率)等,以便更好地同步。常用的波特率有4800bps、9600bps、115200bps等。
        在同步通讯中,数据信号所传输的内容绝大部分是有效数据,而异步通讯中会则会包含数据帧的各种标识符,所以同步通讯效率高,但是同步通讯双方的时钟允许误差小,稍稍时钟出错就可能导致数据错乱,异步通讯双方的时钟允许误差较大。

1.3 常见的串行通信接口

通信标准

引脚说明

通信方式

通信方向

  UART

(通用异步收发器)

TXD:发送端

RXD:接受端

GND:公共地

异步通信

全双工

  单总线

(1-wire)

DQ:发送/接受端

异步通信

半双工

SPI

SCK:同步时钟

MISO:主机输入,从机输出

MOSI:主机输出,从机输入

同步通信

全双工

I2C

SCL:同步时钟

SDA:数据输入/输出端

同步通信

半双工

2 STM32串口通信基础

        STM32的串口通信接口有两种,分别是:UART(通用异步收发器)、USART(通用同步异步收发器)。而对于大容量STM32F10x系列芯片,分别有3个USART和2个UART
        UART引脚连接方法:RXD-----数据输入引脚,数据接受TXD-----数据发送引脚,数据发送

        对于两个芯片之间的连接,两个芯片GND共地,同时TXD和RXD交叉连接。这里的交叉连接的意思就是,芯片1的RxD连接芯片2的TXD,芯片2的RXD连接芯片1的TXD。这样,两个芯片之间就可以进行TTL电平通信了

        若是芯片与PC机(或上位机)相连,除了共地之外,就不能这样直接交叉连接了。尽管PC机和芯片都有TXD和RXD引脚,但是通常PC机(或上位机)通常使用的都是RS232接口(通常为DB9封装),因此不能直接交叉连接。RS232接口是9针(或引脚),通常是TxD和RxD经过电平转换得到的。故,要想使得芯片与PC机的RS232接口直接通信,需要也将芯片的输入输出端口也电平转换成RS232类型,再交叉连接。
        经过电平转换后,芯片串口和rs232的电平标准是不一样的:
                单片机的电平标准(TTL电平)+5V表示1,0V表示0RS232的电平标准(负逻辑)+15/+13 V表示0,-15/-13表示1。
        RS-232通讯协议标准串口的设备间通讯结构图如下: 

        所以单片机串口与PC串口通信就应该遵循下面的连接方式:在单片机串口与上位机给出的rs232口之间,通过电平转换电路(如下面图中的Max232芯片) 实现TTL电平与RS232电平之间的转换。

        STM32的UART特点:
                1)全双工异步通信;
                2)分数波特率发生器系统,提供精确的波特率。
发送和接受共用的可编程波特率,最高可达4.5Mbits/s;
                3)可编程的数据字长度(8位或者9位);
                4)可配置的停止位(支持1或者2位停止位);
                5)可配置的使用DMA多缓冲器通信;
                6)单独的发送器和接收器使能位;

                7)检测标志:① 接受缓冲器  ②发送缓冲器空 ③传输结束标志;
                8)多个带标志的中断源,触发中断;
                9)其他:校验控制,四个错误检测标志。

3 串口通信过程

3.1 USART简介

        USART(通用同步异步收发器)是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。有别于 USART 还有一个UART,它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输出,我们平时用的串口通信基本都是 UART。USART 在 STM32 应用最多莫过于“打印”程序信息,一般在硬件设计时都会预留一USART 通信接口连接电脑,用于在调试程序是可以把一些调试信息“打印”在电脑端的串口调试助手工具上,从而了解程序运行是否正确、如果出错哪具体哪里出错等等。

        STM32中一共有5个USART,如示:

         USART的USB转串口原理图如下:

        USART1的发送和接收端口是事先连接好的,如果要使用其他USART只需要将相应的发送接收端口按图连接好即可。
        USART有多个中断请求事件

3.2 STM32中UART参数

        串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备的RXD接口,通讯双方的数据包格式要规约一致才能正常收发数据。STM32中串口异步通信需要定义的参数:起始位、数据位(8位或者9位)、奇偶校验位(第9位)、停止位(1,15,2位)、波特率设置。由于异步通信中没有时钟信号,所以接收双方要约定好波特率,即每秒传输的码元个数,以便对信号进行解码,常见的波特率有4800、9600、115200等。STM32中波特率的设置通过串口初始化结构体来实现。
        UART串口通信的数据包以帧为单位,常用的帧结构为:1位起始位+8位数据位+1位奇偶校验位(可选)+1位停止位。如下图所示:

        数据位的首尾分别是起始位停止位数据位起始信号一个逻辑0的数据位表示,停止位信号可由0.5、1、1.5、2个逻辑1的数据位表示,双方需约定一致。STM32中起始和停止信号的设置也是通过串口初始化结构体来实现。奇偶校验位分为奇校验偶校验两种,是一种简单的数据误码校验方法。奇校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为奇数;偶校验是指每帧数据中,包括数据位和奇偶校验位的全部9个位中1的个数必须为偶数。
        校验方法除了奇校验(odd)、偶校验(even)之外,还可以有:0 校验(space)、1 校验(mark)以及无校验(noparity)。0/1校验:不管有效数据中的内容是什么,校验位总为0或者1。

3.3 UART(USART)框图

        这个框图分成上、中、下三个部分。本文大概地讲述一下各个部分的内容,具体的可以看《STM32中文参考手册》中的描述。
        框图的上部分,数据从RX进入到接收移位寄存器,后进入到接收数据寄存器,最终供CPU或者DMA来进行读取;数据从CPU或者DMA传递过来,进入发送数据寄存器,后进入发送移位寄存器,最终通过TX发送出去。然而,UART的发送和接收都需要波特率来进行控制的,波特率是怎样控制的呢?这就到了框图的下部分,在接收移位寄存器、发送移位寄存器都还有一个进入的箭头,分别连接到接收器控制、发送器控制。而这两者连接的又是接收器时钟、发送器时钟。也就是说,异步通信尽管没有时钟同步信号,但是在串口内部,是提供了时钟信号来进行控制的。而接收器时钟和发送器时钟有是由什么控制的呢?
        可以看到,接收器时钟和发送器时钟又被连接到同一个控制单元,也就是说它们共用一个波特率发生器。同时也可以看到接收器时钟(发生器时钟)的计算方法、USRRTDIV的计算方法。
        这里需要知道一个知识点:
                1)UART1的时钟:PCLK2(高速);
                2)UART2、UART3、UART4的时钟:PCLK1(低速)。

串口相关配置寄存器

4.1 状态寄存器(USART_SR)

        状态寄存器适用于检测串口此时所处的状态。它能够检测到的状态有:发送寄存器空位、发送完成位、读数据寄存器非空位、检测到主线空闲位、过载错误为等等。

        这边主要关注两个位RXNE和TC(第5、6两位)。
        RXNE(读数据寄存器非空)当该位被置1的时候,就是提示已经有数据被接收到了,并且可以读出来了(即RDR移位寄存器中的数据被转移到USART_DR寄存器中)。这时候要做的就是尽快读取USART_DR,从而将该位清零,也可以向该位写0,直接清除。
        TC(发送完成)当该位被置1的时候,表示USART_DR内的数据已经被发送完成了。如果设置了这个位的中断,则会产生中断。该位也有两种清零方式:读USART_SR,写USART_DR;直接向该位写0。

4.2 数据寄存器(USART_DR)

        USART_DR实际是包含了两个寄存器,一个专门用于发送的TDR,一个专门用于接收的RDR。进行发送数据操作时,往USART_DR写入数据会自动存储在TDR内;当进行读取数据操作时,向USART_DR读取数据会自动提取RDR数据。
        串行通信时一位一位传输的,所以TDR和RDR寄存器都是介于系统总线和移位寄存器间的;发送数据时把TDR内容转移到发送移位寄存器上,接收数据时则是把接收到的每一位顺序保存在接收移位寄存器内进而转移到RDR。

4.3 波特率寄存器 (USART_BRR)

        波特率寄存器包括定义了两个部分:DIV_Mantissa(整数部分)DIV_Fraction(小数部分)

4.4 控制寄存器(USART_CRx)

        控制寄存器主要是设置USART使能、检验控制使能、校验选择(奇校验偶校验)、PE中断使能、发送缓冲区空中断使能、发送完成中断使能、接收缓冲区非空使能、发送使能、接受使能、字长等等。

5 USART外设引脚复用

        当使用USART的时候,GPIO需要引脚复用,下图介绍了USART的引脚设置:

6 波特率计算方法

        学习波特率之前,首先了解一下通讯速率。通讯速率通常是以比特率来表示,即每秒钟传输的二进制位数,单位为比特每秒(bit/s)。容易和比特率混淆的概念是“波特率”,它表示每秒传输了多少码元
        码元是通讯信号调制的概念,时间间隔相同的符号来表示一个二进制数字,这样的信号就称为码元。如常见的通讯传输中:用0V表示数字0,5V表示数字1,那么一个码元可以表示两种状态0和1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;若传输中,有0V、2V、4V和6V分别表示00、01、10、11,那么每个码元可以表示四种状态,两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表示比特率,其实二者是有区别的。 
        异步通讯由于没有时钟信号,所以两个通讯设备需要规约好波特率,即每个码元的长度,以便对信号进行解码。常见的波特率为4800、9600、115200。

        上面的公式中,fpclkx是给串口的时钟(PCLK1用于USART2、3、4、5,PCLK2用于USART1);USARTDIV是一个无符号定点数。我们只要得到USARTDIV的值,就可以计算出波特率。

7 串口操作相关库函数

7.1 1个初始化函数

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

        作用:用于串口波特率,数据字长,奇偶校验,硬件流控以及收发使能等配置的初始化。

7.2 2个使能函数

void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);

        作用:前者使能串口,后者使能串口的相关中断。

7.3 2个数据收发函数

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

        作用:前者发送数据到串口,后者从串口接收数据。

7.4 4个状态位函数

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

        作用:前两者获取(或清除)状态标志位,后两者为获取(或清除)中断状态标志位。

8 串口操作的一般步骤

        1)GPIO时钟使能,串口时钟使能。调用函数:RCC_APB2PeriphClockCmd();
        2)串口复位(这一步不是必须的)。调用函数:USART_DeInit();
        3)GPIO外设功能下的端口模式设置。调用函数:GPIO_Init();Tx(发送引脚)配置为复用推挽输出(GPIO_Mode_AF_PP)用来发送数据,Rx(接收引脚)配置为浮空输入(GPIO_Mode_IN_FLOATING)用来接收数据。
        4)串口参数初始化。调用函数:USART_Init();
        5)开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)。调用函数:NVIC_Init();USART_ITConfig();
        6)使能串口。调用函数:USART_Cmd();
        7)编写中断处理函数。调用函数:USARTx_IRQHandler();
        8)串口数据收发。调用函数:USART_SendData();USART_ReceiveData();
        9)串口传输状态获取。调用函数:USART_GetFlagStatus();USART_ClearITPendingBit();

        下面按照这个一般步骤来进行一个简单的串口程序:

void My_USART1_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStrue;
	USART_InitTypeDef USART_InitStrue;
	NVIC_InitTypeDef NVIC_InitStrue;

		// 打开串口GPIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//GPIO端口使能
	
    	// 将USART Tx的GPIO配置为推挽复用模式  USART1_TX   GPIOA.9
	GPIO_InitStrue.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStrue.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
	GPIO_Init(GPIOA,&GPIO_InitStrue);

        // 将USART Rx的GPIO配置为浮空输入模式  USART1_RX	  GPIOA.10
	GPIO_InitStrue.GPIO_Mode=GPIO_Mode_IN_FLOATING;
	GPIO_InitStrue.GPIO_Pin=GPIO_Pin_10;
	GPIO_InitStrue.GPIO_Speed=GPIO_Speed_10MHz;
	GPIO_Init(GPIOA,&GPIO_InitStrue);

		// 打开串口外设的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口端口使能

	    // 配置串口的工作参数
	    // 配置波特率
	USART_InitStrue.USART_BaudRate=115200;
	    // 配置硬件流控制
	USART_InitStrue.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
	    // 配置工作模式,收发一起
	USART_InitStrue.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
	    // 配置校验位无奇偶校验位
	USART_InitStrue.USART_Parity=USART_Parity_No;
	    // 配置停止位一个停止位
	USART_InitStrue.USART_StopBits=USART_StopBits_1;
	    // 配置数据字长8位数据格式
	USART_InitStrue.USART_WordLength=USART_WordLength_8b;
		// 完成串口的初始化配置
	USART_Init(USART1,&USART_InitStrue);//
	
		// 使能串口
	USART_Cmd(USART1,ENABLE);
	    //开启接收中断
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
	  
        //配置USART为中断源
	NVIC_InitStrue.NVIC_IRQChannel=USART1_IRQn;
        //使能中断
	NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE;
        //抢断优先级
	NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=1;
        //子优先级
	NVIC_InitStrue.NVIC_IRQChannelSubPriority=1;
        //初始化配置NVIC
	NVIC_Init(&NVIC_InitStrue);
	
}

//串口1中断服务程序
void USART1_IRQHandler(void)
{
	u8 Res;
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     } 
}
 
 int main(void)
 {	
    /* 嵌套向量中断控制器组选择 */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

	My_USART1_Init();

	while(1);
	 
 }

        usrt_init函数

                1)此处的GPIO端口模式设置,是在端口复用情况下的端口模式设置。至于在复用功能下,GPIO的模式怎么设置,可以查看手册《STM32中文参考手册》p110的内容;
                2)USART_BaudRate波特率的设定是直接写值进去的,MDK5是没有预设的宏定义来选择的;
                3)USART_Mode模式选择用USART_Mode_Tx|USART_Mode_Rx来表示发送使能和接收使能;
                4)NVIC_IRQChannel中断通道,这是在stm32f10x.h开头部分以IRQn结尾的宏定义。

        USART_ITConfig函数

                1)使能中断配置函数。
                2)我们配置了接收数据寄存器非空中断。我们可以配置很多类型中断,在ST提供的标准库函数中看到。

/**
  * @摘要   启用或禁用指定的USART中断。
  * @参数   USARTx: 其中x可以是1、2、3、4、5或6,以选择USART或UART外设。
  * @参数   USART_IT: 指定要启用或禁用的USART中断源。
  *          该参数可以是以下值之一:
  *            @arg USART_IT_CTS:  CTS更改中断
  *            @arg USART_IT_LBD:  LIN Break检测中断
  *            @arg USART_IT_TXE:  传输数据寄存器空中断
  *            @arg USART_IT_TC:   传输完成中断
  *            @arg USART_IT_RXNE: 接收数据寄存器不为空中断
  *            @arg USART_IT_IDLE: 空闲线路检测中断
  *            @arg USART_IT_PE:   奇偶校验错误中断
  *            @arg USART_IT_ERR:  错误中断(帧错误、噪声错误、超限错误)
  * @参数    NewState: 指定USARTx中断的新状态。
  *          取值为:“启用”或“禁用”。
  * @返回值  无
  */

        USART1_IRQHandlar函数

                1)USART1_IRQHandlar函数是中断处理函数,不能随意定义的,需要遵循MDK的定义。这些函数的声明在启动文件startup_stm32f10x_hd.s文件中,可以在其中找到中断处理函数的名称。
                2)中断处理函数中首先进行状态位判断,这是非常有必要的。因为可能我们在串口的中断设置中,设置了不止一处的中断,但是每个中断进入的中断处理函数都是同一个,这就要求我们要在中断处理函数中通过状态位的判断再进行接下来的操作。

if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) 

        这里通过这个判断来确定是否接收中断。这个函数的返回值是ITStatus类型,它的定义是:

typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;

        SET代表着1(接收到中断),RESET代表着0(未接收中断)。

        除了中断处理函数的ITStatus需要判断之外,通常发送数据、接收数据之后也需要判断。比如,下面一段程序:

USART_SendData(USART1, USART_RX_BUF[t]);     //向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);    //等待发送结束

        即运用while循环,在向串口发送数据之后,通过判断等待发送结束。
        这段程序还有一个问题曾经困扰了我很久很久都没有领悟清楚:
        在中断处理函数中,使用一下程序来接收数据:

	u8 res;
    res= USART_ReceiveData(USART1); 

        由于res是一个u8类型的数,若一次性向串口发送很多的数据,串口使用此程序进行接收的时候,一个u8很多装不下。而这个函数中使用的是if判断,如果一次装不下,if判断程序就走一遍,怎么把所有的数据全部接受呢?

        解答:其实我们看一下RXNE标志位引发的中断,当RDR移位寄存器中的数据被转移到USART_DR寄存器中时,也就是有数据可以被接收到的时候,该位置1,引发中断,进入中断处理函数。但是我们在这个中断处理函数中,并没有和其他中断一样做清除中断位的操作,这就导致该位一直是1,不断地进入中断。那么什么时候停止呢?当数据接收完毕了,此时该位清零。

9 传输数据的函数

        开发板与上位机之间的数据传输可以有多种方法,下面一一介绍:

9.1 发送一个字节

        以USART_SendData(pUSARTx,ch); 函数为基础建立的函数可以向上位机发送一个字节的数据,利用FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG) 读取发送数据寄存器的状态来 等待发送寄存器将数据成功发送。

void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

9.2 发送字符串

        本质是利用上面的字节发送函数逐位发送字符串中的内容。

void USART_SendString(USART_TypeDef * pUSARTx, char *str)
{
	unsigned int   k=0;
	while(*(str+k)!='\0')
	{
		USART_SendData(pUSARTx, *(str+k));
		/* 等待发送数据寄存器为空 */
		while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
		k++;
	}
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);   /* TC:传输完成标志 */
}

9.3 重定向printf函数发送字符串

        在STM32测试串口的时候经常需要在开发板和上位机之间传输数据,我们可以用c语言中的printf()函数和getchar()函数来简化传输。

        printf()为例printf()函数实际上是一个,最终调用的是 fputc(int ch,FILE *f)这个函数来执行输出的,所以我们需要修改这个函数,使函数向串口输出,这样当再次引用printf()函数时,printf()就是通过串口向上位机发送数据的一个函数了。那么如何修改printf()呢?加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。所以我们可以在别的地方定义一个相同名字的函数,而不必也尽量不要修改之前的函数。
        修改fputc函数如示:

/* 重定向printf函数 */
int fputc(int ch, FILE *f)
{
	USART_SendData( DEBUG_USARTx,  (uint8_t) ch);
	/* 等待发送完毕 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET); 
	return ch;
}

        修改完成后需要勾选Target设置中的Use Micro LIB选项,MicroLib提供了一个有限的stdio子系统,所以还要再头文件中包含#include <stdio,h>

        getchar()函数也是如此:

/* 重定向scanf函数 */
int getc(FILE *f)
{
	/* 等待串口输入数据 */
	/* 有了这个等待就不需要在中断中进行了 */
	while(USART_GetFlagStatus(DEBUG_USARTx , USART_FLAG_RXNE)==RESET);
	/* 返回值进行强制类型转换 */
	return (int)USART_ReceiveData(DEBUG_USARTx);
}

        重定向后的printf()函数功能强大,具有向串口调试助手打印数据的功能,使用方法和c语言时一样,比如printf("串口实验\n");就可以将“串口实验”这句话发送到上位机中,而且换行符“\n”还具有换行作用。

/* 重定向printf函数 */
int fputc(int ch, FILE *f)
{
	USART_SendData( DEBUG_USARTx,  (uint8_t) ch);
	/* 等待发送完毕 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET); 
	return ch;
}

9.4 重定向getchar函数接收字符

        具体操作与重定向后的printf函数类似,比如可以通过如下代码向上位机发送已经接收到的数据

x=getchar();
printf("接收到的字符是:%c\n",x);

        重定义如下:

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
}

        在使用此函数作为接收数据时记得关闭串口得接收中断!!!

9.5 通过中断接收

        在stm32f10x_it.c中编写USART1中断源相对应得中断函数,利用了固件库函数中的:
                1)USART_ReceiveData(DEBUG_USARTx);接收函数
                2)USART_SendData(DEBUG_USARTx, x);发送函数
                3)USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE);判断标志位函数

/* #define  DEBUG_USART_IRQn         USART1_IRQn 
   #define  DEBUG_USART_IRQHandler   USART1_IRQHandler */
void DEBUG_USART_IRQHandler(void)
{
	uint16_t  x;
	/* 判断是否收到中断信号 */
	if(USART_GetITStatus(DEBUG_USARTx, USART_IT_RXNE) == SET)
	{
		x = USART_ReceiveData(DEBUG_USARTx);
		USART_SendData(DEBUG_USARTx, x);
	}
}

10 开发板与上位机的连接

开发板与上位机之间通过USB线连接,所以在上位机上要配置一个USB转串口的驱动,以便把USB传输过来的电平转换为TTL电平,TTL电平才能与串口调试助手建立联系。一般使用CH341驱动作为win10下的USB转串口,驱动安装成功的情况下接入USB会在计算机的设备管理器的端口中发现串口:

  • 21
    点赞
  • 118
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值