iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 8
1
IoT

熟悉Arduino的Maker必看!30天帶你快速入門基於STM32嵌入式開發~系列 第 8

[Day 8]-【STM32系列】淺入淺出之 General Purpose Input/Output 介紹 (下)

上篇針對GPIO做了詳細的說明,沒看過的先去[Day 7]-【STM32系列】淺入淺出之 General Purpose Input/Output 介紹 (上)再來

今天要來實作在CubeMX上我們是如何設置GPIO PIN腳以及設定開漏(open-drain,漏極開路)和推輓(push-pull)等等功能,還有修改暫存器的數值,函數的調用等等。那廢話不多說直接開始吧。

需要準備的材料有

  • STM32 開發板一枚
  • usb mini 線材一條
  • 麵包板一塊(我使用830孔)
  • LED 發光二極體至少8(最好準備多一點)
  • 100Ω 限流電阻至少8(跟LED數量一樣)
  • 跳線、排線、排針連接 Nucleo-64 boards

搭拉!

因為控制IO搭配LED觀察HIGH、LOW比較方便
所以我用麵包板接了一排LED出來
今日目標就是它!
https://media.giphy.com/media/asNZqj4rVGguBgekJt/giphy.gif

附上電路圖

圖中沒有標示出來,但兩邊的接地端(Gnd)要接在一起哦,
所以最下面的GND還要拉一條線到STM32 Board 的 GND,
驅動LED時接地才有電流流回形成迴路。
https://ithelp.ithome.com.tw/upload/images/20201020/20120093wKvDAg6ged.png

接著將 GPIOB 的 0 ~ 15 pin,與 LED 對接,
當pin腳輸出HIGH,LED為ON當pin腳輸出LOW,LED為OFF。
https://ithelp.ithome.com.tw/upload/images/20201020/20120093ldFyxC5w1E.jpg
對!你沒聽錯!你要一條一條線拉出來排成 PB0 ~ PB15
光是查接腳圖就快死了,誰叫這顆接腳也不是按照順序啊
重點是這拉完之後很亂很醜啊!!
https://ithelp.ithome.com.tw/upload/images/20201020/20120093X9aYxFctb9.jpg
懶得花錢買點電路板的我於是決定自行土炮了一個
https://ithelp.ithome.com.tw/upload/images/20201020/20120093eLnqMtUEo0.jpg

~~(不喜勿噴XD~~
https://ithelp.ithome.com.tw/upload/images/20201020/20120093SBQbx5BVBW.jpg


:-此系列由STM32CUBEIDE編寫。如果你是使用其他任何的IDE,程式仍然可以運行。但是你必須自行編寫一些項目

Setup

開啟CubeMX並新建專案.
https://ithelp.ithome.com.tw/upload/images/20200920/20120093peI4jkqXSK.png

選擇上次已經加到我的最愛裡的晶片,滑鼠連按兩下LQFP64包裝的晶片,進入設定頁面
https://ithelp.ithome.com.tw/upload/images/20200920/20120093CDJgtEZfTM.png

想要控制GPIO首先要先選擇要使用的IO腳和IO腳將要配置的功能
個別點選PA0~PA7,設置GPIO_Output,並打開外部Clock、debug功能
https://ithelp.ithome.com.tw/upload/images/20200923/20120093f3VmfyBRvc.png

設定MCU速度32MHz
https://ithelp.ithome.com.tw/upload/images/20200920/201200937Ho3hKmwne.png

打開GPIO項,選擇任一Pin腳配置

  • GPIO output level :Low 、 High
  • GPIO mode:Output Push Pull、Output Open Drain
  • GPIO Pull-up/Pull-down:No pull-up and no-down、Pull up、Pull down
  • Maxinum output speed:Low、High
  • User Label:使用者可自訂義tag的部分
    https://ithelp.ithome.com.tw/upload/images/20200923/20120093cRvB7wHSel.png

生成文件
https://ithelp.ithome.com.tw/upload/images/20200920/20120093bgqL2IFcA3.png
1.專案名稱
2.存放路徑
3.用來撰寫的IDE
4.按ok生成並直接open project
https://ithelp.ithome.com.tw/upload/images/20200923/20120093bzwZGynGHy.png

怕大家忘記再提醒一下在https://ithelp.ithome.com.tw/upload/images/20200923/20120093R5eNDKo8nX.png 按右鍵選Add New Item to Group 'Application/User'
選擇 C File
Name 輸入: mycodes.c
Location 選擇 Src
按 Add 按鍵
https://ithelp.ithome.com.tw/upload/images/20200923/201200936EwQi0QUtR.png

接著在main.c定義void mycodes(void);函式
https://ithelp.ithome.com.tw/upload/images/20200923/20120093fXmKbbqu3G.png

main主程式中相同的位置加入mycodes();
當初始化完成就會執行我們的程式
https://ithelp.ithome.com.tw/upload/images/20200923/20120093dhMCPj893x.png

// 複製程式碼
#include "main.h"
#include "stm32l0xx_hal.h"

void mycodes(void){

	while(1){


    }
}

https://ithelp.ithome.com.tw/upload/images/20200923/20120093leBOhzSWhk.png

我們自己的code就寫在這個mycodes.c裡面,這樣寫的好處不僅程式碼方便觀看,也不怕重新生成後因為忘記沒寫在begin end裡面被消失

編譯燒錄吧!
https://ithelp.ithome.com.tw/upload/images/20200923/201200935KnWD3fXIJ.png
0 Error(s), 0 Warning(s)
https://ithelp.ithome.com.tw/upload/images/20200923/20120093XapcDFT1WC.png


HAL_GPIO functions

接下來要說說HAL庫裡提供了甚麼函數供我們使用~

一共有8種方法可以讓我們做使用,
這裡控制LED只介紹兩種~~(好偷懶~~
常用的Write、Toggle


1.) -Write Pin - 控制單一 GPIO Pinout 為 HIGH or LOW

HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)

