STM32-GPIO实践部分1-跑马灯实验

发布于:2025-08-18 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

目录

1.思路分析

🌵理论思路🌵

 🌵硬件设计🌵

🌵 代码思路🌵

方案1-库函数

方案2-寄存器(官方寄存器)

方案3-寄存器(寄存器地址)

2.代码实现

3.经验总结


1.思路分析

🌵理论思路🌵

使指定的GPIO端口引脚输出高/低电平,点亮LED灯,延迟,再熄灭LED,循环如此。

 🌵硬件设计🌵

  • 采用插件式的LED,原理是发光二极管,点亮条件是导通二极管,加正向导通电压。
  • 本次实验,采用LED正极接VCC,负极接GPIOB10,GPIOB11引脚,配置上拉,输出低电平点亮。

🌵 代码思路🌵

方案1-库函数

  • 使GPIOB10,GPIO11输出高低电平
    1. 开启GPIOB的时钟,GPIOB挂载在APB2总线上,调用RCC的ABP2使能函数
    2. 配置GPIOB10,11的模式,上拉输出,速度任意,调用GPIO的Init函数
    3. 输出高低电平,熄灭点亮,调用GPIO的SetBits函数和ResetBits函数。
  • 主函数内点亮,延迟,熄灭,点亮,循环下去

方案2-寄存器(官方寄存器)

该版本不需要stm官方提供的外设库函数和CM3内核提供的库函数,只需要官方进行寄存器地址宏定义的头文件,即stm32f10x.h,由于官方进行寄存器地址宏定义,我们只需要对宏定义后的地址名称进行操作。

  • 使GPIOB10,GPIO11输出高低电平
    1. 开启GPIOB的时钟,GPIOB挂载在APB2总线上,调用RCC的ABP2使能函数
    2. 配置GPIOB10,11的模式,上拉输出,速度任意,调用GPIO的Init函数
    3. 输出高低电平,熄灭点亮,调用GPIO的SetBits函数和ResetBits函数。
  • 主函数内点亮,延迟,熄灭,点亮,循环下去

方案3-寄存器(寄存器地址)

该版本不同之处在于,没有对地址进行宏定义,直接对寄存器的地址进行操作,所以就不需要官方的宏定义文件stm32f10x.h文件。至于跑马灯的实现思路与前者一样。

2.代码实现

库函数实现

//*****led.c*****
#include "led.h"
//跑马灯LED初始化函数
//1.开启连接LED的引脚端口所属的GPIOx时钟
//2.配置引脚端口的模式
void LED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(LED_GPIOx_RCC,ENABLE);//GPIOB挂载在APB2总线
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//输出强有效的低电平
	GPIO_InitStruct.GPIO_Pin = LED_GPIO_Pin_1;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(LED_GPIOx,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//输出强有效的低电平
	GPIO_InitStruct.GPIO_Pin = LED_GPIO_Pin_2;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(LED_GPIOx,&GPIO_InitStruct);
}
//跑马灯LED置高电平
void LED_ON(void)
{
	GPIO_SetBits(LED_GPIOx,LED_GPIO_Pin_1 | LED_GPIO_Pin_2);
}
//跑马灯LED置低电平
void LED_OFF(void)
{
	GPIO_ResetBits(LED_GPIOx,LED_GPIO_Pin_1 | LED_GPIO_Pin_2);
}
//*****main.c*****
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
int main(void)
{
	LED_Init();
	while(1)
	{
		LED_OFF();
		Delay_ms(500);
		LED_ON();
		Delay_ms(500);
	}
}

寄存器实现(官方)

