通信的两种方式:
并行通信
  -传输原理:数据各个位同时传输。
  -优点:速度快
   -缺点:占用引脚资源多

串行通信
  -传输原理:数据按位顺序传输。
  -优点:占用引脚资源少
  -缺点:速度相对较慢

串行通信分类(按照数据传送方向)
  单工(a):
     数据传输只支持数据在一个方向上传输

  半双工(b):
     允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;

  全双工(c):
     允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
在这里插入图片描述

串行通信的通信方式

  同步通信(带时钟同步信号传输):

     SPI,IIC通信接口

  异步通信(不带时钟同步信号传输):

    UART(通用异步收发器),单总线

在这里插入图片描述

STM32的串口通信接口
  UART:通用异步收发器。
  USART:通用同步异步收发器。
    大容量STM32F10x系列芯片,包含3个USART和2个UART

UART异步通信方式引脚连接方法:
  -RXD:数据输入引脚。数据接受。
  -TXD:数据发送引脚。数据发送。
在这里插入图片描述
在这里插入图片描述

UART异步通信方式特点:
  全双工异步通信。
  分数波特率发生器系统,提供精确的波特率。
     -发送和接受共用的可编程波特率,最高可达4.5Mbits/s
  可编程的数据字长度(8位或者9位);
  可配置的停止位(支持1或者2位停止位);
  可配置的使用DMA多缓冲器通信。
  单独的发送器和接收器使能位。
  检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志
  多个带标志的中断源。触发中断。
  其他:校验控制,四个错误检测标志。
在这里插入图片描述

USART的寄存器可以参考STM32中文参考手册的第25部分中的第六小节。
《STM32F1开发指南-库函数版本》- 5.3 usart串口文件夹介绍 -第9章 串口实验
常用的串口相关寄存器
  USART_SR状态寄存器
  USART_DR数据寄存器
  USART_BRR波特率寄存器 (填写下面计算后的数值)
  USART_CR1控制寄存器
根据该图可了解串行通信的相关配置。根据下半图,可得到波特率的计算方法:

在这里插入图片描述

在这里插入图片描述

串口操作相关库函数

void USART_Init(); //串口初始化:波特率,数据字长,奇偶校验,硬件流控以及收发使能
void USART_Cmd();//使能串口
void USART_ITConfig();//使能相关中断

void USART_SendData();//发送数据到串口,DR
uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据

FlagStatus USART_GetFlagStatus();//获取状态标志位,SR
void USART_ClearFlag();//清除状态标志位,SR
ITStatus USART_GetITStatus();//获取中断状态标志位,SR
void USART_ClearITPendingBit();//清除中断状态标志位,SR

实验4串口实验的代码中FWLib文件夹下stm32f10x_usart.c下的stm32f10x_usart.h下,可找到相关函数的声明与定义,然后可再进行追溯。

串口配置一般步骤
  ①串口时钟使能,GPIO时钟使能:RCC_APB2PeriphClockCmd();
  ②串口复位:USART_DeInit(); 这一步不是必须的
  ③GPIO端口模式设置:GPIO_Init(); 模式设置为GPIO_Mode_AF_PP
  ④串口参数初始化:USART_Init();
  ⑤开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
    NVIC_Init();
    USART_ITConfig();
  ⑥使能串口:USART_Cmd();
  ⑦编写中断处理函数:USARTx_IRQHandler();
  ⑧串口数据收发:
    void USART_SendData();//发送数据到串口,DR
    uint16_t USART_ReceiveData();//接受数据,从DR读取接受到的数据
⑨串口传输状态获取:
    FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
    void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

具体实现:
第一步:新建模版,并使能串口时钟和GPIO时钟。
  跟之前一样,先建立一个简单的模版。
  在USER文件夹下找到system_stm32f10x.c下的stm32f10x_rcc.h.中找到void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);之前就知道这个函数是用来使能的,然后先Go To xxx找到函数的定义后,在函数中assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));查看函数中的第一个参数RCC_APB2PeriphGo To xxx后我们可以看到,该函数即可使能GPIOA又可使能USART1。故使能语句可这么编写.

void My_USART1_Init(){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);
}

这边要注意模式,模式设置是要根据STM32中文参考手册中的第8张8.1.11中关于外设的GPIO配置,如下图:


在这里插入图片描述

第三步:串口参数初始化和使能串口
  在USER文件夹下找到system_stm32f10x.c下的stm32f10x_usart.h.中找到void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);、void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);,程序可修改为:

void My_USART1_Init(){
	GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO结构体
	USART_InitTypeDef USART_InitStructure;//定义USART结构体
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);	//使能GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);	//使能串口时钟
	
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;	//设置为复用推挽输出
	GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;	//引脚9
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;	//速度10MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);	//GPIOA模式设置
	
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;	//引脚10
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;	//速度10MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);	//GPIOA模式设置
	
	
	USART_InitStructure.USART_BaudRate=115200;//波特率
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;//硬件流:无
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//TX和RX都使能故使用|
	USART_InitStructure.USART_Parity=USART_Parity_No;//奇偶校验位:无
	USART_InitStructure.USART_StopBits=USART_StopBits_1;//停止位:1
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长:8位
	
	
	USART_Init(USART1,&USART_InitStructure);//串口参数初始化
	USART_Cmd(USART1,ENABLE);	//使能串口
}

