基于STM32F103ZET6库函数串口实验

本次实验实现如下功能

STM32F1 通过串口和上位机的对话,STM32F1 在收到上位机发过来的字符串后,原原本本的返回给上位机。

接下来主要从库函数操作层面结合寄存器的描述,告诉你如何设置串口,以达到最基本的通信功能。 利用串口 1 不停的打印信息到电脑上,同时接收从串口发过来的数据,把发 送过来的数据直接送回给电脑。通过 USB 串口和电脑通信。

串口设置的一般步骤可以总结为如下几个步骤

  1. 串口时钟使能,GPIO 时钟使能
  2. 串口复位
  3. GPIO 端口模式设置
  4. 串口参数初始化
  5. 开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
  6. 使能串口
  7. 编写中断处理函数

与串口基本配置直接相关的几个固件库函数

这些函数和 定义主要分布在 stm32f10x_usart.h 和 stm32f10x_usart.c 文件中。

1.串口时钟使能。

串口是挂载在 APB2 下面的外设,所以使能函数为:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1)

2.串口复位。

当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配置 这个外设达到让其重新工作的目的。一般在系统刚开始配置外设的时候,都会先执行复位该外 设的操作。复位的是在函数 USART_DeInit()中完成:

void USART_DeInit(USART_TypeDef* USARTx);//串口复位 

3.串口参数初始化。

串口初始化是通过 USART_Init()函数实现的

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)

这个函数的第一个入口参数是指定初始化的串口标号,这里选择 USART1。
第二个入口参数是一个 USART_InitTypeDef 类型的结构体指针,这个结构体指针的成员变量用 来设置串口的一些参数。一般的实现格式为:

USART_InitStructure.USART_BaudRate = bound;                 						//波特率设置; 
USART_InitStructure.USART_WordLength = USART_WordLength_8b;							//字长为 8 位数据格式 
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);                   						//初始化串口 

4…数据发送与接收。

STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是 一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收 到数据的时候,也是存在该寄存器内。
STM32 库函数操作 USART_DR 寄存器发送数据的函数是:
通过该函数向串口寄存器 USART_DR 写入一个数据。

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 

STM32 库函数操作 USART_DR 寄存器读取串口接收到的数据的函数是:

uint16_t USART_ReceiveData(USART_TypeDef* USARTx); 

5.串口状态。

在我们固件库函数里面,读取串口状态的函数是:

FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG)

这个函数的第二个入口参数非常关键,它是标示我们要查看串口的哪种状态,比如上面讲解的 RXNE(读数据寄存器非空)以及 TC(发送完成)。
例如我们要判断读寄存器是否非空(RXNE),操作库函数的方法是:

USART_GetFlagStatus(USART1, USART_FLAG_RXNE); 

我们要判断发送是否完成(TC),操作库函数的方法是:

USART_GetFlagStatus(USART1, USART_FLAG_TC); 

这些标识号在 MDK 里面是通过宏定义定义的:

#define USART_IT_PE                          ((uint16_t)0x0028) 
#define USART_IT_TXE                         ((uint16_t)0x0727) 
#define USART_IT_TC                          ((uint16_t)0x0626) 
#define USART_IT_RXNE                        ((uint16_t)0x0525) 
#define USART_IT_IDLE                        ((uint16_t)0x0424) 
#define USART_IT_LBD                         ((uint16_t)0x0846) 
#define USART_IT_CTS                         ((uint16_t)0x096A) 
#define USART_IT_ERR                         ((uint16_t)0x0060) 
#define USART_IT_ORE                         ((uint16_t)0x0360) 
#define USART_IT_NE                          ((uint16_t)0x0260) 
#define USART_IT_FE                          ((uint16_t)0x0160) 

6.串口使能。

串口使能是通过函数 USART_Cmd()来实现的

USART_Cmd(USART1, ENABLE);                    //使能串口 

7.开启串口响应中断。

有些时候当我们还需要开启串口中断,那么我们还需要使能串口中 断,使能串口中断的函数是:

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

这个函数的第二个入口参数是标示使能串口的类型,也就是使能哪种中断,因为串口的中断类 型有很多种。比如在接收到数据的时候(RXNE 读数据寄存器非空),我们要产生中断,那么我 们开启中断的方法是:

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断 

我们在发送数据结束的时候(TC,发送完成)要产生中断,那么方法是:

USART_ITConfig(USART1,USART_IT_TC,ENABLE); 

8.获取相应中断状态。

当我们使能了某个中断的时候,当该中断发生了,就会设置状态寄 存器中的某个标志位。经常我们在中断处理函数中,要判断该中断是哪种中断,使用的函数是:

ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT) 

比如我们使能了串口发送完成中断,那么当中断发生了, 我们便可以在中断处理函数中调用这 个函数来判断到底是否是串口发送完成中断,方法是:

USART_GetITStatus(USART1, USART_IT_TC) 

返回值是 SET,说明是串口发送完成中断发生。

硬件设计

1) 指示灯 DS0
2) 串口 1
本实验用到的串口 1 与 USB 串口并没有在 PCB 上连接在一起, 需要通过跳线帽来连接一下。这里我们把 P3 的 RXD 和 TXD 用跳线帽与 PA9 和 PA10 连接起来。
在这里插入图片描述

软件设计