GPIOx:可以是GPIOA、GPIOB、GPIOC
GPIO_Pin:GPIO_PIN_0、GPIO_PIN_1、GPIO_PIN_3...以此類推,最多看GPIOx支援到多少
PinState:GPIO_PIN_RESET為Low、GPIO_PIN_SET為High

我們將 PB5 為 HIGH 維持 5 秒鐘,LOW 維持 5 秒鐘
這裡需要用到 Delay 函式

void HAL_Delay(__IO uint32_t Delay)
HAL_Delay(x):延遲x毫秒,32位正整數寬

//GPIOB LED(HAL_GPIO_WritePin)
#include "main.h"
#include "stm32l0xx_hal.h"

void mycodes(void){

	while(1){
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);	//PA5 High
		HAL_Delay(500);// Delay 500ms
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);//PA5 Low
		HAL_Delay(500);// Delay 500ms
	}
}

https://media.giphy.com/media/U8avBGg03o633vVRmY/giphy.gif
可以看到PB5 HIGH、LOW的變化在LED上囉!


2.) -Toggle Pin - 切換 GPIO 目前的狀態。如果目前為HIGH則轉為LOW為LOW則轉為HIGH

HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)

GPIOx:可以是GPIOA、GPIOB、GPIOC
GPIO_Pin:GPIO_PIN_0、GPIO_PIN_1、GPIO_PIN_3...以此類推,最多看GPIOx支援到多少

一樣讓 LED 為 HIGH、LOW 間隔500ms

//GPIOB LED閃爍電路(HAL_GPIO_TogglePin)
#include "main.h"
#include "stm32l0xx_hal.h"

void mycodes(void){

	while(1){
		HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);	//PA5 High
		HAL_Delay(500);// Delay 500ms
	}
}

https://media.giphy.com/media/U8avBGg03o633vVRmY/giphy.gif
結果應該是一樣滴!


但是你知道,一條指令就那麼長,有沒有辦法把它縮短呢?
當然有!這裡用一個小技巧,用#define來對函數進行“定義”,舉例來說:

#define 為定義,
定義HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);PB5_H
定義HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);PB5_L

代表以後如果我們需要PB5為HIGH,不用再打一長串的指令,事先定義好就可以使用自己的命名:

#define PB5_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
#define PB5_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);

//GPIOB LED閃爍電路(#define 縮短指令)
#include "main.h"
#include "stm32l0xx_hal.h"

#define PB5_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);
#define PB5_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET);

void mycodes(void){

	while(1){
		PB5_H	//PB5 High
		HAL_Delay(500);// Delay 500ms
		PB5_L	//PB5 Low
		HAL_Delay(500);// Delay 500ms
		
	}
}

https://media.giphy.com/media/U8avBGg03o633vVRmY/giphy.gif


照著個方式把所有的GPIOB 0~15定義出來:

#define PB0_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
#define PB0_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
#define PB1_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
#define PB1_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
#define PB2_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
#define PB2_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
#define PB3_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
#define PB3_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
                                ·
                                ·
                                ·
#define PB15_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
#define PB15_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);

來實際做一些變化~

#include "main.h"
#include "stm32l0xx_hal.h"

#define PB0_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
#define PB0_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
                                ·
                                ·
                                ·
#define PB15_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET);
#define PB15_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET);

