中断方式数据接收的原理与应用

中断接收简介

回顾之前的代码
之前的代码是 等待标志位RXNE位为1才有数据 进而读取数据存放在变量c中 再根据c变量的数据是为0还是为1进而编写灯亮灭的代码 if语句
但这样的代码明显不符合裸机多任务的编程模型 因为在while中为进程 进程执行的时间不能大于5ms 但是while(RXNE==0)这条语句的执行时间是由发送数据的一方决定的 当发送方1s后发送数据 这条语句就执行1s 完全超出了裸机多任务模型的时间
右边的代码则是使用了中断 数据通过RX引脚进入 当状态寄存器SR 的RXNE标志位由0变为1就通过USART1触发一次中断 传递到NVIC进而执行中断函数

配置中断源

产生电平型的中断源

USART产生的是电平型的中断 当标志位由0变为1就产生中断 在SR状态寄存器中 每一个标志位都可以触发中断 都可以触发7个电平型的中断源
中断共用

这七个标志位共用一个中断源(节省中断源) 在stm32中NVIC是管理中断源的 当这七个标志位当中只要有一个为1通过这个或门就会触发中断源 传递到NVIC中 那如果产生了中断 那到底是那个标志位为1触发了中断呢? 我们去查询sr寄存器即可 if 语句判断到底是那个标志位触发了中断 (可能是一个 也可能是多个)
中断屏蔽

中断传输到NVIC过程中有一个开关 闭合就能通过中断 打开就屏蔽了中断信号 那我们如何实现屏蔽一个标志位产生的中断呢? 就是左下角的结构 将中断标志位和中断使能位通过一个与门相互连接 当中断使能位为0不管中断标志位为1还是为0都无法通过或门触发中断源 当中断使能位为1就等于闭合了开关 中断标志位即可正常工作 注意中断标志位FE NE ORE都是共用了一个中断使能位eie 当中断使能位eie为0就屏蔽了这三个中断标志位的中断触发请求 其他的中断标志位都是各自有一个独立的中断使能位
编程接口

第一个编程接口(函数)就是通过配置蓝色的中断使能寄存器来屏蔽 使能中断的 第二个接口就是查询状态寄存器的标志位 第三个接口是清除标志位 当触发了中断 如PE标志位触发了中断就为1 然后需要调用这个函数来手动清零
USART_ITConfig

USART_GetITStatus

USART_ClearITPendingBit

中断接收数据的编程思路

数据处理能够瞬间完成

套用裸机多任务的模型 就是初始化 然后进程函数 再到中断服务函数 但是中断函数中处理数据的速度要大于数据接收的速度(不然等到下一个数据发送过来还在处理上一个数据会造成数据的丢失和出错) 那小于10us就看作瞬间完成 那么这里对数据的处理就是判断if语句还有向对应的gpio模块的ODR寄存器写入对应的0或1 远远小于10us 视为瞬间完成 符合裸机多任务模型的时间需求

如第一幅图 处理数据的时间远远小于数据传来的时间(箭头代表数据传输进来) 在两个数据传输中数据已经处理完成
第二幅图当箭头传入 (数据传入) 当第一个箭头(第一个数据)传入开始处理数据 第二个箭头(代表第二个数据传入)传入后还在处理第一个数据接着第三个数据传入 导致了第二个数据的重载 (丢失了第二个数据) 导致数据传输的错误
但是数据处理的时间确实太长了怎么办?

那我们就不在中断服务函数中处理 就把数据传到缓存区在传入进程函数中延时处理

改进串口编程实验

因为 这次数据处理的时间很短就只是个点灯 所以可以放在中断服务函数中执行

usart初始化


编写中断响应函数

记住RXNE是一开始是0接收到了数据就变为1 从而触发电平 TXE是寄存器TDR内没有数据就为1 有数据就为0 TC也如此只有当数据被发送完成就是TDR寄存器为空然后移位寄存器也为空就表示数据发送完成两个寄存器没有数据此时TC标志位才为1 所以读取数据的时候就相当于清空了中断标志位(收到数据RXNE标志位为1 触发中断 在中断函数中读取数据RXNE寄存器就清空RXNE标志位就清零)

#include "stm32f10x.h"
#include "stm32f10x_pal.h"

static  void USART_Recv_Init(void);


int main(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//NVIC的中断优先级分组
	
	PAL_Init();
	USART_Recv_Init();
	
	while(1)
	{
	}
}



static  void USART_Recv_Init(void)
{
	//1.初始化IO引脚
	//PB6 Tx PB7  Rx (经过了AFIO映射映射到了PB6和PB7引脚)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//开启GPIOB的时钟
	//初始化PB6
	GPIO_InitTypeDef GPIOInitStruct;
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_6;
	GPIOInitStruct.GPIO_Mode =  GPIO_Mode_AF_PP;//PIN6为复用推挽模式
	GPIOInitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	
	GPIO_Init(GPIOB,&GPIOInitStruct);
	
	//初始化PB7
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_7;
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOB,&GPIOInitStruct);

	
	//初始化PC13
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//开启GPIOC的时钟
	GPIOInitStruct.GPIO_Pin = GPIO_Pin_13 ;
	GPIOInitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIOInitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOC,&GPIOInitStruct);
	//复用功能重映射
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
	GPIO_PinRemapConfig(GPIO_Remap_USART1 ,ENABLE);
	
	//使能USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	
	//配置USART的参数
	//9600 8为数据有效位 无奇偶校验 1停止位为1位  Tx|Rx
	USART_InitTypeDef USART1InitStruct;
	USART1InitStruct.USART_BaudRate = 9600;
	USART1InitStruct.USART_WordLength = USART_WordLength_8b ;
	USART1InitStruct.USART_StopBits = USART_StopBits_1;
	USART1InitStruct.USART_Parity = USART_Parity_No;
	USART1InitStruct.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
	USART1InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None ;//硬件流控
	USART_Init(USART1,&USART1InitStruct);
	
	//配置中断源
	USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//标志位RXNE的使能
	
	//NVIC的参数设置
	NVIC_InitTypeDef NVICInitStruct;
	NVICInitStruct.NVIC_IRQChannel = USART1_IRQn;
	NVICInitStruct.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级
	NVICInitStruct.NVIC_IRQChannelSubPriority =0;//子优先级
	NVICInitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVICInitStruct);
	
	
	//闭合总开关
	USART_Cmd(USART1,ENABLE);
}



void USART1_IRQHandler(void)
{
		uint8_t c;
		if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)
		{
				c = USART_ReceiveData(USART1); //清除了中断也读取了数据
			if(c == '0')
			{
					GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
			}
			
			if(c == '1')
			{
				GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
			
			}
			
		}

}
物联沃分享整理
物联沃-IOTWORD物联网 » 中断方式数据接收的原理与应用

发表评论