简介
1.串口通讯的双方若采用不同的电平标准,则需要利用电平转换芯片进行转换。
2.调试程序时可以把一些调试信息“打印”在电脑端的串口调试助手上。
3.硬件原理以后有空再研究,应该跟微机里学的挺类似的。。。

配置一个串口的步骤
1.使能USART时钟,以及RX和TX引脚的GPIO时钟
2.初始化GPIO,配置相关的引脚功能
3.配置USART的工作参数
4.配置中断控制器NVIC,使能串口中断
5.使能USART的接收中断
6.使能USART
7.编写中断服务函数

代码设计
通用代码参考的是野火的代码,自己写的太不规范了。。。

(1)串口初始化

void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	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(DEBUG_USARTx, &USART_InitStructure);
	
	/*========================下面三步是配置中断以及使能串口=================*/
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

(2)NVIC中断控制器的初始化

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

(3)发送一个字符以及发送字符串
对于发送字符,这里封装了库函数并增加了等待发送完成的查询(若无该步,字符串无法发送成功)

/*===================发送一个字符=========================*/
void Usart_SendByte(USART_TypeDef* USARTx, uint8_t Data)
{
	USART_SendData(USARTx,Data);
	
	while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
}

/*===================发送一个字符串======================*/
void Usart_SendString(USART_TypeDef* USARTx,char *str)
{
	uint32_t i=0;
	for(;*(str+i)!='\0';i++)
	{
		Usart_SendByte(USARTx,*(str+i));
	}
	
	while(USART_GetFlagStatus(USARTx,USART_FLAG_TXE)==RESET);
}

(4)printf的重定向
为了使用c库中的printf,需要对fputc进行重定向

//注意添加头文件
#include <stdio.h>

//……

///重定向c库函数printf到串口,重定向后可使用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);
}

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

		return (int)USART_ReceiveData(DEBUG_USARTx);
}

(5)发送和接受数据
定义一个缓冲区,利用中断方式接受数据,利用FIFO模式发送数据,分别贴出中断服务函数以及主函数

//中断服务函数

extern uint8_t Uart_data[64];
extern uint32_t Rx_Num;

void DEBUG_USART_IRQHandler(void)
{
	uint8_t temp;
	if(USART_GetITStatus(DEBUG_USARTx , USART_IT_RXNE)!=RESET)
	{
		
		temp= USART_ReceiveData(DEBUG_USARTx);
		Uart_data[Rx_Num]=temp;
		Rx_Num++;
		Rx_Num&=0x3f;
	}
}

//主函数
uint8_t Uart_data[64];
uint32_t Rx_Num=0;
uint32_t Tx_Num=0;

int main(void)
{
	DEBUG_USART_Config();
	printf("hello,world\r");
	
	while(1)
	{	
		if(Tx_Num!=Rx_Num)
		{
			USART_SendData(DEBUG_USARTx,Uart_data[Tx_Num]);
			Tx_Num++;
			Tx_Num&=0x3f;
		}
	}
}

上述方法定义了三个外部变量,需要注意用法。

(6)设置指令集(控制LED)
利用上位机发送数据时,只能发送单个字符,发送一串字符时仅能接收到第一个。

/*====================字符串匹配=======================*/
uint8_t Cmp(uint8_t *str,uint8_t num)
{
	uint8_t i=0;
	uint8_t	str1[]="111111111";
	uint8_t str2[]="000000000";
	
	for(i=0;i<(num-1);i++)
	{
		if(*(str1+i)!=*(str+i))
			break;
	}
	if(i==(num-1))
		return 1;
	
	for(i=0;i<(num-1);i++)
	{
		if(*(str2+i)!=*(str+i))
			break;
	}
	if(i==(num-1))
		return 2;
	
	return 0;
}

//中断服务函数
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
  	uint8_t ucTemp;
	u8 i;
	if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
	{		
		ucTemp = USART_ReceiveData(DEBUG_USARTx);
    	//USART_SendData(DEBUG_USARTx,ucTemp);  
		Uart_Data[Rx_Num]=ucTemp;
		Rx_Num++;
		for(i=0;i<10;i++)
		{
			printf("%c",Uart_Data[i]);
		}
		
		if(Rx_Num==9)
		{
//			for(i=0;i<10;i++)
//			{
//				printf("%c",Uart_Data[i]);
//			}
			Rx_Num=0;
		}
	}	 
}

//主函数
int main(void)
{	
  	USART_Config();
	LED_Config();

 	while(1)
	{	
		switch(Cmp(Uart_Data,10))
		{
			case 1:	LED1_ON;
					LED2_OFF;
					break;
			case 2:	LED1_OFF;
					LED2_ON;
					break;
			case 0:
					LED2_OFF;
					LED1_OFF;
					break;
			}	
	}
}

填坑

在使用正点原子例程进行接受数据时,出现的问题
功能概述:MCU之间通过USART2进行数据传输,然后主控芯片又通过USART1向电脑端的串口助手发送数据。
存在问题:在高速数据传输的过程中第一位丢失
在这里插入图片描述

原因:printf的问题,重定向时存在问题,导致数据高速传输时,使用printf会导致之后的数据丢失(主控向电脑端传输时)
解决方法:原先用printf发送数据的地方改用USART_SendData发送,在用while等待

while(1)
	{
		/*====================MCU之间串口通信的数据显示=========================*/
		//功能说明
		//MCU之间通过USART2进行通信,主控芯片将数据存入缓冲区,并通过串口1发送到电脑端
		//数组有效数据长度为9
		
		if(USART2_RX_STA&0x8000)											//如果发送完成
		{
			len=(USART2_RX_STA&0x3fff);
			
			USART_SendData(USART1,len);//发长度
			while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
			for(i=0;i<len;i++)				//将数据通过串口1发送
			{
				USART_SendData(USART1,USART2_RX_BUF[i]);
				while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
			}
			while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
			USART_SendData(USART1,'\r');//插入换行
			while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
			USART_SendData(USART1,'\n');//插入换行
			while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);
			USART2_RX_STA=0;
		}
		
		/*==========================================================================*/	
	}

注意:一定要取一中间变量len,在for循环发送数据时USART2_RX_STA可能会进入中断从而改变数据长度
正点原子串口数据接收的思路:
1.利用缓冲区
2.设置状态变量USART2_RX_STA,低13位表示数据长度,14位表示接收到0x0d,15位表示接收到0x0a(接收完成)(通信协议,发送的数据最后需要加’\r\n’才能接受)

void USART2_IRQHandler(void)                	//串口2中断服务程序
{
	u8 Res;
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
	{
		USART_ClearITPendingBit(USART2, USART_IT_RXNE);
		Res =USART_ReceiveData(USART2);//(USART1->DR);	//读取接收到的数据
		
		if((USART2_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART2_RX_STA&0x4000)//接收到了0x0d
			{
				if(Res!=0x0a)USART2_RX_STA=0;//接收错误,重新开始
				else USART2_RX_STA|=0x8000;	//接收完成了 
			}
			else //还没收到0X0D
			{	
				if(Res==0x0d)USART2_RX_STA|=0x4000;
				else
				{
					USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;
					USART2_RX_STA++;
					if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}
  } 
}