目录

一、开发环境的安装、工程搭建和烧录

1.Keil5

烧录测试

STlink接线

STM32F1 模板工程

Keil烧录

3.STM32CubeMX

安装

快速搭建工程

 二、STM32简介

单片机

STM系列单片机命名规则

STM32F103C8T6:

​编辑STM32F103C8T6单片机简介

寄存器和各种库的对比

1. 寄存器

2. 标准库

3. HAL库

4.LL库

 三、通用输入输出端口GPIO

1.简介

定义

命名规则

内部框架图

推挽输出与开漏输出

2.简单实现亮灯交替闪烁效果(GPIO写、HAL_Delay) 

3.按键点灯(GPIO读、轮询法)

多此一举的宏定义和函数:

四、复位、时钟、中断

复位

系统复位

电源复位

备份区复位

时钟控制

什么是时钟?

时钟来源

如何使用CubeMX配置时钟

五、中断和事件

中断概述

什么是中断?

什么是EXTI?

什么是优先级?

抢占优先级和响应优先级的区别:

什么是优先级分组?

什么是NVIC?

什么是中断向量表?

五、按键点亮LED灯(中断法)

1. 配置时钟

2. 配置GPIO口

3. 使能中断

4. 配置工程

按键点亮LED灯

加上消抖


 个人情况:

已经有C语言、C51基础,并玩过各种外设,现需要快速上手使用STM32,所以选择了学习cubeMX+HAL库开发。

一、开发环境的安装、工程搭建和烧录

软件: Keil5 和 STM32CubeMX

1.Keil5

使用 Keil4 写 STM32 代码其实也是可以,但需要很复杂的配置,不建议新手操作。比较推荐 Keil5 编写 STM32 ,只需要一些简单的设置就可以上手,对新手友好。

等待下载固件包,漫长的过程,也可以用已有的固件包直接导入。

十几块买一个,插上电脑,打开设备管理器查看:

可以看到已经连接,点更新驱动程序。选择驱动程序所在文件夹,完成更新 

驱动官网下载(慢)https://www.st.com/en/development-tools/stsw-link009.html

 

 在Keil中配置

 

 更新一下,device connect如果不成功,重新插拔一下

 

烧录测试

STlink接线

STM32F1 模板工程

可以找个模板程序,用STlink烧录测速一下。

Keil烧录

在Keil里点击编译,烧录,成功后,板子复位一下就可以看到效果 

 

3.STM32CubeMX

安装

作用:通过界面的方式,快速生成工程文件。

下载:官网(慢)https://www.st.com/zh/development-tools/stm32cubemx.html#overview

安装:一路下一步,建议不要安装在C盘

配置:更新固件包位置(比较大,默认在C盘,可以更改到其它盘)

 help ---> update settings --> Firmware Repository

快速搭建工程

按图走

 

根据原理图可知LED1、2分别对应PB8、PB9

 

 

 设置PB8、PB9为GPIO输出口,默认低电平,灯会亮

 

 

 

 debug方式修改为串口

可以看到串口会自动配置 

 

 

 只取需要的库

 生成文件

 

打开Keil烧录即可 

 

 二、STM32简介

单片机

单片机(Single-Chip Microcomputer)是一种集成电路芯片,把具有数据处理能力的中央处
理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功
能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成
到一块硅片上构成的一个小而完善的微型计算机系统,在工业控制领域广泛应用。

STM系列单片机命名规则

ST -- 意法半导体

M -- Microelectronics 微电子

32 -- 总线宽度

STM32F103C8T6:

F103 -- STM32 基础型

C -- 48引脚(&49)

8 -- 64kb闪存

T -- QFP封装

6 -- 温度范围-40 ~ 85

STM32F103C8T6单片机简介

项目介绍
内核Cortex-M3
Flash64K x 8bit
SRAM20K x 8bit
GPIO37个GPIO,分别为PA0-PA15、PBO-PB15、PC13-PC15、PDO-PD1
ADC2个12bit ADC合计12路通道,外部通道: PAO到PA7+PBO到PB1内部通道: 温度传感器通道ADC Channel 16和内部参考电压通道ADC Channel 17
定时
器/计
数器
4个16bit定时器/计数器,分别为TIM1、TIM2、TIM3、TIM4TM1带死区插入,常用于产生PWM控制电机

看门狗定时器

2个看门狗定时器 (独立看门狗IWDG、窗口看门狗WWDG)
滴答定时器1个24bit向下计数的滴答定时器systick
工作电压、温度2V3.6V、-40°C85°C
通信串
2 * IIC,2 * SPI,3 * USART,1 * CAN
系统时钟内部8MHz时钟HSI最高可倍频到64MHZ,外部8MHZ时钟HSE最高可倍频到
72MHZ

寄存器和各种库的对比

1. 寄存器

寄存器众多,需要经常翻阅芯片手册,费时费力;

更大灵活性,可以随心所欲达到自己的目的;

深入理解单片机的运行原理,知其然更知其所以然。

2. 标准库

将寄存器底层操作都封装起来,提供一整套接口(API)供开发者调用

每款芯片都编写了一份库文件,也就是工程文件里stm32F1xx…之类的;

配置结构体变量成员就可以修改外设的配置寄存器,从而选择不同的功能;

大大降低单片机开发难度,但是在不同芯片间不方便移植。

3. HAL库

ST公司目前主力推的开发方式,新的芯片已经不再提供标准库;

为了实现在不同芯片之间移植代码;

为了兼容所有芯片,导致代码量庞大,执行效率低下。

4.LL库

弥补了HAL库效率低的问题。

 三、通用输入输出端口GPIO

1.简介

定义

GPIO是通用输入输出端口的简称,简单来说就是STM32可控制的引脚STM32芯片的GPIO引脚与外部设备连接起来,从而实现与外部通讯、控制以及数据采集的功能。

简单来说我们可以控制GPIO引脚的电平变化,达到我们的各种目的。

命名规则

组编号+引脚编号

组编号:GPIOA, GPIOB, GPIOC, GPIOD .. GPIOG

引脚编号:0,1,2,3,4...15

组合起来:

PA0, PA1, PA2 .. PA15

PB0, PB1, PB2 .. PB15

PC0, PC1, PC2 .. PC15

...

有一些特殊功能的引脚是不能用作IO的。

内部框架图

下图来源于官方参考手册,了解即可。

推挽输出与开漏输出

内部结构图

推挽输出: 可以真正能真正的输出高电平和低电平

开漏输出: 开漏输出无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动

2.简单实现亮灯交替闪烁效果(GPIO写、HAL_Delay) 

在生成的代码中可以看看大概都是什么内容

Keil5中按F12可以溯源(要先编译)

主函数中找到

 找到cubeMXGPIO初始化函数,

里面有HAL库的GPIO写函数,阅读得知前两个参数是选择IO口,

第三个RESET表示低电平,而SET表示高电平

在main函数while循环里就可以复制并改写这段代码,

用HAL的delay函数延时500ms,两口SET、RESET交替,两LED交替闪烁

 

  while (1)
  {
    /* USER CODE END WHILE */
		
		 /*Configure GPIO pin Output Level */
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET);
		
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET);
		
		HAL_Delay(500);
		
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET);
		
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET);
		
		HAL_Delay(500);

    /* USER CODE BEGIN 3 */
  }

继续溯源,可以看一些GPIO相关源码 

常用的GPIO HAL库函数:

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);

void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinStatePinState);

void HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)

 结构体 GPIO_InitTypeDef 定义:

typedef struct
{
uint32_t Pin;

uint32_t Mode;

uint32_t Pull;

uint32_t Speed;

} GPIO_InitTypeDef;

3.按键点灯(GPIO读、轮询法)

位于A0、A1,

 按下变为低电平。

