STM32串口发送中断

发布者:brian808090最新更新时间:2016-06-14 来源: eefocus关键字:STM32  串口发送中断 手机看文章 扫描二维码
随时随地手机看文章

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 == '\0' )//TC需要 读SR+写DR 方可清0,当发送到最后,到'\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 == '\0' )//待发送的字节发到末尾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),另一个是程序看不到的移位寄存器(下图中阴影部分Transmit Shift Register)。

对应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                     { 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串口发送中断

上一篇:STM32串口中断接收一个完整的数据帧
下一篇:STM32缺陷之一:串口中断标志位缺陷

推荐阅读最新更新时间:2024-03-16 14:57

STM32笔记——外部中断
基本概念 一、中断和异常 1、中断——当需要出现时,CPU暂时停止当前程序的执行,转而执行处理新情况的程序和执行过程。 2、异常——处理器要复位,不可屏蔽中断,存储故障,这些由内核产生的就叫做异常,也就是硬件异常。 二、EXIT和NVIC EXIT外部中断/事件控制器 外部中断/事件控制器包含多达 23 个用于产生事件/中断请求的边沿检测器。每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发)。每根输入线还可单独屏蔽。挂起寄存器用于保持中断请求的状态线。 外部中断框图: 触发机制 上升沿:数字电路中,数字电平从低电平(数字“0”)变为高电平(数字“1”)的那
[单片机]
<font color='red'>STM32</font>笔记——外部<font color='red'>中断</font>
stm32中定时器中断的套路
首先要提到的是,在stm32中,定时器相关的库函数主要集中在固件库文件 stm32f10x_tim.h 和 stm32f10x_tim.c 文件中,所以在进行定时器方面的处理时,需要加载这两个文件进来。在进行定时器操作时,一般按如下步骤来进行: 1、打开相关外设的时钟。以定时器TIM3为例,由stm32的时钟树可以看到,TIM3时钟挂接在APB1上面,所以打开TIM3时钟时使用RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE),如果其中还使用到了其他外设,如GPIO等,再打开相关的外设时钟就可以了。 2、清除中断挂起位。由于各种不可知的因素作用,在程序运行前要操
[单片机]
STM32+ucosll程序跑飞问题解决办法
STM32单片机移植ucosll程序,运行几个小时之后跑飞。 ucsll创建4个任务,OSQPend()消息队列任务,1s定时任务,2s定时任务,按键扫描任务。其中OSQPend()消息队列任务优先级最高,用于接收各中断消息,由于消息队列在没有消息时会将任务挂起,所有没有添加延时函数。 一开始怀疑堆栈溢出,将单片机栈区扩大,各任务堆栈也扩大,仍然无效。 多次测试发现,程序跑飞后OSQPend()消息队列任务仍然能够接受中断消息,怀疑优先级有问题,将消息队列任务的优先级调至最低,再未遇到程序跑飞情况。 只是仍然不解为何会停留在消息队列任务中!!
[单片机]
STM32+超声波
1.初始化IO口 void HC_SR04Init(void){ GPIO_InitTypeDef GPIOInit; TIM_TimeBaseInitTypeDef TIMInit; NVIC_InitTypeDef NVICInit; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //负责发送高电平PB11 GPIOInit.GPIO_Mode =
[单片机]
51,AVR,PIC,MSP430,STM32单片机比较
51,AVR,PIC,MSP430,STM32比较:常用型号,特点,优缺点,编译软件,下载软件,下载方式。 传统8051:适合菜鸟入门,容易上手,价格一般(从性价比方面说)。 缺点:解密容易(传统51说:谁让咱出道早呢,大家都研究我,哎!哭......)一般功能也有,, 但AD、eeprom等功能要靠扩展,增加硬件和软件负担。 IDE环境推荐 keil。编程器自己自制ISP下载线就行,好做,成本5元左右。 8051:MCS-51系列 INTEL公司 8031:无ROM 8051:一次性掩膜ROM 8751:多次写EPROM stc 51系列:(增强型51单片机)价格便宜(从性价比方面说),功能多,
[单片机]
介绍一种关于STM32 ADC 多通道16路电压采集技术
下面介绍一种利用STM32单片机制作的16路多通道ADC采集电路图和源程序。采用USB接口与电脑连接,实则USB转串口方式,所以上位机可以用串口作为接口。电路图中利用LM324作为电压跟随器,起到保护单片机引脚的作用。直接在电脑USB取点,省去外接电源麻烦,实测耗电电流不到20ma. 1.主控电路图: 2. USB转串口电路图 3.LM324电压跟随器电路图 4.滤波电路图 5.16路接口电路图 6.电源电路图 7.16路ADC初始化程序: voidAdc_Init(void) { //先初始化IO口 RCC-》APB2ENR|=0X7《《2;//使能PORTAPORABPORTC口时钟 GPIOA-》CR
[单片机]
介绍一种关于<font color='red'>STM32</font> ADC 多通道16路电压采集技术
能够完美替代STM32的两款产品介绍和评测
今日带来能够完美替代STM32的产品是沁恒微电子的CH32F103以及RISC-V内核的CH32V103两款产品。 CH32F1系列是基于32位Cortex-M3内核设计的通用微控制器。片上集成了时钟安全机制、多级电源管理、 通用DMA控制器等。此系列具有 2 路 USB2.0接口、多通道 TouchKey、 12 位 DAC 转换模块,多通道 12 位 ADC、 多组定时器、 CAN 通讯控制器、 I2C/USART/SPI 等丰富的外设资源。 CH32V103则是其在去年上半年推出的RISC-V架构的32位通用MCU微控制器。CH32V103系列是以RISC-V3A处理器为核心的32位通用微控制器,该处理器是基于RISC-V开
[单片机]
能够完美替代<font color='red'>STM32</font>的两款产品介绍和评测
认识STM32的复位与电源管理
复位功能是一个非常重要的功能,大到PC,小到单片机,每一台计算机系统都有。在我小时候,去网吧的时候,但凡是电脑出现任何一点小问题,网管的第一回答一定是重启。重启和复位就是同一个意思。 发展到现在PC已经很稳定了,复位按钮慢慢的淡出我们的视野,但是在主板上我们依然可以找到复位电路的存在。单片机上的复位功能也有着类似的变化,在我们学单片机那个年代,需要在单片机的外部一个复位电路,从专用引脚上接一个电阻和电容组成的复位电路。如果没有这个电路,单片机就没法工作。现在的新款单片机都把复位功能内置到单片机中。如果是开发项目要手动复位,我们可以在复位引脚上接一个按键用来手动复位。 复位 复位功能的作用是让RAM 中的数据清空,让所有连接
[单片机]
认识<font color='red'>STM32</font>的复位与电源管理
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

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

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved