软件:STM32CubeMX Version 6.2.1
MDK5 for ARM
硬件:战舰精英V3 STM32F103ZET6
Nucleo-F411RE
文章目录
- 前言
- 一、使能GPIO的外设时钟(AHB-APB)
- 1.1.时钟总线框图
- 1.2.GPIO时钟寄存器
- 1.3.使能GPIO时钟的代码
- 二、配置工作模式
- 2.1.输入/输出模式
- 2.2.翻转速率
- 2.3.GPIO控制相关寄存器
- 三、配置GPIO初始化
- 3.1.配置F103
- STM32CubeMX使用HAL库;
- 3.1.1 配置输出LED灯;
- 3.1.2 配置输入按键;
- 3.1.3 输出并保存工程;
- 3.2.配置F411
- 3.2.1 STM32CubeMX使用HAL库;
- 四、编写用户程序使用GPIO
- 4.1 stm32f1xxhal_gpio.c函数描述;
- 4.2 按键控制LED灯闪烁模式代码;
- 总结
前言
本文使用STM32CubeMX的HAL库对F103和F411的GPIO进行配置并学习其使用方法,以及了解它们之间的一些差异。
一、使能GPIO的外设时钟(AHB-APB)
对于STM32这样的32位单片机而言,由于外设多且系统时钟频率高考虑功耗所以对部分外设实行独立管理(控制外设时钟的使能与否)。
1.1.时钟总线框图
根据用户手册STM32F1的GPIO是挂载在AHB-APB2总线上的,STM32F4是AHB1上。
总线可以理解为系统时钟输往外设的管路,不同总线的流速是不一样的。AHB最快可以等于系统时钟,APB2>APB1。STMF1;APB2=72MHz,APB1=36MHz.
F1是流水型的,F4是矩阵型的(我也看不懂)。直接看"存储器映射"地址会更直接一点。
左图是F1的存储器映射,右图是F4的。
1.2.GPIO时钟寄存器
使用STM32CubeMX配置GPIO时无需人为的再使能GPIO的时钟,选中某GPIO后软件会自动使能相应GPIO的时钟总线。
但是在使用“寄存器/标准库”开发时需要单独的使能相应的时钟。
通过参考手册6.3 RCC寄存器描述找到相应的时钟控制寄存器RCC_APB2ENR(F1)和RCC_AHB1ENR(F4)。
1.3.使能GPIO时钟的代码
STM32CubeMX使用HAL库;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
#define __HAL_RCC_GPIOA_CLK_ENABLE() do { \
__IO uint32_t tmpreg = 0x00U; \
SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
/* Delay after an RCC peripheral clock enabling */ \
tmpreg = READ_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN);\
UNUSED(tmpreg); \
} while(0U)
寄存器版本;
RCC->APB2ENR|=1<<2; //使能 PORTA 时钟
RCC->APB2ENR|=1<<3; //使能 PORTB 时钟
RCC->APB2ENR|=1<<6; //使能 PORTE 时钟
标准库版本;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE,ENABLE); //使能 GPIOB,GPIOE 端口时钟
二、配置工作模式
2.1.输入/输出模式
这里主要区别就是F4芯片把上下拉电阻放到了IO口的最外边这样输入和输出都可以配置需不需要上下拉电阻了。
每种模式的功能我就不介绍了,手册上都有非常详细的说明且这些功能一般单片机都有。
2.2.翻转速率
IO口输出速度并不是选择越大越好,在对速度没要求的情况下尽量选取第的速度。速度越大电平上升沿和下降沿越平滑(不好),因为IO口驱动能力和输出上/下拉电阻固定。以后有时间可以用示波器验证一下
2.3.GPIO控制相关寄存器
STM32F1
- GPIOx_CRL:
- GPIOx_CRH:
32 位配置寄存器 - GPIOx_IDR:
- GPIOx_ODR:
32 位数据寄存器 - GPIOx_BSRR:
32 位置位/复位寄存器 - GPIOx_BRR:
16位复位寄存器 - GPIOx_LCKR:
32位锁定寄存器
STM32F4
- GPIOx_MODER:
- GPIOx_OTYPER:
- GPIOx_OSPEEDR:
- GPIOx_PUPDR:
32 位配置寄存器 - GPIOx_IDR:
- GPIOx_ODR:
32 位数据寄存器 - GPIOx_BSRR:
32 位置位/复位寄存器 - GPIOx_LCKR:
32 位锁定寄存器 - GPIOx_AFRH:
- GPIOx_AFRL:
32 位复用功能选择寄存器
三、配置GPIO初始化
3.1.配置F103
STM32CubeMX使用HAL库;
这里直接跳过新建工程,这里默认是使用内部晶振,我们外接了8M的晶振使用选择外部高频晶振;
然后在时钟树中设置外部晶振的频率以及AHB总线的频率;
通过原理图在芯片引脚示意图中配置相应引脚;
3.1.1 配置输出LED灯;
3.1.2 配置输入按键;
这里SYS中(Debug)有个告警,参考网上资料说这里一定要设置不然只能下载一次程序;
STM32CUBEMX忘记配置sys中的debug导致程序只能下载一次的问题 但是这里实际提示的是已使用的外设IO口与此处功能相互冲突,(PE2~5我们已经使用了所以这里JTAG后面几个模式无法使用)。
3.1.3 输出并保存工程;
最后选择右上角"GENERATE CODE"生成代码就可以了。
3.2.配置F411
3.2.1 STM32CubeMX使用HAL库;
这里配置F411的方法和F103的基本一致,只要根据实际硬件选择相应的IO口就行了。
四、编写用户程序使用GPIO
4.1 stm32f1xxhal_gpio.c函数描述;
在HAL库中GPIO配置在stm32f1xxhal_gpio.c文件中,该文件中共有8个函数分别是;
- void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
重置GPIO的寄存器为默认值; - __weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
GPIO外部中断回调函数; - void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
GPIO外部中断执行函数; - void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)
GPIO初始化函数; - HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
锁定GPIO寄存器防止修改; - GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
读取GPIO状态函数; - void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
翻转GPIO状态函数; - void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin,GPIO_PinState PinState)
写GPIO状态函数;
4.2 按键控制LED灯闪烁模式代码;
功能是LED1在按键按下后200ms连续闪烁两次,LED2一直以约500ms闪烁一次。
int main(void)
{
/* USER CODE BEGIN 1 */
uint8_t key_status=0;
uint8_t LED1_Start =0,LED1_STEP =0;
uint16_t LED1_Delay =0,LED2_Delay =0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(10); //延时10ms
/****************** 按键检测 ***********************/
if(key_status==0 && HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_2) ==GPIO_PIN_RESET)//按键按下后LED1闪烁两次
{
key_status =1;
}
else if(key_status==1 && HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_2) ==GPIO_PIN_RESET)//消抖后再确认真的按下
{
key_status =2;LED1_Start =1;
}
else if(key_status==2 && HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_2) ==GPIO_PIN_RESET)//长按后只执行一次,可添加长按判断
{
key_status =2;
}
else
{
key_status =0;
}
/****************** LED灯闪烁 ***********************/
if(LED1_Start ==1) //按键按下后LED1闪烁两次
{
if(LED1_STEP<=3)
{
if(LED1_Delay++>=20)
{
LED1_Delay =0;
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
LED1_STEP ++;
}
}
else
{
LED1_STEP =0;
LED1_Start =0;
}
}
//else if(LED2_Delay++>=50) //LED2一直闪烁,之前错误加了else if。会导致LED1闪烁过程中LED2会不动。
if(LED2_Delay++>=50) //LED2一直闪烁
{
LED2_Delay =0;
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
}
}
/* USER CODE END 3 */
}
总结
这里自己简单的介绍了GPIO的一些使用方法,详细内容还请参考数据手册或者其他博主的博文。我也是现学现写如有错误还请读者指出,下期将使用按键和LED做个简单的“红蓝警示灯”的程序。