STM32之GPIO输出

141 阅读6分钟

达成成就:点灯大师!!! 记录下学到的一些要点

操作 GPIO 步骤分解

总共涉及了RCCGPIO两个外设

  1. 使用RCC开启GPIO的时钟
  2. 使用GPIO_Init函数初始化GPIO
  3. 使用输出或者输入的函数控制GPIO

两个外设的库函数

RCC最主要的只有三个函数:RCC AHB外设时钟控制 || RCC APB2外设时钟控制 || RCC APB1外设时钟控制

image-20260129095454888.png

GPIO最主要的是GPIO_Init和八个读写函数

image-20260129100330064.png

点亮 LED 代码

#include "stm32f10x.h"                  // Device header
​
int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    GPIO_ResetBits(GPIOA, GPIO_Pin_0);
    
    while (1)
    {
    
    }
}
​

这里学到了一些新东西,主要在第一行RCC开启时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);可以翻译成:请 RCCAPB2 总线上的 GPIOA 外设 打开时钟,让它开始工作

  • RCC的三个函数RCC_AHBPeriphClockCmd、RCC_APB1PeriphClockCmd、RCC_APB2PeriphClockCmd表示的是要开启时钟的外设在哪条总线

    不记得没关系,可以跳转到定义查看,以RCC_APB2PeriphClockCmd为例,其定义如下(可以看到 GPIOA 外设在这上面

    image-20260129104023111.png

  • 时钟是给外设的,不是给某个引脚的。这里我们要操作的是GPIOA上的0号引脚,所以用RCC开启GPIOA的时钟

LED 闪烁代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
​
int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    while (1)
    {
        GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
        Delay_ms(500);
        GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
        Delay_ms(2000);
    }
}
​

这里并没有什么特别的,新学了GPIO的写函数GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);功能上感觉和GPIO_SetBitsGPIO_ResetBits没什么区别,这个函数可以通过第三个参数来决定高/低电平

LED 流水灯代码

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
​
int main(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    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);
    }
}
​
  • 这里获得了非常重要的印象:一个比特位控制一个引脚
  • GPIO_Write(GPIOA, ~0x0001);GPIO的第四个写函数,它的特别之处在于通过16进制数据(第二个参数)来一次设置指定的GPIO外设的全部引脚的电平

思考与体会

我现在觉得很好玩的一点是我点亮了8LED组成的流水灯,改动一下程序不就可以传各种信息了吗,只要有对应的协议,比如摩斯密码这种(想到了寄生虫电影里的情节,也是通过灯亮和频率来传递信息的)。所以这相当于是硬件之间通信的一种外显对吗,其实原理是差不多的

所以后面要学的各种通信协议也都像这个小灯一样吗?保留这种印象学习吧

给自己准备的小练习

我们可以用这个流水灯写一个这样的小程序,来输出字符串,比如 hello world

英文单词一共26个,只需要用5个流水灯来表示,后3个流水灯来指示输出的开始、结束、单词拼写的开始、结束

实现代码如下:

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

int main(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_All;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOA, &GPIO_InitStruct);
        
	// 字母对应的16进制码
	uint16_t letter_table[26] = {
		0x0001, // a 0000 0001
		0x0002, // b 0000 0010
		0x0003, // c 0000 0011
		0x0004, // d 0000 0100
		0x0005, // e 0000 0101
		0x0006, // f 0000 0110
		0x0007, // g 0000 0111
		0x0008, // h 0000 1000
		0x0009, // i 0000 1001
		0x000A, // j 0000 1010
		0x000B, // k 0000 1011
		0x000C, // l 0000 1100
		0x000D, // m 0000 1101
		0x000E, // n 0000 1110
		0x000F, // o 0000 1111
		0x0010, // p 0001 0000
		0x0011, // q 0001 0001
		0x0012, // r 0001 0010
		0x0013, // s 0001 0011
		0x0014, // t 0001 0100
		0x0015, // u 0001 0101
		0x0016, // v 0001 0110
		0x0017, // w 0001 0111
		0x0018, // x 0001 1000
		0x0019, // y 0001 1001
		0x001A  // z 0001 1010
};
	
	// 准备好要输出的字符串
	char str[] = "hello world and world peace";
	
	// 后三个灯全亮表示开始
	GPIO_Write(GPIOA, ~0x00E0);
	Delay_ms(2000);
	
	// 遍历的方式用前5个LED灯逐个输出字符
	for(int i = 0; str[i] != '\0'; i++)
	{
		char c = str[i];
		
		// 用空格来标记单词的结束,配合尾灯特效
		if(c == ' ') 
		{
			GPIO_Write(GPIOA, ~0x0020);
			Delay_ms(500);
			GPIO_Write(GPIOA, ~0x0040);
			Delay_ms(500);
			GPIO_Write(GPIOA, ~0x0080);
			Delay_ms(500);
		}
		else 
		{
			int num = c - 'a';
			uint16_t code = letter_table[num];
			GPIO_Write(GPIOA, ~code);
			Delay_ms(1000);
		
			// 全暗表示一个字符输出结束
			GPIO_Write(GPIOA, 0xFFFF);
			Delay_ms(500);
		}
		
	}
	
	// 后三个灯第二次全亮表示结束
	GPIO_Write(GPIOA, ~0x00E0);
	Delay_ms(2000);
	
	GPIO_Write(GPIOA, 0xFFFF);
		
	while (1)
	{
		
	}
}