设为首页 收藏本站
开启辅助访问 切换到宽版

  找回密码
  立即注册

QQ登录

只需一步,快速开始

快捷导航
  • 论坛
  • 问答区
  • 单片机教程
  • 单片机DIY
  • 电子DIY
  • 帮助

热门: 51单片机 | 24小时必答区 | 单片机教程 | 单片机DIY制作 | STM32 | Cortex M3 | 模数电子 | 电子DIY制作 | 音响/功放 | 拆机乐园 | Arduino | 嵌入式OS | 程序设计

搜索
» 论坛 嵌入式/单片机论坛 STM32/8 stm32发送中断编程思路
返回列表 发新帖
查看: 3574|回复: 3
打印 上一主题 下一主题
收起左侧

stm32发送中断编程思路

[复制链接]
跳转到指定楼层
楼主
ID:104126 发表于 2016-1-23 00:47 | 只看该作者 回帖奖励 | 倒序浏览 | 阅读模式
u8 TxBuffer[2];
u8 TxCounter=0;
u8 count=0;
void USART_SendData_Int(u8 *str)
{
        while(*str != '\0')
        {
                TxBuffer[count++] = *str++;
        }  
        USART_ITConfig(USART1, USART_IT_TXE, ENABLE);        
}
void delay(void)
{
        long i,j;
        for(i=0;i<1500;i++)
                for(j=0;j<2000;j++);
}
int main(void)
{
    USART_Configuration();
    NVIC_Configuration();

    while (1)
    {
          USART_SendData_Int("012345678901234567890123456789012345\n");        
          USART_SendData_Int("abcdefghijklmn\n");
          delay();   
    }
}
void USART_Configuration(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  USART_InitTypeDef USART_InitStructure;

  RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1,ENABLE);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;                 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);                  

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;               
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);

  USART_InitStructure.USART_BaudRate = 115200;
  USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  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);
  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
  USART_ITConfig(USART1, USART_IT_TXE, DISABLE);        
  USART_ClearFlag(USART1,USART_FLAG_TC);
  USART_Cmd(USART1, ENABLE);
}
void NVIC_Configuration(void)
{
        NVIC_InitTypeDef NVIC_InitStructure;

        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 7;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
}
void USART1_IRQHandler(void)
{

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

    USART_SendData(USART1, TxBuffer[TxCounter++]);                    


    USART_ClearITPendingBit(USART1, USART_IT_TXE);

    if(TxCounter == count)
    {

      USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
    }   
  }
}

这个程序经过调试,在串口调试助手上一点问题都没有!!!不过似乎从程序上看问题很多:1、我定义的串口缓存TxBuffer[2],为什么大小只有两个字节(应该说是任意字节大小都可以),我程序在发送数据时,数据大小远远超过两个字节,但是却没报错,更是还能正确运行。。。没懂!?2、我定义的两个全局变量u8 TxCounter=0;u8 count=0;每发送一次数据,都会相应自加上数据的长度,程序中没有对其清零, 却似乎永远没有溢出的迹象。怎么解释?

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:104126 发表于 2016-1-23 00:47 | 只看该作者
查询方式:发送数据——先发后查;

                     接收数据——先查后收。

中断方式:发送数据——发送、等待中断、中断中发送;

                    接收数据——等待中断、在中断中接收。


发送一个字符串
查询发送:发送完一个字节,等待发送完,继续发送下个字节,直到整个字符串发送完
          在这期间一直占用CPU
中断发送:发送完一个字节CPU响应中断将下个字节放到外设寄存器,直到整个字符串发完
          只有在中断时占用CPU资源

串口发送中断我只用过一次:
只要将第一个字节送进串口发送数据寄存器,就可以在串口发送中断里依次把剩余的字节都发送完,省去了等待每一个字节发送完的时间。
不过这是在AVR中用的。STM32的DMA就可以实现自动发送。
回复

使用道具 举报

板凳
ID:104126 发表于 2016-1-23 00:48 | 只看该作者

SECTION 2

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


void USART_SendDataString( u8 *pData )
{
    pDataByte = pData;
  
    USART_ClearFlag(USART1, USART_FLAG_TC);//清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.
   
    USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次
}


中断处理函数如下

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 导致反复进入中断了。

串口初始化函数如下


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不同之处。

发送函数如下:

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

中断处理函数如下:


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的中断了(是在发送函数中打开的)如下:

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的器件。



回复

使用道具 举报

地板
ID:75321 发表于 2016-3-24 11:33 | 只看该作者
最近有个项目的程序用到串口中断,看了楼主写的东西受益匪浅,感谢
回复

使用道具 举报

返回列表 发新帖
高级模式
B Color Image Link Quote Code Smilies
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版| 小黑屋| 51黑电子论坛 | 51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表

深圳SEO优化公司民治企业网站改版哪家好绍兴网页设计哪家好保定品牌网站设计价格宿迁网页制作报价平顶山百搜标王多少钱潍坊网站建设设计哪家好汉中SEO按天计费哪家好南宁企业网站设计推荐铁岭网站优化排名报价怀化网站推广工具推荐成都网站推广系统邯郸品牌网站设计推荐蚌埠如何制作网站曲靖优秀网站设计报价白城百度网站优化排名崇左网站优化推广哪家好龙华外贸网站制作公司湘潭高端网站设计价格岳阳高端网站设计哪家好金昌百姓网标王推广孝感关键词排名报价南充营销型网站建设多少钱中山网站设计模板多少钱龙岩关键词按天收费价格随州seo排名多少钱九江网站优化按天计费多少钱长葛百度竞价哪家好绍兴外贸网站设计推荐平凉网站搜索优化价格贺州网站关键词优化报价歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化