第四步:开启中断并且初始化NVIC
  在FWLIB文件夹下找到misc下找到void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup),函数中找到;assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));,在Go To xxx,查看填写格式,选择NVIC_PriorityGroup_2,即:两位响应优先级和两位抢占优先级。
  在USER文件夹下找到system_stm32f10x.c下的stm32f10x_usart.h.中找到void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)

#include "stm32f10x.h"

void My_USART1_Init(){
	GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO结构体
	USART_InitTypeDef USART_InitStructure;//定义USART结构体
	NVIC_InitTypeDef NVIC_InitStructure;//定义NVIC结构体
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);	//使能GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);	//使能串口时钟
	
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;	//设置为复用推挽输出
	GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;	//引脚9
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;	//速度10MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);	//GPIOA模式设置
	
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;	//引脚10
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;	//速度10MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);	//GPIOA模式设置
	
	
	USART_InitStructure.USART_BaudRate=115200;//波特率
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;//硬件流:无
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//TX和RX都使能故使用|
	USART_InitStructure.USART_Parity=USART_Parity_No;//奇偶校验位:无
	USART_InitStructure.USART_StopBits=USART_StopBits_1;//停止位:1
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长:8位
	
	
	USART_Init(USART1,&USART_InitStructure);//串口参数初始化
	USART_Cmd(USART1,ENABLE);	//使能串口
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//接收缓冲区非空就接收USART_IT_RXNE,打开中断服务
	
	
	NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;//在stm32f10x.h头文件中上面部分有些IRQn。
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//开不开器通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//设置抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//设置抢占子优先级
	NVIC_Init(&NVIC_InitStructure);
}

 int main(void)
 {	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	 
 }

第五步:编写中断处理函数

  在文件stm32f10x_usart.h文件下打开:ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);

#include "stm32f10x.h"

void My_USART1_Init(){
	GPIO_InitTypeDef GPIO_InitStructure; //定义GPIO结构体
	USART_InitTypeDef USART_InitStructure;//定义USART结构体
	NVIC_InitTypeDef NVIC_InitStructure;//定义NVIC结构体
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);	//使能GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);	//使能串口时钟
	
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF_PP;	//设置为复用推挽输出
	GPIO_InitStructure.GPIO_Pin= GPIO_Pin_9;	//引脚9
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;	//速度10MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);	//GPIOA模式设置
	
	GPIO_InitStructure.GPIO_Mode= GPIO_Mode_IN_FLOATING;//浮空输入
	GPIO_InitStructure.GPIO_Pin= GPIO_Pin_10;	//引脚10
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_10MHz;	//速度10MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);	//GPIOA模式设置
	
	
	USART_InitStructure.USART_BaudRate=115200;//波特率
	USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;//硬件流:无
	USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;//TX和RX都使能故使用|
	USART_InitStructure.USART_Parity=USART_Parity_No;//奇偶校验位:无
	USART_InitStructure.USART_StopBits=USART_StopBits_1;//停止位:1
	USART_InitStructure.USART_WordLength=USART_WordLength_8b;//字长:8位
	
	
	USART_Init(USART1,&USART_InitStructure);//串口参数初始化
	USART_Cmd(USART1,ENABLE);	//使能串口
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//接收缓冲区非空就接收USART_IT_RXNE,打开中断服务
	
	
	NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;//在stm32f10x.h头文件中上面部分有些IRQn。
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//开不开器通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;//设置抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//设置抢占子优先级
	NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler(void){
	u8 res;
	if(USART_GetITStatus(USART1,USART_IT_RXNE)){//若是接收到中断
		res=USART_ReceiveData(USART1);//接收数据
		USART_SendData(USART1,res);//接收到在发送数据,才可以在串口监视器中看到数据
		
	}
}

 int main(void)
 {	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	My_USART1_Init();
	while(1);
	 
 }

最后编译就通过了,注意要把模版中文件夹SYSTEM中的uart删除,因为定义重复了。

之后就可以上传程序,然后利用串口调试器,注意调试器中的设置要跟程序一样,波特率为115200,停止位1,等等。最终的实验现象就是发送什么,最后在调试软件中就会看到什么。

SYSTEM文件夹下,usart.c文件中,可看到以下代码(实验4串口实验):

void USART1_IRQHandler(void)                	//串口1中断服务程序
	{
	u8 Res;
#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntEnter();    
#endif
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
		{
		Res =USART_ReceiveData(USART1);	//读取接收到的数据
		
		if((USART_RX_STA&0x8000)==0)//接收未完成,判断最高位是不是0,是0表示接收未完成,则往下执行。若为1则不往下执行(上一次接收没清空)
			{
			if(USART_RX_STA&0x4000)//接收到了0x0d,若第二位为1则表示接收到0x0d,在往下判断下一位是不是0x0a,若不是重新开始,是的话则则给第一位置1
				{
				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
				else USART_RX_STA|=0x8000;	//接收完成了 
				}
			else //还没收到0X0D,第二位不是1,则判断什么时候接收到0x0d时,将第二位置为1
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				else//若一直没收到的话,则一直讲Res变量中的数值给变量,且通过位与来判断该位是否溢出(若第1、2位有数据给他清零)
					{
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
					USART_RX_STA++;
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
					}		 
				}
			}   		 
     } 
#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
	OSIntExit();  											 
#endif
} 
#endif