浮空输入、上拉输入、下拉输入、模拟输入、
开漏输出、推挽输出、推挽复用、开漏复用
每组GPIO端口的寄存器包括:
两个32位配置寄存器(GPIOx_CRL ,GPIOx_CRH) ,
两个32位数据寄存器 (GPIOx_IDR和GPIOx_ODR),
一个32位置位/ 复位寄存器(GPIOx_BSRR),
一个16位复位寄存器(GPIOx_BRR),
一个32位锁定寄存器(GPIOx_LCKR)。
每个I/O端口位可以自由编程,然而 I/O端口寄存器必须按32位字被访问 (不允许半字或字节访问) 。

CRL 0-7
CRH 8-15
IDR 0-15 输入 只读
ODR 0-15 输出 控制上拉/下拉电阻的使能
复位&=(清0 看0的位,其余位置填F)
置为|= (置1 看1的位 其余位置填0)


如:设置PORTC的11位为上拉输入,12位为推挽输出:
GPIOC->CRH &= 0xFFF00FFF;
GPIOC->CRH |= 0x00038000;
GPIOC->ODR = 1<<11;
引脚 | 模式 | 配置方式 |
---|---|---|
PC11 | 上拉输入 | CNF11=10 + ODR=1 |
PC12 | 推挽输出 | CNF12=00 + MODE12=1 |
-
为什么用
ODR
设置上拉? -
STM32 的 上拉/下拉电阻 由 ODR 寄存器 控制:
-
输入模式下,
ODR=0
→ 下拉使能- 输入模式下,
ODR=1
→ 上拉使能
- 输入模式下,


流水灯
【主要是CRL/CRH 配置 一般为0011 推挽输出50HZ 及ODR】
电路中有 L0,L1,L2,L3,L4,L5,L6,L7 共八个发光二极管,当引脚 LED_SEL(PB4) 输入为 1 ,对
于 A 、 B 、 C 、 D 、 E 、 F 、 G 、 H(PE8-15) 引脚,只要输入为 1 ,则点亮相连接的发光二极管。
A~H 引脚连接 STM32F108VB 芯片的 PE8~PE15 ,程序初始化时,对其进行初始设置。引
脚 LED_SEL 为 1 时,发光二极管才工作,否则右边的数码管工作。 注意: LED_SEL 连接于 PB4

