【STM32】基于库函数/寄存器的简单串口通信

目录

一、STM32串口

二、可配置的中断

三、使用的相关寄存器

1、状态寄存器(USART_SR)

2、数据寄存器(USART_DR)

3、控制寄存器1(USART_CR1)

四、相关库函数

1、初始化函数

2、使能函数

3、发送数据函数

4、接收数据函数

5、使能中断函数

6、获取标志函数

五、程序设计

1、库函数

2、寄存器


一、STM32串口

        串口通信是一种全双工的异步串行通信方式,通常用于设备与设备、设备和电脑间的通信。由TX和RX两根线传输数据:

TX:发送数据

RX:接收数据

两设备的接线方式交叉相连(TX接RX,RX接TX)。

STM32的USART框图如下:

        精简后的串口通信只用到TX和RX两条线。数据的发送和接收由发送器和接收器来完成,我们只需将数据写入发送数据寄存器(TDR)中即可,在接收数据寄存器(RDR)中读取接收到的数据,TDR和RDR是同一个数据寄存器寄存器(DR),该寄存器只有8位有效,因此一次只能传输一个字节(8bit)。

       波特率:每秒传输的bit位个数,通常使用115200、9600。两个设备必须设置相同的波特率才能正常通信,否则发送的数据错乱。串口通信没有时钟线,发送的每一bit位有一个固定的时长,接收器依据这个时长来读取数据,这个时长就是由波特率决定的,因此发送设备和接收设备的波特率设置需一致。

       串口每次发送的数据包包含起始位、数据位(8位)、校验位、停止位(默认1位)。

校验位:(可不使用)

        奇校验:数据位和校验位中“1”的个数为奇数,例如数据为1100 1001,含有4个“1”,则校验位为1;

        偶校验:数据位和校验位中“1”的个数为偶数,例如数据为1100 1001,含有4个“1”,则校验位为0;

二、可配置的中断

三、使用的相关寄存器

1、状态寄存器(USART_SR)

常用到的有位4、5、6、7。

2、数据寄存器(USART_DR)

3、控制寄存器1(USART_CR1)

该寄存器用来设置中断使能及其他配置。

位5置1时,当接收寄存器不为空时将产生中断,即接收中断;

位6置1时,当检测到空闲时产生中断。

四、相关库函数

1、初始化函数

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);

2、使能函数

void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

3、发送数据函数

//将一个字节(8bit)数据写入发送数据寄存器
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data); 

4、接收数据函数

//在就收数据寄存器读取一个字节(8bit)数据
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);

5、使能中断函数

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

6、获取标志函数

//读取状态寄存器的某个位
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);

五、程序设计

步骤:

1、使能引脚和串口时钟;

2、引脚和串口配置;

3、中断配置及优先级设置;

4、使能串口开启串口;

发送接收数据思路:

        发送:使用USART_SendData()函数发送,一个字节一个字节的发送。

        接收:使用中断接收,需使能接收数据寄存器不为空中断和空闲中断。每接收到一个字节(8bit)的数据发生一个接收数据寄存器不为空中断,在中断中完成数据的读取并保存。当接收数据结束会产生一个空闲中断,此时标记接收数据结束即可

1、库函数

uart1.h中:

#ifndef __UART1_H
#define __UART1_H

#include "stm32f4xx.h" 

//结构体声明
typedef struct
{
	char RXBUFF[255];	//用于保存数据
	int end_len_flag;	//结束标志和数据下标 最高位为接收结束标志位
}Uart1_Data;

void Uart1_Init(u32 bound);
void Uart1_SendString(char *string);
char *GetResiveData(void);

#endif

uart1.c中:


#include "uart1.h"

Uart1_Data Uart1_ReData; //定义结构体变量

//初始化串口1
void Uart1_Init(u32 bound)
{
	/*定义相关结构体变量*/
	GPIO_InitTypeDef GPIO_Uart1;
	USART_InitTypeDef USART_Uart1;
	NVIC_InitTypeDef NVIC_Uart1;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE); //使能引脚时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);	//使能串口1时钟
	
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 ,GPIO_AF_USART1); //复用
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource10,GPIO_AF_USART1); //复用
	
	/*GPIO引脚配置*/
	GPIO_Uart1.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
	GPIO_Uart1.GPIO_Mode = GPIO_Mode_AF;
	GPIO_Uart1.GPIO_OType = GPIO_OType_PP;
	GPIO_Uart1.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Uart1.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_Uart1);
	
	/*串口配置*/
	USART_Uart1.USART_BaudRate = bound;			//波特率
	USART_Uart1.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; //模式:发送和接收
	USART_Uart1.USART_Parity = USART_Parity_No;		//校验位无
	USART_Uart1.USART_StopBits = USART_StopBits_1;	//停止位1
	USART_Uart1.USART_WordLength = USART_WordLength_8b;	//数据长度8位	
	USART_Uart1.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件流不使用
	USART_Init(USART1,&USART_Uart1);

	USART_Cmd(USART1,ENABLE); //使能串口
	
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //使能串口接收中断
	USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //使能空闲中断

	/*串口中断配置*/
	NVIC_Uart1.NVIC_IRQChannel = USART1_IRQn;
	NVIC_Uart1.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Uart1.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_Uart1.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_Uart1);
		
}

