mian.c

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

int main(void)
{
	u8 length;
	u16 time=0;
  
    HAL_Init();                     //初始化HAL库   
    Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
    delay_init(180);                //初始化延时函数
	led_init();
	usart_init();
	
	while(1)
	{
		if(RE_CPLMT_STATUS&0x8000)//这里是只取这个接收状态标志位的最高位
			                      //最高位为1的时候执行发送函数
		{
			length=RE_CPLMT_STATUS&0x3fff;//计算出接收的字节的长度
			printf("\r\n你发送的数据为\r\n");
			HAL_UART_Transmit(&usart_hander,(u8 *)USRAT1_RE_BUFF,length,1000);
			RE_CPLMT_STATUS=0;//数据发送完成了,要给它重新置0 
			
		}
		else //就是不执行数据的发送的时候,进行普通的提示的打印。
		{
			time++;
			if(time>2)
			{
				printf("\r\n请输入要发送给MCU串口的数据\r\n");
			}
			
			HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
			delay_ms(200);
			HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
			delay_ms(200);
			
		}
	}
 
}

usart.h

#ifndef _USART_H
#define _USART_H

#include "sys.h"
#include "stdio.h"	
#define USART1_RE_LENLIM 200

extern UART_HandleTypeDef usart_hander;
extern u16 RE_CPLMT_STATUS;
extern u8 USRAT1_RE_BUFF[USART1_RE_LENLIM];

void usart_init(void);



#endif

usart.c

#include "sys.h"
#include "usart.h"
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)	

#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
	USART1->DR = (u8) ch;      
	return ch;
}



u8 USRAT1_RE_BUFF[USART1_RE_LENLIM];
u16 RE_CPLMT_STATUS=0;
u8 rxbuff[1];
UART_HandleTypeDef usart_hander;
void usart_init()
{
	
	usart_hander.Instance=USART1;
	usart_hander.Init.BaudRate=115200;
	usart_hander.Init.WordLength=UART_WORDLENGTH_8B;
	usart_hander.Init.StopBits=UART_STOPBITS_1;
	usart_hander.Init.Parity=UART_PARITY_NONE;
	usart_hander.Init.Mode=UART_MODE_TX_RX;
	usart_hander.Init.HwFlowCtl=UART_HWCONTROL_NONE;
	HAL_UART_Init(&usart_hander);//以上的是串口外设的一些基本的配置参数
	
	HAL_UART_Receive_IT(&usart_hander,(u8 *)rxbuff,1);//打开串口接收中断函数
	
}
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
	__HAL_RCC_GPIOA_CLK_ENABLE();
	__HAL_RCC_USART1_CLK_ENABLE();
	
	GPIO_InitTypeDef GPIO_INIT_STRUCTURE;
	GPIO_INIT_STRUCTURE.Pin=GPIO_PIN_9|GPIO_PIN_10;
	GPIO_INIT_STRUCTURE.Mode=GPIO_MODE_AF_PP;
	GPIO_INIT_STRUCTURE.Pull=GPIO_PULLUP;
	GPIO_INIT_STRUCTURE.Speed=GPIO_SPEED_FREQ_VERY_HIGH;
	GPIO_INIT_STRUCTURE.Alternate=GPIO_AF7_USART1;
	HAL_GPIO_Init(GPIOA,&GPIO_INIT_STRUCTURE);//以上就是GPIO复用成串口的一般设置
	
	HAL_NVIC_EnableIRQ(USART1_IRQn);//需要使能中断服务函数,并且还要设置抢占优先级和次优先级
	HAL_NVIC_SetPriority(USART1_IRQn,3,3);
	
	
}
void USART1_IRQHandler(void)
{
	HAL_UART_IRQHandler(&usart_hander);
	HAL_UART_Receive_IT(&usart_hander,(u8 *)rxbuff,1);//打开串口接收中断函数
	
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==USART1)//首先进行一个判断,是不是串口1,如果是串口1就执行下面的情况。
	{
		if((RE_CPLMT_STATUS&0x8000)==0)//先进行状态位的判断,先判断最高位,此时最高位的首位是0,表示没有接收到完整的数据
		{
			if(RE_CPLMT_STATUS&0x4000)//在进行次高位的判断,如果次高位是1的话,表示接收到了0x0d,这里的就表示次高位是1,表示接收到了0x0d
			{
				if(rxbuff[0]!=0x0a)//本身发送结束码就因该是回车,换行连在一起的,如果分开来收到了,就表示出错了,出错了的话,就将状态标志位置0开始重新接收和判断。
				RE_CPLMT_STATUS=0;
				else//如果接收到的东西的确是0x0a的话,就表示,连续接收到了回车,换行的命令,接下来就需要把状态位的最高位也置成1才可以。
				RE_CPLMT_STATUS|=0x8000;
			}
			else//在最高位是0并且没有接收到0x0d的情况那么就表示,现在的数据是真正的有效数据,需要把他存储在一个我自己定义的数组之中。
			{
				if(rxbuff[0]==0x0d)//在最高位是0并且在之前没有接收到0x0d的情况下,去判断此次接收的是不是0x0d
				{
					RE_CPLMT_STATUS|=0x4000;
				}
				else//在最高位是0并且在之前没有接收到0x0d的情况下,且此次接收的不是0x0d那么就表示,现在的数据是真正的有效数据,需要把他存储在一个我自己定义的数组之中。
				{
					USRAT1_RE_BUFF[RE_CPLMT_STATUS&0x3fff]=rxbuff[0];
					RE_CPLMT_STATUS++;
					if(RE_CPLMT_STATUS>USART1_RE_LENLIM-1)//下面两行的代码表示如果,接收到的字节的个数,
					RE_CPLMT_STATUS=0;                   //大于了我定义的最大接收的个数,就表示接收不了了,
				                                        //就将接收标志位清0.重新开始计数
				}
			}
			
				
		}
	}
}