GPIOB->CRL &= 0xFFF0FFFF; // 清零 PB4 的配置位(CNF4、MODE4)
GPIOB->CRL |= 0x00030000; // 设置 PB4 为推挽输出(50MHz)
GPIOB->ODR |= 0x00000010; // 初始置 PB4=1(默认点亮 LED) 1<<4 = 0x00000010
-
MODE4=11
(50MHz 输出速度)。 -
CNF4=00
(推挽输出) -
GPIOE->ODR &= ~(0xff << 8); // 清除PE8-PE15(熄灭所有LED)
LED_SEL = 1; // 使能LED控制信号
light = 0x01; // 初始化light(从PE8开始)
-
GPIOE->ODR &= ~(0xff << 8)
:-
0xff << 8
:二进制0000 0000 1111 1111 0000 0000
(即PE8-PE15全1)。 -
~
取反后:1111 1111 0000 0000 1111 1111
。 -
&=
操作:将PE8-PE15的位清零(熄灭LED),其他位不变。 -
while(1) {
GPIOE->ODR |= (light << 8); // 点亮当前LEDdelay_ms(300); // 延时300ms
light = light << 1; // 左移1位,切换到下一个LED
if(light == 0x00) { // 如果light溢出(所有LED已点亮一轮)
GPIOE->ODR &= ~(0xff << 8); // 熄灭所有LED
delay_ms(300); // 延时300ms
light = 0x01; // 重置light,重新开始循环
}
}
-
-
法二四个灯
typedef unsigned long u32;
#define RCC_APB2ENR (*(u32 *)0x40021018) //定义APB2ENR寄存器地址
#define GPIOE_CRL (*(u32 *)0x40011800) //定义GPIOE_CRH寄存器
#define GPIOE_ODR (*(u32 *)0x4001180C) //定义GPIOE_ODR寄存器
#define LED0 (*(u32 *)(0x42000000 +(0x4001180C-0x40000000)*32 + 0*4)) //位带定义PE0
#define LED1 (*(u32 *)(0x42000000 +(0x4001180C-0x40000000)*32 + 1*4)) //位带定义PE1
#define LED2 (*(u32 *)(0x42000000 +(0x4001180C-0x40000000)*32 + 2*4)) //位带定义PE2
#define LED3 (*(u32 *)(0x42000000 +((u32)(0x4001180C & 0xFFFFF)<<5) + (3<<2))) //位带定义PE3
int delay(int Time)
{
//简单延时程序
unsigned short t, i, j;
for(t = 0; t < Time; t++)
for(i = 0; i < 1000; i++)
for(j = 0; j < 1000; j++)
;
return 0;
}
int main(void)
{
// 先声明所有局部变量
u32 mode_left[] = {0x00000001, 0x00000002, 0x00000004, 0x00000008};
u32 mode_right[] = {0x00000008, 0x00000004, 0x00000002, 0x00000001};
int i;
int delay_time = 1;
// 硬件初始化 GPIOE端口基地址为:0x4001 1800。
(*(u32 *)0x40021018) |= 1 << 6; //使能PORTE时钟
(*(u32 *)0x40011800) &= 0XFFFF0000;//清除PE.0-3原先配置
(*(u32 *)0x40011800) |= 0X00003333; //PE.0-3配置为推挽输出
(*(u32 *)0x4001180C) |= 0x0000000F; //PE.0-3输出高,四个LED灯全亮1111
delay(1);
(*(u32 *)0x4001180C) &= 0xFFFFFFF0; //复位 PE.0-3输出低,四个LED灯全灭 保留寄存器中不需要修改的位(高28位),仅清零目标位(低4位)。
delay(1);
// (*(u32 *)0x4001180C) = 0x00000001; //仅LED1亮,其他灯灭 向GPIOE_ODR写入 0x00000001(Bit0=1,其他位=0)。 0001
// delay(1);
// (*(u32 *)0x4001180C) = 0x00000002; //仅LED2亮,其他灯灭 写入 0x00000001(Bit0=1,其他位=0)0010
// delay(1);
// (*(u32 *)0x4001180C) = 0x00000004; //仅LED3亮,其他灯灭 0x00000004(Bit2=1,其他位=0)。0100
// delay(1);
// (*(u32 *)0x4001180C) = 0x00000008; //仅LED4亮,其他灯灭 0x00000008(Bit3=1,其他位=0)。1000
// delay(1);
while(1)
{
// 从中间向两边扩散
(*(u32 *)0x4001180C) = 0x00000002; // LED2亮
delay(delay_time);
(*(u32 *)0x4001180C) = 0x00000004; // LED3亮
delay(delay_time);
(*(u32 *)0x4001180C) = 0x00000006; // LED2+3亮
delay(delay_time);
(*(u32 *)0x4001180C) = 0x0000000F; // 全亮
delay(delay_time);
(*(u32 *)0x4001180C) &= 0xFFFFFFF0;
delay(3);
// 从两边向中间收缩
(*(u32 *)0x4001180C) = 0x00000009; // LED1+4亮
delay(delay_time);
(*(u32 *)0x4001180C) = 0x00000006; // LED2+3亮
delay(delay_time);
(*(u32 *)0x4001180C) = 0x00000002; // LED2亮
delay(delay_time);
(*(u32 *)0x4001180C) = 0x00000004; // LED3亮
delay(delay_time);
(*(u32 *)0x4001180C) &= 0xFFFFFFF0;
delay(3);
//上
for(i = 0; i < 4; i++) {
(*(u32 *)0x4001180C) = mode_left[i];
delay(1);
}
(*(u32 *)0x4001180C) &= 0xFFFFFFF0;
delay(3);
//下
for(i = 0; i < 4; i++) {
(*(u32 *)0x4001180C) = mode_right[i];
delay(1);
}
(*(u32 *)0x4001180C) &= 0xFFFFFFF0;
}
}
8 位数码管显示原理
8 位数码管的连接原理图如图 3.1 所示, LED 数码管引脚定义如图 3.2 所示,其中 a-g 分
别对应一个笔画, dp 对应小数点,负极连接到一起接到 cc ,因此叫共阴极数码管。共阴极
8 位数码管显示 0-F 时对应亮的 LED 如图 3.2 所示。
-
u8 segTable[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
//0-9 0 DP和G不亮 0011111111 1:00000110
数码管中的 A~G 、 DP 段分别连接到电路图中的 A~G 、 H 线上,当某段上有一定的电压
差值时,便会点亮该段。
当 E3 输入为 1 ,也就是 LED_SEL 输入为 0 时,根据 SEL0~SEL2 的值确定选中的数码管位, 即位选,再根据 A~H 引脚的高低电平,点亮对应段,即段选。
// 3. 配置 PB.0-PB.2 为推挽输出(位选控制)
GPIOB->CRL &= 0xFFF0F000; //清除PB.0-3配置
GPIOB->CRL |= 0x00030333; //PB.0-2 4推挽输出
GPIOB->ODR |= 0x00000017; //PB.0-2 4输出高
// 4. 配置 PE.8-PE.15 为推挽输出(段选控制)
GPIOE->CRH &= 0X00000000; //清除PE.8-15配置
GPIOE->CRH |= 0X33333333; //PE.8-15推挽输出
GPIOE->ODR |= 0x0000FF00; //PE.8-15输出高
/***************************************
* 流水灯选择,或数码管段选
* value:显示的数值对应的段选二进制值 //设置数码管E-A的值;流水灯L7-L0的值设置数码管的段选信号 (a~g + dp)或 控制8个LED的亮灭(L0~L7)。
通过操作
GPIOE->ODR
寄存器,将value
的值映射到 PE8~PE15 引脚。
void LedValue(u8 value)
{
// LED0 = (value&0x01)?1:0;
// LED1 = (value&0x02)?1:0;
// LED2 = (value&0x04)?1:0;
// LED3 = (value&0x08)?1:0;
// LED4 = (value&0x10)?1:0;
// LED5 = (value&0x20)?1:0;
// LED6 = (value&0x40)?1:0;
// LED7 = (value&0x80)?1:0;
GPIOE->ODR &= ~(0xff << 8);
GPIOE->ODR |= value << 8; // 将value左移8位,写入PE8~PE15value
是8位数(0x00~0xFF
),左移8位对齐到PE8~PE15。
}/***************************************
* 数码管显示不带小数点的数值
* 参数 w:显示的位置,即位选,左-右:0-7
* value:要显示的数值
****************************************/void SetLed(u8 w, u8 value) {
// 1. 设置位选(选择哪一位数码管)
SEL0 = w % 2; // 最低位
SEL1 = w / 2 % 2; // 中间位
SEL2 = w / 4; // 最高位//方法二
GPIOB->ODR &= ~(0x07 << 0); // 清除GPIOB的低3位(SEL0~SEL2)
GPIOB->ODR |= (w & 0x07); // 将w的低3位写入GPIOB(位选信号)w & 0x07
:取w
的低3位(确保值在0~7范围内)。|=
操作将w
的值写入GPIOB->ODR
的低3位(SEL0~SEL2)。示例:
w=3
(011
)→GPIOB->ODR
的低3位变为011
,选中第4位数码管。
// 2. 设置段选(显示什么数字)
LedValue(segTable[value]);
}位选控制(
SEL0/1/2
)-
假设
SEL0/1/2
是连接到3-8译码器(如74HC138)的引脚,用于选择8位数码管中的某一位。 -
计算方式 :将位置
w
转换为3位二进制(SEL2 SEL1 SEL0
):-
w=0
→000
→ 第1位数码管 -
w=3
→011
→ 第4位数码管 -
w=7
→111
→ 第8位数码管
-
段选控制(
LedValue
)-
从预定义的段码表
segTable
中获取数字value
对应的段码,传递给LedValue
函数。 -
示例:
-
value=0
→segTable[0]=0x3F
→ 数码管显示0
。 -
value=1
→segTable[1]=0x06
→ 数码管显示1
。
-
SetLed(2, 5); // 在第3位数码管(w=2)显示数字5
位选:
w=2
→SEL0=0
,SEL1=1
,SEL2=0
(二进制010
)→ 选中第3位数码管。
段选:
segTable[5] = 0x6D
→LedValue(0x6D)
→ PE8~PE15输出0110 1101
,点亮对应段。