日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

STM32F429第四篇之跑马灯程序详解

發(fā)布時(shí)間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 STM32F429第四篇之跑马灯程序详解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 前言
  • 硬件
  • 軟件
    • 編寫代碼
      • 主程序
        • HAL庫(kù)初始化
        • RCC時(shí)鐘初始化
        • 延時(shí)函數(shù)初始化
      • LED驅(qū)動(dòng)
    • HAL庫(kù)詳解
      • GPIO
        • GPIO初始化
        • GPIO位操作
      • RCC
        • 振蕩器參數(shù)設(shè)置
        • 時(shí)鐘初始化
      • systick

前言

本文以上篇博文<STM32F429第三篇之GPIO的模板工程構(gòu)建>構(gòu)建的項(xiàng)目為歷程,講解在HAL庫(kù)中如何操作控制GPIO的輸出,以及STM32編程的步驟。

本文主要參考文獻(xiàn)為:

  • 正點(diǎn)原子.STM32F429開發(fā)指南——HAL庫(kù)版本
  • STM32F429xx中文數(shù)據(jù)手冊(cè)——DocID024030 Rev 4
  • RM0090 參考手冊(cè)——文檔 ID 018909 第 4 版
  • RM0090 Reference manual——RM0090 Rev 18
  • Cortex-M3 權(quán)威指南

本文更新順序:

  • 20200911——更新LED驅(qū)動(dòng)程序部分,以及HAL庫(kù)講解中的GPIO部分。
  • 20201020——更新主程序中的RCC時(shí)鐘初始化部分,延時(shí)函數(shù)初始化部分。
  • 20201023——更新RCC時(shí)鐘初始化部分和RCC部分。
  • 20201104——更新振蕩器初始化程序部分。
  • 20201106——更新了時(shí)鐘初始化部分。

硬件

軟件

編寫代碼

在該程序中,主要需要編寫兩個(gè)部分的代碼:

  • 主程序
  • led初始化程序

下面分別講解:

主程序

#include "sys.h" #include "delay.h" #include "led.h"int main(void) {HAL_Init(); //初始化HAL庫(kù) Stm32_Clock_Init(360,25,2,8); //設(shè)置時(shí)鐘,180Mhzdelay_init(180); //初始化延時(shí)函數(shù)LED_Init(); //初始化LEDwhile(1){HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET); //LED0對(duì)應(yīng)引腳PB1拉低,亮HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET); //LED1對(duì)應(yīng)引腳PB0拉高,滅delay_ms(500); //延時(shí)500msHAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET); //LED0對(duì)應(yīng)引腳PB1拉高,滅HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET); //LED1對(duì)應(yīng)引腳PB0拉低,亮delay_ms(500); //延時(shí)500ms}}

主程序很簡(jiǎn)單,可以概括為以下幾個(gè)步驟:

  • HAL庫(kù)初始化
  • RCC時(shí)鐘初始化
  • 相關(guān)外設(shè)初始化
    • 延時(shí)函數(shù)初始化
    • LED相關(guān)的GPIO初始化
  • while循環(huán)

HAL庫(kù)初始化

HAL初始化部分比較復(fù)雜,涉及的內(nèi)容比較多,這部分的詳細(xì)講解以后補(bǔ)上。這里大致說(shuō)明其所實(shí)現(xiàn)的功能有4個(gè)方面:

  • 初始化FLASH部分。使能FLASH的預(yù)存,數(shù)據(jù)緩存,指令緩存。
  • 設(shè)置NVIC組的優(yōu)先級(jí)為4;
  • 將systick(系統(tǒng)定時(shí)器)作為time base的時(shí)鐘源,且將其配置為1ms。
  • Msp初始化。

其源程序如下:

