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

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 3608|回復: 0
收起左側

(菜鳥筆記)類比STC51單片機的STM32串口中斷純寄存器操作

[復制鏈接]
ID:104497 發表于 2021-2-3 15:01 | 顯示全部樓層 |閱讀模式
      本菜文以STM32的USART1中斷為例,類比STC系列的51單片機相對應的操作,粗淺地分析一下STM32串口中斷各項配置的含義。本著知其所以然的理念,例程采用了直接訪問寄存器的方式,未使用任何庫函數。
    盡管STM32和STC相差很大,但它們的串口操作流程還是比較相近的,都包括引腳配置、通訊參數配置、中斷配置和中斷服務程序等步驟。為了節約時間,前兩項內容就不多說了。
    例程的運行條件如下:
1.USART1_RX在PA10上,配置成上拉/下拉輸入模式,USART1_TX在PA9上,配置成2MHz的復用推挽模式;
2.9600bps,8N1數據格式;
3.PB0接了一只LED。
    下面進入串口中斷配置主題。
    先通過下圖回顧一下STC單片機串口1中斷邏輯,以及流程中涉及到的概念。
圖1.jpg






    寄存器SCON的位1(TI發送中斷請求)和位0(RI接收中斷請求)是兩個中斷源,也就是說,在串口發送或接收數據時,硬件會使SCON.1或SCON.0置位,從而產生TI發送中斷請求或接收中斷請求,但這兩個請求能不能被程序響應,還得過ES和EA兩關,如果IE.4(ES串口中斷使能)和IE.7(EA中斷總開關)都為1,TI或RI的請求才能進入中斷優先級評估模塊。在這個模塊里,TI或RI中斷會根據寄存器IPH和IP相應位的值分為4個優先級。
    不管TI或RI中斷是什么優先級,只要中斷被CPU響應了,那就會把串口1中斷向量賦值給程序計數器PC,也就是把程序存儲器0023H和0024H兩個單元的值賦給PC,從而進入串口的中斷服務程序,直至執行到RETI指令為止才退出。
    這個過程至少涉及五個主要概念:中斷源、中斷使能、中斷優先級、中斷向量、中斷服務程序。同樣,STM32的串口1中斷也有這五個概念。
    下圖是STM32F1xx串口1中斷邏輯:
圖3.jpg
    現在開始直接面向寄存器操作。
1、中斷源使能配置
目標寄存器:USART1_CR1串口1控制寄存器1
寄存器地址:0x4001380c
賦值:0x202c
    本例僅開放了接收中斷,所以只用到了USART1_CR1寄存器。UE(USART1_CR1.13)、RXNEIE(USART1_CR1.5)、TE(USART1_CR1.3)和RE(USART1_CR1.2)同時置位,這就產生了0x202c的賦值。
圖2.jpg
    這是USART1中斷源及相應使能位的邏輯圖,圖中標紅線路是可以產生中斷的信號,綠色信號代表各個中斷線路的使能信號。咱得承認,人家STM32F1xx的中斷源還真是挺多的,而且都能單獨使能,比STC靈活多了。
2、串口中斷開放
    USART1沒有像STC那樣的中斷總開關EA,但它有和ES類似的串口中斷使能,所有的中斷都有自己的使能開關,它們集合在一個寄存器族NVIC_ISER0~NVIC_ISER7里,每個中斷各占其中的一個位。
    Cortex-M3在這方面的做法很有意思,它通過中斷向量表為每個中斷分配了一個向量號,然后再以這個向量號為索引,找到并操作NVIC寄存器組中相關的寄存器或位,包括后面要說的中斷優先級設置也是如此,所以咱們先來看看STM32中斷向量表:
圖4.jpg
    USART1的向量號是37,請記住這個‘門牌號’。
    開放中斷需要操作NVIC_ISERx寄存器:
圖5.jpg
    NVIC_ISER共8個寄存器,即NVIC_ISER0---NVIC_ISER7,而STM32F1xx只排了60多個中斷號(不同型號可用的中斷數量不同),所以實際上只使用了NVIC_ISER0~NVIC_ISER2,其中在NVIC_ISER0占座的是0號窗口看門狗WWDG中斷---31號I2C1事件中斷,USART1中斷在NVIC_ISER1的bit5上。所以,USART1中斷使能的操作如下:
目標寄存器:NVIC_ISER1中斷設置使能寄存器1
寄存器地址:0xe000e104
賦值:bit5置位
3、中斷優先級分組設置
    STM32的中斷優先級設置比STC復雜一些,它需要先設置所有中斷的優先級分組,也就是通過SCB_AIRCR寄存器將中斷優先級分為搶占級和響應級,這是兩個與STC不太一樣的優先級概念。
    首先需要明確的是,SCB_AIRCR設置的不是某個具體中斷的搶占級和響應級,而是針對所有中斷。
