STM32F407系列文章之第四章 串口实验

STM32F407系列文章目录

第一章 获取相关组件(注意:下载或安装不要有中文路径)
第二章 新建工程
第三章 GPIO相关配置实验
第四章 串口实验



前言

本文章采用Keil5及STM32F4(SDK-设计资源)进行STM32F407(ARM芯片)系列学习

  • 萌新入坑,如有不对的地方请大佬多多指教,谢谢!->欢迎评论区留言
  • 对于萌新来说,学习无非就是多看博文,但是…0.0大佬们的思维大多都有些跳跃,真的很难理解(当然大佬们估计都是为了记录或者提醒自己的工作及学习例程),所以想通过慢慢学习,把每一步都解释出来(保姆版)

第四章 串口实验

1.原理图

  • MCU引脚
    串口实验MCU引脚
  • 涉及外设
    串口实验涉及外设

2.代码示例

  • usart.c
  • 库函数版本
#include "sys.h"
#include "usart.h"	
 
//如果使用ucos,则包括下面的头文件即可
#if SYSTEM_SUPPORT_OS
#include "includes.h" // ucos 使用	  
#endif

//支持适应不同频率下的串口波特率设置
//加入了对printf的支持
//增加了串口接收命令功能
//修正了printf第一个字符丢失的bug

//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ 
	x = x; 
} 

//重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	while ((USART1->SR & 0X40) == 0); // 循环发送,直到发送完毕   
	USART1->DR = (u8) ch;      
	return ch;
}
#endif
 
#if EN_USART1_RX // 如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
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端口设置
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);  // 使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); // 使能USART1时钟

	//串口1对应引脚复用映射
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);  // GPIOA9复用为USART1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1); // GPIOA10复用为USART1

	//USART1端口配置
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; // GPIOA9与GPIOA10
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;            // 复用功能
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	    // 速度50MHz
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;          // 推挽复用输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;            // 上拉
	GPIO_Init(GPIOA,&GPIO_InitStructure);                   // 初始化PA9,PA10

	//USART1 初始化设置
	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_Cmd(USART1, ENABLE); // 使能串口1 

	//USART_ClearFlag(USART1, USART_FLAG_TC);

#if EN_USART1_RX	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启相关中断

	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;         // 串口1中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		  // 子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			  // IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	// 根据指定的参数初始化VIC寄存器
#endif
}

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); // (USART1->DR); // 读取接收到的数据
		
		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; // 接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
	} 
#if SYSTEM_SUPPORT_OS // 如果SYSTEM_SUPPORT_OS为真,则需要支持OS
	OSIntExit();  											 
#endif
} 
#endif	
  • 寄存器版本
#include "sys.h"
#include "usart.h"	  
 
//如果使用ucos,则包括下面的头文件即可.
#if SYSTEM_SUPPORT_OS
#include "includes.h" // ucos 使用	  
#endif

//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
#if 1
#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
	/* Whatever you require here. If the only file you are using is */ 
	/* standard output using printf() for debugging, no file handling */ 
	/* is required. */ 
}; 

/* FILE is typedef’ d in stdio.h. */ 
FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
_sys_exit(int x) 
{ 
	x = x; 
} 

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

#if EN_USART1_RX // 如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误   	
u8 USART_RX_BUF[USART_REC_LEN]; // 接收缓冲,最大USART_REC_LEN个字节

//接收状态
//bit15,	接收完成标志
//bit14,	接收到0x0d
//bit13~0,	接收到的有效字节数目
u16 USART_RX_STA = 0; // 接收状态标记	  

void uart_init(u32 pclk2, u32 bound)
{  	 
	float temp;
	u16 mantissa;
	u16 fraction;
	
	temp = (float)(pclk2 * 1000000) / (bound * 16); // 得到USARTDIV@OVER8=0
	mantissa = temp;		       // 得到整数部分
	fraction = (temp - mantissa) * 16; // 得到小数部分@OVER8=0 
    mantissa <<= 4;
	mantissa += fraction; 
	
	RCC->AHB1ENR |= 1 << 0; // 使能PORTA口时钟  
	RCC->APB2ENR |= 1 << 4; // 使能串口1时钟 
	GPIO_Set(GPIOA, PIN9|PIN10, GPIO_MODE_AF, GPIO_OTYPE_PP, GPIO_SPEED_50M, GPIO_PUPD_PU); // PA9,PA10,复用功能,上拉输出
 	GPIO_AF_Set(GPIOA, 9, 7);  // PA9,AF7
	GPIO_AF_Set(GPIOA, 10, 7); // PA10,AF7  	   
	// 波特率设置
 	USART1->BRR = mantissa;    // 波特率设置	 
	USART1->CR1 &= ~(1 << 15); // 设置OVER8=0 
	USART1->CR1 |= 1 << 3;     // 串口发送使能 
#if EN_USART1_RX		  	   // 如果使能了接收
	// 使能接收中断 
	USART1->CR1 |= 1 << 2;     // 串口接收使能
	USART1->CR1 |= 1 << 5;     // 接收缓冲区非空中断使能	    	
	MY_NVIC_Init(3, 3, USART1_IRQn, 2); // 组2,最低优先级 
#endif
	USART1->CR1 |= 1 << 13;    // 串口使能
}

void USART1_IRQHandler(void)
{
	u8 res;	
#if SYSTEM_SUPPORT_OS // 如果SYSTEM_SUPPORT_OS为真,则需要支持OS
	OSIntEnter();    
#endif
	if (USART1->SR & (1 << 5)) // 接收到数据
	{	 
		res = USART1->DR; 
		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; // 接收数据错误,重新开始接收	  
				}		 
			}
		}  		 									     
	} 
#if SYSTEM_SUPPORT_OS // 如果SYSTEM_SUPPORT_OS为真,则需要支持OS
	OSIntExit();  											 
#endif
} 
#endif
  • usart.h
#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "stm32f4xx_conf.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
  • main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"

int main(void)
{ 
	u8 t;
	u8 len;	
	u16 times=0;  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置系统中断优先级分组2
	//Stm32_Clock_Init(336, 8, 2, 7); // 设置时钟,168Mhz 
	delay_init(168);   // 延时初始化 
	uart_init(115200); // 串口初始化波特率为115200
	
	while(1)
	{
		if (USART_RX_STA & 0x8000)
		{					   
			len = USART_RX_STA & 0x3fff; // 得到此次接收到的数据长度
			printf("\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); // 等待发送结束
				/* 寄存器操作 */
				USART1->DR = USART_RX_BUF[t];
				while ((USART1->SR & 0X40) == 0); // 等待发送结束
			}
			printf("\r\n\r\n"); // 插入换行
			USART_RX_STA = 0;
		}
		else
		{
			times++;
			if (times % 5000 == 0)
			{
				printf("\r\n STM32F407串口实验 \r\n");
				printf(" STM32F407 \r\n\r\n\r\n");
			}
			if (times % 200 == 0) printf("请输入数据,以回车键结束\r\n");  
			if (times % 30 == 0) LED0 = !LED0; // 闪烁LED,提示系统正在运行
			delay_ms(10);   
		}
	}
}

总结

本章为TMS320F280049学习系列文章 第四章:串口实验

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值