多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
>[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灯的原理图如下图所示。 ![](https://img.kancloud.cn/85/fd/85fdd1fd06f6f2b128cfb3e47d2c4d4b_854x274.png =500x) >[danger] 如您因缺少硬件原理相关知识导致未能看懂本图,需先补充相关知识 <br/> ## **架构设计** 在学习使用LED灯之前,先简单讲解一下嵌入式系统的常用架构设计,这对以后开发嵌入式软件有莫大的好处,希望读者好好学习。 ### 常见的小型嵌入式系统可以划分为硬件、标准库、HAL/BSP和上层应用,如图所示。 ![](https://img.kancloud.cn/11/2e/112e615cd37a5e1e71903af6a8ddca98_379x402.png =200x) * 硬件是指主控芯片以及相关外围设备等,例如本课程配套的开发板 * 标准库是指有主控芯片配套的驱动程序库,例如本课程用到的ST标准库 * 上层应用是指有应用软件开发者针对各种实际的应用需求而开发出来的应用软件 有架构思想的工程师通常会结合硬件资源在标准库和上层应用之间构建HAL(Hardware Abstraction Layer,硬件抽象层)。构建HAL的好处在于极大地方便应用层使用各种硬件资源,例如方便地控制LED灯的开关、获取温湿度传感器数值等。有些资源较为丰富的硬件设备会为开发者提供BSP(Board Support Package),其作用跟HAL层也是类似的。 <br/> **LED HAL API 设计** 为LED应用设计HAL API前,需要先思考一下应用开发者需要对LED进行什么操作。一般地,应用开发者需要打开、关闭、反转和初始化LED灯,因此HAL可以提供以下API供应用层调用: * Init : 初始化 * SetOn : 打开LED * SetOff : 关闭LED * Toggle : 反转LED 因此LED的HAL的抽象定义如图所示。 ![](https://img.kancloud.cn/d9/ed/d9ede6413ce7abb54f3c13ca7ab71770_563x211.png =300x) <br/> ## **实现LED HAL API** 学习完架构设计思想后,接下来实现LED应用的HAL。 <br/> #### **准备工作** 在上节课移植好的模板工程的根目录创建hal\_led.h和hal\_led.c文件,然后把hal\_led.c文件添加进工程中,如图所示。 ![](https://img.kancloud.cn/57/c5/57c56cbb2195f9528f09aaaac404793d_881x670.png =500x) <br/> 由于hal_led.c文件将会使用到ST标准库,因此也需要把相关的标准库文件添加进工程中。 1. 选择StdPeriph_Driver,然后点击Add Files... ![](https://img.kancloud.cn/d9/59/d959b878a96981dbb0e0126ffda7b76d_799x604.png =500x) ### 2. 在本模板工程中,依次找到文件夹Libraries、STM32F0xx\_StdPeriph\_Driver、src,然后添加 stm32f0xx_gpio.c 和 stm32f0xx_rcc.c文件 ![](https://img.kancloud.cn/67/73/677360af63735db387d391c23bbe29c8_896x640.png =500x) ### 3. 添加完成后,点击 "Close" <br/> 由于配套开发板的资源与ST默认的有所不同,因此修改文件 system_stm32f0xx.c 文件中的 SetSysClock 函数,如下图所示。 ![](https://img.kancloud.cn/33/00/330065dba7fe50fd1bf1cf00db674012_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是与STM32F030F4P6的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/0e/71/0e71b73feaa3a52ad1c8483406df7ceb_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/ca/73/ca739f92cab220a3059378642e3bd502_430x430.png =150x) (非商务合作**勿扰**,此处**非**技术支持)