久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 4369|回復: 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;每發送一次數據,都會相應自加上數據的長度,程序中沒有對其清零, 卻似乎永遠沒有溢出的跡象。怎么解釋?

回復

使用道具 舉報

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 | 顯示全部樓層
最近有個項目的程序用到串口中斷,看了樓主寫的東西受益匪淺,感謝
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 久久久国产精品 | 欧美日韩一区在线 | 欧美色性 | 精品精品 | 国产一区二区在线播放 | 91精品国产91久久久久久 | 久草成人 | 久久久www成人免费无遮挡大片 | 国产伦精品一区二区三区精品视频 | 99久久精品免费看国产小宝寻花 | 国产婷婷精品av在线 | 范冰冰一级做a爰片久久毛片 | 一级日韩 | 精品96久久久久久中文字幕无 | 久久久久国产一区二区三区四区 | 在线观看中文视频 | av在线播放免费 | 91久久精品一区二区二区 | 成人不卡视频 | 欧美最猛黑人 | 久久久久久美女 | 精品一区国产 | 一区二区在线 | 一区二区视频免费观看 | 久久久久国产 | 99精品久久 | 久久国产精品久久 | 久久久久久久久久久蜜桃 | 久久中文字幕一区 | 欧美激情国产精品 | 欧美激情黄色 | 欧美在线精品一区 | 国产一区2区 | 天色综合网 | 综合网在线 | 91在线视频观看 | 在线观看毛片网站 | 四季久久免费一区二区三区四区 | 一级在线毛片 | 99精品久久久 | 久草在线视频中文 |