1. 时钟树
- 什么是时钟?
时钟是由电路产生的具有周期性的脉冲信号,相当于单片机的心脏,给单片机提供一个统一的信号。
要想使用单片机的外设必须开启相应的时钟。
HSE = 高速外部时钟
HSI = 高速内部时钟
LSI = 低速内部时钟
LSE = 低速外部时钟
- 时钟树
- 时钟树初始化代码
c
#include "sys.h"
void stm32_clock_init(uint32_t plln)
{
HAL_StatusTypeDef ret = HAL_ERROR;
//定义晶振初始化结构体
RCC_OscInitTypeDef rcc_osc_init = {0};
//定义时钟初始化结构体
RCC_ClkInitTypeDef rcc_clk_init = {0};
//采用外部高速时钟
rcc_osc_init.OscillatorType = RCC_OSCILLATORTYPE_HSE;
//设置时钟打开
rcc_osc_init.HSEState = RCC_HSE_ON;
//设置HSE预分频系数为1
rcc_osc_init.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
//打开锁相环
rcc_osc_init.PLL.PLLState = RCC_PLL_ON;
//时钟来源选择HSE
rcc_osc_init.PLL.PLLSource = RCC_PLLSOURCE_HSE;
//设置PLL倍频系数
rcc_osc_init.PLL.PLLMUL = plln;
//结构体写入
ret = HAL_RCC_OscConfig(&rcc_osc_init);
if(ret != HAL_OK){
while(1);
}
//设置时钟种类
rcc_clk_init.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
//设置时钟来源为锁相环
rcc_clk_init.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
//设置AHB分频系数为1
rcc_clk_init.AHBCLKDivider = RCC_SYSCLK_DIV1;
//设置APB1分频系数为2
rcc_clk_init.APB1CLKDivider = RCC_HCLK_DIV2;
//设置APB2分频系数为1
rcc_clk_init.APB2CLKDivider = RCC_HCLK_DIV1;
//结构体写入
ret = HAL_RCC_ClockConfig(&rcc_clk_init, FLASH_LATENCY_2);
if(ret != HAL_OK){
while(1);
}
}
2. GPIO
2.1 什么是GPIO?
- GPIO是通用输入输出端口(General-purpose input/output)的英文简写,是所有的微控制器必不可少的外设之一,可以由STM32直接驱动从而实现与外部设备通信、控制以及采集和捕获的功能。
- GPIO口可配置为多种输入输出模式,如输入浮空、输入上拉、输入下拉、模拟输入、开漏输出、开漏复用功能、推挽式输出以及推挽式复用功能等。
- STM32单片机的GPIO被分为很多组,每组最多有16个引脚,不同型号的 MCU 的GPIO个数是不同的。
- 引脚电平:0V~3.3V,部分引脚可容忍5V。
2.2 GPIO内部结构

2.3 GPIO工作模式
2.4 点灯实验
led.c
c
#include "led.h"
#include "sys.h"
//初始化GBIO口函数
void led_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_8; //LED1对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //上拉
gpio_initstruct.Pull = GPIO_PULLUP; //高速
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
//关闭LED1
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
}
//点亮LED1的函数
void led1_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); //拉低LED1引脚,点亮LED1
}
//熄灭LED1的函数
void led1_off(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); //拉高LED1引脚,熄灭LED1
}
//翻转LED1的函数
void led1_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8); //翻转LED1引脚电平
}
led.h
c
#ifndef __LED_H__
#define __LED_H__
//初始化GBIO口函数
void led_init(void);
//点亮LED1的函数
void led1_on(void);
//熄灭LED1的函数
void led1_off(void);
//翻转LED1的函数
void led1_toggle(void);
#endif
main.c
c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
//开灯
led1_on();
//灭灯
led1_off();
while(1)
{
led1_toggle();
delay_ms(500);
}
}
2.5 流水灯实验
led.c
c
#include "led.h"
#include "sys.h"
//初始化GBIO口函数
void led_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_8 | GPIO_PIN_9; //LED1,LED2对应引脚
gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP; //推挽输出
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //上拉
gpio_initstruct.Pull = GPIO_PULLUP; //高速
HAL_GPIO_Init(GPIOB, &gpio_initstruct);
//关闭LED
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
}
//点亮LED1的函数
void led1_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET); //拉低LED1引脚,点亮LED1
}
//熄灭LED1的函数
void led1_off(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET); //拉高LED1引脚,熄灭LED1
}
//翻转LED1的函数
void led1_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8); //翻转LED1引脚电平
}
//点亮LED2的函数
void led2_on(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET); //拉低LED2引脚,点亮LED2
}
//熄灭LED2的函数
void led2_off(void)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET); //拉高LED2引脚,熄灭LED2
}
//翻转LED2的函数
void led2_toggle(void)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9); //翻转LED2引脚电平
}
main.c
c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
while(1)
{
led1_on();
led2_off();
delay_ms(500);
led1_off();
led2_on();
delay_ms(500);
}
}
2.5 按键实验
key.c
c
#include "key.h"
#include "delay.h"
//初始化GPIO
void key_init(void)
{
GPIO_InitTypeDef gpio_initstruct;
//使能GPIOA时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//调用GPIO初始化函数
gpio_initstruct.Pin = GPIO_PIN_0 | GPIO_PIN_1; //KEY1,KEY2对应引脚
gpio_initstruct.Mode = GPIO_MODE_INPUT; //输入
gpio_initstruct.Pull = GPIO_PULLUP; //上拉
gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH; //高速
HAL_GPIO_Init(GPIOA, &gpio_initstruct);
}
//按键扫描函数
uint8_t key_scan(void)
{
//检测按键1是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
//消抖
delay_ms(10);
//再次判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
//如果确实按下,那么等待按键松开
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
//返回按键值
return 1;
}
}
//检测按键2是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
//消抖
delay_ms(10);
//再次判断按键是否按下
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
//如果确实按下,那么等待按键松开
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
//返回按键值
return 2;
}
}
//返回默认值
return 0;
}
key.h
c
#ifndef __KEY_H__
#define __KEY_H__
#include "sys.h"
void key_init(void);
uint8_t key_scan(void);
#endif
main.c
c
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "key.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
//LED初始化
led_init();
key_init();
uint8_t key_num = 0;
while(1)
{
key_num =key_scan();
if(key_num == 1){
led1_toggle();
}
if(key_num == 2){
led2_toggle();
}
}
}