//初始化 IO 串口 1 
void uart_init(u32 bound)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	/*1.使能USART1,GPIOA时钟*/ 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	

	/*2.串口复位,这里没有 */
	
	/*3.GPIO 端口模式设置 */
	//USART1_TX   GPIOA.9
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 					//PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);						//初始化GPIOA.9
   
	//USART1_RX	  GPIOA.10初始化
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;					//PA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;		//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);						//初始化GPIOA.10  

	/*4.串口参数初始化 */
	//USART 初始化设置
	USART_InitStructure.USART_BaudRate = bound;											//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;							//字长为8位数据格式
	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); 											//初始化串口1

	/*5.初始化 NVIC */
	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;							//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;									//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;										//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);														//根据指定的参数初始化VIC寄存器
	
	/*5.开启中断 */
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);										//开启串口接受中断
	
	/*6.使能串口 */
	USART_Cmd(USART1, ENABLE);                    										//使能串口1 

}

① 串口时钟使能,GPIO 时钟使能
② 串口复位
③ GPIO 端口模式设置
④ 串口参数初始化
⑤ 初始化 NVIC 并且开启中断
⑥ 使能串口

usart.c

#include "sys.h"
#include "usart.h"	  


//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
}; 
FILE __stdout;       


//重定义fputc函数 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0)
	{
		;																			//循环发送,直到发送完毕   
	}
    USART1->DR = (u8) ch;      
	return ch;
} 

 
#if EN_USART1_RX   																	//如果使能了接收
	
u8 USART_RX_BUF[USART_REC_LEN];     												//接收缓冲,最大USART_REC_LEN个字节.

//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA=0;       															//接收状态标记	  
  
void uart_init(u32 bound)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	 
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);		//使能USART1,GPIOA时钟
  
	/*USART1_TX   GPIOA.9*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; 										//PA.9
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;									//复用推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);											//初始化GPIOA.9

	/*USART1_RX	  GPIOA.10*/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;										//PA.10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;							//浮空输入
	GPIO_Init(GPIOA, &GPIO_InitStructure);											//初始化GPIOA.10  

	//Usart1 NVIC 配置
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;						//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;								//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;									//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);													//根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置
	USART_InitStructure.USART_BaudRate = bound;										//串口波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;						//字长为8位数据格式
	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); 										//初始化串口1
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);									//开启串口接收中断
	USART_Cmd(USART1, ENABLE);                    									//使能串口1 

}

/*串口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;												//接收数据错误,重新开始接收
					}
				}		 
			}
		}   		 
    } 
} 
#endif	

如果EN_USART1_RX等于1下面代码就执行
定义缓冲区和接收状态标记,接收标记为16位
0到13位为接收到的有效数目
14位为接收到0x0d的标记
15位为接收到0x0a的标记(即接收完成)

串口初始化这里按着步骤配置就好

下面来说一下串口1的中断服务函数
在这里插入图片描述
第一步读取接收的数据存到Res(这里是8位,每8位接收一次)
第二步如果还没读到0x0d就存进缓冲区USART_RX_BUF[USART_RX_STA&0X3FFF]=Res;
USART_RX_STA&0X3FFF,这里限制只有用到0到13位
if(USART_RX_STA>(USART_REC_LEN-1))这里也限制了缓冲区的大小
第三步如果读到了0x0d就改变14位的状态USART_RX_STA|=0x4000;让14位置1
第四步接收到0x0d下一位就判断是否是0x0a,如果是就改变15位的状态USART_RX_STA|=0x8000;让15位置1,即接收完成

这里还有一个重定向函数int fputc(int ch, FILE *f)
此函数原本是将字符ch打印到文件指针stream所指向的文件流去的,现在我们不需要打印到文件流,而是打印到串口1

usart.h

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 

#define USART_REC_LEN  			200  	//定义最大接收字节数 200
#define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收
	  	
extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
extern u16 USART_RX_STA;         		//接收状态标记	

void uart_init(u32 bound);
#endif

extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; //接收状态标记
extern声明给其它的文件使用这些变量

main.c

#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
 

int main(void)
{		
	u16 t;  
	u16 len;	
	u16 times=0;
	delay_init();	    	 																	//延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 											//设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 																		//串口初始化为115200
 	LED_Init();			     																	//LED端口初始化
	KEY_Init();          																		//初始化与按键连接的硬件接口
 	
	while(1)
	{
		if(USART_RX_STA&0x8000)
		{					   
			len=USART_RX_STA&0x3fff;															//得到此次接收到的数据长度
			printf("\r\n您发送的消息为:\r\n\r\n");
			for(t=0;t<len;t++)
			{
				USART_SendData(USART1, USART_RX_BUF[t]);										//向串口1发送数据
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);							//等待发送结束
			}
			printf("\r\n\r\n");																	//插入换行
			USART_RX_STA=0;
		}
		else
		{
			times++;
			if(times%5000==0)
			{
				printf("\r\n串口实验\r\n");
				printf("是北豼不太皮吖\r\n\r\n");
			}
			if(times%200==0)
			{
				printf("请输入数据,以回车键结束,或勾上发送新行\r\n");
			}
			if(times%30==0)
			{
				LED0=!LED0;																		//闪烁LED,提示系统正在运行.
			}
			delay_ms(10);   
		}
	}	 
}

if(USART_RX_STA&0x8000)判断是否接收完成
如果接收完成就把数据发回去
USART_RX_STA为传过来实际的字节数
len=USART_RX_STA&0x3fff;这里把14,15位的标记位去掉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是北豼不太皮吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值