前言
上一节,我们完成了STM32单片机开发环境的搭建,本节我们正式学习STM32单片机,编程语言的学习,通常是从第一个"hello world"开始,而点灯实验便是单片机学习的开始。
一
基础知识
1. STM32最小系统简介
一个最小的STM32系统,需要有单片机、电源电路、晶振电路、复位电路、启动电路、调试电路组成,这几部分存在就可以使STM32正常工作。
(1) STM32F103C8T6
ARM的Cortex-M3处理器是最新一代的嵌入式ARM处理器,它为实现MCU的需要提供了低成本的平台、缩减的引脚数目、降低的系统功耗,同时提供卓越的计算性能和先进的中断系统响应。
ARM的Cortex-M3是32位的RISC处理器,提供额外的代码效率,在通常8和16位系统的存储空间上发挥了ARM内核的高性能。
(2) 电源电路
和STC89C52单片机5V供电不同,STM32需要3.3V电压供电,直接输入的电压不太稳定性,需要电路稳压,同时点亮LED1,可以通过LED1的亮灭初步观察系统运行情况,正常运行情况,LED1常亮。
使用稳压芯片,将USB输入的5V电压转换为STM32需要的3.3V电压
LED1电路如下,插上Micro USB线后即可点亮
(3) 复位电路
单片机有一个RESET引脚,只需要将此引脚保持一段时间低电平即可复位STM32单片机,当按键按下瞬间,RESET电平为低电平,复位STM32单片机,之后C2开始充电,C2电源不断上升,R2两端电压不断下降,当C2两端电压达到3.3V时,充电结束,此时RESET引脚变为高电平,单片机进入正常工作状态,复位完成。
注意:51单片机是高电平复位,STM32是低电平复位
(4) 晶振电路
上图中有两个晶振,一个是8MHz,另一个是32.768KHz,8M晶振的作用是为最小系统提供最基本的时钟信号,方便倍频,一般STM32F103系列正常使用过程需要倍频到72MHz。32.768KHZ晶振经过15次分频后可以得到1HZ的频率(原因是32768 = 2^15),可以实现精准定时,用于精准计时电路,比如作为万年历。
(5) boot启动电路
启动方式如下表所示:
BOOT0 | BOOT1 | 启动方式 |
0 | x | 从主闪存存储器启动 |
1 | 0 | 从系统存储器启动 |
1 | 1 | 从内置SRAM启动 |
(6) 调试接口电路
本系列教程使用STM32F103C8T6核心板,其调试接口电路采用JLink SWD方式进行
2.LED灯发光原理
LED灯中有电流通过时候,将点亮LED灯,单片机系统中,常见的LED灯如下所示,其中长的引脚一端为正极,短的为负极。
那么问题来了,如何让LED中有电流通过呢?电流到多少才能点亮LED呢?我们先看下实际电路设计中常用的LED设计电路
上图中LED1左边接入电源正极、右边接入负极,电流方能通过点亮LED,此时LED电阻几乎为0,如果电源接反方向了,LED产生很大电阻,阻止电流通过,此时,不能LED不能点亮,一般而言LED正向接入电源,保证通过LED中的电流为20mA左右即可点亮LED。
3.查看开发底板LED部分原理图
上图中led_wifi led_red led_green分别连接单片机PB12 PB13 PB14引脚,只需要控制单片机给低电平即可点亮LED。
开发板实物图如下:
二
实例
1. 新建工程
使用STM32CubeMX创建一个新的工程,参考上节配置方式,设置RCC和PB12 PB13 PB14引脚输出
进入Clock configuration页面,选择HSE时钟源,倍频后主时钟为72MHz
切换到Project Manager栏目,设置工程名字、工程保存目录、工具链等信息,具参数如下图所示
点击左边栏目Code Generator,然后勾选Generate peripheral initialization as a pair of '.c/.h' files per peripheral,勾选此选项,外设将单独保存在一个文件中,而不是全部都在main.c中。
然后打开02Led.uvprojx工程
打开后工程如下
可以看到多了一个gpio.c文件,main.c中MX_GPIO_Init()便是调用的gpio.c中的文件,其函数声明在gpio.h中
2. GPIO函数说明
(1) 首先查看下gpio.c中MX_GPIO_Init()函数
void MX_GPIO_Init(void){ GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14, GPIO_PIN_RESET); /*Configure GPIO pins : PB12 PB13 PB14 */ GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14; 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);}
在GPIO初始化函数中,首先使能GPIOB时钟,然后初始化PB12 PB13 PB14,初始化函数用到了GPIO_InitTypeDef定义的GPIO_InitStruct,我们接下来看看GPIO_InitTypeDef结构
typedef struct{ uint32_t Pin; /*!< Specifies the GPIO pins to be configured. This parameter can be any value of @ref GPIO_pins_define */ uint32_t Mode; /*!< Specifies the operating mode for the selected pins. This parameter can be a value of @ref GPIO_mode_define */ uint32_t Pull; /*!< Specifies the Pull-up or Pull-Down activation for the selected pins. This parameter can be a value of @ref GPIO_pull_define */ uint32_t Speed; /*!< Specifies the speed for the selected pins. This parameter can be a value of @ref GPIO_speed_define */} GPIO_InitTypeDef;
其中Pin表示需要操作的GPIO引脚,GPIO_PIN_All表示选择该端口所有引脚GPIO_PIN_0~GPIO_PIN_15
Mode 用以设置选中管脚的工作状态
Mode | 描述 |
GPIO_MODE_INPUT | 输入模式 |
GPIO_MODE_OUTPUT_PP | 推挽输出 |
GPIO_MODE_OUTPUT_OD | 开漏输出 |
GPIO_MODE_AF_PP | 复用开漏输出 |
GPIO_MODE_AF_OD | 复用推挽输出 |
Pull表示引脚下拉或上拉设置
Pull | 描述 |
GPIO_NOPULL | 引脚悬空 |
GPIO_PULLUP | 引脚上拉 |
GPIO_PULLDOWN | 引脚下拉 |
Speed引脚驱动速率设置
Speed | 描述 |
GPIO_SPEED_FREQ_LOW | 低速输出 |
GPIO_SPEED_FREQ_MEDIUM | 中速输出 |
GPIO_SPEED_FREQ_HIGH | 高速输出 |
3. 修改程序
/* USER CODE BEGIN Header *//** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * *
© Copyright (c) 2020 STMicroelectronics.
* All rights reserved. * * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** *//* USER CODE END Header *//* Includes ------------------------------------------------------------------*/#include "main.h"#include "gpio.h"/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes *//* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*//* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*//* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/void SystemClock_Config(void);/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 *//* 一个__NOP()为1/72us */void delay_us(uint32_t time){ uint32_t i=0; for(i=0;i__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP(); __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP(); __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP(); __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP(); __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP(); }}void delay_ms(uint16_t time){ delay_us(time*1000);}/* USER CODE END 0 *//** * @brief The application entry point. * @retval int */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) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14,GPIO_PIN_SET); delay_ms(1000); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14, GPIO_PIN_RESET); delay_ms(1000); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */}/** * @brief System Clock Configuration * @retval None */void SystemClock_Config(void){ RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); }}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//** * @brief This function is executed in case of error occurrence. * @retval None */void Error_Handler(void){ /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ /* USER CODE END Error_Handler_Debug */}#ifdef USE_FULL_ASSERT/** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */void assert_failed(uint8_t *file, uint32_t line){ /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */}#endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
三
下载运行
按照上一节方式给核心板下载程序,然后将核心板直插到底板上面,可以看到底板三个LED灯在交替闪烁。
四
小结
如您在使用过程中有任何问题,请加QQ群进一步交流。
QQ交流群:906015840 (备注:物联网项目交流)
源码获取:关注公众号,回复stm32_hal获取资料
硬件获取:某宝搜索小驿物联
PCB开源:https://lceda.cn/solitary_sand/51-dan-pian-ji-wu-lian-wang-kai-fa-ban_base
一叶孤沙出品:一沙一世界,一叶一菩提