目標寄存器:SCB_AIRCR系統控制模塊應用程序中斷和復位控制寄存器
寄存器地址:0xe000ed0c
賦值:0x05fa0400
圖6.jpg
    書上說了,每次操作這個寄存器都必須在VECTKEY段上寫0x05fa,否則它不認賬,會忽略寫入操作。SCB_AIRCR的復位值很‘聰明’,恰好是密鑰0x05fa的反碼。
    在寫入密鑰的同時對PRIGROUP[2:0]賦值才是核心目的,在英文手冊上它的全稱是interrupt priority grouping,字面意思是中斷優先級分組,而按照它的功能來說,俺覺得叫‘中斷優先級分隔位標志’比較貼切,因為它指示出了搶占級和響應級的分隔位置。
    STM32F1xx用1個叫IP[x]的字節來定義優先級,但只用了它的高4位,并且按照PRIGROUP[2:的值把這4位分成搶占和響應兩組,以本例中PRIGROUP[2:0]=100(也就是4)為例,IP[x]從位4分隔開來,位7~位5成為搶占組,可形成23(8級)搶占級別,又因為位3~位0沒有啟用,所以只剩下位4作為響應組,也就只有0、1兩個響應級別。
位7
位6
位5
位4
位3~位0
搶占級
響應級
未啟用
    在后序的步驟當中,這個IP[x]字節將在具體中斷優先級配置當中賦不同的數值,從而確定相應中斷的優先級。
    回過頭來簡單說一說什么是搶占級(Group priority)和響應級(Sub priority)。
    搶占級的數值越低,優先級就越高,可以嵌入低級別中斷;響應沒有搶先嵌套權,但響應級別高的可以優先執行。假設正在執行一個搶占級為1/響應級為1的A中斷,簡稱為A(g1s1),又來了3個中斷,分別是B(g0s1)、C(g2s1)、D(g2s0),那么B就會打斷A嵌套進去被執行,沒辦法,誰讓B的搶占級別高呢。等B執行完了,A就會繼續執行,C和D也只能在外面等著,因為它們的搶占級別都比A低。等A執行完了就會優先執行D,因為盡管C和D的搶占級是一樣的,但D的響應優先級高于C,C只好眼巴巴了。
3、結合PRIGROUP[2:的具體中斷優先級設定
    具體到USART1中斷,剛才說的PRIGROUP[2:0]和優先級字節IP[x]該怎樣落地呢?
目標寄存器:NVIC_IPR9中斷優先級設置寄存器
寄存器地址:0xe000e409
賦值:對應IP[37]的字節賦值0x30
    STM32設置了一個叫做NVIC_IPRx的寄存器陣列,每個寄存器可以存放4個中斷的優先級8位(1個字節)配置數據,這個陣列與NVIC_ISERx的布局方式類似,只不過NVIC_ISERx是位布局,而NVIC_IPRx則是字節布局:
寄存器
地址
字節4
bit[31:24]
字節3
bit[23:16]
字節2
bit[15:8]
字節0
bit[7:0]
NVIC_IPR0
0xe000e400
中斷3
中斷2
中斷1
中斷0
RTC
TAMPER
PVD
WWDG
IP[3]
IP[2]
IP[1]
IP[0]
NVIC_IPR1
0xe000e401
中斷7
中斷6
中斷5
中斷4
EXTI1
EXTI0
RCC
FLASH
IP[7]
IP[6]
IP[5]
IP[4]
……
NVIC_IPR9
0xe000e409
中斷39
中斷38
中斷37
中斷36
USART3
USART2
USART1
SPI2
IP[39]
IP[38]
IP[37]
IP[36]
……
    IP[37]就是USART1中斷優先級配置的字節,
    PRIGROUP[2:0]=4已經確定了搶占級用3個位、響應級用1個位,那么IP[37]=0x30的意思就是配置USART1中斷的搶占級為1,響應級也為1。

位7
位6
位5
位4
位3~位0
PRIGROUP
產生的分隔
搶占級
響應級
未啟用
IP[37]
0
0
1
1
0000
4、例程
//串口1中斷測試程序
//GPIOA/GPIOB相關寄存器定義
#define GPIOA_Base  0x40010800
#define GPIOB_Base  0x40010C00
#define GPIO_ConfigurationRegisterLow        0x00
#define GPIO_ConfigurationRegisterHigh        0x04
#define GPIO_OutputDataRegister              0x0c
#define GPIOA_CRH (*(volatile unsigned int*)(GPIOA_Base + GPIO_ConfigurationRegisterHigh))
#define GPIOB_CRL (*(volatile unsigned int*)(GPIOB_Base + GPIO_ConfigurationRegisterLow))
#define LED1  *(volatile unsigned int *)0x42218180        
//RCC相關寄存器定義
#define RCC_APB2ENR        (*(volatile unsigned int*)0x40021018)
//USART1相關寄存器定義
#define                USART1_Base        0x40013800
#define                USART_DataRegister                0x04
#define                USART_BaudRateRegister                0x08
#define                USART_ControlRegister1                0x0c
#define                USART_ControlRegister2                0x10
#define                USART1_DR        (*(volatile unsigned int*)(USART1_Base + USART_DataRegister))
#define                USART1_BRR        (*(volatile unsigned int*)(USART1_Base + USART_BaudRateRegister))
#define                USART1_CR1        (*(volatile unsigned int*)(USART1_Base + USART_ControlRegister1))
#define                USART1_CR2        (*(volatile unsigned int*)(USART1_Base + USART_ControlRegister2))
#define                ResetUSART1        *(volatile unsigned int*)0x424201b8
#define                USART1_RXNE        *(volatile unsigned int*)0x42270014
//NVIC相關寄存器定義
#define                SCB_AIRCR        *(volatile unsigned int*)0xe000ed0c
#define                NVIC_ISER1        *(volatile unsigned int*)0xe000e104
#define                NVIC_IPR9        *(volatile unsigned int*)0xe000e409
//---------------------------------------
void Delay_ms(unsigned short int MsCount)
{
          unsigned int i = 0;
   while(MsCount--)
   {
        i=8030;
        while(i--);
   }
}

void U1BaudRate(unsigned int PCLK2,unsigned int BaudRate)
//波特率設置        
{
    float UsartDiv;
    unsigned short int Mantissa;//波特率換算參數整數部分
    unsigned short int Fraction;//波特率換算參數小數部分
    UsartDiv = (float)(PCLK2*1000000)/(BaudRate*16);
    Mantissa = UsartDiv;//得到整數部分
    Fraction = (UsartDiv-Mantissa)*16;//得到小數部分
    Mantissa <<= 4;
    Mantissa = Mantissa + Fraction;
    USART1_BRR = Mantissa;
}

int main(void)
{
        /*為節約篇幅,就不把時鐘初始化程序列出來了,PCLK2為72MHz.*/
    //開啟USART1和GPIOA/GPIOB時鐘
    RCC_APB2ENR |=0x0000400c;
    //配置USART1_RX(PA10)和USART1_TX(PA9)
    GPIOA_CRH = (GPIOA_CRH & 0xfffff00f) | 0x000008a0;
    //配置LED1(接在了PB0上)引腳狀態
    GPIOB_CRL = (GPIOB_CRL & 0xfffffff0) | 0xee000006;
    //復位USART1
    ResetUSART1=1;
    ResetUSART1=0;
   //配置波特率為9600,數據格式為8N1
    U1BaudRate(72,9600);
   //使能USART1模塊,使能發送和接收,開啟接收中斷
    USART1_CR1 |=0x202c;
    //寫入VECTKEY,PRIGROUP賦值為4
    SCB_AIRCR &=0x05faf8ff;
    SCB_AIRCR |=0x05fa0400;
    //使能USART1中斷
    NVIC_ISER1 |=1<<5;
    //配置USART1的搶占優先級和響應優先級
    NVIC_IPR9 &=0xffff00ff;
    NVIC_IPR9 |=0x3000;

    while(1)
    {
        LED1 = 0;
        Delay_ms(1000);
        LED1 = 1;
        Delay_ms(1000);               
     }
}

void USART1_IRQHandler(void)
//串口1中斷服務程序        
{
    unsigned char tmp;
    if(USART1_RXNE ==1)
    {
        USART1_RXNE=0;
        tmp=USART1_DR;
        USART1_DR=tmp;
    }
}

評分

參與人數 1黑幣 +100 收起 理由
admin + 100 共享資料的黑幣獎勵!

查看全部評分

回復

使用道具 舉報

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

本版積分規則

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

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 鸡毛片 | 91精品一区| 一级少妇女片 | 正在播放亚洲 | 国产一区二区三区久久久久久久久 | 不卡一区二区三区四区 | 国产激情91久久精品导航 | 四虎影音 | 久久国产欧美日韩精品 | 日韩一区中文字幕 | 亚洲精品视频在线 | 中文成人在线 | 91久久久久久久久久久 | 成人在线免费 | 成人激情视频 | jizz中国日本 | 欧美狠狠操| 精品日韩一区二区 | 亚洲免费三级 | 美女黄视频网站 | 黄色精品 | 国产精品久久久久久久久久久久久 | 亚洲91精品| 成人在线中文字幕 | 特级特黄特色的免费大片 | 高清一区二区三区 | 日韩中文一区二区三区 | 日本不卡一二三 | 国产亚洲日本精品 | 亚洲小视频在线播放 | 久久夜视频 | 欧美看片 | 欧美爱爱视频网站 | 福利视频一区二区 | 国产成人啪免费观看软件 | 久久成人免费 | 97精品视频在线 | 毛片一区二区三区 | 日韩一区二区在线播放 | 亚洲精品福利在线 | 欧洲av在线 |