这里主要的就是串口的一些配置与中断处理的一些流程。主函数里面还有一个关于LED灯闪烁的代码的编写,这里不做重点的描述,是很简单的有兴趣的同学可以自己去看。

下面主要讲解一下串口的使用配置以及相关的一些流程。

       在使用串口时,我们需要明确,串口是什么?总所周知,串口是MCU的一个重要的外设,常用于设备之间的通信。

在使用串口时,因该先开始使用串口的初始化函数HAL_UART_INIT()函数来对外设串口进行相关的参数配置,比如要配置使用串口几?使用的波特率是多少?停止位的设置,奇偶校验位的设置,有无硬件流的设置等等,这里需要注意,我们的串口初始化函数HAL_UART_INIT()会去调用HAL_MSPINIT()函数,我们关于将GPIO口复用成串口的配置就是写在这个函数里面的,使用我们特别熟悉的HAL_GPIO_INIT()函数就可以实现相关的配置。设置好相关的串口初始化设置以后我们要进行中断的选择,如果说要使用串口中断的话,就要在HAL_MSPINIT()里面在加上中断使能函数HAL_NVIC_ENABLEIRQ()函数,意思就是打开允许串口几产生中断的请求并且要进行中断优先级的配置包括抢占优先级和此优先级,使用的函数为:HAL_NVIC_SetPrority()函数。

       随后我们就要打开对应的中断,比如这个实验当中,我们要使用的是串口接收中断。意思就是在使用函数HAL_UART_Receive_IT()函数,这个函数的意思是,跟着前面的,前面有一句是打开了中断的使能中断需求函数,这里就是使用的是接收中断的函数,里面有对应的参数的设置,比如,哪个串口,接收的数据放到哪里?接收几个字符就会产生中断?当满足接收中断的条件时,就是说满足了接收到了N个字符产生中断时,就会调用USART1_IRQHandler()函数,就是串口中断处理函数,因为这次的实验使用的是,串口1,所以这里使用的是这个函数,如果使用的是其他串口,这个也是可以更改的。

       关于各种中断处理的函数都是在f429的启动头文件里面可以找到。在这个函数里面会先调用HAL_UART_IRQHandler()函数,这个是HAL库封装的函数,在这个函数里边会先调用一些串口相关的标志位以此去判断是什么类型的中断,并且调用相关的中断处理的函数,比如这里调用的就是接收中断处理函数,然而接收中断处理函数里面还会进行接收完成回调函数的调用,这个接收完成回调函数,是真真的用户层面的处理逻辑,可以将有关用户层的中断处理逻辑写入到这个函数里面去。

       这里需要注意一点的就是,在调用这个中断接收完成回调函数之前会进行一个停止中断函数的调用,所以当中断接收完成回调函数执行完毕时,以为者已经将中断给关闭了,如果想反复打开的话,可以在执行完成HAL_UART_IRQHandler()函数的语句后面再跟上一个打开中断函数的语句也就是HAL_UART_Receive_IT()。这个样子就可以实现反复的中断的使用。至于这个代码里面的中断接收完成回调函数在备注的部分写的十分的详细,可以供大家做一个串口接收的参考,备注已经很详细了,如果有疑问或者错误欢迎指出。