失蹤人口回歸了,stm32的學習比起51要慢一些,因為涉及插線,可能存在漏插,不牢固等問題。
相對于51直接對寄存器的設置,stm32因為是32位修改起來比較麻煩,江協課程是基于標準庫的,是對封裝函數進行操作,這要求我們對于模塊的使用在開始就規劃好。
GPIO通用輸入輸出口
APB2是外設總線
輸入模式
- 浮空輸入
- 原理:GPIO 端口無內部上拉或下拉電阻,電平狀態完全由外部輸入決定,引腳懸空時電平不確定。
- 使用場景:用于串口通信接收端(如 UART、USART 的 RX 引腳 ),接收外部設備電平變化信號;檢測有穩定高低電平的外部傳感器信號(如霍爾、紅外傳感器 );外部中斷信號輸入檢測;數字輸入信號檢測。
- 特點:能真實反映外部電平,但易受干擾,引腳懸空時讀數無參考意義。
- 上拉輸入
- 原理:內部連接上拉電阻,無外部輸入信號時,GPIO 端口保持高電平。
- 使用場景:機械按鍵或撥動開關輸入,未按下時為高電平,按下接地變低電平;IIC 通信 SDA 引腳,保證總線數據傳輸默認高電平;SPI 通信從設備選擇引腳(NSS),無信號時保持高,主設備選擇時拉低 ;繼電器狀態等開關量信號輸入檢測;電源檢測引腳,檢測電源供電狀態。
- 特點:確保無外部信號時輸入為高電平,增強信號穩定性,防信號漂移。
- 下拉輸入
- 原理:內部連接下拉電阻,無外部輸入信號時,GPIO 端口保持低電平。
- 使用場景:CAN_RX 引腳接收 CAN 總線信號,確保總線無信號時引腳低電平;按鈕接地觸發的按鍵輸入,未按下時低電平,按下拉高 ;默認低電平的數字電路信號輸入;下拉電阻保持低電平的光電開關等傳感器輸入;檢測外部設備低電平狀態的電路。
- 特點:確保無外部信號時輸入為低電平,適用于外部信號常態為高的檢測場景。
- 模擬輸入
- 原理:輸入信號不經施密特觸發器處理,直接接入內部 ADC,將模擬信號轉為數字信號。
- 使用場景:連接溫度、光照、濕度、氣壓等模擬傳感器采集信號;電池電壓檢測;電流檢測(通過分流電阻和運算放大器轉換為電壓信號 );光強檢測等。
- 特點:用于采集連續變化的模擬量,供 MCU 處理分析。
輸出模式
- 推挽輸出
- 原理:由兩個互補晶體管組成,可輸出高電平(接 VDD )和低電平(接 VSS ),能向負載灌電流或抽取電流,導通損耗小、效率高。
- 使用場景:驅動 LED、繼電器、蜂鳴器;控制小型直流電機;SPI 通信的 SCK、MOSI、MISO 等需強電平信號的總線通信引腳;各類狀態指示燈控制。
- 特點:驅動能力強,可快速切換高低電平,適合直接驅動數字負載。
- 開漏輸出
- 原理:輸出端類似三極管集電極,只能輸出低電平(接 VSS ),輸出高電平時為高阻態,需外部上拉電阻拉高。
- 使用場景:IIC 總線通信的 SCL 和 SDA 引腳;多設備共享數據線的通信總線;GPIO 中斷信號輸出(通過外部上拉電阻共享中斷信號線 );不同電壓域電源管理切換電路;電平轉換(適配不同電壓器件 )。
- 特點:可實現線與邏輯,方便電平匹配,適合多設備通信及跨電壓域應用,但高電平需外部上拉。
- 復用推挽輸出
- 原理:GPIO 端口由片上外設控制,如定時器 PWM 輸出、SPI 的 MOSI 和 MISO 等,兼具推挽輸出特性,能主動提供電流驅動負載。
- 使用場景:UART 通信發送端(TX 引腳 );SPI 通信的時鐘線(SCK)、主輸出從輸入(MOSI)、主輸入從輸出(MISO)引腳 ;CAN 通信發送端(TX 引腳 );伺服電機或 DC 電機控制的 PWM 信號輸出;外部設備的時鐘、使能等控制信號輸出。
- 特點:用于特定外設功能,借助推挽輸出特性提供穩定驅動。
- 復用開漏輸出
- 原理:GPIO 端口由片上外設控制,輸出模式為開漏輸出,高電平需外部或內部上拉電阻,可實現線與邏輯。
- 使用場景:IIC 通信的 SDA 和 SCL 引腳(多設備共享 );SMBus 通信(類似 IIC 協議 );1 - Wire 單總線通信;MCU 接收多個外設中斷信號(避免電平沖突 );電源管理信號(控制電壓域轉換 )。
- 特點:適用于特定外設多設備共享總線通信,需外部上拉電阻配合,可解決電平沖突問題。
這里只需要大概了解一下即可,后面結合具體外設理解
GPIO在后面會頻繁使用,我們要熟悉使用流程
1.配置時鐘2.初始化結構體
GPIO輸出
以LED介紹GPIO的使用
#include "stm32f10x.h" // Device header
void LED_Init()
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);
}
void LED1_ON()
{GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void LED1_OFF()
{GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
void LED1_Turn()
{if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1)==0){GPIO_SetBits(GPIOA,GPIO_Pin_1);}else{GPIO_ResetBits(GPIOA,GPIO_Pin_1);}
}
LED閃爍
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘//使用各個外設前必須開啟時鐘,否則對外設的操作無效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定義結構體變量GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,賦值為推挽輸出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //GPIO引腳,賦值為第0號引腳GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,賦值為50MHzGPIO_Init(GPIOA, &GPIO_InitStructure); //將賦值后的構體變量傳遞給GPIO_Init函數//函數內部會自動根據結構體的參數配置相應寄存器//實現GPIOA的初始化/*主循環,循環體內的代碼會一直循環執行*/while (1){/*設置PA0引腳的高低電平,實現LED閃爍,下面展示3種方法*//*方法1:GPIO_ResetBits設置低電平,GPIO_SetBits設置高電平*/GPIO_ResetBits(GPIOA, GPIO_Pin_0); //將PA0引腳設置為低電平Delay_ms(500); //延時500msGPIO_SetBits(GPIOA, GPIO_Pin_0); //將PA0引腳設置為高電平Delay_ms(500); //延時500ms/*方法2:GPIO_WriteBit設置低/高電平,由Bit_RESET/Bit_SET指定*/GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET); //將PA0引腳設置為低電平Delay_ms(500); //延時500msGPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET); //將PA0引腳設置為高電平Delay_ms(500); //延時500ms/*方法3:GPIO_WriteBit設置低/高電平,由數據0/1指定,數據需要強轉為BitAction類型*/GPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)0); //將PA0引腳設置為低電平Delay_ms(500); //延時500msGPIO_WriteBit(GPIOA, GPIO_Pin_0, (BitAction)1); //將PA0引腳設置為高電平Delay_ms(500); //延時500ms}
}
LED流水燈
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //開啟GPIOA的時鐘//使用各個外設前必須開啟時鐘,否則對外設的操作無效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定義結構體變量GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,賦值為推挽輸出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //GPIO引腳,賦值為所有引腳GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,賦值為50MHzGPIO_Init(GPIOA, &GPIO_InitStructure); //將賦值后的構體變量傳遞給GPIO_Init函數//函數內部會自動根據結構體的參數配置相應寄存器//實現GPIOA的初始化/*主循環,循環體內的代碼會一直循環執行*/while (1){/*使用GPIO_Write,同時設置GPIOA所有引腳的高低電平,實現LED流水燈*/GPIO_Write(GPIOA, ~0x0001); //0000 0000 0000 0001,PA0引腳為低電平,其他引腳均為高電平,注意數據有按位取反Delay_ms(100); //延時100msGPIO_Write(GPIOA, ~0x0002); //0000 0000 0000 0010,PA1引腳為低電平,其他引腳均為高電平Delay_ms(100); //延時100msGPIO_Write(GPIOA, ~0x0004); //0000 0000 0000 0100,PA2引腳為低電平,其他引腳均為高電平Delay_ms(100); //延時100msGPIO_Write(GPIOA, ~0x0008); //0000 0000 0000 1000,PA3引腳為低電平,其他引腳均為高電平Delay_ms(100); //延時100msGPIO_Write(GPIOA, ~0x0010); //0000 0000 0001 0000,PA4引腳為低電平,其他引腳均為高電平Delay_ms(100); //延時100msGPIO_Write(GPIOA, ~0x0020); //0000 0000 0010 0000,PA5引腳為低電平,其他引腳均為高電平Delay_ms(100); //延時100msGPIO_Write(GPIOA, ~0x0040); //0000 0000 0100 0000,PA6引腳為低電平,其他引腳均為高電平Delay_ms(100); //延時100msGPIO_Write(GPIOA, ~0x0080); //0000 0000 1000 0000,PA7引腳為低電平,其他引腳均為高電平Delay_ms(100); //延時100ms}
}
? ?蜂鳴器
這個是有源蜂鳴器? ?,沒法唱天空之城,差評
#include "stm32f10x.h" // Device header
#include "Delay.h"int main(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //開啟GPIOB的時鐘//使用各個外設前必須開啟時鐘,否則對外設的操作無效/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure; //定義結構體變量GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,賦值為推挽輸出模式GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //GPIO引腳,賦值為第12號引腳GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,賦值為50MHzGPIO_Init(GPIOB, &GPIO_InitStructure); //將賦值后的構體變量傳遞給GPIO_Init函數//函數內部會自動根據結構體的參數配置相應寄存器//實現GPIOB的初始化/*主循環,循環體內的代碼會一直循環執行*/while (1){GPIO_ResetBits(GPIOB, GPIO_Pin_12); //將PB12引腳設置為低電平,蜂鳴器鳴叫Delay_ms(100); //延時100msGPIO_SetBits(GPIOB, GPIO_Pin_12); //將PB12引腳設置為高電平,蜂鳴器停止Delay_ms(100); //延時100msGPIO_ResetBits(GPIOB, GPIO_Pin_12); //將PB12引腳設置為低電平,蜂鳴器鳴叫Delay_ms(100); //延時100msGPIO_SetBits(GPIOB, GPIO_Pin_12); //將PB12引腳設置為高電平,蜂鳴器停止Delay_ms(700); //延時700ms}
}
GPIO輸入
?按鍵控制LED
IPU為上拉輸入(這里還沒學定時器,還是用delay消抖)
#include "stm32f10x.h" // Device header
#include "Delay.h"/*** 函 數:按鍵初始化* 參 數:無* 返 回 值:無*/
void Key_Init(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //開啟GPIOB的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //將PB1和PB11引腳初始化為上拉輸入
}/*** 函 數:按鍵獲取鍵碼* 參 數:無* 返 回 值:按下按鍵的鍵碼值,范圍:0~2,返回0代表沒有按鍵按下* 注意事項:此函數是阻塞式操作,當按鍵按住不放時,函數會卡住,直到按鍵松手*/
uint8_t Key_GetNum(void)
{uint8_t KeyNum = 0; //定義變量,默認鍵碼值為0if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //讀PB1輸入寄存器的狀態,如果為0,則代表按鍵1按下{Delay_ms(20); //延時消抖while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); //等待按鍵松手Delay_ms(20); //延時消抖KeyNum = 1; //置鍵碼為1}if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0) //讀PB11輸入寄存器的狀態,如果為0,則代表按鍵2按下{Delay_ms(20); //延時消抖while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0); //等待按鍵松手Delay_ms(20); //延時消抖KeyNum = 2; //置鍵碼為2}return KeyNum; //返回鍵碼值,如果沒有按鍵按下,所有if都不成立,則鍵碼為默認值0
}
光敏電阻控制蜂鳴器
? 也是化身電報專家了
#include "stm32f10x.h" // Device header/*** 函 數:光敏傳感器初始化* 參 數:無* 返 回 值:無*/
void LightSensor_Init(void)
{/*開啟時鐘*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //開啟GPIOB的時鐘/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //將PB13引腳初始化為上拉輸入
}/*** 函 數:獲取當前光敏傳感器輸出的高低電平* 參 數:無* 返 回 值:光敏傳感器輸出的高低電平,范圍:0/1*/
uint8_t LightSensor_Get(void)
{return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13); //返回PB13輸入寄存器的狀態
}
????????學習stm32過程中,感覺自己真的要成為工程師了,cv工程師(bushi)。在使用外設前,要先去函數庫找到需要使用的函數,然后一個個復制過去,然后再去查看函數的定義,配置里面的參數,需要邏輯的大多都是在主函數里面寫(也有可能是入門學習的原因)
今日語錄:打不倒我的,只會讓我更加強大(農p無疑了)