/*** @brief This function is used to initialize the HAL Library; it must be the first * instruction to be executed in the main program (before to call any other* HAL function), it performs the following:* Configure the Flash prefetch, instruction and Data caches.* Configures the SysTick to generate an interrupt each 1 millisecond,* which is clocked by the HSI (at this stage, the clock is not yet* configured and thus the system is running from the internal HSI at 16 MHz).* Set NVIC Group Priority to 4.* Calls the HAL_MspInit() callback function defined in user file * "stm32f4xx_hal_msp.c" to do the global low level hardware initialization * * @note SysTick is used as time base for the HAL_Delay() function, the application* need to ensure that the SysTick time base is always set to 1 millisecond* to have correct HAL operation.* @retval HAL status*/ HAL_StatusTypeDef HAL_Init(void) {/* Configure Flash prefetch, Instruction cache, Data cache */ #if (INSTRUCTION_CACHE_ENABLE != 0U)__HAL_FLASH_INSTRUCTION_CACHE_ENABLE(); #endif /* INSTRUCTION_CACHE_ENABLE */#if (DATA_CACHE_ENABLE != 0U)__HAL_FLASH_DATA_CACHE_ENABLE(); #endif /* DATA_CACHE_ENABLE */#if (PREFETCH_ENABLE != 0U)__HAL_FLASH_PREFETCH_BUFFER_ENABLE(); #endif /* PREFETCH_ENABLE *//* Set Interrupt Group Priority */HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);/* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */HAL_InitTick(TICK_INT_PRIORITY);/* Init the low level hardware */HAL_MspInit();/* Return function status */return HAL_OK; }

RCC時(shí)鐘初始化

RCC時(shí)鐘初始化主要通過(guò)函數(shù)Stm32_Clock_Init實(shí)現(xiàn),需要注意的是,該函數(shù)并非是HAL庫(kù)官方提供的函數(shù),而是由正點(diǎn)原子實(shí)現(xiàn)。其源程序如下:

void Stm32_Clock_Init(u32 plln, u32 pllm, u32 pllp, u32 pllq) {HAL_StatusTypeDef ret = HAL_OK;/***********************************1.使能PWR時(shí)鐘*****************************************************/__HAL_RCC_PWR_CLK_ENABLE();/***********************************2.設(shè)置調(diào)壓器輸出電壓級(jí)別*******************************************/__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); //設(shè)置調(diào)壓器輸出電壓級(jí)別1/***********************************3.配置時(shí)鐘源相關(guān)參數(shù)**********************************************/RCC_OscInitTypeDef RCC_OscInitStructure;RCC_OscInitStructure.OscillatorType = RCC_OSCILLATORTYPE_HSE; //振蕩器類型為HSE(外部高速振蕩器)RCC_OscInitStructure.HSEState = RCC_HSE_ON; //打開HSERCC_OscInitStructure.PLL.PLLState = RCC_PLL_ON; //打開PLLRCC_OscInitStructure.PLL.PLLSource = RCC_PLLSOURCE_HSE; //PLL時(shí)鐘源選擇HSERCC_OscInitStructure.PLL.PLLM = pllm; //主PLL和音頻PLL分頻系數(shù)(PLL之前的分頻),取值范圍:2~63.RCC_OscInitStructure.PLL.PLLN = plln; //主PLL倍頻系數(shù)(PLL倍頻),取值范圍:64~432.RCC_OscInitStructure.PLL.PLLP = pllp; //系統(tǒng)時(shí)鐘的主PLL分頻系數(shù)(PLL之后的分頻),取值范圍:2,4,6,8.(僅限這4個(gè)值!)RCC_OscInitStructure.PLL.PLLQ = pllq; //USB/SDIO/隨機(jī)數(shù)產(chǎn)生器等的主PLL分頻系數(shù)(PLL之后的分頻),取值范圍:2~15.ret = HAL_RCC_OscConfig(&RCC_OscInitStructure); //振蕩器參數(shù)初始化if(ret != HAL_OK) while(1);/***********************************4.開啟over-driver功能**********************************************/ret = HAL_PWREx_EnableOverDrive(); //開啟Over-Driver功能if(ret != HAL_OK) while(1);/***********************************5.配置系統(tǒng)時(shí)鐘相關(guān)參**********************************************/RCC_ClkInitTypeDef RCC_ClkInitStructure;//選中PLL作為系統(tǒng)時(shí)鐘源并且配置HCLK,PCLK1和PCLK2RCC_ClkInitStructure.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);RCC_ClkInitStructure.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; //設(shè)置系統(tǒng)時(shí)鐘時(shí)鐘源為PLLRCC_ClkInitStructure.AHBCLKDivider = RCC_SYSCLK_DIV1; //AHB分頻系數(shù)為1RCC_ClkInitStructure.APB1CLKDivider = RCC_HCLK_DIV4; //APB1分頻系數(shù)為4RCC_ClkInitStructure.APB2CLKDivider = RCC_HCLK_DIV2; //APB2分頻系數(shù)為2ret = HAL_RCC_ClockConfig(&RCC_ClkInitStructure, FLASH_LATENCY_5); //同時(shí)設(shè)置FLASH延時(shí)周期為5WS,也就是6個(gè)CPU周期。if(ret != HAL_OK) while(1); }

上述代碼可以大致分成以下五個(gè)部分:

  • 使能PWR時(shí)鐘。
  • 設(shè)置調(diào)壓器輸出的電壓級(jí)別。
  • 配置時(shí)鐘源相關(guān)參數(shù)。
  • 開啟Over-Driver功能。
  • 配置系統(tǒng)時(shí)鐘相關(guān)參數(shù)。
  • 其中,第1,2,4步驟與PWR模塊有關(guān)系。此處不再詳細(xì)解釋。第1步之所以需要使能PWR時(shí)鐘,是因?yàn)樵诘?步和第4步都需要電源相關(guān)的配置。
    我們確定電源電壓和HCLK時(shí)鐘頻率之后,電壓幾倍VOS,Over-Driver功能和FLASH的延時(shí)Latency參數(shù)是固定的。VOS的參數(shù)含義官方解釋為:

    通過(guò)查詢數(shù)據(jù)手冊(cè)可以得到:

    通過(guò)以上官方文檔可以了解,若需要將STM32F429運(yùn)行在最高時(shí)鐘頻率180MHz,則需要選擇電源級(jí)別為1,且打開 超載(Over-Driver) 功能。而上述的第1,2,4步驟即實(shí)現(xiàn)該功能,讓ARM可以運(yùn)行在頻率180MHz處。
    步驟3和步驟5是通過(guò)HAL庫(kù)中RCC功能實(shí)現(xiàn)時(shí)鐘分配,關(guān)于此處使用到的結(jié)構(gòu)體和函數(shù)的用法可以參考博客<STM32F429第八篇之stm32f4xx_hal_rcc>。關(guān)于RCC時(shí)鐘配置的更多信息,可以參考博客<STM32F429第七篇之RCC(復(fù)位與時(shí)鐘)>

    Flash等待周期可以通過(guò)下表確定:

    一般地,我們開發(fā)板工作在3.3V,180MHz的環(huán)境下,因此可知,等待周期為5WS(6CPU周期)。

    延時(shí)函數(shù)初始化

    //初始化延遲函數(shù) //當(dāng)使用ucos的時(shí)候,此函數(shù)會(huì)初始化ucos的時(shí)鐘節(jié)拍 //SYSTICK的時(shí)鐘固定為AHB時(shí)鐘 //SYSCLK:系統(tǒng)時(shí)鐘頻率 void delay_init ( u8 SYSCLK ) { #if SYSTEM_SUPPORT_OS //如果需要支持OS.u32 reload; #endifHAL_SYSTICK_CLKSourceConfig ( SYSTICK_CLKSOURCE_HCLK ); //SysTick頻率為HCLKfac_us = SYSCLK; //不論是否使用OS,fac_us都需要使用#if SYSTEM_SUPPORT_OS //如果需要支持OS.reload = SYSCLK; //每秒鐘的計(jì)數(shù)次數(shù) 單位為Kreload *= 1000000 / delay_ostickspersec; //根據(jù)delay_ostickspersec設(shè)定溢出時(shí)間//reload為24位寄存器,最大值:16777216,在180M下,約合0.745s左右fac_ms = 1000 / delay_ostickspersec; //代表OS可以延時(shí)的最少單位SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //開啟SYSTICK中斷SysTick->LOAD = reload; //每1/OS_TICKS_PER_SEC秒中斷一次SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //開啟SYSTICK #else #endif }

    LED驅(qū)動(dòng)

    頭文件

    #ifndef _LED_H #define _LED_Hextern void LED_Init(void);#endif

    C文件

    #include "led.h" #include "stm32f4xx.h"void LED_Init(void) {__HAL_RCC_GPIOB_CLK_ENABLE(); //開啟GPIOB時(shí)鐘GPIO_InitTypeDef GPIO_Initure;GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1; //PB1,0GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽輸出GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速HAL_GPIO_Init(GPIOB,&GPIO_Initure);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET); //PB0置1,默認(rèn)初始化后燈滅HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET); //PB1置1,默認(rèn)初始化后燈滅 }

    led驅(qū)動(dòng)程序主要實(shí)現(xiàn)以下幾個(gè)功能:

    • 開啟GPIOB的時(shí)鐘。
    • 對(duì)led相關(guān)的引腳PB0和PB1進(jìn)行功能初始化。
    • 初始化LED的初始狀態(tài)為熄滅狀態(tài)。

    其主要的初始化步驟本質(zhì)上為GPIO的初始化步驟,其官方的步驟可以參考博客<STM32F429第六篇之stm32f4xx_hal_gpio>中的使用方法 節(jié)。

    HAL庫(kù)詳解

    GPIO

    通過(guò)編寫代碼部分,我們可以總結(jié)出,該例程關(guān)于GPIO部分主要使用了兩個(gè)HAL庫(kù)函數(shù):

    • HAL_GPIO_Init();//GPIO初始化程序
    • HAL_GPIO_WritePin();//GPIO位操作

    關(guān)于兩個(gè)函數(shù)的使用方法,可以參考博客<STM32F429第六篇之stm32f4xx_hal_gpio>中 函數(shù) 節(jié)。

    GPIO初始化

    /*** @brief Initializes the GPIOx peripheral according to the specified parameters in the GPIO_Init.* @param GPIOx: where x can be (A..K) to select the GPIO peripheral for STM32F429X device or* x can be (A..I) to select the GPIO peripheral for STM32F40XX and STM32F427X devices.* @param GPIO_Init: pointer to a GPIO_InitTypeDef structure that contains* the configuration information for the specified GPIO peripheral.* @retval None*/ void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init) {uint32_t position;uint32_t ioposition = 0x00U;uint32_t iocurrent = 0x00U;uint32_t temp = 0x00U;/* Check the parameters */assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));assert_param(IS_GPIO_PIN(GPIO_Init->Pin));assert_param(IS_GPIO_MODE(GPIO_Init->Mode));assert_param(IS_GPIO_PULL(GPIO_Init->Pull));//此處沒(méi)有檢查所有的參數(shù)/* Configure the port pins *///進(jìn)入循環(huán),GPIO_NUMBER為16,表示端口有16個(gè)引腳for(position = 0U; position < GPIO_NUMBER; position++){/* Get the IO position */ioposition = ((uint32_t)0x01U) << position; //當(dāng)前處理數(shù)據(jù)位的位置/* Get the current IO position */iocurrent = (uint32_t)(GPIO_Init->Pin) & ioposition; //獲取當(dāng)前位的PIN值是否置1if(iocurrent == ioposition) //當(dāng)前數(shù)據(jù)數(shù)據(jù)位需要初始化{/*--------------------- GPIO Mode Configuration ------------------------*//* In case of Alternate function mode selection *///判斷引腳是否為復(fù)用功能,若是,將該引腳對(duì)應(yīng)的復(fù)用功能寫入。if((GPIO_Init->Mode == GPIO_MODE_AF_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_OD)){/* Check the Alternate function parameter */assert_param(IS_GPIO_AF(GPIO_Init->Alternate)); //此處檢測(cè)復(fù)用功能是否參數(shù)正確/* Configure Alternate function mapped with the current IO */temp = GPIOx->AFR[position >> 3U]; //AFR數(shù)組有兩個(gè)元素,分別對(duì)應(yīng)AFRL和AFRH。position取值為0-15,其中0-7對(duì)應(yīng)AFRL,8-15對(duì)應(yīng)AFRH。//因?yàn)橐呀?jīng)區(qū)分成兩個(gè)元素。所以,第一步,取低三位的二進(jìn)制數(shù)即可。(position & (uint32_t)0x07U),例如,第3位和第11位的處理方法相同//position的取值為[0,15]。其對(duì)應(yīng)的AFRL/AFRH的二進(jìn)制數(shù)位為position*4。每個(gè)數(shù)位占有4個(gè)寄存器AFRL/AFRH二進(jìn)制位。//例如,當(dāng)position=3時(shí)。對(duì)應(yīng)AFRL[15:12]。即(0xF<<(3*4))對(duì)應(yīng)二進(jìn)制位0000 1111 0000 0000。其中數(shù)據(jù)1的位置恰好對(duì)應(yīng)AFRL[15:12]。//最后,再將結(jié)果進(jìn)行取反,相位與。即將寄存器AFRL/AFRH的對(duì)應(yīng)二進(jìn)制位清0。其余位不變。temp &= ~((uint32_t)0xFU << ((uint32_t)(position & (uint32_t)0x07U) * 4U)) ;//和上面執(zhí)行相同。將GPIO_Init->Alternate的數(shù)值寫入寄存器AFRL/AFRH的對(duì)應(yīng)二進(jìn)制位。temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & (uint32_t)0x07U) * 4U));GPIOx->AFR[position >> 3U] = temp; //將改變后的值,復(fù)原到AFR寄存器。}/* Configure IO Direction mode (Input, Output, Alternate or Analog) *///將引腳的對(duì)應(yīng)模式寫入temp = GPIOx->MODER;temp &= ~(GPIO_MODER_MODER0 << (position * 2U)); //先將對(duì)應(yīng)位清零,GPIO_MODER_MODER0=3(0b11)temp |= ((GPIO_Init->Mode & GPIO_MODE) << (position * 2U)); //再將將對(duì)應(yīng)位置1,GPIO_MODE=3(0b11)GPIOx->MODER = temp;/* In case of Output or Alternate function mode selection *///判斷是否為輸出或者復(fù)用功能。若是,則需要設(shè)置引腳的類型與速度。if((GPIO_Init->Mode == GPIO_MODE_OUTPUT_PP) || (GPIO_Init->Mode == GPIO_MODE_AF_PP) ||(GPIO_Init->Mode == GPIO_MODE_OUTPUT_OD) || (GPIO_Init->Mode == GPIO_MODE_AF_OD)){//寫入速度/* Check the Speed parameter */assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));/* Configure the IO Speed */temp = GPIOx->OSPEEDR;temp &= ~(GPIO_OSPEEDER_OSPEEDR0 << (position * 2U));temp |= (GPIO_Init->Speed << (position * 2U));GPIOx->OSPEEDR = temp;//寫入輸出類型:推挽輸出還是開漏輸出/* Configure the IO Output Type */temp = GPIOx->OTYPER;temp &= ~(GPIO_OTYPER_OT_0 << position) ; // GPIO_OTYPER_OT_0=0x00000001U(0b0000 0001)temp |= (((GPIO_Init->Mode & GPIO_OUTPUT_TYPE) >> 4U) << position); // GPIO_OUTPUT_TYPE=0x00000010U(0b0001 0000)GPIOx->OTYPER = temp;}//寫入上拉,下拉還是浮空功能/* Activate the Pull-up or Pull down resistor for the current IO */temp = GPIOx->PUPDR;temp &= ~(GPIO_PUPDR_PUPDR0 << (position * 2U));temp |= ((GPIO_Init->Pull) << (position * 2U));GPIOx->PUPDR = temp;/*--------------------- EXTI Mode Configuration ------------------------*//* Configure the External Interrupt or event for the current IO */if((GPIO_Init->Mode & EXTI_MODE) == EXTI_MODE){/* Enable SYSCFG Clock */__HAL_RCC_SYSCFG_CLK_ENABLE();temp = SYSCFG->EXTICR[position >> 2U];temp &= ~(((uint32_t)0x0FU) << (4U * (position & 0x03U)));temp |= ((uint32_t)(GPIO_GET_INDEX(GPIOx)) << (4U * (position & 0x03U)));SYSCFG->EXTICR[position >> 2U] = temp;/* Clear EXTI line configuration */temp = EXTI->IMR;temp &= ~((uint32_t)iocurrent);if((GPIO_Init->Mode & GPIO_MODE_IT) == GPIO_MODE_IT){temp |= iocurrent;}EXTI->IMR = temp;temp = EXTI->EMR;temp &= ~((uint32_t)iocurrent);if((GPIO_Init->Mode & GPIO_MODE_EVT) == GPIO_MODE_EVT){temp |= iocurrent;}EXTI->EMR = temp;/* Clear Rising Falling edge configuration */temp = EXTI->RTSR;temp &= ~((uint32_t)iocurrent);if((GPIO_Init->Mode & RISING_EDGE) == RISING_EDGE){temp |= iocurrent;}EXTI->RTSR = temp;temp = EXTI->FTSR;temp &= ~((uint32_t)iocurrent);if((GPIO_Init->Mode & FALLING_EDGE) == FALLING_EDGE){temp |= iocurrent;}EXTI->FTSR = temp;}}} }

    通過(guò)上文源程序可知,GPIO初始化共分成兩個(gè)部分:

    • GPIO模式初始化
    • EXIT模式初始化

    本文只涉及到第一部分,所以第二部分略過(guò)不談。

    在GPIO初始化的函數(shù)總體思路是:

    • 調(diào)用一次函數(shù),初始化循環(huán)一組端口所有位。
    • 因?yàn)橐唤M端口有16個(gè)引腳,所以,函數(shù)循環(huán)16次, 依次判斷該引腳Pin對(duì)應(yīng)位是否置1。
    • 若當(dāng)前引腳對(duì)應(yīng)位置1,那么對(duì)該位進(jìn)行初始化,否則,跳過(guò)該位,循環(huán)至下一個(gè)引腳。
    • 直到16個(gè)引腳循環(huán)結(jié)束。

    在GPIO模式初始化部分,基本流程如下所示:

    • 判斷該引腳是否為復(fù)用功能。若是,將復(fù)用功能對(duì)應(yīng)數(shù)據(jù)寫入復(fù)用功能寄存器AFRL/AFRH。
    • 將引腳對(duì)應(yīng)的模式(Mode)寫入GPIO端口模式寄存器(MODER)。
    • 判斷該引腳是否為輸出或者復(fù)用功能。若是,則將引腳的速度和輸出類型分別寫入GPIO輸出速度寄存器(OSPEEDR)和GPIO端口輸出類型寄存器(OTYPER)。
    • 將引腳對(duì)應(yīng)的上拉/下拉功能寫入GPIO端口上拉/下拉寄存器(PUPDR)。

    其中,寫入寄存器一般分成兩個(gè)步驟:

    • 將引腳對(duì)應(yīng)寄存器數(shù)據(jù)位清零。
    • 將引腳對(duì)應(yīng)寄存器數(shù)據(jù)位寫入數(shù)據(jù)。

    以復(fù)用功能寄存器為例,其源程序如下:

    temp = GPIOx->AFR[position >> 3U]; temp &= ~((uint32_t)0xFU << ((uint32_t)(position & (uint32_t)0x07U) * 4U)) ; temp |= ((uint32_t)(GPIO_Init->Alternate) << (((uint32_t)position & (uint32_t)0x07U) * 4U)); GPIOx->AFR[position >> 3U] = temp;

    總共分成四個(gè)語(yǔ)句,如下:

  • 因?yàn)楣δ軓?fù)位寄存器有兩個(gè)(AFRL和AFRH)。其中AFRH對(duì)應(yīng)端口引腳的高8位,AFRL對(duì)應(yīng)端口引腳低8位。所以,根據(jù)position是大于等于8或者小于7來(lái)獲取對(duì)應(yīng)復(fù)用功能寄存器的數(shù)值,且保存在temp中。
  • 將引腳的寄存器對(duì)應(yīng)位清零。
  • 因?yàn)楦?位與低8位在上一步已經(jīng)區(qū)分。所以此時(shí),高8位應(yīng)該以第8位為基點(diǎn)。即第8位等同于第0位,就9位等同于第1位,以此類推。 position & (uint32_t)0x07U相當(dāng)于將大于8的position減去8,小于8的position不變。
  • 因?yàn)槊總€(gè)引腳對(duì)應(yīng)的復(fù)位寄存器是4個(gè)二進(jìn)制位,((uint32_t)(position & (uint32_t)0x07U) * 4U)),將處理過(guò)的偏移量*4。即第0偏移到0,第1位偏移到4,從而對(duì)應(yīng)其在寄存器中的二進(jìn)制對(duì)應(yīng)位。
  • (uint32_t)0xFU << ((uint32_t)(position & (uint32_t)0x07U) * 4U)。因?yàn)?strong>0xF 二進(jìn)制形式為 0b1111,即四個(gè)二進(jìn)制位為1,其余所有位為0。所以,將其左移上一步計(jì)算出的偏移量,即將引腳對(duì)應(yīng)的復(fù)用功能寄存器位置1,其余位置0。假設(shè)position為3的情況下,此時(shí),計(jì)算結(jié)果二進(jìn)制形式為 0x 0000 0000 0000 0000 0000 1111 0000 0000
  • 最后將上一步計(jì)算結(jié)果取反,且與temp相位與。則,將引腳對(duì)應(yīng)的復(fù)位功能寄存器對(duì)應(yīng)位清0,其余位不變。
  • 該步驟和上一步處理方式基本相同 。即將復(fù)用功能數(shù)據(jù)寫入到復(fù)位功能寄存器對(duì)應(yīng)的temp二進(jìn)制位中。
  • 最后,將temp寫入AFR寄存器中。
  • GPIO位操作

    /*** @brief Sets or clears the selected data port bit.** @note This function uses GPIOx_BSRR register to allow atomic read/modify* accesses. In this way, there is no risk of an IRQ occurring between* the read and the modify access.** @param GPIOx: where x can be (A..K) to select the GPIO peripheral for STM32F429X device or* x can be (A..I) to select the GPIO peripheral for STM32F40XX and STM32F427X devices.* @param GPIO_Pin: specifies the port bit to be written.* This parameter can be one of GPIO_PIN_x where x can be (0..15).* @param PinState: specifies the value to be written to the selected bit.* This parameter can be one of the GPIO_PinState enum values:* @arg GPIO_PIN_RESET: to clear the port pin* @arg GPIO_PIN_SET: to set the port pin* @retval None*/ void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState) {/* Check the parameters */assert_param(IS_GPIO_PIN(GPIO_Pin));assert_param(IS_GPIO_PIN_ACTION(PinState));if(PinState != GPIO_PIN_RESET){GPIOx->BSRR = GPIO_Pin;}else{GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U;} }

    該函數(shù)比較簡(jiǎn)單,即判斷是需要清零還是置位。

    • 若是需要置位,將GPIO端口置位/復(fù)位寄存器(BSRR)對(duì)應(yīng)位置1.
    • 若是需要清零,將GPIO端口置位/復(fù)位寄存器(BSRR)對(duì)應(yīng)位+16置1.

    RCC

    通過(guò)編寫代碼部分,我們可以總結(jié)出,該例程關(guān)于RCC部分主要使用了兩個(gè)HAL庫(kù)函數(shù):

    • HAL_RCC_OscConfig();//初始化振蕩器相關(guān)參數(shù)
    • HAL_RCC_ClockConfig();//初始化系統(tǒng)時(shí)鐘

    關(guān)于此兩個(gè)函數(shù)的使用方法,可以參考博客<STM32F429第八篇之stm32f4xx_hal_rcc>

    另外還有一個(gè)外設(shè)初始化的宏,如:

    • __HAL_RCC_PWR_CLK_ENABLE();
    • ___HAL_RCC_GPIOB_CLK_ENABLE();

    關(guān)于外設(shè)初始化宏部分比較簡(jiǎn)單,此處不再詳細(xì)展開,可以參考博客<STM32F429第九篇之stm32f4xx_hal_rcc_ex>。

    下面重點(diǎn)分析兩個(gè)函數(shù)的源代碼。

    振蕩器參數(shù)設(shè)置

    __weak HAL_StatusTypeDef HAL_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct) {uint32_t tickstart = 0U;/* Check the parameters */assert_param(IS_RCC_OSCILLATORTYPE(RCC_OscInitStruct->OscillatorType));//檢測(cè)振蕩器類型/*------------------------------- HSE Configuration ------------------------*/if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSE) == RCC_OSCILLATORTYPE_HSE)//判斷是否有HSE{/* Check the parameters *//********************1.檢測(cè)參數(shù)狀態(tài)**************************************/assert_param(IS_RCC_HSE(RCC_OscInitStruct->HSEState));//判斷HSE狀態(tài)/* When the HSE is used as system clock or clock source for PLL in these cases HSE will not disabled *//********************2.條件判斷:若HSE用作系統(tǒng)時(shí)鐘,則不可以禁用HSE**************************************/if((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_HSE) || \((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_PLL) && ((RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) == RCC_PLLCFGR_PLLSRC_HSE)))//判斷HSE是否已經(jīng)直接或者間接用于系統(tǒng)時(shí)鐘{if((__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET) && (RCC_OscInitStruct->HSEState == RCC_HSE_OFF))//若HSE用做系統(tǒng)時(shí)鐘,則不可以禁用HSE。{return HAL_ERROR;}//若HSE已經(jīng)成為系統(tǒng)時(shí)鐘,且并非禁用HSE,則無(wú)須任何操作。}/********************3.設(shè)置HSE狀態(tài)**************************************/else//HSE并未直接或者間接用做系統(tǒng)時(shí)鐘{/* Set the new HSE configuration ---------------------------------------*/__HAL_RCC_HSE_CONFIG(RCC_OscInitStruct->HSEState);//將HSE狀態(tài)直接寫入寄存器/* Check the HSE State */if((RCC_OscInitStruct->HSEState) != RCC_HSE_OFF)//若設(shè)置的狀態(tài)不是關(guān)閉HSE{/* Get Start Tick*/tickstart = HAL_GetTick();//獲得開始時(shí)間/* Wait till HSE is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)//HSE準(zhǔn)備結(jié)束就退出循環(huán){if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)//等待時(shí)間超過(guò)等待最大值(100){return HAL_TIMEOUT;//返回超時(shí)}}}else//若設(shè)置的狀態(tài)為關(guān)閉HSE{/* Get Start Tick*/tickstart = HAL_GetTick();/* Wait till HSE is bypassed or disabled */while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET){if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}}}/*----------------------------- HSI Configuration --------------------------*/if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSI) == RCC_OSCILLATORTYPE_HSI)//判斷是否有HSI{/* Check the parameters */assert_param(IS_RCC_HSI(RCC_OscInitStruct->HSIState));//兩種狀態(tài)RCC_HSI_OFF或者RCC_HSI_ON,此處存疑。assert_param(IS_RCC_CALIBRATION_VALUE(RCC_OscInitStruct->HSICalibrationValue));//值小于等于0x1F/* Check if HSI is used as system clock or as PLL source when PLL is selected as system clock */if((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_HSI) || \((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_PLL)&& ((RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) == RCC_PLLCFGR_PLLSRC_HSI)))//判斷HSI是否已經(jīng)直接或者間接用于系統(tǒng)時(shí)鐘{/* When HSI is used as system clock it will not disabled */if((__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) != RESET) && (RCC_OscInitStruct->HSIState != RCC_HSI_ON))//HSI已經(jīng)直接或者間接作為系統(tǒng)時(shí)鐘,不可以清零。{return HAL_ERROR;}/* Otherwise, just the calibration is allowed */else//當(dāng)系統(tǒng)時(shí)鐘已經(jīng)直接或者間接使用HSI,而HSI尚未打開或者設(shè)置狀態(tài)不是RCC_HSI_ON{/* Adjusts the Internal High Speed oscillator (HSI) calibration value.*/__HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->HSICalibrationValue);//設(shè)置內(nèi)部高速時(shí)鐘的微調(diào)}}else//若HSI并沒(méi)有直接或者間接用于系統(tǒng)時(shí)鐘。{/* Check the HSI State */if((RCC_OscInitStruct->HSIState) != RCC_HSI_OFF)//需要使能RCC{/* Enable the Internal High Speed oscillator (HSI). */__HAL_RCC_HSI_ENABLE();//通過(guò)改變RCC_CR寄存器使能HSION/* Get Start Tick*/tickstart = HAL_GetTick();//得到當(dāng)前時(shí)間/* Wait till HSI is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET)//等待HSI啟動(dòng){if((HAL_GetTick() - tickstart ) > HSI_TIMEOUT_VALUE)//若是啟動(dòng)時(shí)間超時(shí),則返回錯(cuò)誤{return HAL_TIMEOUT;}}/* Adjusts the Internal High Speed oscillator (HSI) calibration value.*/__HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->HSICalibrationValue);//設(shè)置HSI的微調(diào)}else//需要禁用RCC{/* Disable the Internal High Speed oscillator (HSI). */__HAL_RCC_HSI_DISABLE();//禁用RCC/* Get Start Tick*/tickstart = HAL_GetTick();//獲取當(dāng)前時(shí)間/* Wait till HSI is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) != RESET)//等待HSI禁用{if((HAL_GetTick() - tickstart ) > HSI_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}}}/*------------------------------ LSI Configuration -------------------------*/if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_LSI) == RCC_OSCILLATORTYPE_LSI)//判斷是否有LSI{/* Check the parameters */assert_param(IS_RCC_LSI(RCC_OscInitStruct->LSIState));//判斷LSI狀態(tài)為RCC_LSI_OFF或RCC_LSI_ON/* Check the LSI State */if((RCC_OscInitStruct->LSIState) != RCC_LSI_OFF)//要使能LSI{/* Enable the Internal Low Speed oscillator (LSI). */__HAL_RCC_LSI_ENABLE();//通過(guò)改變RCC_CSR使能LSI/* Get Start Tick*/tickstart = HAL_GetTick();//得到當(dāng)前時(shí)間值/* Wait till LSI is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET)//等待LSI準(zhǔn)備好{if((HAL_GetTick() - tickstart ) > LSI_TIMEOUT_VALUE){return HAL_TIMEOUT;//超時(shí)返回錯(cuò)誤}}}else//要禁止LSI{/* Disable the Internal Low Speed oscillator (LSI). */__HAL_RCC_LSI_DISABLE();//禁止LSI/* Get Start Tick*/tickstart = HAL_GetTick();/* Wait till LSI is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) != RESET)//等待LSI準(zhǔn)備好{if((HAL_GetTick() - tickstart ) > LSI_TIMEOUT_VALUE){return HAL_TIMEOUT;//超時(shí)返回錯(cuò)誤}}}}/*------------------------------ LSE Configuration -------------------------*/if(((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_LSE) == RCC_OSCILLATORTYPE_LSE)//判斷是否有LSE{/* Check the parameters */assert_param(IS_RCC_LSE(RCC_OscInitStruct->LSEState));//LSE三種狀態(tài):RCC_LSE_OFF,RCC_LSE_ON,RCC_LSE_BYPASS/* Enable Power Clock*/__HAL_RCC_PWR_CLK_ENABLE();//因?yàn)橐獙?duì)寄存器RCC_PWR進(jìn)行操作,所以對(duì)該時(shí)鐘進(jìn)行使能。/* Enable write access to Backup domain */PWR->CR |= PWR_CR_DBP; //使能RTC以及RTC備份寄存器和備份SRAM的訪問(wèn) /* Wait for Backup domain Write protection enable */tickstart = HAL_GetTick();//記錄當(dāng)前時(shí)間while((PWR->CR & PWR_CR_DBP) == RESET)//等待寫入信息生效{if((HAL_GetTick() - tickstart ) > RCC_DBP_TIMEOUT_VALUE){return HAL_TIMEOUT;}}/* Set the new LSE configuration -----------------------------------------*/__HAL_RCC_LSE_CONFIG(RCC_OscInitStruct->LSEState);//直接設(shè)置LSE狀態(tài)/* Check the LSE State */if((RCC_OscInitStruct->LSEState) != RCC_LSE_OFF)//等待LSE狀態(tài)生效{/* Get Start Tick*/tickstart = HAL_GetTick();/* Wait till LSE is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET){if((HAL_GetTick() - tickstart ) > RCC_LSE_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}else{/* Get Start Tick*/tickstart = HAL_GetTick();/* Wait till LSE is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) != RESET){if((HAL_GetTick() - tickstart ) > RCC_LSE_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}}/*-------------------------------- PLL Configuration -----------------------*//* Check the parameters */assert_param(IS_RCC_PLL(RCC_OscInitStruct->PLL.PLLState));//檢測(cè)PLL狀態(tài):RCC_PLL_NONE,RCC_PLL_OFF,RCC_PLL_ONif ((RCC_OscInitStruct->PLL.PLLState) != RCC_PLL_NONE)//判斷是否需要設(shè)置PLL{/* Check if the PLL is used as system clock or not */if(__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_CFGR_SWS_PLL)//若PLL不是系統(tǒng)時(shí)鐘{if((RCC_OscInitStruct->PLL.PLLState) == RCC_PLL_ON)//需要將PLL設(shè)置為使能{/* Check the parameters */assert_param(IS_RCC_PLLSOURCE(RCC_OscInitStruct->PLL.PLLSource));//RCC_PLLSOURCE_HSI或者RCC_PLLSOURCE_HSEassert_param(IS_RCC_PLLM_VALUE(RCC_OscInitStruct->PLL.PLLM));//0~63assert_param(IS_RCC_PLLN_VALUE(RCC_OscInitStruct->PLL.PLLN));//50~432assert_param(IS_RCC_PLLP_VALUE(RCC_OscInitStruct->PLL.PLLP));//2,4,6,8assert_param(IS_RCC_PLLQ_VALUE(RCC_OscInitStruct->PLL.PLLQ));//4~15/* Disable the main PLL. */__HAL_RCC_PLL_DISABLE();//禁止PLL/* Get Start Tick*/tickstart = HAL_GetTick();//記錄當(dāng)前時(shí)間/* Wait till PLL is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) != RESET)//等待設(shè)置生效{if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE){return HAL_TIMEOUT;}}/* Configure the main PLL clock source, multiplication and division factors. */WRITE_REG(RCC->PLLCFGR, (RCC_OscInitStruct->PLL.PLLSource | \RCC_OscInitStruct->PLL.PLLM | \(RCC_OscInitStruct->PLL.PLLN << POSITION_VAL(RCC_PLLCFGR_PLLN)) | \(((RCC_OscInitStruct->PLL.PLLP >> 1U) - 1U) << POSITION_VAL(RCC_PLLCFGR_PLLP)) | \(RCC_OscInitStruct->PLL.PLLQ << POSITION_VAL(RCC_PLLCFGR_PLLQ))));//配置PLL信息/* Enable the main PLL. */__HAL_RCC_PLL_ENABLE();//將PLL打開/* Get Start Tick*/tickstart = HAL_GetTick();/* Wait till PLL is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)//等待PLL設(shè)置生效{if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}else{/* Disable the main PLL. */__HAL_RCC_PLL_DISABLE();//禁止PLL/* Get Start Tick*/tickstart = HAL_GetTick();/* Wait till PLL is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) != RESET)//等待PLL設(shè)置生效{if((HAL_GetTick() - tickstart ) > PLL_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}}else//若PLL已經(jīng)成為系統(tǒng)時(shí)鐘,則PLL的狀態(tài)只能為RCC_PLL_NONE,否則報(bào)錯(cuò){return HAL_ERROR;}}return HAL_OK; }

    該函數(shù)總體分成5個(gè)部分:

  • HSE設(shè)置
  • HSI設(shè)置
  • LSI設(shè)置
  • LSE設(shè)置
  • PLL設(shè)置
  • 其中,以HSE設(shè)置為典型,詳細(xì)介紹該函數(shù)實(shí)現(xiàn)過(guò)程。HSE設(shè)置大致可以分成三個(gè)部分:

  • 檢測(cè)參數(shù)數(shù)據(jù)。
  • 條件判斷:若HSE用作系統(tǒng)時(shí)鐘,則不可以禁用HSE。
  • HSE并沒(méi)有用作系統(tǒng)時(shí)鐘 ,則設(shè)置HSE的狀態(tài)。
  • 在判斷HSE是否直接或者間接用于系統(tǒng)時(shí)鐘的時(shí)候,源代碼如下:

    if((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_HSE) ||\((__HAL_RCC_GET_SYSCLK_SOURCE() == RCC_CFGR_SWS_PLL) && ((RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) == RCC_PLLCFGR_PLLSRC_HSE)))

    第一行的含義是:判斷系統(tǒng)時(shí)鐘為RCC_CFGR_SWS_HSE。
    第二行的含義是:判斷系統(tǒng)時(shí)鐘為RCC_CFGR_SWS_PLL且PLL的時(shí)鐘源為HSE。
    所以,綜合以上兩行語(yǔ)句,則表示HSE直接或者間接用于系統(tǒng)時(shí)鐘。

    其中,宏__HAL_RCC_GET_SYSCLK_SOURCE()的定義為:

    /** @brief Macro to get the clock source used as system clock.* @retval The clock source used as system clock. The returned value can be one* of the following:* - RCC_SYSCLKSOURCE_STATUS_HSI: HSI used as system clock.* - RCC_SYSCLKSOURCE_STATUS_HSE: HSE used as system clock.* - RCC_SYSCLKSOURCE_STATUS_PLLCLK: PLL used as system clock.* - RCC_SYSCLKSOURCE_STATUS_PLLRCLK: PLLR used as system clock.*/ #define __HAL_RCC_GET_SYSCLK_SOURCE() ((uint32_t)(RCC->CFGR & RCC_CFGR_SWS))

    若當(dāng)HSE直接或者間接用做系統(tǒng)時(shí)鐘時(shí),則不可以禁用HSE,如下圖參考手冊(cè)所示:

    這個(gè)條件由以下程序給出判定:

    if((__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) != RESET) && (RCC_OscInitStruct->HSEState == RCC_HSE_OFF))//若HSE用做系統(tǒng)時(shí)鐘,則不可以禁用HSE。 {return HAL_ERROR; }

    其中兩個(gè)判斷條件分別為:

  • HSE已經(jīng)啟動(dòng)。
  • 將要進(jìn)制HSE。
  • 其中,第二條語(yǔ)句比較簡(jiǎn)單,這里不再詳述。第一條語(yǔ)句的宏定義為:

    /** @brief Check RCC flag is set or not.* @param __FLAG__: specifies the flag to check.* This parameter can be one of the following values:* @arg RCC_FLAG_HSIRDY: HSI oscillator clock ready.* @arg RCC_FLAG_HSERDY: HSE oscillator clock ready.* @arg RCC_FLAG_PLLRDY: Main PLL clock ready.* @arg RCC_FLAG_PLLI2SRDY: PLLI2S clock ready.* @arg RCC_FLAG_LSERDY: LSE oscillator clock ready.* @arg RCC_FLAG_LSIRDY: LSI oscillator clock ready.* @arg RCC_FLAG_BORRST: POR/PDR or BOR reset.* @arg RCC_FLAG_PINRST: Pin reset.* @arg RCC_FLAG_PORRST: POR/PDR reset.* @arg RCC_FLAG_SFTRST: Software reset.* @arg RCC_FLAG_IWDGRST: Independent Watchdog reset.* @arg RCC_FLAG_WWDGRST: Window Watchdog reset.* @arg RCC_FLAG_LPWRRST: Low Power reset.* @retval The new state of __FLAG__ (TRUE or FALSE).*/ #define RCC_FLAG_MASK ((uint8_t)0x1FU) #define __HAL_RCC_GET_FLAG(__FLAG__) (((((((__FLAG__) >> 5U) == 1U)? RCC->CR :((((__FLAG__) >> 5U) == 2U) ? RCC->BDCR :((((__FLAG__) >> 5U) == 3U)? RCC->CSR :RCC->CIR))) & ((uint32_t)1U << ((__FLAG__) & RCC_FLAG_MASK)))!= 0U)? 1U : 0U)/*** @}*/

    這條語(yǔ)句很難讀懂,是因?yàn)閼?yīng)用了很多三目運(yùn)算符,可以將上述定義用條件語(yǔ)句改寫為:

    int __HAL_RCC_GET_FLAG(uint8_t __FLAG__) {flag = __FLAG__ >> 5U;//將__FLAG__右移5位,為新的判斷條件。uint32_t x = 0;//x用于存儲(chǔ)對(duì)應(yīng)的寄存器數(shù)據(jù)//根據(jù)__FLAG__高3位數(shù)據(jù)的不同,來(lái)選擇對(duì)應(yīng)的寄存器if(flag == 1){x = RCC->CR;}else if (flag == 2){x = RCC->BDCR;}else if(flag == 3){x = RCC->CSR;}else{x = RCC->CIR;}di5wei = __FLAG__ & 0x1FU; //獲取低5位,低5位是用來(lái)確定是寄存器的哪一個(gè)二進(jìn)制位。y = 1 << di5wei;//將1移動(dòng)到對(duì)應(yīng)的二進(jìn)制位if(x & y == 0)//返回值為1或者0return 0;elsereturn 1; }

    根據(jù)以上代碼可以分析出,宏__HAL_RCC_GET_FLAG(__FLAG__)的參數(shù)由兩部分組成:

  • 高三位,對(duì)應(yīng)其所在的寄存器
  • 低五位,對(duì)應(yīng)其在寄存器的二進(jìn)制位
  • 通過(guò)該宏就可以根據(jù)標(biāo)志位得知系統(tǒng)的狀態(tài)。

    最后,看一下設(shè)置HSE狀態(tài),該部分大致可以分成兩個(gè)部分:

  • 設(shè)置HSE的新狀態(tài)寫入寄存器。
  • 等待寫入的HSE狀態(tài)有效。
  • 寫入寄存器的宏定義為:

    /*** @brief Macro to configure the External High Speed oscillator (HSE).* @note Transition HSE Bypass to HSE On and HSE On to HSE Bypass are not supported by this macro. * User should request a transition to HSE Off first and then HSE On or HSE Bypass.* @note After enabling the HSE (RCC_HSE_ON or RCC_HSE_Bypass), the application* software should wait on HSERDY flag to be set indicating that HSE clock* is stable and can be used to clock the PLL and/or system clock.* @note HSE state can not be changed if it is used directly or through the* PLL as system clock. In this case, you have to select another source* of the system clock then change the HSE state (ex. disable it).* @note The HSE is stopped by hardware when entering STOP and STANDBY modes. * @note This function reset the CSSON bit, so if the clock security system(CSS)* was previously enabled you have to enable it again after calling this* function. * @param __STATE__: specifies the new state of the HSE.* This parameter can be one of the following values:* @arg RCC_HSE_OFF: turn OFF the HSE oscillator, HSERDY flag goes low after* 6 HSE oscillator clock cycles.* @arg RCC_HSE_ON: turn ON the HSE oscillator.* @arg RCC_HSE_BYPASS: HSE oscillator bypassed with external clock.*/ #define __HAL_RCC_HSE_CONFIG(__STATE__) (*(__IO uint8_t *) RCC_CR_BYTE2_ADDRESS = (__STATE__))

    此處比較簡(jiǎn)單,不再詳述。

    因?yàn)閷懭氲腍SE狀態(tài)大致可以分成兩類:

  • 打開HSE
  • 關(guān)閉HSE
  • 以打開HSE為例,源代碼為:

    if((RCC_OscInitStruct->HSEState) != RCC_HSE_OFF) {/* Get Start Tick*/tickstart = HAL_GetTick();//記錄當(dāng)前時(shí)間/* Wait till HSE is ready */while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)//判斷是否生效{if((HAL_GetTick() - tickstart ) > HSE_TIMEOUT_VALUE)//用當(dāng)前時(shí)間減去第一次記錄的時(shí)間,計(jì)算消耗時(shí)間{return HAL_TIMEOUT;}} }

    若上述代碼所示,記錄當(dāng)前的時(shí)間。若設(shè)置并未生效,則循環(huán)等待且等待時(shí)間超過(guò)最大等待時(shí)間,則報(bào)錯(cuò) HAL_TIMEOUT。至此HSE部分基本結(jié)束。其余四個(gè)部分與其基本一致,不再詳細(xì)展開,只是其中幾點(diǎn)需要特別注意為:

  • HSI 需要設(shè)置微調(diào)信息。
  • PLL設(shè)置需要寫入分頻與倍頻的參數(shù)。需要注意的是,需要首先關(guān)閉PLL,然后將PLL倍頻與分頻的參數(shù)寫入,最后在將PLL打開。
  • 下面重點(diǎn)講述HSI設(shè)置微調(diào)信息的過(guò)程,其對(duì)應(yīng)的源碼為:

    __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(RCC_OscInitStruct->HSICalibrationValue);

    則宏定義為:

    /** @brief Macro to adjust the Internal High Speed oscillator (HSI) calibration value.* @note The calibration is used to compensate for the variations in voltage* and temperature that influence the frequency of the internal HSI RC.* @param __HSICalibrationValue__: specifies the calibration trimming value.* (default is RCC_HSICALIBRATION_DEFAULT).* This parameter must be a number between 0 and 0x1F.*/ #define __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(__HSICalibrationValue__) (MODIFY_REG(RCC->CR,\RCC_CR_HSITRIM, (uint32_t)(__HSICalibrationValue__) << POSITION_VAL(RCC_CR_HSITRIM)))

    若想理解該宏的意思,需要了解的有:

  • MODIFY_REG宏定義
  • RCC_CR_HSITRIM
  • POSITION_VAL宏定義
  • 其中,第二點(diǎn)最簡(jiǎn)單,源代碼為:

    #define RCC_CR_HSITRIM ((uint32_t)0x000000F8U)

    RCC_CR_HSITRIM 為HSI微調(diào)偏移量在CR中的掩碼,換句話說(shuō), RCC_CR_HSITRIM中二進(jìn)制1對(duì)應(yīng)的數(shù)據(jù)位就是RCC_CR寄存器中HSI微調(diào)偏移量對(duì)應(yīng)的數(shù)據(jù)位。

    MODIFY_REG宏定義如下所示:

    #define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))

    可以大致分成兩個(gè)步驟:

  • 通過(guò)CLEARMASK將REG對(duì)應(yīng)位清零。
  • 在將SETMASK對(duì)應(yīng)的二進(jìn)位置一。
  • POSITION_VAL宏定義最難理解,其對(duì)應(yīng)的宏定義為

    #define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL)))

    其中__RBIT()和__CLA()都對(duì)應(yīng)著匯編指令,可以通過(guò)查詢keil幫助文檔或者<權(quán)威指南>來(lái)獲得指令的意思如下:

    __RBIT()//ARM 32位數(shù)據(jù)匯編指令,將數(shù)據(jù)的二進(jìn)制值進(jìn)行反轉(zhuǎn)。 __CLA()//ARM 32位數(shù)據(jù)匯編指令,計(jì)算二進(jìn)制數(shù)前導(dǎo)零的個(gè)數(shù)


    注意:

    兩個(gè)指令只適用于32位整形。

    其中,__RBIT()即將二進(jìn)制數(shù)最高位和最低位調(diào)換位置,次高位和次低位調(diào)換位置,以此類推。__CLA(),計(jì)算從最高位到第一個(gè)0,該二進(jìn)制數(shù)中有多少個(gè)零。所以,POSITION_VAL即求取該32位二進(jìn)制數(shù)從從最位開始,到第一個(gè)1,共有多少個(gè)0.

    綜上所述,可以理解

    #define __HAL_RCC_HSI_CALIBRATIONVALUE_ADJUST(__HSICalibrationValue__) (MODIFY_REG(RCC->CR,\RCC_CR_HSITRIM, (uint32_t)(__HSICalibrationValue__) << POSITION_VAL(RCC_CR_HSITRIM)))

    程序的含義就是先將CR寄存器對(duì)應(yīng)HSI微調(diào)部分進(jìn)行清零,然后將微調(diào)數(shù)據(jù)__HSICalibrationValue__左移適當(dāng)位置,寫入對(duì)應(yīng)的寄存器位中。

    時(shí)鐘初始化

    HAL_StatusTypeDef HAL_RCC_ClockConfig(RCC_ClkInitTypeDef *RCC_ClkInitStruct, uint32_t FLatency) {uint32_t tickstart = 0U;/* Check the parameters */assert_param(IS_RCC_CLOCKTYPE(RCC_ClkInitStruct->ClockType));//IS_RCC_CLOCKTYPE(CLK) ((1U <= (CLK)) && ((CLK) <= 15U))assert_param(IS_FLASH_LATENCY(FLatency));//FLASH_LATENCY_0-FLASH_LATENCY_15/* To correctly read data from FLASH memory, the number of wait states (LATENCY)must be correctly programmed according to the frequency of the CPU clock(HCLK) and the supply voltage of the device. *//**************************************1.調(diào)高Flash延時(shí)時(shí)間 *******************************************//* Increasing the number of wait states because of higher CPU frequency */if(FLatency > (FLASH->ACR & FLASH_ACR_LATENCY))//若設(shè)定值比原有值大{/* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */__HAL_FLASH_SET_LATENCY(FLatency);//將設(shè)定值寫入寄存器/* Check that the new number of wait states is taken into account to access the Flashmemory by reading the FLASH_ACR register */if((FLASH->ACR & FLASH_ACR_LATENCY) != FLatency)//檢查寫入值是否有效{return HAL_ERROR;}}/**************************************2.HCLK設(shè)置 *******************************************//*-------------------------- HCLK Configuration --------------------------*/if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_HCLK) == RCC_CLOCKTYPE_HCLK){assert_param(IS_RCC_HCLK(RCC_ClkInitStruct->AHBCLKDivider));//分頻數(shù)可以為1,2,4,8,16,64,128,256,512。注意沒(méi)有32分頻MODIFY_REG(RCC->CFGR, RCC_CFGR_HPRE, RCC_ClkInitStruct->AHBCLKDivider);//將分頻數(shù)寫入對(duì)應(yīng)的寄存器}/**************************************3.SYSCLK設(shè)置 *******************************************//*------------------------- SYSCLK Configuration ---------------------------*/if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_SYSCLK) == RCC_CLOCKTYPE_SYSCLK){assert_param(IS_RCC_SYSCLKSOURCE(RCC_ClkInitStruct->SYSCLKSource));//檢測(cè)范圍/* HSE is selected as System Clock Source */if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_HSE)//若HSE作為系統(tǒng)時(shí)鐘{/*3.1檢測(cè)選擇系統(tǒng)時(shí)鐘是否處于禁用狀態(tài),需要注意的是設(shè)置PLL源的時(shí)候沒(méi)有這個(gè)步驟*//* Check the HSE ready flag */if(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET)//若HSE處于禁用狀態(tài),則返回錯(cuò)誤HAL_ERROR{return HAL_ERROR;}}/* PLL is selected as System Clock Source */else if((RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK) ||(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLRCLK)){/* Check the PLL ready flag */if(__HAL_RCC_GET_FLAG(RCC_FLAG_PLLRDY) == RESET)//若PLL處于禁用狀態(tài),則返回錯(cuò)誤HAL_ERROR{return HAL_ERROR;}}/* HSI is selected as System Clock Source */else{/* Check the HSI ready flag */if(__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET)//若HSI處于禁用狀態(tài),則返回錯(cuò)誤HAL_ERROR{return HAL_ERROR;}}//若選擇的系統(tǒng)時(shí)鐘處于啟動(dòng)狀態(tài),則使其生效。__HAL_RCC_SYSCLK_CONFIG(RCC_ClkInitStruct->SYSCLKSource);/* Get Start Tick*/tickstart = HAL_GetTick();//開始計(jì)時(shí),記錄當(dāng)前的時(shí)間//判斷設(shè)置是否生效,若等待時(shí)間超時(shí),返回錯(cuò)誤HAL_TIMEOUTif(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_HSE){while (__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_SYSCLKSOURCE_STATUS_HSE){if((HAL_GetTick() - tickstart ) > CLOCKSWITCH_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}else if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLCLK){while (__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_SYSCLKSOURCE_STATUS_PLLCLK){if((HAL_GetTick() - tickstart ) > CLOCKSWITCH_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}else if(RCC_ClkInitStruct->SYSCLKSource == RCC_SYSCLKSOURCE_PLLRCLK){while (__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_SYSCLKSOURCE_STATUS_PLLRCLK){if((HAL_GetTick() - tickstart ) > CLOCKSWITCH_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}else{while(__HAL_RCC_GET_SYSCLK_SOURCE() != RCC_SYSCLKSOURCE_STATUS_HSI){if((HAL_GetTick() - tickstart ) > CLOCKSWITCH_TIMEOUT_VALUE){return HAL_TIMEOUT;}}}}/**************************************4.降低Flash延時(shí)時(shí)間 *******************************************//* Decreasing the number of wait states because of lower CPU frequency */if(FLatency < (FLASH->ACR & FLASH_ACR_LATENCY)){/* Program the new number of wait states to the LATENCY bits in the FLASH_ACR register */__HAL_FLASH_SET_LATENCY(FLatency);/* Check that the new number of wait states is taken into account to access the Flashmemory by reading the FLASH_ACR register */if((FLASH->ACR & FLASH_ACR_LATENCY) != FLatency){return HAL_ERROR;}}/**************************************5.PCLK1設(shè)置 *******************************************//*-------------------------- PCLK1 Configuration ---------------------------*/if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK1) == RCC_CLOCKTYPE_PCLK1){assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB1CLKDivider));//1,2,4,8,16MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE1, RCC_ClkInitStruct->APB1CLKDivider);//設(shè)置APB1分頻數(shù)}/**************************************6.PCLK2設(shè)置 *******************************************//*-------------------------- PCLK2 Configuration ---------------------------*/if(((RCC_ClkInitStruct->ClockType) & RCC_CLOCKTYPE_PCLK2) == RCC_CLOCKTYPE_PCLK2){assert_param(IS_RCC_PCLK(RCC_ClkInitStruct->APB2CLKDivider));//1,2,4,8,16MODIFY_REG(RCC->CFGR, RCC_CFGR_PPRE2, ((RCC_ClkInitStruct->APB2CLKDivider) << 3U));//設(shè)置APB2分頻數(shù)}/* Configure the source of time base considering new system clocks settings*/HAL_InitTick (TICK_INT_PRIORITY);return HAL_OK; }

    這部分代碼比較簡(jiǎn)單,比較難理解的地方在上面一節(jié)已經(jīng)提到了。所以,本部分代碼不再詳細(xì)展開,只是添加了中文備注,如上所示。

    時(shí)鐘初始化總共分成六個(gè)部分:

  • 提高Flash延時(shí)時(shí)間
  • HCLK設(shè)置
  • SYSCLK設(shè)置
  • 降低Flash延時(shí)時(shí)間
  • PCLK1設(shè)置
  • PCLK2設(shè)置
  • 需要注意的是,每個(gè)不同的模塊對(duì)于分頻數(shù)和倍頻數(shù)設(shè)置的限制是不同的。所以,為了得到想要的主頻,還需要自己配合不同模塊的限制。另外一點(diǎn)是關(guān)于Flash延時(shí)時(shí)間的設(shè)置,若想要提高Flash延時(shí)時(shí)間,則先進(jìn)行Flash時(shí)間設(shè)置,再進(jìn)行主頻設(shè)置。若想要降低Flash延時(shí)時(shí)間,則需要先進(jìn)行主頻設(shè)置,再進(jìn)行Flash延時(shí)時(shí)間設(shè)置。這是因?yàn)閷捲5腇lash延時(shí)時(shí)間是更加安全的。
    最后,關(guān)于SYSCLK源設(shè)置還需要注意,在設(shè)置之前,需要保證SYSCLK源是處于啟動(dòng)狀態(tài)。

    systick

    這部分內(nèi)容請(qǐng)參考博客<STM32F429第十篇之systick>

    總結(jié)

    以上是生活随笔為你收集整理的STM32F429第四篇之跑马灯程序详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。