//串口1发送字符串
void Uart1_SendString(char *string)
{
	u8 i=0;
	while(*(string + i) != '\0') //未到字符串的结尾
	{
		USART_SendData(USART1,*(string + i));  //发送一个字符
		
		    /*等待发送数据寄存器为空	读取状态寄存器(SR)的位7 TEX
                  0:数据未传输到移位寄存器
		          1:数据传输到移位寄存器
		    */
		while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
		i++;
	}
	    /*等待发送完成	读取状态寄存器(SR)的位6 TC
            0:传送未完成
		    1:传送已完成
	    */
	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}


//获取接收到的数据
char *GetResiveData(void)
{
	Uart1_ReData.end_len_flag= 0; //清标志位
	return Uart1_ReData.RXBUFF;   //返回数据地址
}

//串口接收中断函数
void USART1_IRQHandler()
{
	if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET)  //读取数据寄存器不为空中断
	{
		if( (Uart1_ReData.end_len_flag & 0x7fff ) < 255)	//接收缓存区未满
		{
			Uart1_ReData.RXBUFF [ Uart1_ReData.end_len_flag & 0x7fff ] = USART_ReceiveData(USART1);
//			USART_SendData(USART1,Uart1_ReData.RXBUFF[ Uart1_ReData.end_len_flag & 0x7fff ]);
			Uart1_ReData.end_len_flag++;		//下标后移
		}
//		else 
//			Uart1_ReData.end_len_flag = 0; //接收到的数据过长
	}
	if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE) != RESET)  //触发空闲中断
	{
		Uart1_ReData.RXBUFF[ Uart1_ReData.end_len_flag & 0x7fff ] = '\0'; //添加结束符
		Uart1_ReData.end_len_flag  |= 0x8000; //标记接收结束,最高位置1
		USART_ReceiveData(USART1);			//读取DR会对SR清零
	}

}

main函数中:

#include "stm32f4xx.h" 
#include "uart1.h"

extern Uart1_Data Uart1_ReData;
extern void * memset(void *,int,unsigned int);

int main(void)
{ 

	Uart1_Init(115200);	  //初始化串口1
	Uart1_SendString("串口通信练习\r\n");

	for(;;)
	{
		if(Uart1_ReData.end_len_flag & 0x8000 ) //接收结束
		{
			Uart1_SendString("发送的数据为:\r\n");
			Uart1_SendString(GetResiveData());      //向串口发送接收到的数据
			memset(Uart1_ReData.RXBUFF,0,255); //清空接收缓存区
		}
	}
	return 0;
}

2、寄存器

#include "usart.h"

#define Fck 100000000			//串口时钟频率

char RX_BUFF[50]={0};
u8 Usart1_flag = 0;

//A9 TX	A10 RX
//PA9 推挽输出 速度25MHZ
//PA10上拉输入
	
void Usart1_Init(u32 bound)
{
		
	RCC->APB2ENR |= 1<<4;   //使能串口1时钟
	RCC->AHB1ENR |= 1<<0;   //使能A口时钟
	
	/*引脚配置*/
	GPIOA->MODER &= ~(0xf <<9*2 );	//清0
	GPIOA->MODER |= (0xa <<9*2 ); //复用 1010
	
	GPIOA->OTYPER &=~( 1<<9);     //A9推挽
	GPIOA->OSPEEDR &= ~(3 << 9*2);
	GPIOA->OSPEEDR |=(1 << 9*2);  //A9 25MHZ
	GPIOA->PUPDR &= ~( 3<<2*10);
	GPIOA->PUPDR |=( 1<<2*10);     //A10上拉 01
	
	GPIOA->AFR[1] |= (0x77 << 4);   //0111 0111高位  复用为AF7 usart1
	
	
	USART1->BRR =  Fck/bound;		//波特率
	
	USART1->CR1 |= 3 << 2;				//使能发送接收功能
	USART1->CR1 |= (1 << 13) |(1 << 5); 	//使能 开启接收中断

	
//	NVIC_SetPriorityGrouping(7-3);//抢占占3位(0~7),响应1位(0~1)//优先级分组设置,放在主函数中
	NVIC_EnableIRQ((IRQn_Type)37);    //使能串口中断
	NVIC_SetPriority((IRQn_Type)37, NVIC_EncodePriority (7-2, 0, 1));//配置抢占0和响应优先级1
	
	
}

//发送一个字节数据
void Usart1_SendData(char ch)
{
	while( !(USART1->SR & (1<<6)));	//等待数据发送完成
	USART1->DR = ch;
}

//发送字符串
void Usart1_Send_Srting(char *str)
{
	char i=0;
	while( *(str + i)!= '\0')
	{
		Usart1_SendData(*(str + i));
		i++;
	}
}


//重映射fputc函数
//可以直接使用C库printf函数
//需要添加<stdio.h>头文件
//并勾选MDK中Use MicroLIB选项(魔术棒中打开)
int fputc(int ch,FILE *stream)
{
	Usart1_SendData(ch);
	return 0;
}



//接收一个字节数据
char Usart1_ReceiveData(void)
{
	char ch=0;
	if( USART1->SR & (1<<5))	//接收到数据
	{
		ch = USART1->DR;
		return ch;
	}
	return 0;
}



//中断接收函数
void USART1_IRQHandler(void)
{
	char ch = 0;
	static u8 count = 0;
	
	if( USART1->SR & (1<<5))	//接收到数据
	{
		ch = USART1->DR;
		if( (ch != '\r') && (ch != '\n') )  //不是回车符(未结束),发送时需要添加回车
		{
			RX_BUFF[count++] = ch;
		}
		else 
		{
			RX_BUFF[count] = '\0'; //添加结束符
			Usart1_flag = 1;	//标记接收结束
			count = 0;
		}
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值