#include "stm32f10x.h"
int main(void)
{
	while(1)
	{
		//使用stm公司宏定义后的地址进行操作
		//规则:先FLASH,SRAM,PERIPH,再进行分类
		//AHB,APB1,APB2挂载在PERIPH上,外设再挂载在相应的总线上
		//外设下的寄存器,以结构体的形式挂载
		RCC->APB2ENR |= 1<<3; //APB2外设时钟使能寄存器,对应位置1开始外设
		GPIOB->CRH &= ~(0x0F<<(2*4));//端口配置高寄存器,Pin10配置清0
		GPIOB->CRH &= ~(0x0F<<(3*4));//端口配置高寄存器,Pin11配置清0
		GPIOB->CRH |= (0x01<<(2*4));//端口配置高寄存器,Pin10配置清0
		GPIOB->CRH |= (0x01<<(3*4));//端口配置高寄存器,Pin11配置清0
		
		//1.操作BSSR寄存器
		GPIOB->ODR &= ~(0x01<<10);//Pin10 = 0;数据输出寄存器ODR写低电平
		GPIOB->ODR &= ~(0x01<<11);//Pin11 = 0;数据输出寄存器ODR写低电平
		
//		//2.操作BSSR寄存器
//		GPIOB->BSRR |= (1<<(10+16));//Pin10 = 0;位设置/清除寄存器,低16位置1,高16位置0
//		GPIOB->BSRR |= (1<<(11+16));//Pin11 = 0;
//		
//		//3.操作BRR寄存器
//		GPIOB->BRR |= (1<<10);//Pin10 = 0;位清除寄存器,低16位置0,高16位无效
//		GPIOB->BRR |= (1<<11);//Pin11 = 0;
		
	}

}

寄存器实现(纯地址)

int main(void)
{
	while(1)
	{
		//1.开启GPIOB的时钟
		*(unsigned int*)(0x40021000+0x18) |= (1<<3);//往APB2外设时钟控制寄存器中GPIOB对应的位写1
		//2.配置GPIO的模式
		*(unsigned int*)(0x40010C00+0x04) &= ~(0x0F<<(2*4));//将GPIOB的端口高配置寄存器对应的引脚清0
		*(unsigned int*)(0x40010C00+0x04) &= ~(0x0F<<(3*4));//GPIOB_pin10,pin11配置清0
		
		*(unsigned int*)(0x40010C00+0x04) |= (0x01<<(2*4));//将GPIOB的端口高配置寄存器对应的引脚配置为推挽输出
		*(unsigned int*)(0x40010C00+0x04) |= (0x01<<(3*4));//GPIOB_pin10,pin11配置为00 01推挽输出,速度10Mhz
		//3.输出低电平
//		*(unsigned int*)(0x40010C00+0x0C) &= ~(1<<10);//往ODR寄存器中设置清0
//		*(unsigned int*)(0x40010C00+0x0C) &= ~(1<<11);
//		*(unsigned int*)(0x40010C00+0x10) |= (1<<(10+16));//往BSRR寄存器的高16位中设置清0
//		*(unsigned int*)(0x40010C00+0x10) |= (1<<(11+16));
		
//		*(unsigned int*)(0x40010C00+0x14) |= (1<<10);//往BRR寄存器的低16位中设置清0
//		*(unsigned int*)(0x40010C00+0x14) |= (1<<11);
	}

}
void SystemInit(void)
{

}

3.经验总结

  • 第一步都是开启时钟,每个外设都有其对应的时钟,时钟相关的内容就查阅RCC时钟这一章节的内容,不同的外设都查与之相对应的章节,在调用库函数时,根据所使用到外设,查看对应库函数头文件,一般函数声明放在最后(前面是一些宏定义),根据函数名称可大致知道其作用,将鼠标放在函数处可左击进行跳转定义,定义处有详细的英文注释函数作用,可结合翻译使用,函数的参数具体填什么,可以根据函数每次调用时使用到的断言函数进行跳转至宏定义处,都会注释该参数应当填入什么值。
  • 在使用纯地址进行操作时,第一步应都是查找参考手册,找到寄存器映射那一章节,可以看到外设的地址,在使用时将地址转化为指针类型的变量进行操作,操作时1位的写1写0使用或上1与上0进行实现,与上0为了保证其他位不变,其他位应是1,得到这个相与的数,通常采用改位为1其位为0的数取反得到。

网站公告

今日签到

点亮在社区的每一天
去签到