STM32學習_GPIO簡介
一、GPIO輸出部分
- 實現 LED 點亮的代碼:
#include "stm32f10x.h" // Device header
int main(void)
{
// 開啓 RCC 時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 配置結構體,用於初始化 GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIOA
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0); // 設置 GPIOA,GPIO_Pin_0 為低電平,燈亮
GPIO_SetBits(GPIOA,GPIO_Pin_0); // 設置 GPIOA,GPIO_Pin_0 為高電平,燈滅
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET); // 設置 GPIOA,GPIO_Pin_0 為低電平,燈亮
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET); // 設置 GPIOA,GPIO_Pin_0 為低電平,燈滅
while(1)
{
}
}
- 實現 LED 閃爍的代碼:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
// 開啓 RCC 時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 配置結構體,用於初始化 GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIOA
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_0); // 設置 GPIOA,GPIO_Pin_0 為低電平,燈亮
Delay_ms(500);
GPIO_SetBits(GPIOA,GPIO_Pin_0); // 設置 GPIOA,GPIO_Pin_0 為低電平,燈滅
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET); // 設置 GPIOA,GPIO_Pin_0 為低電平,燈亮
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET); // 設置 GPIOA,GPIO_Pin_0 為低電平,燈滅
Delay_ms(500);
}
}
- 實現 LED 流水燈的代碼:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
// 開啓 RCC 時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// 配置結構體,用於初始化 GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
//GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | ···;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; // 選擇全部的端口
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIOA
GPIO_Init(GPIOA,&GPIO_InitStructure);
while(1)
{
GPIO_Write(GPIOA,~0x0001); // 0000 0000 0000 0001
Delay_ms(500);
GPIO_Write(GPIOA,~0x0002); // 0000 0000 0000 0010
Delay_ms(500);
GPIO_Write(GPIOA,~0x0004); // 0000 0000 0000 0100
Delay_ms(500);
GPIO_Write(GPIOA,~0x0008); // 0000 0000 0000 1000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0010); // 0000 0000 0001 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0020); // 0000 0000 0010 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0040); // 0000 0000 0100 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0080); // 0000 0000 1000 0000
Delay_ms(500);
}
}
- 實現 蜂鳴器的代碼:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
// 開啓 RCC 時鐘,易錯點:時鐘沒有從 GPIOA 換成 GPIOB,導致蜂鳴器不響
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
// 配置結構體,用於初始化 GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 初始化GPIOA
GPIO_Init(GPIOB,&GPIO_InitStructure);
while(1)
{
// 輸出低電平,蜂鳴器響
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
Delay_ms(100);
GPIO_SetBits(GPIOB,GPIO_Pin_12);
Delay_ms(700);
}
}
二、GPIO輸入部分
目錄結構,創建代碼前先完善目錄,並在魔術棒內添加路徑配置
- 按鍵控制代碼展示:
main.c 主函數代碼:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h"
#include "Key.h"
uint8_t KeyNum;
int main(void)
{
LED_Init();
Key_Init();
while(1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
LED1_Turn();
}
if(KeyNum == 2)
{
LED2_Turn();
}
}
}
LED.c 和 LED.h 代碼:
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
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(void) // 點亮 Led1
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
void LED1_OFF(void) // 熄滅 Led1
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
void LED1_Turn(void) // 翻轉 Led1 GPIO,實現按下點亮,再按下熄滅
{
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_1) == 0) // 關於這個函數,下面會給個表格
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
}
// 和上面一樣,這裏不贅述
void LED2_ON(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
void LED2_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
void LED2_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_2) == 0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);
}
}
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
void LED1_ON(void);
void LED1_OFF(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED1_Turn(void);
void LED2_Turn(void);
#endif
Key.c 和 Key.h 代碼:
#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; //定義變量,默認鍵碼值為0
if (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
}
#ifndef __KEY_H
#define __KEY_H
void Key_Init(void);
uint8_t Key_GetNum(void);
#endif
表格只保留小白最常用的操作,用PA1(通用引腳)和PC13(LED常用)做示例,重點標註你提到的GPIO_ReadOutputDataBit。
|
操作類型
|
標準庫函數
|
功能説明(大白話)
|
示例代碼(直接能用)
|
|
開啓GPIO時鐘
|
|
給GPIO端口“通電”,必須第一步做
|
開啓GPIOA時鐘:
|
|
GPIO引腳初始化
|
|
配置引腳的工作模式(比如輸出/輸入)
|
配置PA1為推輓輸出:
|
|
設置引腳為高電平
|
|
把引腳拉成高電平(比如讓PC13的LED熄滅)
|
PA1設為高電平:
|
|
設置引腳為低電平
|
|
把引腳拉成低電平(比如讓PC13的LED點亮)
|
PA1設為低電平:
|
|
讀取引腳的輸出電平 |
|
查“軟件設置的引腳電平”(不是實際外部電平)
|
讀取PA1的輸出電平:
|
|
讀取引腳的實際輸入電平
|
|
查“引腳實際接的外部電平”(比如讀按鍵)
|
讀取PA0的實際電平:
|
|
讀取整個端口的輸出數據
|
|
一次性讀整個GPIO口的所有引腳輸出電平(少用)
|
讀取GPIOA所有引腳輸出電平:
|
|
讀取整個端口的輸入數據
|
|
一次性讀整個GPIO口的所有引腳實際電平(少用)
|
讀取GPIOA所有引腳實際電平:
|
- GPIO操作的3個核心步驟
無論控制LED還是讀按鍵,都要按這個流程來,少一步都不行!
步驟1:開啓GPIO時鐘
// 以操作PC13(LED)為例,開啓GPIOC時鐘
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
小白提醒:忘記開時鐘是最常見的錯誤,代碼寫了也沒反應!
步驟2:初始化GPIO引腳
需要用一個叫GPIO_InitTypeDef的結構體來配置引腳參數,小白只需關注3個參數:
GPIO_Pin:選要操作的引腳(比如GPIO_Pin_13)GPIO_Mode:引腳模式(常用2種:
GPIO_Mode_Out_PP:推輓輸出(適合驅動LED、繼電器,最常用)GPIO_Mode_IPU:上拉輸入(適合讀按鍵,避免引腳懸空)
GPIO_Speed:輸出速度(選GPIO_Speed_50MHz就行,對LED、按鍵無影響)
示例(配置PC13為推輓輸出):
GPIO_InitTypeDef GPIO_InitStruct; // 定義配置結構體
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13; // 選擇PC13引腳
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; // 推輓輸出模式
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // 輸出速度50MHz
GPIO_Init(GPIOC, &GPIO_InitStruct); // 執行初始化
步驟3:用函數操作引腳(電平設置/讀取)
// 比如讓PC13點亮(低電平)
GPIO_ResetBits(GPIOC, GPIO_Pin_13);
// 讀取PC13的輸出電平
uint8_t level = GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_13);
- 關鍵函數詳解:GPIO_ReadOutputDataBit
小白最容易搞混“讀取輸出電平”和“讀取輸入電平”,用表格對比一下:
|
函數
|
讀取的是什麼?
|
舉個例子(PC13)
|
|
|
讀取軟件設置的電平(輸出寄存器ODR的值) |
你用 |
|
|
讀取引腳實際的外部電平(輸入寄存器IDR的值) |
你用 |
小白總結:
- 想知道“我讓引腳輸出什麼電平”,用
GPIO_ReadOutputDataBit(); - 想知道“引腳實際接的是什麼電平”,用
GPIO_ReadInputDataBit()。
- 光敏電阻控制蜂鳴器
main.c 主函數代碼
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Buzzer.h"
#include "LightSensor.h"
uint8_t KeyNum;
int main(void)
{
Buzzer_Init();
LightSensor_Init();
while(1)
{
if(LightSensor_Get() == 1)
{
Buzzer_ON();
}
else
{
Buzzer_OFF();
}
}
}
蜂鳴器代碼:
#include "stm32f10x.h" // Device header
void Buzzer_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
void Buzzer_ON(void)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
void Buzzer_OFF(void)
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
void Buzzer_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOB,GPIO_Pin_12) == 0)
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);
}
else
{
GPIO_ResetBits(GPIOB,GPIO_Pin_12);
}
}
#ifndef __BUZZER_H
#define __BUZZER_H
void Buzzer_Init(void);
void Buzzer_ON(void);
void Buzzer_OFF(void);
void Buzzer_Turn(void);
#endif
光敏傳感器代碼:
#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);
}
uint8_t LightSensor_Get(void)
{
return GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13);
}
#ifndef __LIGHT_SENSOR_H
#define __LIGHT_SENSOR_H
void LightSensor_Init(void);
uint8_t LightSensor_Get(void);
#endif
|
對比項
|
有源蜂鳴器
|
無源蜂鳴器
|
備註(STM32使用建議)
|
|
核心結構 |
內置振盪電路(含三極管、振盪芯片)
|
僅電磁線圈+振膜,無振盪電路
|
有源的“源”指內置振盪源,並非“電源” |
|
驅動方式 |
直接接直流電壓(如3.3V/5V)即可發聲 |
需輸入一定頻率的方波信號(如1kHz~5kHz)才能發聲 |
有源:STM32用GPIO輸出高低電平就能控制;無源:需用PWM或延時翻轉GPIO產生方波
|
|
聲音控制 |
只能控制響/不響,音調固定 |
可通過改變方波頻率控制音調(如不同頻率出不同聲音)
|
做音樂播放選無源,僅做報警提示選有源
|
|
工作電壓 |
有固定電壓(如3V、5V,錯壓易燒燬)
|
電壓範圍寬(如3V~5V),由方波幅值決定
|
需匹配STM32引腳輸出電平(3.3V)
|
|
引腳標識 |
通常標“+”“-”(正負極性,反接不響)
|
無正負極(兩根引腳可互換)
|
有源反接不會燒,但不工作;無源無極性限制
|
|
外觀特徵 |
頂部蓋板多為黑色/紅色,底部電路板可見芯片 |
頂部蓋板多為白色/透明,內部可見線圈和振膜 |
肉眼可快速區分(新手實用技巧)
|
|
STM32驅動示例 |
|
用延時生成方波:
|
有源驅動代碼簡單,無源需生成方波
|
|
成本與適用場景 |
成本稍高,適合簡單報警(如按鍵提示、故障報警)
|
成本稍低,適合音樂播放、複雜音效(如電子琴)
|
新手入門先玩有源,易上手
|
- 光敏電阻模塊工作原理
光敏模塊是STM32入門常用的環境光檢測模塊,核心是光敏電阻,配合簡單的電路實現“光強→電信號”的轉換,新手易上手。
1. 核心元件:光敏電阻的工作原理
光敏電阻(也叫光導管)是一種半導體光敏元件,基於光電導效應工作:
- 當光線照射到光敏電阻表面時,其內部的半導體材料會產生更多的載流子,導致電阻值急劇減小(光線越強,電阻越小);
- 當無光線(黑暗環境)時,光敏電阻的電阻值很大(可達數兆歐)。
簡單説:光強 ↗ → 電阻 ↘;光弱 ↗ → 電阻 ↗。
2. 光敏模塊的電路結構(兩種常見類型)
市面上的光敏模塊主要分數字輸出型和模擬輸出型,新手常用數字輸出型(接線簡單)。
- 數字輸出型光敏模塊(最常用)
電路組成:
- 光敏電阻 + 固定電阻 → 分壓電路;
- 比較器(如LM393)→ 將分壓電壓與參考電壓比較,輸出高低電平。
工作過程:
- 模塊上有一個電位器,可調節參考電壓(即靈敏度);
- 當環境光強超過設定值時,光敏電阻阻值變小,分壓後的電壓低於參考電壓,比較器輸出低電平;
- 當環境光強低於設定值時,光敏電阻阻值變大,分壓後的電壓高於參考電壓,比較器輸出高電平。
關鍵特點:
輸出只有高/低兩種電平,可直接接STM32的GPIO輸入引腳,用GPIO_ReadInputDataBit()讀取。
- 模擬輸出型光敏模塊
電路組成:
僅由光敏電阻 + 固定電阻組成的分壓電路,無比較器。
工作過程:
輸出端直接輸出分壓後的模擬電壓(範圍0~3.3V/5V),電壓值隨光強連續變化(光越強,電壓越高;光越弱,電壓越低)。
關鍵特點:
需接STM32的ADC引腳(如PA0),通過ADC採集電壓值,計算出具體的光強大小(精度更高)。
寫在最後
- 有源蜂鳴器和無源蜂鳴器的核心區別是是否內置振盪電路,新手入門選有源更易實現;
- 光敏模塊通過光敏電阻的阻值變化將光強轉為電信號,數字輸出型適合簡單的開關控制,模擬輸出型適合高精度的光強檢測。
結合STM32的GPIO和ADC操作,能快速實現對這兩種模塊的控制,是新手練手的好案例。