STM32 UART2发送后中断的原因是什么?

发布者:yunhao最新更新时间:2024-04-30 来源: elecfans关键字:STM32  中断 手机看文章 扫描二维码
随时随地手机看文章

SECTION 2

先说TC。即Transmission Complete。发送一个字节后才进入中断,这里称为“发送后中断”。和原来8051的TI方式一样,都是发送后才进中断,需要在发送函数中先发送一个字节触发中断。发送函数如下

陆奇为何要离开百度,接下来的百度会向何方发展

/*******

功能:中断方式发送字符串。采用判断TC的方式。即 判断 发送后中断 位。

输入:字符串的首地址

输出:无

*******/

void USART_SendDataString( u8 *pData )

{

pDataByte = pData;

USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据。网友提供。

USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次

}

中断处理函数如下

/********

* Function Name : USART1_IRQHandler

* Description : This function handles USART1 global interrupt request.

* Input : None

* Output : None

* Return : None

*********/

void USART1_IRQHandler(void)

{

if( USART_GetITStatus(USART1, USART_IT_TC) == SET )

{

if( *pDataByte == ‘’ )//TC需要 读SR+写DR 方可清0,当发送到最后,到‘’的时候用个if判断关掉

USART_ClearFlag(USART1, USART_FLAG_TC);//不然TC一直是set, TCIE也是打开的,导致会不停进入中断。 clear掉即可,不用关掉TCIE

else

USART_SendData(USART1, *pDataByte++ );

}

}

其中u8 *pDataByte;是一个外部指针变量

在中断处理程序中,发送完该字符串后,不用关闭TC的中断使能TCIE,只需要清掉标志位TC;这样就能避免TC == SET 导致反复进入中断了。

串口初始化函数如下

/*********

名称: USART_Config

功能: 设置串口参数

输入: 无

输出: 无

返回: 无

**********/

void USART_Config()

{

USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体

USART_InitStructure.USART_BaudRate = 9600; //波特率9600

USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位

USART_InitStructure.USART_StopBits = USART_StopBits_1;//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_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭

USART_InitStructure.USART_CPOL = USART_CPOL_Low;

USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;

USART_InitStructure.USART_LastBit = USART_LastBit_Disable;

USART_Init(USART1, &USART_InitStructure);//设置到USART1

USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断。 开TC中断必须放在这里,否则还是会丢失第一字节

USART_Cmd(USART1, ENABLE); //使能USART1

}

这里请问一个问题:开TC中断USART_ITConfig()如果放在我的USART_SendDataString()中再开,会丢失字符串的第一字节。必须放在串口初始化函数中才不会丢。不知道为什么??

这里笔者可以给出解释,你看下SECTION1 就可以知道为什么呢,你这样做的原理和SECTION1讲解的差不多,就相当于延时,而你后面没有丢失数据的主要原因就是你代码中有这么一句 USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据。网友提供。

再说判断TXE。即Tx DR Empty,发送寄存器空。当使能TXEIE后,只要Tx DR空了,就会产生中断。所以,发送完字符串后必须关掉,否则会导致重复进入中断。这也是和TC不同之处。

发送函数如下:

/*******

功能:中断方式发送字符串。采用判断TC的方式。即 判断 发送后中断 位。

输入:字符串的首地址

输出:无

*******/

void USART_SendDataString( u8 *pData )

{

pDataByte = pData;

USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。

}

中断处理函数如下:

/********

* Function Name : USART1_IRQHandler

* Description : This function handles USART1 global interrupt request.

* Input : None

* Output : None

* Return : None

********/

void USART1_IRQHandler(void)

{

if( USART_GetITStatus(USART1, USART_IT_TXE) == SET )

{

if( *pDataByte == ‘’ )//待发送的字节发到末尾NULL了

USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因为是 发送寄存器空 的中断,所以发完字符串后必须关掉,否则只要空了,就会进中断

else

USART_SendData(USART1, *pDataByte++ );

}

}

在串口初始化函数中就不用打开TXE的中断了(是在发送函数中打开的)如下:

/************

名称: USART_Config

功能: 设置串口参数

输入: 无

输出: 无

返回: 无

************/

void USART_Config()