设置GPIO_Input,其它配置一样,生成 

 

 HAL_GPIO_ReadPin 源码:

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
  GPIO_PinState bitstatus;

  /* Check the parameters */
  assert_param(IS_GPIO_PIN(GPIO_Pin));

  if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)GPIO_PIN_RESET)
  {
    bitstatus = GPIO_PIN_SET;
  }
  else
  {
    bitstatus = GPIO_PIN_RESET;
  }
  return bitstatus;
}

 根据 HAL_GPIO_ReadPin 函数,写代码,添加一个 while() 用于软件消抖。

  while (1)
  {
    /* USER CODE END WHILE */
		
		//循环检测A0是否低电平
		if (HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
		{
			//软件按钮消抖:检测如果一直按住,直到松手再继续
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
			//B8状态翻转
			HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
		}
		
		//循环检测A1是否低电平
		if (HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
		{
			//软件按钮消抖:检测如果一直按住,直到松手再继续
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
			//B9状态翻转
			HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
		}
		
    /* USER CODE BEGIN 3 */
  }

多此一举的宏定义和函数:

或者也可以定义一个返回按钮状态的函数,用 unsigned char 型,查看源码可知为 uint8_t

    /* 7.18.1.1 */

    /* exact-width signed integer types */
typedef   signed          char int8_t;
typedef   signed short     int int16_t;
typedef   signed           int int32_t;
typedef   signed       __INT64 int64_t;

    /* exact-width unsigned integer types */
typedef unsigned          char uint8_t;
typedef unsigned short     int uint16_t;
typedef unsigned           int uint32_t;
typedef unsigned       __INT64 uint64_t;

 宏定义、函数:

注意宏定义结尾不能+“ ; ” ,

函数只有一个return,这是一种规范形式

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define KEY_ON  0
#define KEY_OFF 1

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t Key_scan(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
	uint8_t Key_Status;
	if (HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET)
	{
		while(HAL_GPIO_ReadPin(GPIOx,GPIO_Pin) == GPIO_PIN_RESET);
		Key_Status = KEY_ON;
	}
	else
	{
		Key_Status = KEY_OFF;
	}
	return Key_Status;
}
/* USER CODE END 0 */

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* 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 */
		
		if (Key_scan(GPIOA, GPIO_PIN_0) == KEY_ON)
		{
			HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
		}
		if (Key_scan(GPIOA, GPIO_PIN_1) == KEY_ON)
		{
			HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
		}
		
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

四、复位、时钟、中断

复位

系统复位

当发生以下任一事件时,产生一个系统复位:

1. NRST引脚上的低电平(外部复位)

2. 窗口看门狗计数终止(WWDG复位)

3. 独立看门狗计数终止(IWDG复位)

4. 软件复位(SW复位)

5. 低功耗管理复位

电源复位

当以下事件中之一发生时,产生电源复位:

1. 上电/掉电复位(POR/PDR复位)

2. 从待机模式中返回

备份区复位

备份区域拥有两个专门的复位,它们只影响备份区域。

当以下事件中之一发生时,产生备份区域复位。

1. 软件复位,备份区域复位可由设置备份域控制寄存器 (RCC_BDCR)(见6.3.9节)中的

BDRST位产生。

2. 在VDD和VBAT两者掉电的前提下,VDD或VBAT上电将引发备份区域复位。

时钟控制

什么是时钟?

时钟打开,对应的设备才会工作。

时钟来源

三种不同的时钟源可被用来驱动系统时钟(SYSCLK)

HSI振荡器时钟(高速内部时钟)

HSE振荡器时钟(高速外部时钟)

PLL时钟(锁相环倍频时钟)

二级时钟源:

40kHz低速内部RC(LSIRC)振荡器

32.768kHz低速外部晶体(LSE晶体)

如何使用CubeMX配置时钟

 72MHz为最高

 

五、中断和事件

中断概述

什么是中断?

中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的

程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

什么是EXTI?

外部中断/事件控制器(EXTI)管理了控制器的 23 个中断/事件线。每个中断/事件线都对应有一

个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。 EXTI 可以实现对每个中断/事

件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

 EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不
同。
产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软
件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传
输,属于硬件级的。

EXTI初始化结构体:

typedef struct
{
  uint32_t Line;      /*!< The Exti line to be configured. This parameter
                           can be a value of @ref EXTI_Line */
  uint32_t Mode;      /*!< The Exit Mode to be configured for a core.
                           This parameter can be a combination of @ref EXTI_Mode */
  uint32_t Trigger;   /*!< The Exti Trigger to be configured. This parameter
                           can be a value of @ref EXTI_Trigger */
  uint32_t GPIOSel;   /*!< The Exti GPIO multiplexer selection to be configured.
                           This parameter is only possible for line 0 to 15. It
                           can be a value of @ref EXTI_GPIOSel */
} EXTI_ConfigTypeDef;

中断/事件线:

#define EXTI_LINE_0                        (EXTI_GPIO     | 0x00u)    /*!< External interrupt line 0 */
#define EXTI_LINE_1                        (EXTI_GPIO     | 0x01u)    /*!< External interrupt line 1 */
#define EXTI_LINE_2                        (EXTI_GPIO     | 0x02u)    /*!< External interrupt line 2 */
#define EXTI_LINE_3                        (EXTI_GPIO     | 0x03u)    /*!< External interrupt line 3 */
#define EXTI_LINE_4                        (EXTI_GPIO     | 0x04u)    /*!< External interrupt line 4 */
#define EXTI_LINE_5                        (EXTI_GPIO     | 0x05u)    /*!< External interrupt line 5 */
#define EXTI_LINE_6                        (EXTI_GPIO     | 0x06u)    /*!< External interrupt line 6 */
#define EXTI_LINE_7                        (EXTI_GPIO     | 0x07u)    /*!< External interrupt line 7 */
#define EXTI_LINE_8                        (EXTI_GPIO     | 0x08u)    /*!< External interrupt line 8 */
#define EXTI_LINE_9                        (EXTI_GPIO     | 0x09u)    /*!< External interrupt line 9 */
#define EXTI_LINE_10                       (EXTI_GPIO     | 0x0Au)    /*!< External interrupt line 10 */
#define EXTI_LINE_11                       (EXTI_GPIO     | 0x0Bu)    /*!< External interrupt line 11 */
#define EXTI_LINE_12                       (EXTI_GPIO     | 0x0Cu)    /*!< External interrupt line 12 */
#define EXTI_LINE_13                       (EXTI_GPIO     | 0x0Du)    /*!< External interrupt line 13 */
#define EXTI_LINE_14                       (EXTI_GPIO     | 0x0Eu)    /*!< External interrupt line 14 */
#define EXTI_LINE_15                       (EXTI_GPIO     | 0x0Fu)    /*!< External interrupt line 15 */
#define EXTI_LINE_16                       (EXTI_CONFIG   | 0x10u)    /*!< External interrupt line 16 Connected to the PVD Output */
#define EXTI_LINE_17                       (EXTI_CONFIG   | 0x11u)    /*!< External interrupt line 17 Connected to the RTC Alarm event */
#if defined(EXTI_IMR_IM18)
#define EXTI_LINE_18                       (EXTI_CONFIG   | 0x12u)    /*!< External interrupt line 18 Connected to the USB Wakeup from suspend event */
#endif /* EXTI_IMR_IM18 */
#if defined(EXTI_IMR_IM19)
#define EXTI_LINE_19                       (EXTI_CONFIG   | 0x13u)    /*!< External interrupt line 19 Connected to the Ethernet Wakeup event */
#endif /* EXTI_IMR_IM19 */

 EXTI模式:产生中断、产生事件

#define EXTI_MODE_NONE                      0x00000000u
#define EXTI_MODE_INTERRUPT                 0x00000001u
#define EXTI_MODE_EVENT                     0x00000002u

 触发类型:上升沿、下降沿

#define EXTI_TRIGGER_NONE                   0x00000000u
#define EXTI_TRIGGER_RISING                 0x00000001u
#define EXTI_TRIGGER_FALLING                0x00000002u
#define EXTI_TRIGGER_RISING_FALLING         (EXTI_TRIGGER_RISING | EXTI_TRIGGER_FALLING)

EXTI控制:

使能 EXTI ,一般都是使能, ENABLE

什么是优先级?

抢占优先级和响应优先级的区别:

高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。

抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。

抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。

如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行

什么是优先级分组?

Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:

第0组:所有4位用于指定响应优先级

第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级

第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级

第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级

第4组:所有4位用于指定抢占式优先级

什么是NVIC?

STM32通过中断控制器NVIC(Nested Vectored Interrupt Controller)进行中断的管理 。

NVIC是属于Cortex内核的器件,不可屏蔽中断(NMI)和外部中断都由它来处理,但是SYSTICK不是由NVIC控制的。

什么是中断向量表?

每个中断源都有对应的处理程序,这个处理程序称为中断服务程序,其入口地址称为中断向量。所有中断的中断服务程序入口地址构成一个表,称为中断向量表;也有的机器把中断服务程序入口的跳转指令构成一张表,称为中断向量跳转表。

五、按键点亮LED灯(中断法)

1. 配置时钟

同上

2. 配置GPIO口

A0、A1 设置为 EXIT0、EXIT1,

B0、B1 改为默认高电平 GPIO_Output

3. 使能中断

下降沿触发

 使能中断

4. 配置工程

同上

生成的代码:

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8|GPIO_PIN_9, GPIO_PIN_SET);

  /*Configure GPIO pins : PA0 PA1 */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /*Configure GPIO pins : PB8 PB9 */
  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI0_IRQn);

  HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI1_IRQn);

}

 

溯源可以找到一个虚函数(weak) 可以重写它

 

按键点亮LED灯

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	switch(GPIO_Pin)
	{
		case GPIO_PIN_0:
			HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
		break;
		case GPIO_PIN_1:
			HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);
	}
}
/* USER CODE END 0 */

加上消抖

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	switch(GPIO_Pin)
	{
		HAL_Delay(20);//延时再判断,排除抖动
		case GPIO_PIN_0:
			//消抖:检测A0是否低电平
			if (HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)
				HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);//B8状态翻转
		break;
			
		case GPIO_PIN_1:
			//消抖:检测A1是否低电平
			if (HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET)
				HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);//B9状态翻转
		break;
	}
}
/* USER CODE END 0 */

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