赞
踩
目录
2.简单实现亮灯交替闪烁效果(GPIO写、HAL_Delay)
个人情况:
已经有C语言、C51基础,并玩过各种外设,现需要快速上手使用STM32,所以选择了学习cubeMX+HAL库开发。
软件: Keil5 和 STM32CubeMX
使用 Keil4 写 STM32 代码其实也是可以,但需要很复杂的配置,不建议新手操作。比较推荐 Keil5 编写 STM32 ,只需要一些简单的设置就可以上手,对新手友好。
等待下载固件包,漫长的过程,也可以用已有的固件包直接导入。
十几块买一个,插上电脑,打开设备管理器查看:
可以看到已经连接,点更新驱动程序。选择驱动程序所在文件夹,完成更新
驱动官网下载(慢)https://www.st.com/en/development-tools/stsw-link009.html
在Keil中配置
更新一下,device connect如果不成功,重新插拔一下
可以找个模板程序,用STlink烧录测速一下。
在Keil里点击编译,烧录,成功后,板子复位一下就可以看到效果
作用:通过界面的方式,快速生成工程文件。
下载:官网(慢)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烧录即可
单片机(Single-Chip Microcomputer)是一种集成电路芯片,把具有数据处理能力的中央处
理器CPU、随机存储器RAM、只读存储器ROM、多种I/O口和中断系统、定时器/计数器等功
能(可能还包括显示驱动电路、脉宽调制电路、模拟多路转换器、A/D转换器等电路)集成
到一块硅片上构成的一个小而完善的微型计算机系统,在工业控制领域广泛应用。
ST -- 意法半导体
M -- Microelectronics 微电子
32 -- 总线宽度
F103 -- STM32 基础型
C -- 48引脚(&49)
8 -- 64kb闪存
T -- QFP封装
6 -- 温度范围-40 ~ 85
项目 | 介绍 |
内核 | Cortex-M3 |
Flash | 64K x 8bit |
SRAM | 20K x 8bit |
GPIO | 37个GPIO,分别为PA0-PA15、PBO-PB15、PC13-PC15、PDO-PD1 |
ADC | 2个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 |
寄存器众多,需要经常翻阅芯片手册,费时费力;
更大灵活性,可以随心所欲达到自己的目的;
深入理解单片机的运行原理,知其然更知其所以然。
将寄存器底层操作都封装起来,提供一整套接口(API)供开发者调用
每款芯片都编写了一份库文件,也就是工程文件里stm32F1xx…之类的;
配置结构体变量成员就可以修改外设的配置寄存器,从而选择不同的功能;
大大降低单片机开发难度,但是在不同芯片间不方便移植。
ST公司目前主力推的开发方式,新的芯片已经不再提供标准库;
为了实现在不同芯片之间移植代码;
为了兼容所有芯片,导致代码量庞大,执行效率低下。
弥补了HAL库效率低的问题。
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的。
下图来源于官方参考手册,了解即可。
内部结构图
推挽输出: 可以真正能真正的输出高电平和低电平
开漏输出: 开漏输出无法真正输出高电平,即高电平时没有驱动能力,需要借助外部上拉电阻完成对外驱动
在生成的代码中可以看看大概都是什么内容
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;
位于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晶体)
72MHz为最高
中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的
程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
外部中断/事件控制器(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位用于指定抢占式优先级
STM32通过中断控制器NVIC(Nested Vectored Interrupt Controller)进行中断的管理 。
NVIC是属于Cortex内核的器件,不可屏蔽中断(NMI)和外部中断都由它来处理,但是SYSTICK不是由NVIC控制的。
每个中断源都有对应的处理程序,这个处理程序称为中断服务程序,其入口地址称为中断向量。所有中断的中断服务程序入口地址构成一个表,称为中断向量表;也有的机器把中断服务程序入口的跳转指令构成一张表,称为中断向量跳转表。
同上
A0、A1 设置为 EXIT0、EXIT1,
B0、B1 改为默认高电平 GPIO_Output
下降沿触发
使能中断
同上
生成的代码:
- 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) 可以重写它
- /* 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 */
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。