{

USART_InitTypeDef USART_InitStructure;//定义一个包含串口参数的结构体

USART_InitStructure.USART_BaudRate = 9600; //波特率9600

USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位

USART_InitStructure.USART_StopBits = USART_StopBits_1;//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_InitStructure.USART_Clock = USART_Clock_Disable;//时钟关闭

USART_InitStructure.USART_CPOL = USART_CPOL_Low;

USART_InitStructure.USART_CPHA = USART_CPHA_2Edge;

USART_InitStructure.USART_LastBit = USART_LastBit_Disable;

USART_Init(USART1, &USART_InitStructure);//设置到USART1

USART_Cmd(USART1, ENABLE); //使能USART1

}

SECTION 3

在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器(下图中阴影部分的TDR),另一个是程序看不到的移位寄存器

对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束;对照下图,当TDR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,但因为TDR已经变空,程序可以把下一个要发送的字节(操作USART_DR)写入TDR中,而不必等到移位寄存器中所有位发送结束,所有位发送结束时(送出停止位后)硬件会设置TC标志。

另一方面,在刚刚初始化好USART还没有发送任何数据时,也会有TXE标志,因为这时发送数据寄存器是空的。

TXEIE和TCIE的意义很简单,TXEIE允许在TXE标志为‘1’时产生中断,而TCIE允许在TC标志为‘1’时产生中断。

至于什么时候使用哪个标志,需要根据你的需要自己决定。但我认为TXE允许程序有更充裕的时间填写TDR寄存器,保证发送的数据流不间断。TC可以让程序知道发送结束的确切时间,有利于程序控制外部数据流的时序。

SECTION 4

总的来说,STM32单片机的串口还是很好理解的,编程也不算复杂。当然我更愿意希望其中断系统51单片机一样的简单。

对于接收终端,就是RXNE了,这只在接收完成后才产生,在执行USART_ITConfig(USART1, USART_IT_RXNE, ENABLE)代码时不会进入ISR。但麻烦的就是发送有关的中断了:TXE或者TC,根据资料和测试的结果,TXE在复位后就是置1的,即在执行USART_ITConfig(USART1, USART_IT_TXE, ENABLE)后会立即产生中断请求。因此这造成一个麻烦的问题:如果没有真正的发送数据,TXE中断都会发生,而且没有休止,这将占用很大部分的CPU时间,甚至影响其他程序的运行!

因此建议的是在初始化时不好启用TXE中断,只在要发送数据(尤其是字符串、数组这样的系列数据)时才启用TXE。在发送完成后立即将其关闭,以免引起不必要的麻烦。

对于发送,需要注意TXE和TC的差别——这里简单描述一下,假设串口数据寄存器是DR、串口移位寄存器是SR以及TXD引脚TXDpin,其关系是DR-》SR-》TXDpin。当DR中的数据转移到SR中时TXE置1,如果有数据写入DR时就能将TXE置0;如果SR中的数据全部通过TXDpin移出并且没有数据进入DR,则TC置1。并且需要注意TXE只能通过写DR来置0,不能直接将其清零,而TC可以直接将其写1清零。

对于发送单个字符可以考虑不用中断,直接以查询方式完成。

对于发送字符串/数组类的数据,唯一要考虑的是只在最后一个字符发送后关闭发送中断,这里可以分为两种情况:对于发送可显示的字符串,其用0x00作为结尾的,因此在ISR中就用0x00作为关闭发送中断(TXE或者TC)的条件;第二种情况就是发送二进制数据,那就是0x00~0xFF中间的任意数据,就不能用0x00来判断结束了,这时必须知道数据的具体长度。

这里简单分析上面代码的执行过程:TXE中断产生于前一个字符从DR送入SR,执行效果是后一个字符送入DR。对于第一种情况,如果是可显示字符,就执行USART_SendData来写DR(也就清零了TXE),当最后一个可显示的字符从DR送入SR之后,产生的TXE中断发现要送入DR的是字符是0x00——这当然不行——此时就关闭TXE中断,字符串发送过程就算结束了。当然这时不能忽略一个隐含的结果:那就是最后一个可显示字符从DR转入SR后TXE是置1的,但关闭了TXE中断,因此只要下次再开启TXE中断就会立即进入ISR。对于第二种情况,其结果和第一种的相同。

对于第一种情况,其程序可以这么写:其中TXS是保存了要发送数据的字符串,TxCounter1是索引值:

