>[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燈的原理圖如下圖所示。

>[danger] 如您因缺少硬件原理相關知識導致未能看懂本圖,需先補充相關知識
<br/>
## **架構設計**
在學習使用LED燈之前,先簡單講解一下嵌入式系統的常用架構設計,這對以后開發嵌入式軟件有莫大的好處,希望讀者好好學習。
###
常見的小型嵌入式系統可以劃分為硬件、標準庫、HAL/BSP和上層應用,如圖所示。

* 硬件是指主控芯片以及相關外圍設備等,例如本課程配套的開發板
* 標準庫是指有主控芯片配套的驅動程序庫,例如本課程用到的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的抽象定義如圖所示。

<br/>
## **實現LED HAL API**
學習完架構設計思想后,接下來實現LED應用的HAL。
<br/>
#### **準備工作**
在上節課移植好的模板工程的根目錄創建hal\_led.h和hal\_led.c文件,然后把hal\_led.c文件添加進工程中,如圖所示。

<br/>
由于hal_led.c文件將會使用到ST標準庫,因此也需要把相關的標準庫文件添加進工程中。
1. 選擇StdPeriph_Driver,然后點擊Add Files...

###
2. 在本模板工程中,依次找到文件夾Libraries、STM32F0xx\_StdPeriph\_Driver、src,然后添加 stm32f0xx_gpio.c 和 stm32f0xx_rcc.c文件

###
3. 添加完成后,點擊 "Close"
<br/>
由于配套開發板的資源與ST默認的有所不同,因此修改文件 system_stm32f0xx.c 文件中的 SetSysClock 函數,如下圖所示。

###
修改后的代碼如下:
```
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文件,如圖所示。

###
2.把該Hex文件燒錄到配套的開發板中,可以看到LED不斷地閃爍
<br/>
<br/>
## **商務合作**
如有以下需求,可掃碼添加管理員好友,注明“**商務合作**”
* 項目定制開發,技術范圍:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等;
* 入駐平臺,成為講師;
* 接項目賺外快;
* 善學坊官網:[www.sxf-iot.com](https://www.sxf-iot.com/)

(非商務合作**勿擾**,此處**非**技術支持)
- 課程介紹
- 配套資源下載
- 配套開發套件簡介
- 簡介
- 硬件組成 & 技術參數
- 電路原理圖 & PCB圖
- 撥碼開關使用說明
- 第一部分:無線通信 開發指南
- 1.1.1 NB-IoT:技術簡介
- 1.1.2 NB:CH34x USB轉串口驅動安裝
- 1.1.3 NB:AT 指令開發與測試
- 1.1.4 NB:基礎指令集簡介
- 1.1.5 NB:云端服務器
- 1.1.5.1 PuTTY 簡介與安裝
- 1.1.5.2 登錄云端服務器
- 1.1.6 NB:移遠官方工具簡介
- 1.1.7 NB:使用UDP協議與云端服務器通信
- 1.1.8 NB:使用TCP協議與云端服務器通信
- 1.1.9 NB:使用MQTT協議與云端服務器通信
- 進階課程
- 第二部分:STM32 開發指南
- 2.1 搭建開發環境
- 2.1.1 Keil MDK 簡介與安裝
- 2.1.2 STM32 Pack 簡介與安裝
- 2.1.3 CH34x 驅動簡介與安裝
- 2.1.4 其他開發工具
- 2.2 STM32 開發基礎
- 2.2.1 新建工程
- 2.2.2 實現第1個程序
- 2.2.3 Hex 文件燒錄詳解
- 2.3 移植官方標準工程模板
- 2.4 GPIO實驗——LED燈
- 2.5 系統延時應用
- 2.6 GPIO實驗——按鍵
- 2.7 GPIO中斷實驗——按鍵觸發
- 2.8 使用定時器TIM3
- 2.9 串口通信實驗
- 2.10 ADC 實驗
- 2.11 OLED顯示器實驗
- 2.12 SDK 設計思想
- 2.13 SDK 架構解析
- 2.14 多任務應用
- 2.15 輸入型任務:按鍵輸入
- 2.16 輸入型任務:串口接收
- 課外篇:項目實戰
- 基于STM32+NB-IoT的溫濕度采集
- 系統簡介
- 系統搭建
- 系統詳解
- 1.代碼編譯與架構說明
- 2.DHT11溫濕度傳感器
- 3. 數據通信任務說明
- 版權聲明與免責聲明