void mycodes(void){

	while(1){
		PB0_H;	PB1_H;	PB2_H;	PB3_H;	//PB0 ~ PB3 HIGH
		HAL_Delay(500);									// Delay 500ms
		PB4_H;	PB5_H;	PB6_H;	PB7_H;	//PB4 ~ PB7 HIGH
		HAL_Delay(500);									// Delay 500ms
		PB8_H;	PB9_H;	PB10_H;	PB11_H;	//PB8 ~ PB11 HIGH
		HAL_Delay(500);									// Delay 500ms
		PB12_H;	PB13_H;	PB14_H;	PB15_H;	//PB12 ~ PB15 HIGH
		HAL_Delay(500);									// Delay 500ms
		
		PB0_L;	PB1_L;	PB2_L;	PB3_L;	//PB0 ~ PB3 LOW
		HAL_Delay(500);									// Delay 500ms
		PB4_L;	PB5_L;	PB6_L;	PB7_L;	//PB4 ~ PB7 LOW
		HAL_Delay(500);									// Delay 500ms
		PB8_L;	PB9_L;	PB10_L;	PB11_L;	//PB8 ~ PB11 LOW
		HAL_Delay(500);									// Delay 500ms
		PB12_L;	PB13_L;	PB14_L;	PB15_L;	//PB12 ~ PB15 LOW
		HAL_Delay(500);									// Delay 500ms
	}
}

https://media.giphy.com/media/OeRjNPHUGp9cPTRNAM/giphy.gif

看程式碼,我讓LED先亮四顆PB0~PB3、接著再亮四顆PB4~PB7

接續亮下去,然後再暗回來,差不多做到這裡,大家應該有些概念了吧!


GPIO Register

可是你說,每次控制IO都要先#define會不會太麻煩
整體控制來很沒有效率,一整個是土法煉鋼,有沒有更好的辦法呢?
當然有!這樣說當然就是有啦~XD

還記得昨天我們說過,GPIOx的每一組暫存器都是32位寬,每個GPIO底下都有自己的多組暫存器來記錄當前的值,例如IDR、ODR、BSRR、BRR...等等。用法也非常簡單如下:

//GPIOB LED(控制暫存器)
#include "main.h"
#include "stm32l0xx_hal.h"

void mycodes(void){

	while(1){
		GPIOB->BSRR = (1<<5);
        HAL_Delay(500);
		GPIOB->BRR = (1<<5);
        HAL_Delay(500);
	}
}

GPIOB->BSRR:控制GPIOB的BSRR暫存器,BSRR可以針對某一個pin腳改動它的值為High
GPIOB->BRR:控制GPIOB的BRR暫存器,BRR可以針對某一個pin腳改動它的值為Low

(1<<5):1左移5位,所以是從PB0 --> PB5,PB5為High

這裡要注意一點,BRR、BSRR只會針對單一pin腳更動它的HIGH、LOW,並不會影響其他未被配置的值
甚麼意思?假如今天你塞了GPIOB->BSRR = 5,以二進制來說它是0101,所以PB0和PB2會被點亮,其餘IO不論原先是HIGH或LOW會保持原先的狀態,並不會被影響。這種特性在BRR上也是如此。

//GPIOB LED(控制暫存器)
#include "main.h"
#include "stm32l0xx_hal.h"

void mycodes(void){
int i;
	while(1){
        for(i=0;i<16;i++){
		GPIOB->BSRR = (1<<i);
        HAL_Delay(50);
        }
        
        for(i=0;i<16;i++){
		GPIOB->BRR = (1<<i);
        HAL_Delay(50);
        }
        
	}
}

https://media.giphy.com/media/1eoTx9ET46Hm1HB1kM/giphy.gif

我這裡寫了一個for迴圈,隨著變數 i 的增加,GPIO會逐一被點亮,點滅。

但是!使用GPIOB -> ODR 將會變動整個暫存器的值~

//GPIOB LED(控制暫存器)
#include "main.h"
#include "stm32l0xx_hal.h"

void mycodes(void){
int i;
	while(1){
        for(i=0;i<=15;i++)
        {
            GPIOB -> ODR = (1<<i);
            HAL_Delay(50);
        }
        for(b=14;b>=1;b--)
        {
        GPIOB -> ODR = (1<<i);
        HAL_Delay(50);
        }
	}
}

一樣的方法,只是換成丟給 ODR ,像這樣亮過去亮回來,會更動暫存器整個的值
https://media.giphy.com/media/asNZqj4rVGguBgekJt/giphy.gif

以上這些是基本GPIO使用LED的方法與技巧,
剛開始就這樣會不會太難XDD,
可以試試看用不同的方式會有不同的結果
請熟悉以上這些方法,之後會有更多應用哦!

本文先到這裡,下台一鞠躬!

相關連結


結語

實在是想不到甚麼文案啊...


上一篇
[Day 7]-【STM32系列】淺入淺出之 General Purpose Input/Output 介紹 (上)
下一篇
[Day 9]-【STM32系列】GPIO應用篇- 7 segment led display
系列文
熟悉Arduino的Maker必看!30天帶你快速入門基於STM32嵌入式開發~30

尚未有邦友留言

立即登入留言