STM32-GPIO实践部分1-跑马灯实验
目录
目录
1.思路分析
🌵理论思路🌵
🌵硬件设计🌵
🌵 代码思路🌵
方案1-库函数
方案2-寄存器(官方寄存器)
方案3-寄存器(寄存器地址)
2.代码实现
3.经验总结
1.思路分析
🌵理论思路🌵
使指定的GPIO端口引脚输出高/低电平,点亮LED灯,延迟,再熄灭LED,循环如此。
🌵硬件设计🌵
- 采用插件式的LED,原理是发光二极管,点亮条件是导通二极管,加正向导通电压。
- 本次实验,采用LED正极接VCC,负极接GPIOB10,GPIOB11引脚,配置上拉,输出低电平点亮。
🌵 代码思路🌵
方案1-库函数
- 使GPIOB10,GPIO11输出高低电平
- 开启GPIOB的时钟,GPIOB挂载在APB2总线上,调用RCC的ABP2使能函数
- 配置GPIOB10,11的模式,上拉输出,速度任意,调用GPIO的Init函数
- 输出高低电平,熄灭点亮,调用GPIO的SetBits函数和ResetBits函数。
- 主函数内点亮,延迟,熄灭,点亮,循环下去
方案2-寄存器(官方寄存器)
该版本不需要stm官方提供的外设库函数和CM3内核提供的库函数,只需要官方进行寄存器地址宏定义的头文件,即stm32f10x.h,由于官方进行寄存器地址宏定义,我们只需要对宏定义后的地址名称进行操作。
- 使GPIOB10,GPIO11输出高低电平
- 开启GPIOB的时钟,GPIOB挂载在APB2总线上,调用RCC的ABP2使能函数
- 配置GPIOB10,11的模式,上拉输出,速度任意,调用GPIO的Init函数
- 输出高低电平,熄灭点亮,调用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配置清0GPIOB->CRH &= ~(0x0F<<(3*4));//端口配置高寄存器,Pin11配置清0GPIOB->CRH |= (0x01<<(2*4));//端口配置高寄存器,Pin10配置清0GPIOB->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的数取反得到。