STM32之串口

一些概念

并行通信
一次性一起传,需要引脚比较多,速度快。
串行同行
一个个传,需要引脚比较少,速度较慢

串行通信的通信方式
1、同步(带时钟同步信号),如:SPI,IIC
2、异步(不带时钟同步信号),如:UART,单总线
在这里插入图片描述
UART:通用异步收发传输器
USART:通用同步/异步串行接收/发送器

USART
USART_SRx 状态寄存器
USART_DR 数据寄存器
USART_BRR波特率寄存器,12位的整数和4位小数
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

串口初始化

在文件夹system下的usart.c文件里,已经写了串口的初始化及相关函数,可以直接用。
关于使用串口的步骤
1、时钟使能
2、串口复位
3、设置GPIO端口初始化(就是速度,模式,功率那些)
4、串口初始化
5、初始化NVIC
6、使能串口

1、时钟使能。

TX和RX的复用GPIO口是GPIOA
在这里插入图片描述
在这里插入图片描述
在这里是需要使能两个时钟,USART1和GPIOA的时钟

void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	
2、串口复位
USART_DeInit(USART1);

在sysytem文件夹下的usart.h文件夹下可以找到对应的函数。
在这里插入图片描述

3、GPIO模式配置

(跟之前点灯一样),但是设置的输出输入模式参考那个文档

在这里插入图片描述

  //USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  
4、串口参数初始化

在这里插入图片描述

 //USART 初始化设置

	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
5、初始化NVIC并开启中断

关于NAVIC的一些概念
https://blog.csdn.net/hongliwong/article/details/111034892

在这里插入图片描述
高优先级的抢占优先级是可以打断正在进行的低抢古优先级中断的。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;

在这里插入图片描述

//Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
6、开启中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
7、使能串口

(我的理解就是开串口了)

  USART_Cmd(USART1, ENABLE);                    //使能串口1 
完整的初始化代码
#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_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
  
	//USART1_TX   GPIOA.9
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
   
  //USART1_RX	  GPIOA.10初始化
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

  //Usart1 NVIC 配置
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
  
   //USART 初始化设置

	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_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
  USART_Cmd(USART1, ENABLE);                    //使能串口1 

}

中断函数

在这里插入图片描述

在这里插入图片描述
USART_RX_STA 是接收是否结束的标志位

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)//接收未完成
			{
			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	

一个别人的解释
http://t.csdn.cn/30Lbe

void USART1_IRQHandler(void)                	//串口1中断服务程序
	{
	u8 Res;     //定义unsigned char型字符Res
 
	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  
		//接收中断(接收到的数据必须是0x0d 0x0a结尾)
		//这里判断发送接收完成的依据就是串口数据0x0d 0x0a,
		//0x0d是CR(carriage return)回车的意思,光标回到最左边,
		//0x0a是LF(line feed)换行的意思,光标到达下一行,
		//但是在PC上回车和换行是在一起的就是按下回车按键
		//当然可以更改程序使用其他进行判断例如使用0x2a也就是*进行结束判断
		{
		Res =USART_ReceiveData(USART1);//(USART1->DR);	
		//读取接收到的数据,存放到变量Res中
		if((USART_RX_STA&0x8000)==0)
			//判断接收是否未完成
			//接收完成未清除标志位,还是会不断进入到接收中断,所以使用标志进行判断,
		  //当接收完成便不会跳入到判断,从而不执行任何指令,空等待
			//使用条件判断是否已经接收完数据,这里判断接收完的依据就是收到了0x0a;
			//具体判断在后面
			{
			if(USART_RX_STA&0x4000)
			//如果接收到了0x0d,那么再进一步执行是否接收到0x0a的判断
				{
				if(Res!=0x0a)USART_RX_STA=0;
			//没有接收到0x0a那么说明,数据未正确传输或者接收错误,重新开始判断,
			//但是这里没有将接收到的数据进行清空,也没有退出接收中断,此程序只是从头开始执行接收判断
				else USART_RX_STA|=0x8000;	
			//接收完成了,收到了0x0a那么标志位USART_RX_STA将会变成0x8000,将不再进行数据检测与存储
				}
			else 
				//还没收到0X0D,说明数据还未发送结束继续进行数据的检测与存储
				{	
				if(Res==0x0d)USART_RX_STA|=0x4000;
				//收到了数据0x0d,标志位USART_RX_STA变成0x4000
				else
					{
				//如果没有接收到数据0x0d,执行判断是否存储数组已满,已满则重新开始接收
					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
				//将接收到的数据写进数组,标志位USART_RX_STA与上0X3FFF清除前两位以防止标志位与80004000冲突
					USART_RX_STA++;
				//数组地址加一,向后排
					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;
				//接收数据错误,超出数组大小,又开始接收向数组重新写  
					}		 
				}
			}   		 
     } 
 
} 

一个数据后会加上0X0D 0X0A,如果接收到了0X0A就表示接收结束,没接收到0X0D就表示接收错误。

实验接收到A,LED0亮,再接收到关。

在这里插入图片描述

#include "stm32f10x.h"
#include "KEY.h"
#include "LED.h"
#include "delay.h"
#include "usart.h"

 int main(void)
 {	
	u8 t;
	u8 len;	 
	delay_init();	    	 //延时函数初始化	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
	uart_init(9600);	 //串口初始化为9600
	LED_Init();		  	 //初始化与LED连接的硬件接口 
  
	while(1)
	{
		if(USART_RX_STA&0x8000) //接收未完成
		{					   
			len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
			printf("\r\n您发送的消息为:\r\n");
			for(t=0;t<len;t++)
			{
				//USART1->DR=USART_RX_BUF[t];
				USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据
				while((USART1->SR&0X40)==0);//接收到了0X40就发送结束
			}
			printf("\r\n\r\n");//插入换行
			USART_RX_STA=0;	
		}else{  //接收完成
		
			u16 res;
			res = USART_ReceiveData(USART1);
			if(res == '2'){
				LED0 = !LED0;
				delay_ms(10); 
			}
			if(res == 'B'){
				LED1 = !LED1;
				delay_ms(10); 
			}
			if(res == 'X'){
				LED1 = 1;
				LED0 = 1;
				delay_ms(10); 
			}
		}
  }
}

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值