extern __IO uint8_t TxCounter1;

extern uint8_t *TXS;

extern __IO uint8_t TxLen;

void USART1_IRQHandler(void)

{

if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)

{

if(TXS[TxCounter1]) //如果是可显示字符

{ USART_SendData(USART1,TXS[TxCounter1++]);}

else //发送完成后关闭TXE中断,

{ USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}

}

}

对于第二种情况,和上面的大同小异,其中TXLen表示要发送的二进制数据长度:

void USART1_IRQHandler(void)

{

if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET) //对USART_DR的写操作,将该位清零。

{

if(TxCounter1《TxLen)

{ USART_SendData(USART1,TXS[TxCounter1++]);}

else //发送完成后关闭TXE中断

{ USART_ITConfig(USART1,USART_IT_TXE,DISABLE);}

}

}

事实上第一种情况是第二种的特殊形式,就是说可以用第二种情况去发送可显示的字符——当然没人有闲心去数一句话里有多少个字母空格和标点符号!

在使用时,只要将TXS指向要发送的字符串或者数组,设置TxLen为要发送的数据长度,然后执行USART_ITConfig(USART1, USART_IT_TXE,ENABLE)就立即开始发送过程。用户可以检查TxCounter1来确定发送了多少字节。比如以第二种情况为例:

uint32_t *TXS;

uint8_t TxBuffer1[]=“0123456789ABCDEF”;

uint8_t DST2[]=“ASDFGHJKL”;

__IO uint8_t TxLen = 0x00;

TxLen=8; //发送8个字符,最终发送的是01234567

TXS=(uint32_t *)TxBuffer1; //将TXS指向字符串TxBuffer1

TxCounter1=0; //复位索引值

USART_ITConfig(USART1, USART_IT_TXE,ENABLE); //启用TXE中断,即开始发送过程

while(TxCounter1!=TxLen); //等待发送完成

TXS=(uint32_t *)TxBuffer2; //同上,最终发送的是ASDFGHJK

TxCounter1=0;

USART_ITConfig(USART1, USART_IT_TXE,ENABLE);

while(TxCounter1!=TxLen);

以上就是我认为的最佳方案,但串口中断方式数据有多长就中断多少次,我认为还是占用不少CPU时间,相比之下DMA方式就好多了,因为DMA发送字符串时最多中断两次(半传输完成,全传输完成),并且将串口变成类似16C550的器件。


关键字:STM32  中断 引用地址:STM32 UART2发送后中断的原因是什么?

上一篇:STM32F030 ADC DMA乱序问题
下一篇:STM32之RS485通讯方式实现

推荐阅读最新更新时间:2026-03-24 19:01

