沒寫部落格的感悟:昨天沒有寫部落格,今天就倒黴了,得寫兩篇,果然不能偷懶,當天沒有做的事,無論如何你都得要做,為了改掉這個拖延的小毛病,給自己定了一條規則,無論多晚,哪怕沒有網沒有電也得寫完每天更新的部落格,以此勉勵。
今天主要總結和複習三個知識點,NVIC中斷優先級管理、序列槽通信、及序列槽通信的簡單配置執行個體:
第一部分:
NVIC中斷優先級管理:
首先是中斷分組,我們知道所用的CM3核心支援256個中斷,其中包含了16個核心中斷和240個外部中斷,并且具有256級的可程式設計中斷設定。然而STM32并沒有使用CM3核心的全部東西,而是隻用了它的一部分。STM32有84個中斷,包括16個核心中斷和68個可屏蔽中斷,具有16級可程式設計的中斷優先級。而我所用的STM32F103系列上面,又隻有60個可屏蔽中斷(在107系列才有68個),而STM系列把中斷分為5個組,如下圖一所示
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DM2YDN1ATMwITMxETM1EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
圖一
這麼多大概60個中斷如何管理,這是我一開始想到的問題,當然是用寄存器進行管理,有七組寄存器對所有的中斷進行管理,中斷寄存器分别如下,引用MDK對寄存器組的分類:
typedef struct
{
__IO uint32_t ISER[8]; //中斷使能寄存器組:設定為:8個32位寄存器來控制256個中斷,由于我用的隻有60個故隻需設定ISER[0]-ISER[2]即可
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; //中斷除能寄存器組:設定和中斷使能一樣
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; //中斷挂起寄存器組:設定和中斷使能一樣,通過置一把正在執行的中斷挂起進而執行同級别或更進階别的中斷,寫0無效
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; //中斷解挂控制寄存器組:設定和中斷使能一樣作用和挂起寄存器組相反
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; // 中斷激活标志位寄存器組:這是一個隻讀寄存器,如果置一可以知道該位所對應的正在運作的中斷,運作完畢由硬體自動清零
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; // 中斷優先級控制寄存器組:這是非常重要的一個寄存器,總共有240個8位的寄存器組成,每8位代表一個中斷,而每八位隻用了其高八位,我所用的103系列隻用了0-//67即可,詳細的會在下面給出
uint32_t RESERVED5[644];
__O uint32_t STIR; // 軟體觸發中斷寄存器組
} NVIC_Type;
中斷優先級控制寄存器組,說這個之前得明白一個概念,優先級概念,STM系列的中斷優先級分為兩級,搶占優先級和響應優先級。而又得知道兩者的差別,其中差別如下:
1、 高優先級的搶占優先級是可以打斷正在進行的低搶占優先級中斷的。 2、 搶占優先級相同的中斷,高響應優先級不可以打斷低響應優先級的中斷 。 3、 搶占優先級相同的中斷,當兩個中斷同時發生的情況下,哪個響應優先級高,哪個先執行。 4、 如果兩個中斷的搶占優先級和響應優先級都是一樣的話,則看哪個中斷先發生就先執行; 搶占優先級和響應優先級中,數字越小說明級别越大。舉個例子,假定設定中斷優先級組為2,然後設定中斷1(RTC中斷)的搶占優先級為2,響應優先級為1。 中斷6(外部中斷0)的搶占優先級為3,響應優先級為0。中斷4(外部中斷1)的搶占優先級為2,響應優先級為0。則中斷優先級為中斷4>中斷1>中斷6. 在詳細弄懂了上述所有知識後,就可以設定自己我們自己的中斷,具體步驟如下: ① 系統運作後先設定中斷優先級分組。調用函數:
voidNVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);/
整個系統執行過程中,隻設定一次中斷分組。
②針對每個中斷,設定對應的搶占優先級和響應優先級:
voidNVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
③ 如果需要挂起/解挂,檢視中斷目前激活狀态,分别調用相關函數即可。
第二部分:序列槽通信基本原理 這個複習得從通信的背景開始闡述,處理器與外部的通信分為兩種方式并行和串行方式:并行的優點是速度快缺點為占用IO口較多,串行則速度慢優點為占用的IO口較少。串行通信按照傳輸方向又有三種方式,單工,半雙工,全雙工如圖二所示:
圖二
看完圖應該就很清晰了,再解釋一下 1、 單工: 資料傳輸隻支援資料在一個方向上傳輸 2、 半雙工: 允許資料在兩個方向上傳輸,但是,在某一時刻,隻允許數 據在一個方向上傳輸,它實際上是一種切換方向的單工通信; 3、 全雙工: 允許資料同時在兩個方向上傳輸,是以,全雙工通信是兩個 單工通信方式的結合,它要求發送裝置和接收裝置都有獨立 的接收和發送能力。 按照是否帶時鐘分為同步和異步,其中STM32的序列槽通信接口, UART: 通用異步收發器 , USART: 通用同步異步收發器 我所用的闆子中的大概電路如圖三:
圖三
STM32序列槽異步通信主要的參數定義為:起始位,資料位,硬體流位,停止位,波特率設定。舉個例子如圖四:
圖四
第三部分: 下面進行第三部分,執行個體示範,在這之前得明白所用的三個寄存器 1、 USART_SR 狀态寄存器 2、 USART_DR 資料寄存器 3、 USART_BRR 波特率寄存器 而我們在運用庫函數編寫序列槽代碼過程一般會運用到下面的幾個庫函數:
voidUSART_Init();//序列槽初始化:波特率,資料字長,奇偶校驗,硬體流控以及收發使能
voidUSART_Cmd();//使能序列槽
voidUSART_ITConfig();//使能相關中斷
voidUSART_SendData();//發送資料到序列槽,DR
uint16_tUSART_ReceiveData();//接受資料,從DR讀取接受到的資料
FlagStatusUSART_GetFlagStatus();//擷取狀态标志位
voidUSART_ClearFlag();//清除狀态标志位
ITStatusUSART_GetITStatus();//擷取中斷狀态标志位
voidUSART_ClearITPendingBit();//清除中斷狀态标志位
其中提出一點波特率的計算方式如圖5所示:
圖五
最後就可以進行執行個體操作:總結步驟如下: ①序列槽時鐘使能,GPIO時鐘使能:RCC_APB2PeriphClockCmd(); ②序列槽複位:USART_DeInit(); 這一步不是必須的 ③GPIO端口模式設定:GPIO_Init(); 模式設定為GPIO_Mode_AF_PP ④序列槽參數初始化:USART_Init(); ⑤開啟中斷并且初始化NVIC(如果需要開啟中斷才需要這個步驟)
NVIC_Init();
USART_ITConfig();
⑥使能序列槽:USART_Cmd();
⑦編寫中斷處理函數:USARTx_IRQHandler();
⑧序列槽資料收發:
void USART_SendData();//發送資料到序列槽,DR
uint16_t USART_ReceiveData();//接受資料,從DR讀取接受到的資料
⑨序列槽傳輸狀态擷取:
FlagStatusUSART_GetFlagStatus(USART_TypeDef* USARTx,uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
編寫得主函數代碼如下:
#include "stm32f10x.h"
//序列槽通信實驗1
void my_usart_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructA;
USART_InitTypeDef USART_InitStructA;
NVIC_InitTypeDef NVIC_InitStructA;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);//enable GPIOA和USART1 的時鐘
//初始化GPIOA,查表可知序列槽1發送模式下是全雙工,GPIO口設定為推挽複用模式
GPIO_InitStructA.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructA.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructA.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructA);
//初始化GPIOA,查表可知序列槽1接受模式下是全雙工,GPIO口設定為浮空輸入模式
GPIO_InitStructA.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructA.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructA.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructA);
//初始化序列槽參數
USART_InitStructA.USART_BaudRate=115200;
USART_InitStructA.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructA.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
USART_InitStructA.USART_Parity=USART_Parity_No;
USART_InitStructA.USART_StopBits=USART_StopBits_1;
USART_InitStructA.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructA);
//開啟接受中斷且初始化NVIC
NVIC_InitStructA.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructA.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructA.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructA.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructA);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//設定序列槽狀态
USART_Cmd(USART1, ENABLE); //使能序列槽1
}
void USART1_IRQHandler(void)
{
u8 res;
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE))//由于有可能開啟了很多中斷故需要判斷是否是我們所要的中斷函數
{
res=USART_ReceiveData(USART1);//接受來自序列槽一的資料
USART_SendData(USART1,res);//
}
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
my_usart_Init();
while(1);
//運作以後就一直在主函數等待,當電腦給單片機發送資料時就跳到中斷
}
由于我沒有JLINK線上調試,故用USB直接下載下傳到單片機進行調試,最後用序列槽助手可以得到所設想達到的輸出:結果如圖六
圖六
其中白色的發送是先經過PC先發送給單片機,如然後單片機又發送給PC由序列槽助手在列印得出;