💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
>[success] **技术支持说明:** >**1**.一般以自主学习为主 > **2**.可到官方问答社区中提问:[**去提问**](https://bbs.csdn.net/forums/nb-iot) > **3**.工程师**会尽快**解答社区问题,但他们是一线开发,【**难以保证**】解答时效,解答辛苦,感谢理解! <br/> ## **LED控制原理** **计算机的逻辑层与电路层** 在计算机逻辑层中,一般是使用二进制数字(0或1)来控制对象或表示对象的状态。在计算机电路层上,一般是用高电平(3.3 v)来表示逻辑上的1,用低电平(0 v)来表示逻辑上的0。 ### **CPU的IO口** CPU的IO口(引脚)可以理解为从CPU里面引出来的一根导线,用于连接外部的设备。通俗地讲,一个CPU会有多个IO口,每个IO口有两种工作模式,分别是输出信号模式和接收信号模式。 ### 在输出信号模式时,我们可以通过代码来控制这个IO口输出的电平状态,这个电平状态有高电平(3.3 v)或低电平(0 v)。在输入信号模式时,我们可以通过代码来检测这个IO口是处于高电平还是低电平状态。 ### **LED简介** LED是Light Emitting Diode的缩写,中文意思是“发光二极管”,是一种能够把电能转化为可见光的元器件。STM32开发板配备了一盏可以给开发者使用的LED,如图所示。 ![](https://img.kancloud.cn/0d/d0/0dd069a4653360a88179e3df0858e2cc_2646x2884.png =350x) 此LED的原理图如下图所示。 ![](https://img.kancloud.cn/e0/83/e083e4e29c39bc2cf79ad5f12f8d2d1a_854x274.png =500x) 其中的D2表示LED,其右端接地(GND),因此右端的电压为0v。LED的左端依次连接着R6和PB1。PB1是STM32F030的一个IO口,能够输出高电平(3.3v)或低电平(0v)。R6是一个稳压电阻,用于防止电路的电压过大而烧坏LED。当PB1输出高电平时,LED左端电压为3.3v,右端电压为0v,左端和右端形成了3.3v的电压差,因此LED被点亮;反之,当PB1输出低电平时,LED左端和右端电压均为0v,因此LED被熄灭。 >[danger] 如您因缺少硬件原理相关知识导致未能看懂本图,需先补充相关知识。 <br/> ## **架构设计** 在学习使用LED灯之前,先简单讲解一下嵌入式系统的常用架构设计,这对以后开发嵌入式软件有莫大的好处,希望读者好好学习。 ### 常见的小型嵌入式系统可以划分为硬件、标准库、HAL/BSP和上层应用,如图所示。 ![](https://img.kancloud.cn/0a/b9/0ab9dfb736ea561e84b7a40c827c39d5_379x402.png =300x) * 硬件是指主控芯片以及相关外围设备等,例如本课程配套的开发板。 * 标准库是指有主控芯片配套的驱动程序库,例如本课程用到的ST标准库。 * 上层应用是指由应用软件开发者针对各种实际的应用需求而开发出来的应用软件。 有架构思想的工程师通常会结合硬件资源在标准库和上层应用之间构建HAL(Hardware Abstraction Layer,硬件抽象层)。构建HAL的好处在于极大地方便应用层使用各种硬件资源,例如方便地控制LED的开关、接收按键信息,以及获取温湿度传感器数值等。有些资源较为丰富的硬件设备会为开发者提供BSP(Board Support Package),其作用跟HAL也是类似的。 <br/> **LED HAL API 设计** 为LED应用设计HAL 前,需要先思考一下应用开发者需要对LED进行什么操作。一般地,应用开发者需要打开、关闭、反转和初始化LED,因此HAL可以提供以下API供应用层调用: * Init : 初始化 * SetOn : 打开LED * SetOff : 关闭LED * Toggle : 反转LED,即如果当前LED是打开的,那么就关闭,如果是关闭的,那就打开。 因此LED的HAL抽象定义如图所示。 ![](https://img.kancloud.cn/2a/34/2a34cb892dc610dddd11e0cbfbb6bf6a_563x211.png =500x) API是Application Programming Interface的简称,中文意思是应用编程接口,开发者可以使用API来实现各种操作,例如使用上述的SetOn来打开LED、使用SetOff来关闭LED等。对于初学者来说,这是一个不容易理解概念,可以先跟随本课程一步一步学习,在学习的过程中逐渐地领悟它的含义。 <br/> ## **实现LED HAL API** 学习完架构设计思想后,接下来实现LED应用的HAL。 <br/> #### **准备工作** 在上节课移植好的模板工程的根目录创建两个空文件,分别为hal\_led.h和hal\_led.c文件,如图所示。 ![](https://img.kancloud.cn/23/1d/231d1a514c48b682c0c2f301625041a1_848x824.png =200x) ### 然后打开文件管理器,选择STM32F030→User,然后点击Add Files,把hal\_led.c文件添加进工程中,如图所示。 ![](https://img.kancloud.cn/38/a1/38a1c792b5bf6b325533b8471d3d2ec5_881x670.png =500x) <br/> 由于hal_led.c文件将会使用到ST标准库,因此也需要把相关的标准库文件添加进工程中,步骤如下: 1. 打开文件管理器,如图所示。 ![](https://img.kancloud.cn/06/72/0672e0f1ca6df75cec4d3ff3ab0d0bd1_1722x764.png =250x) ### 2. 选择STM32F030→StdPeriph_Driver,然后点击Add Files... ![](https://img.kancloud.cn/52/64/526466ce6627f873ab6f939b892e81dc_799x604.png =500x) ### 3. 进入到如下目录中, ``` Libraries\\TM32F0xx\_StdPeriph\_Driver\\src ``` ### 4. 添加 stm32f0xx_gpio.c 和 stm32f0xx_rcc.c文件,如图所示。 ![](https://img.kancloud.cn/50/83/508387c2253b6d3e5f5a831f2a417dd6_896x640.png =500x) ### 5. 添加完成后,点击 "Close"按钮。 <br/> 由于配套开发板的资源与ST默认的有所不同,因此需要修改文件 system_stm32f0xx.c 文件中的 SetSysClock 函数,如下图所示。 ![](https://img.kancloud.cn/ec/53/ec53e5478a1ae42780a2f5c5f3253588_1268x774.png =500x) ### 修改后的代码如下: ``` static void SetSysClock(void) { __IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* SYSCLK, HCLK, PCLK configuration ----------------------------------------*/ /* Enable HSE */ RCC->CR |= ((uint32_t)RCC_CR_HSEON); /* Wait till HSE is ready and if Time out is reached exit */ do { HSEStatus = RCC->CR & RCC_CR_HSERDY; StartUpCounter++; } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT)); if ((RCC->CR & RCC_CR_HSERDY) != RESET) { HSEStatus = (uint32_t)0x01; } else { HSEStatus = (uint32_t)0x00; } if (HSEStatus == (uint32_t)0x01) { /* Enable Prefetch Buffer and set Flash Latency */ FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY; /* HCLK = SYSCLK */ RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; /* PCLK = HCLK */ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1; /* PLL configuration = HSE * 6 = 48 MHz */ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL)); RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLMULL6); /* Enable PLL */ RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready */ while((RCC->CR & RCC_CR_PLLRDY) == 0) { } /* Select PLL as system clock source */ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; /* Wait till PLL is used as system clock source */ while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL) { } } else { /* Enable Prefetch Buffer and set Flash Latency */ FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY; /* HCLK = SYSCLK */ RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1; /* PCLK = HCLK */ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE_DIV1; // PLL configuration = (HSI/2) * 12 = 48 MHz RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_12); // 8M/2 * 12 = 48M /* Enable PLL */ RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready */ while ((RCC->CR & RCC_CR_PLLRDY) == 0) { } /* Select PLL as system clock source */ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // PLL as system clock /* Wait till PLL is used as system clock source */ while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL) { } } } ``` >[warning] 上述代码较为复杂,读者暂时无需理解。 准备工作至此完成,接下开始编写代码实现LED的HAL。 <br/> #### **编写源代码** 在头文件hal_led.h中定义前面设计的各个API,代码如下: ``` #ifndef __HAL_LED_H__ #define __HAL_LED_H__ /*定义LED灯连接的GPIO口B1*/ #define HAL_LED_PORT GPIOB #define HAL_LED_PIN GPIO_Pin_1 #define HAL_LED_CLOCK RCC_AHBPeriph_GPIOB void halLedInit(void);//初始化LED灯 void halLedSetOn(void);//打开LED灯 void halLedSetOff(void);//关闭LED灯 void halLedToggle(void);//反转LED灯 #endif /* #ifndef __HAL_LED_H__ */ ``` ### 上述代码中使用到GPIO口B1,这是由于配套的开发板板载的LED是与STM32F030的B1连接的。如果开发者需要外接其它的LED,从而使用了其他的GPIO,那么直接修改此定义即可,例如修改为B2的代码如下: ``` #define HAL_LED_PORT GPIOB #define HAL_LED_PIN GPIO_Pin_2 ``` <br/> 接着,在hal_led.c文件中,实现各个API,代码如下: ``` #include "hal_led.h" #include "stm32f0xx_gpio.h" /* *初始化LED灯,在使用LED灯前必须调用此函数 */ void halLedInit() { GPIO_InitTypeDef ledGPIO;//定义配置 ledGPIO.GPIO_Mode = GPIO_Mode_OUT;//配置该GPIO为输出模式 ledGPIO.GPIO_Speed = GPIO_Speed_2MHz;//速率为2MHz ledGPIO.GPIO_Pin = HAL_LED_PIN;//引脚 RCC_AHBPeriphClockCmd(HAL_LED_CLOCK, ENABLE);//使用LED时钟 GPIO_Init(HAL_LED_PORT, &ledGPIO);//初始化指定的GPIO } /* *打开LED灯 */ void halLedSetOn() { //置位指定的GPIO GPIO_SetBits(HAL_LED_PORT, HAL_LED_PIN); } /* *关闭LED灯 */ void halLedSetOff() { //重置指定的GPIO GPIO_ResetBits(HAL_LED_PORT, HAL_LED_PIN); } /* *反转LED灯 */ void halLedToggle() { //读取指定GPIO的状态 uint8_t level; level = GPIO_ReadInputDataBit(HAL_LED_PORT, HAL_LED_PIN); if (level == 0)//如果为0,则表示该LED灯关闭 GPIO_SetBits(HAL_LED_PORT, HAL_LED_PIN); else GPIO_ResetBits(HAL_LED_PORT, HAL_LED_PIN); } ``` 其中的stm32f0xx\_gpio.h文件是由ST标准库提供的头文件,提供了GPIO相关的配置API。 <br/> ## **使用 HAL API** 编写好LED的HAL后,LED的使用非常简单。在配套工程的main.c文件中添加如下代码: ``` #include "main.h" #include "hal_led.h" /* * 延时函数 */ static void delay() { for (uint32_t i = 0; i < 6553000; i++); } int main(void) { halLedInit();//初始化LED等 while (1) { halLedToggle();//反转LED灯的状态 delay();//延时 } } ``` 上述代码在初始化LED后,不断地反转LED灯的状态,达到了闪烁LED的效果。 <br/> ## **测试验证** 1.编译整个工程,生成Hex文件,如图所示。 ![](https://img.kancloud.cn/60/b5/60b53db64b11f01ed1df4f8ee9c0a0db_1121x695.png =500x) ### 2.把该Hex文件烧录到配套的开发板中,可以看到LED不断地闪烁。 <br/> <br/> ## **商务合作** 如有以下需求,可扫码添加管理员好友,注明“**商务合作**” * 项目定制开发,技术范围:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等; * 入驻平台,成为讲师; * 接项目赚外快; * 善学坊官网:[www.sxf-iot.com](https://www.sxf-iot.com/) ![](https://img.kancloud.cn/b7/5a/b75ac8c3da945f20568771f5935c388f_430x430.png =150x) (非商务合作**勿扰**,此处**非**技术支持)