??本專欄爭取每周三更新直到更新完成,期待大家的訂閱關注,歡迎互相學習交流。
目錄
- 一、復位
- 1.1 軟件復位
- 1.2 低功耗管理復位
- 二、時鐘
- 2.1 系統時鐘(SYSCLK)選擇
- 2.2 系統時鐘初始化
- 三、滴答定時器(Systick)
- 3.1 SysTick部分寄存器
- 3.2 SysTick相關函數
- 3.2.1 SysTick時鐘源配置函數
- 3.2.2 SysTick初始化函數
一、復位
??STM32F10系列單片機的復位方式有三種,分別是系統復位、上電復位和備份區復位,這里只介紹系統復位,另外兩種如果又想了解的友友可以參考芯片手冊,這里就不再介紹了。
??STM32的系統復位將復位所有寄存器至它們的復位狀態,以下幾種情況會觸發系統復位
- NRST引腳上的低電平(外部復位),這實際也就是我們復位按鍵利用的復位方式。
- 窗口看門狗計數終止(WWDG復位)
- 獨立看門狗計數終止(IWDG復位)
- 軟件復位(SW復位)
- 低功耗管理復位
可通過查看RCC_CSR控制狀態寄存器中的復位狀態標志位識別復位事件來源。
疑問:復位標志位怎么清除,如果發生了復位,重啟后能否看到復位標志位
1.1 軟件復位
??通過將Cortex?-M3中斷應用和復位控制寄存器中的SYSRESETREQ位置’1’,可實現軟件復位。STM32的庫文件中提供了軟件復位函數
/*** @brief Initiate a system reset request.** Initiate a system reset request to reset the MCU*/
static __INLINE void NVIC_SystemReset(void)
{SCB->AIRCR = ((0x5FA << SCB_AIRCR_VECTKEY_Pos) | (SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) | SCB_AIRCR_SYSRESETREQ_Msk); /* Keep priority group unchanged */__DSB(); /* Ensure completion of memory access */ while(1); /* wait until reset */
}
??需要注意的是,從SYSRESETREQ 被置為有效,到復位發生器執行復位命令,往往會有一個延時。在此延時期間,處理器仍然可以響應中斷請求。因此我們在軟件復位前,需要關閉全部中斷,同時在程序最開始的時候開啟全部中斷。STM32庫文件中也提供了一個關閉全部中斷和開啟全部中斷的函數
//關閉所有中斷
void INTX_DISABLE(void)
{ __ASM volatile("cpsid i");
}
//開啟所有中斷
void INTX_ENABLE(void)
{__ASM volatile("cpsie i");
}
關于其他開啟和關閉全局中斷的方法大家可以自行搜索一下,這里就不再贅述了。
??下面我們來簡單嘗試以下系統的軟件復位,每次進入系統前都輸出一串字符串用來提示進入系統,設置延時一段時間后軟件復位系統,查看一下現象
??通過串口的信息我們可以看到,軟件復位生效了。這里的main函數比較簡單,貼一下,僅供參考
int main(void)
{ delay_init(); //延時函數初始化 uart_init(115200); // 串口初始化printf ("Enter System!\r\n");while(1){// 延時delay_ms(1000);// 軟件復位INTX_DISABLE();NVIC_SystemReset();}
}
1.2 低功耗管理復位
??在以下兩種情況下可產生低功耗管理復位
- 在進入待機模式時產生低功耗管理復位
通過將用戶選擇字節中的nRST_STDBY位置’1’將使能該復位。這時,即使執行了進入待機模式的過程,系統將被復位而不是進入待機模式。 - 在進入停止模式時產生低功耗管理復位
通過將用戶選擇字節中的nRST_STOP位置’1’將使能該復位。這時,即使執行了進入停機
模式的過程,系統將被復位而不是進入停機模式。
二、時鐘
??在介紹STM32F10系列的時鐘之前,先貼一下時鐘樹,可以對照時鐘數來看下面的內容
??STM32有5個時鐘源,分別是HSI、HSE、PLL、LSI和LSE。
- HSI是高速內部時鐘,RC振蕩器,頻率為8MHz,精度不高。
- HSE是高速外部時鐘,可以接石英石/陶瓷諧振器,或者接外部時鐘源,頻率范圍是4MHz~16MHz
- PLL實際是一個鎖相環,也可以理解為倍頻器,它的時鐘源可以選擇為 H S I 2 \frac{HSI}{2} 2HSI?、HSE或者 H S E 2 \frac{HSE}{2} 2HSE?,倍頻可以選擇x2~x16,但是它的輸出最大不可超過72MHz
- LSI是低速內部時鐘,RC振蕩器,頻率為40KHz,提供獨立看門狗時鐘等。
- LSE是低速外部時鐘,可以接頻率為32.768KHz的石英晶體,提供RTC時鐘。
??STM32的系統時鐘SYSCLK可以來源于三個時鐘源,分別是HSI、HSE和PLL時鐘。STM32也可以輸出時鐘信號,可以選擇一個時鐘信號出書到PA8引腳上,可選PLL輸出的二分頻、HSI、HSE或者系統時鐘作為時鐘源。
2.1 系統時鐘(SYSCLK)選擇
??系統復位后,HSI振蕩器被選為系統時鐘。我們可以通過設置時鐘配置寄存器(RCC_CFGR)來切換系統時鐘,在切換系統時鐘時,需要等待目標時鐘源準備就緒,當被選擇的時鐘沒有就緒時,系統時鐘不會發生切換。直至目標時鐘源準備就緒,才會發生切換。我們可以通過在時鐘控制寄存器(RCC_CR)里的狀態位指示哪個時鐘已經準備好了,哪個時鐘目前被用作系統時鐘。
在從停止或待機模式中返回時或直接或間接作為系統時鐘的HSE出現故障時,由硬件強制選擇HSI作為系統時鐘(如果時鐘安全系統已經啟動),關于時鐘安全系統(CSS)這就不再贅述。
2.2 系統時鐘初始化
??STM32的庫函數中提供了系統時鐘初始化函數
/*** @brief Setup the microcontroller system* Initialize the Embedded Flash Interface, the PLL and update the * SystemCoreClock variable.* @note This function should be used only after reset.* @param None* @retval None*/
void SystemInit (void)
{/* Reset the RCC clock configuration to the default reset state(for debug purpose) *//* Set HSION bit */RCC->CR |= (uint32_t)0x00000001;/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
#ifndef STM32F10X_CLRCC->CFGR &= (uint32_t)0xF8FF0000;
#elseRCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */ /* Reset HSEON, CSSON and PLLON bits */RCC->CR &= (uint32_t)0xFEF6FFFF;/* Reset HSEBYP bit */RCC->CR &= (uint32_t)0xFFFBFFFF;/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */RCC->CFGR &= (uint32_t)0xFF80FFFF;#ifdef STM32F10X_CL/* Reset PLL2ON and PLL3ON bits */RCC->CR &= (uint32_t)0xEBFFFFFF;/* Disable all interrupts and clear pending bits */RCC->CIR = 0x00FF0000;/* Reset CFGR2 register */RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)/* Disable all interrupts and clear pending bits */RCC->CIR = 0x009F0000;/* Reset CFGR2 register */RCC->CFGR2 = 0x00000000;
#else/* Disable all interrupts and clear pending bits */RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)#ifdef DATA_IN_ExtSRAMSystemInit_ExtMemCtl(); #endif /* DATA_IN_ExtSRAM */
#endif /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers *//* Configure the Flash Latency cycles and enable prefetch buffer */SetSysClock();#ifdef VECT_TAB_SRAMSCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#elseSCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
}
關于系統時鐘配置函數的內容,大家可以自行對照STM32中文參考手冊來查看具體每一行代碼的含義,當然也有英文注釋可參考,這里就不再贅述了。
??不知道大家是否好奇,我們在編程時并未特地調用過時鐘初始化函數SystemInit(),我們是在什么時候配置的系統時鐘呢?實際在STM32的啟動文件中已經調用過該函數,配置完了系統時鐘
??關于STM32啟動文件的詳細注釋,大家可以移步至該帖查看,這里也不再貼出來了,STM32啟動文件詳解。
三、滴答定時器(Systick)
??SysTick定時器被捆綁在NVIC中,它通常用來做延時或者實時系統的心跳,使用SysTick可以節省一個定時器資源。
??SysTick定時器也可以叫做系統滴答定時器,它是一個24位的倒計數計時器,當它計數到0時,將從RELOAD寄存器中自動重裝載定時器初值。只要我們不把它在SysTick控制及狀態寄存器中的使能位清除,滴答定時器就一直在工作,即使在睡眠模式下也不會停止。SysTick有自己的中斷,中斷優先級可以設置。
3.1 SysTick部分寄存器
??接下來我們來看一下SysTick的一些寄存器。
- SysTick控制及狀態寄存器(CTRL)
- SysTick重裝載數值寄存器(LOAD)
- SysTick當前數值寄存器(VAL)
3.2 SysTick相關函數
??STM32庫函數給出了SysTick的一些配置函數,這里我們簡單介紹一下,在看下面的函數時,大家可以對照上面的寄存器來分析一下每一行代碼的作用和含義。
3.2.1 SysTick時鐘源配置函數
/*** @brief Configures the SysTick clock source.* @param SysTick_CLKSource: specifies the SysTick clock source.* This parameter can be one of the following values:* @arg SysTick_CLKSource_HCLK_Div8: AHB clock divided by 8 selected as SysTick clock source.* @arg SysTick_CLKSource_HCLK: AHB clock selected as SysTick clock source.* @retval None*/
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
{/* Check the parameters */assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource));if (SysTick_CLKSource == SysTick_CLKSource_HCLK){SysTick->CTRL |= SysTick_CLKSource_HCLK;}else{SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8;}
}
??下面是一些相關的宏定義
#define SysTick_CLKSource_HCLK_Div8 ((uint32_t)0xFFFFFFFB)
#define SysTick_CLKSource_HCLK ((uint32_t)0x00000004)
#define IS_SYSTICK_CLK_SOURCE(SOURCE) (((SOURCE) == SysTick_CLKSource_HCLK) || \((SOURCE) == SysTick_CLKSource_HCLK_Div8))
??對照上面對于SysTick控制及狀態寄存器的介紹我們可以知道,當SysTick時鐘源配置函數的輸入值為1時,選擇外部時鐘作為時鐘源,當輸入值為0時,選擇內部時鐘作為時鐘源。
3.2.2 SysTick初始化函數
/*** @brief Initialize and start the SysTick counter and its interrupt.** @param ticks number of ticks between two interrupts* @return 1 = failed, 0 = successful** Initialise the system tick timer and its interrupt and start the* system tick timer / counter in free running mode to generate * periodical interrupts.*/
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */SysTick->VAL = 0; /* Load the SysTick Counter Value */SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */return (0); /* Function successful */
}
??下面是一些相關的宏定義
/* SysTick Reload Register Definitions */
#define SysTick_LOAD_RELOAD_Pos 0 /*!< SysTick LOAD: RELOAD Position */
#define SysTick_LOAD_RELOAD_Msk (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos) /*!< SysTick LOAD: RELOAD Mask */#define SysTick_CTRL_CLKSOURCE_Pos 2 /*!< SysTick CTRL: CLKSOURCE Position */
#define SysTick_CTRL_CLKSOURCE_Msk (1ul << SysTick_CTRL_CLKSOURCE_Pos) /*!< SysTick CTRL: CLKSOURCE Mask */#define SysTick_CTRL_TICKINT_Pos 1 /*!< SysTick CTRL: TICKINT Position */
#define SysTick_CTRL_TICKINT_Msk (1ul << SysTick_CTRL_TICKINT_Pos) /*!< SysTick CTRL: TICKINT Mask */#define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */
#define SysTick_CTRL_ENABLE_Msk (1ul << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */
??該函數的功能是使能SysTick,開啟中斷,函數的輸入參數ticks是設置兩次中斷間的間隔,也就是設置兩次中斷之間有多少個SysTick時鐘周期。我們如下配置就可以設置SysTick中斷時間間隔為1ms。
SysTick_Config(SystemCoreClock / 1000); // 設置SysTick中斷時間間隔為1ms
??這里介紹一下為什么按照上面的設置,SysTick兩次中斷之間的時間間隔為1ms。比如系統頻率為72MHz,我們設置的是每72K個SysTick周期進入一次中斷,也就是說進入中斷的時間間隔為72K / 72M,也就是1ms。
??其實我們用到的延時函數也是利用滴答定時器來實現的延時,這里我們就不再詳細介紹,大家有興趣的可以自行分析學習一下。