STM32的EXTI外部中断踩坑经验分享
一、EXTI(Extern Interrupt)简介 1、中断系统 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回 2、stm32中断 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设(几乎所有外设都可以申请中断)
[单片机]
<font color='red'>STM32</font>的EXTI外部<font color='red'>中断</font>踩坑经验分享
STM32中TIM定时中断功能详解(进阶篇)
一、TIM介绍 1、TIM简介 TIM(Timer)定时器 定时器的基本功能:定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。 即定时触发中断,同时也可以看出,定时器就是一个计数器,当这个计数器的输入是一个准确可靠的基准时钟的时候, 那它在对这个基准时钟进行计数的过程,实际上就是计时的过程。 比如在STM32中,定时器的基准时钟一般都是主频72MHz,如果对72MHz计72个数,计1个数的周期那就是1MHz,1/1MHz=0.000001s,也就是1us的时间,如果对72MHz计72000个数,计1个数的周期那就是1KHz,1/1KHz=0.001,也就是1ms的时间。 STM32的定时器拥有16位计
[单片机]
<font color='red'>STM32</font>中TIM定时<font color='red'>中断</font>功能详解(进阶篇)
STM32中DMA传输中断功能的关闭方法详解
一、为什么我们要关中断功能呢? 1.在撰写类似DMA相关串口传输程序时,我们会使用到队列指针的操作,而这个操作绝对不可以被任何行为打断,打断就寄。 2. 因此我们要在进行队列指针的数据处理时,将全局的中断全部关闭。(很快,就一瞬间开关一下) 3.举个例子: 例如在某一串口传输函数中,以下操作需原子性: ENTER_CRITICAL(); if (队列未满) { txHead = next_head; // 更新队列头 if (UART空闲) { 启动DMA发送(); // 更新txTail和isUART3Busy } } EXIT_CRITICAL(); 若此处不关闭中断,DMA完成回
[单片机]
<font color='red'>STM32</font>中DMA传输<font color='red'>中断</font>功能的关闭方法详解
STM32串口中断控制LED闪烁速度:原理详解与代码实现指南
一、项目背景与目标 在嵌入式系统开发中,经常需要通过外部输入动态调整设备行为。本项目实现通过串口中断接收上位机指令,实时修改 LED 的闪烁间隔,从而控制闪烁速度。核心目标是理解 STM32 的串口中断机制、GPIO 输出控制以及中断优先级配置,掌握通过外部输入动态调整程序逻辑的方法。 二、硬件准备 1.硬件平台: STM32F103 开发板(如 Blue Pill,核心为 STM32F10x 系列) 板载 LED(通常连接到 GPIOC Pin13,即 PC13) USB 转 TTL 模块(用于串口通信,连接 STM32 的 USART1) 连接线(杜邦线) 2.硬件连接: STM32 引脚 功能 连接对象
[单片机]
单片机GD32F3X0 STM32中断总开关汇编代码
__asm void disableAllInt(void) { CPSID I BX LR } __asm void enableAllInt(void) { CPSIE I BX LR } __asm void MSR_MSP(uint32_t addr) { MSR MSP, r0 //set Main Stack value BX r14 } void iap_boot_loader(unsigned int addr) { if(((*(volatile uint32_t *)addr) & 0x2ff00000) == 0x20000000) {
[单片机]
stm32定时器初始化后自动进入一次中断问题
今天在调试定时器时,定时器3出现了自动停止工作的问题,中断设置是每过一秒,进一次中断,相应标志位+1,然后每次都是在标志位=4时停止工作,但是有时候又能正常工作,暂时未解决。 在调试时,发现一个有趣的现象,本次项目我同时配置了定时器4,初始化后是DISABLE未使能状态,但是开始运行,定时器还是会进一次中断,相应的标志位+1,后面不能继续增加。 motor_run_time这个标志位在程序启动后会进一次中断导致+1,而我的定时器并未使能。 我的相关代码是 初始化部分代码: TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE); //允许定时器4更新中断 TIM_Cmd(TIM4,
[单片机]
<font color='red'>stm32</font>定时器初始化后自动进入一次<font color='red'>中断</font>问题
关于stm32不常用的中断,如何添加, 比如timer10 timer11等
首先可以从keil中找到 比如找到定时器11的溢出中断,如上图是26 然后,配置定时器11 溢出中断的时候,我就在:下面填上这个变量. 之后要写中断服务函数,也就是发生中断后要跳转到的函数. 需要知道函数名,如何找呢:(我是找到其他已知的中断函数名字,进行搜索,找到这里的) 第一个是0,数到 26,因为上面图片是26 这里上面它就是函数名字了 然后编写函数:(可以在下面函数中填入点亮小灯什么的...) 如果你找错,就可能会进入中断无法出来,或者找不到中断后跳转的函数而发生故障...
[单片机]
关于<font color='red'>stm32</font>不常用的<font color='red'>中断</font>,如何添加, 比如timer10 timer11等
明解STM32—GPIO应用设计篇之IO外部中断EXTI原理及使用方法
一、前言 在之前针对STM32的GPIO相关API函数及配置使用进行了详细的介绍,GPIO作为输入引脚时,调用相关读信号引脚函数接口就可以在程序的循环中,轮询的对输入信号进行读取检测操作,除了轮询的方式访问输入引脚,还可以通过另外一种叫做外部中断的方式来对引脚的输入信号进行检测,本篇首先介绍下EXTI的结构,接着介绍外部中断的相关概念,对STM32的IO外部中断EXTI有个初步的了解,在此基础上重点围绕IO外部中断EXTI的使用展开分析。 图1 外部中断设计 二、EXTI结构 EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20个中断
[单片机]
明解<font color='red'>STM32</font>—GPIO应用设计篇之IO外部<font color='red'>中断</font>EXTI原理及使用方法
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved