0、GPIO回顧
GPIO,通用型輸入輸出,控制stm32輸入輸出的引腳,統稱GPIO。
主功能是默認的功能
復用的功能在芯片里都是由連線的,有聯系才能復用。所以GPIO引腳能復用的功能只能是它默認復用功能和重定義功能。一般都使用默認功能,重定義往往不會打開,一旦要用重定義功能需要用到重映射。
想象成多路開關,內部的配置決定輸入輸出模式。
端口配置寄存器? ?低位CRL (0-7) 高位CRH(8-15) 4位配一個引腳,CNF和MODE各配2個。
一、改進GPIO流水燈
(1)復制案例,修改名稱
03_led_flow_pro_register
(2)刪減
刪除除user和start以外的文件夾和文件,保留.uvprojx文件
(3)創建文件
像這樣的基礎配置,都可以放在一起稱為初始化。也就是將外圍的LED燈,寫一個驅動程序。LED燈是屬于硬件外設,所以新建文件夾Hardware,新建兩個文件,如下:
(4)打開keil文件項目
(5)打開項目管理,添加User文件夾下delay.c文件
(6)在Hardware文件下創建LED文件夾,將led.c和led.h文件放進去。
?
(7)配完關掉keil,通過vscode打開。
首先補充delay.c,第一步肯定是引入.h文件。
在delay.h中如此定義
前一個文件在main中定義的函數可以直接挪移。
led.c也是同理。引入led.h
之后把所有定義函數的操作放進.c文件里。
#include "led.h"//初始化
void LED_Init(void)
{//1.時鐘配置,開啟GPIOA時鐘RCC->APB2ENR|=RCC_APB2ENR_IOPAEN;//2.工作模式配置,PA0 PA1 PA8通用推挽輸出 CNF=00,MODE=11GPIOA->CRL&=~GPIO_CRL_CNF0;GPIOA->CRL|=GPIO_CRL_MODE0;GPIOA->CRL&=~GPIO_CRL_CNF1;GPIOA->CRL|=GPIO_CRL_MODE1;GPIOA->CRH&=~GPIO_CRH_CNF8;GPIOA->CRH|=GPIO_CRH_MODE8;//3.初始全高電平,都置1, 全關燈/*GPIOA->ODR|=GPIO_ODR_ODR0; LED_Off(LED_1);GPIOA->ODR|=GPIO_ODR_ODR1; LED_Off(LED_2)GPIOA->ODR|=GPIO_ODR_ODR8; LED_Off(LED_3);*/uint16_t leds[]={LED_1,LED_2,LED_3};LED_OffAll(leds,3);
}//開關LED燈,參數就定為led,要開哪個燈就傳哪個燈
void LED_On(uint16_t led)
{
GPIOA->ODR&=~led;
}
void LED_Off(uint16_t led)
{GPIOA->ODR|=led;
}//反轉LED燈狀態
void LED_Toggle(uint16_t led)
{//根據IDR對應位的值,判斷當前LED狀態. IDR和ODR對應的位是一樣的if((GPIOA->IDR&led)==0){LED_Off(led);}else{LED_On(led);}
}//控制所有燈的開關
void LED_OnAll(uint16_t leds[],uint8_t size)//全開
{for (uint8_t i = 0; i < size; i++){LED_On(leds[i]);}}
void LED_OffAll(uint16_t leds[],uint8_t size)//全關
{for (uint8_t i = 0; i < size; i++){LED_Off(leds[i]);}}
為了看起來方便,將開啟數據端口的寄存器寫法宏定義為我們的白話。
合理規范代碼:
delay.c
#include "delay.h"
//延時函數
void Delay_us(uint16_t us)
{SysTick->LOAD=72*us;SysTick->CTRL=0x05;while(!(SysTick->CTRL&SysTick_CTRL_COUNTFLAG)){}SysTick->CTRL&=~SysTick_CTRL_ENABLE;
}
void Delay_ms(uint16_t ms)
{while(ms--){
Delay_us(1000);}}
void Delay_s(uint16_t s)
{while(s--){
Delay_ms(1000);}}
delay.h
#ifndef __DELAY_H
#define __DELAY_H#include "stm32f10x.h"
//定義延時函數
void Delay_us(uint16_t us);
void Delay_ms(uint16_t ms);
void Delay_s(uint16_t s);
#endif
led.c
#include "led.h"void LED_Init(void)
{// 時鐘配置,打開時鐘RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;// 工作模式配置,PA0 PA1 PA8通過推挽輸出。CNF=00,MODE=11GPIOA->CRL |= GPIO_CRL_MODE0;GPIOA->CRL &= ~GPIO_CRL_CNF0;GPIOA->CRL |= GPIO_CRL_MODE1;GPIOA->CRL &= ~GPIO_CRL_CNF1;GPIOA->CRH |= GPIO_CRH_MODE8;GPIOA->CRH &= ~GPIO_CRH_CNF8;// 初始化引腳輸出高電平,關燈LED_Off(LED_1);LED_Off(LED_2);LED_Off(LED_3);
}void LED_On(uint16_t led)
{GPIOA->ODR &= ~led;
}void LED_Off(uint16_t led)
{GPIOA->ODR |= led;
}void LED_Toggle(uint16_t led)
{// 根據IDR對應位的值,判斷當前LED狀態if ((GPIOA->IDR & led) == 0){LED_Off(led);}else{LED_On(led);}
}void LED_OnAll(uint16_t leds[], uint8_t size)
{for (uint8_t i = 0; i < size; i++){LED_On(leds[i]);}
}void LED_OffAll(uint16_t leds[], uint8_t size)
{for (uint8_t i = 0; i < size; i++){LED_Off(leds[i]);}
}
led.h
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
//定義LED燈
#define LED_1 GPIO_ODR_ODR0
#define LED_2 GPIO_ODR_ODR1
#define LED_3 GPIO_ODR_ODR8
//初始化
void LED_Init(void);
//開關LED燈
void LED_On(uint16_t led);
void LED_Off(uint16_t led);//翻轉LED燈狀態
void LED_Toggle(uint16_t led);
//控制所有LED燈的開關
void LED_OnAll(uint16_t leds[],uint8_t size);
void LED_OffAll(uint16_t leds[],uint8_t size);
#endif
main.c
#include <stdint.h>
#include "delay.h"
#include "led.h"
int main(void){//初始化
LED_Init();
uint16_t leds[]={LED_1,LED_2,LED_3};
uint8_t n=3;while(1){for(uint8_t i=0;i<n;i++){LED_On(leds[i]);Delay_ms(500);LED_Off(leds[i]);}}}
二、總體架構和時鐘系統
1.總體架構stm32
(1)3個被動單元
內部SRAM
存儲程序執行時用到的變量
在嵌入式環境中,SRAM相當于運行內存
內部閃存存儲器?
存儲下載的程序 程序執行時用到的常量
flash嚴格來講劃分為ROM(隨機訪問存儲器,掉電就丟)類型
在嵌入式環境中,flash相當于存儲內存
AHB到APB的橋(AHB to APBx)?
B:Bus? 總線的意思? AHB:高速系統總線? 是總線的核心
橋1,通過APB2總線連接到APB2上的外設。屬于高速外設,最高72MHz.
橋2,通過APB1總線連接APB1的外設。低速外設,最高36MHz。時鐘配置時需要二分頻。
(2)四個驅動(主動)單元
(3)其他單元
內核Code總線
通過外部的ICode總線連接Flash,實現指令的讀取
FSMC
2.時鐘系統
51不需要開啟時鐘,因為簡單,只有一個時鐘,不用配。
32有不同時鐘來源,高速設備接高速時鐘,低速設備接低速時鐘。這樣效率最高。
時鐘源
內部的低速時鐘 40k? ? 外部的低速時鐘 32.768k
SYSCLK 系統時鐘
RTCCLK 實時時鐘
(1)HSE時鐘
高速外部時鐘是由外部時鐘源提供。做過STM32項目的同學都知道,幾乎所有的單片機都會外部接一個8Mhz的晶振,經過PLL九倍頻得到72MHZ的系統時鐘,這是系統默認的時鐘。
(2)HSI時鐘
HSI時鐘是內部的8MHZ的RC振蕩器產生,可直接作為系統時鐘或在2分頻后作為PLL輸入。不需要任何外部器件就能提供系統時鐘。啟用時間比HSE晶體振蕩器短。缺點是就算校準,時鐘頻率精度較差。
?
(3)PLL時鐘
內部PLL用來倍頻HSI RC的輸出時鐘或HSE晶體輸出時鐘。PLL設置必須在其被激活前完成,一旦激活,參數不能被改動。如果PLL中斷在時鐘中斷寄存器里被允許,當PLL準備就緒時,可產生中斷申請。
PLL時鐘對外部8MHZ時鐘信號9倍頻,得到72MHZ時鐘頻率,這是STM32F1系列允許的最高時鐘頻率。
(4)LSE時鐘
LSE晶體是一個32.768khz的低速外部晶體或陶瓷諧振器。它為實時時鐘或者其他定時功能他提供一個低功耗且精確的時鐘源。LSE不能驅動系統時鐘。
(5)LSI時鐘
低功耗時鐘源,在停機和待機模式下保持運行,為獨立看門狗和自動喚醒單元提供時鐘。時鐘頻率大約40kHZ(30-60之間)。
不能驅動系統時鐘。
(6)stm32時鐘配置源碼分析
關于時鐘配置的源代碼,往往在啟動文件中已經做完了。
CR是時鐘的控制器
CFGR 時鐘配置寄存器
三、HAL庫開發
1.簡介
寄存器效率雖高,但是開發效率低,對開發者來說不太友好。
2.環境與安裝
Java8很穩定,我們就安裝這個。
win+r? cmd??
輸入 java -version? ?檢驗是否安裝java8
能夠顯示版本號,就說明電腦安裝著Java的jdk.
然后就是傻瓜式安裝。cubmx? 圖形化界面,自動生成ST的hal庫文件,讓我們的配置變成點點點。
之后就是芯片支持包了。雙擊打開cubeMX.
這是在線安裝,可能時間費得有些多。
下面是離線安裝步驟:
文章結尾我會給大家資料包,里面就有離線芯片支持包
里面有兩個,一個是基礎的,一個是升級的1-8-5,比較尷尬的是只能安裝基礎包,但我們可以李代桃僵。
我們安裝好CUBEMX之后,就去找它的倉庫,之前安裝的時候,不改路徑的話,應該在C盤用戶
我們在CUBEmx上先裝基礎包,之后將高版本的解壓到倉庫文件夾替換掉它
必須解壓到這里哦。
解壓之后替換掉基礎包。
3.流水燈案例(HAL庫)
(1)配置
聯網的話,會出現更新提示,直接關掉即可。
之后讓我開始點點點。
這里面都是我們所要配的引腳,找準需求,定位普通或要復用的引腳吧。
系統核心配置:
SYS是必配的。選擇模式和系統時鐘,單線模式SW,一根數據線,一根時鐘線。JTAG有四根線。
如果是標準庫寄存器寫法,時鐘需要自己配。
前邊我們說過,PLL時鐘會選擇外部晶振提供時鐘。這一點主要在下圖體現:
之后配置GPIO,點亮LED燈主要是靠GPIO的通用推挽輸出。
剩下兩個引腳都一樣。
之后創建工程文件
之后跟隨窗口就打開KEIL。
(2)代碼實現
話不多說,開始寫代碼。將我們之前寫的硬件外設LED燈的兩個文件導入工程。
led.c
#include "led.h"//開關LED燈,參數就定為led,要開哪個燈就傳哪個燈
void LED_On(uint16_t led)
{
HAL_GPIO_WritePin(LED_1_GPIO_Port,led,GPIO_PIN_RESET);
}
void LED_Off(uint16_t led)
{HAL_GPIO_WritePin(LED_1_GPIO_Port,led,GPIO_PIN_SET);
}//反轉LED燈狀態
void LED_Toggle(uint16_t led)
{HAL_GPIO_TogglePin(LED_1_GPIO_Port,led);
}//控制所有燈的開關
uint16_t leds[]={LED_1_Pin,LED_2_Pin,LED_3_Pin};
void LED_OnAll(uint16_t leds[],uint8_t size)//全開
{for (uint8_t i = 0; i < size; i++){LED_On(leds[i]);}}
void LED_OffAll(uint16_t leds[],uint8_t size)//全關
{for (uint8_t i = 0; i < size; i++){LED_Off(leds[i]);}}
main.c
/* USER CODE BEGIN Header */
/********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
#include "led.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();//初始化,所有引腳高電平,等全滅uint8_t n=3;uint16_t leds[]={LED_1_Pin,LED_2_Pin,LED_3_Pin};while (1){for (uint8_t i = 0; i < n; i++){LED_On(leds[i]);HAL_Delay(1000);LED_Off(leds[i]);}}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef USE_FULL_ASSERT
/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
四、資料下載
stm32開發官方版下載丨最新版下載丨綠色版下載丨APP下載-123云盤123云盤為您提供stm32開發最新版正式版官方版綠色版下載,stm32開發安卓版手機版apk免費下載安裝到手機,支持電腦端一鍵快捷安裝https://www.123684.com/s/TQubTd-1eEtv?提取碼:xscF