# 2019-MPSL Lab3
> 2019 Fall Microprocessor System Lab
> Lab3 STM32 GPIO System
> [name=0516009 吳宗達、0616015 劉姿利]
## What to Do
- 了解 `GPIO` 的基本設定以及電路接法
- 了解 Active Low 電路的實現方法
## How to Do
### LED Pattern Displayer
```cpp=
.syntax unified
.cpu cortex-m4
.thumb
.data
leds: .byte 0
.text
.global main
.equ RCC_AHB2ENR, 0x4002104C
.equ GPIOA_MODER, 0x48000000
.equ GPIOB_MODER, 0x48000400
.equ GPIOC_MODER, 0x48000800
.equ GPIOA_ODR, 0x48000014
.equ GPIOB_ODR, 0x48000414
.equ GPIOC_IDR, 0x48000810
.equ GPIOC_PUPDR, 0x4800080C
main:
bl GPIO_init
ldr R4, =leds
movs R3, #1
Loop:
bl DisplayLED
bl Delay
b Loop
GPIO_init:
ldr R0, =RCC_AHB2ENR
mov R1, #0x6
str R1, [R0]
ldr R0, =GPIOB_MODER
movs R1, #0x1540
ldr R2, [R0]
and R2, #0xFFFFC03F
orrs R2, R2, R1
str R2, [R0]
ldr R0, =GPIOC_MODER
//movs R1, #0x4000000
ldr R2, [R0]
and R2, #0xF3FFFFFF
//orrs R2, R2, R1
str R2, [R0]
ldr R0, =GPIOC_PUPDR
movs R1, #0x4000000
ldr R2, [R0]
and R2, #0xF3FFFFFF
orrs R2, R2, R1
str R2, [R0]
ldr R0, =GPIOB_ODR
ldr R5, =GPIOC_IDR
mov R7, #0
mov R8, #1
//ldr R0, =GPIOB_ODR
//movs R1, #0x78
//strh R1, [R0]
bx LR
// R3: state
// R1 ldrb ??
DisplayLED:
mov R2, #0x3
ldrb R1, [R4] // R1 = leds
lsl R2, R1
lsr R2, #1
lsl R2, #3
mvn R2, R2
strh R2, [R0]
cmp R3, #1
ite eq
addeq R1, #1
subne R1, #1
strb R1, [R4]
cmp R1, #0
it eq
moveq R3, #1
cmp R1, #4
it eq
moveq R3, #2
bx LR
// R5: idr_address
// R6: input data
// R7: count
// R8: state
// R9: -1
Delay:
mov R2, #(1<<18)
L:
ldr R6, [R5]
asr R6, #13
and R6, #1
cmp R6, #0
beq Zero
One:
cmp R7, #(1<<13)
mov R7, #0
bgt Trigger
blt NotChange
Zero:
add R7, #1
b NotChange
Trigger:
eor R8, #1
b NotChange
NotChange:
cmp R8, #0
it ne
subne R2, #1
cmp R2, #0
bne L
bx LR
```
#### GPIO Configuration - Enable `GPIOB/C` Through `AHB2`

想要 Enable `GPIOB/C` 需要將 Register `RCC_AHB2ENR` 上的 bit1 和 bit2 的位置設為 1
```cpp
ldr R0, =RCC_AHB2ENR
mov R1, #0x6
str R1, [R0]
```
#### GPIO Configuration - `GPIOB` Mode
想要將 `PB3`、`PB4`、`PB5`、`PB6` 設定為 Output Mode 需要去改 Register `GPIOB_Moder`。
每個 Port 會被分到 2 個 bit
- `00`: Input mode
- `01`: General purpose output mode
- `10`: Alternate function mode
- `11`: Analog mode (reset state)
所以要將 Register `GPIOB_Moder` 設定成以下的樣子。
```cpp
// GPIOB_MODER
Port 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Mode x x x x x x x x x x x x x x x x x x 0 1 0 1 0 1 0 1 x x x x x x
```
實作上我們使用了兩個 Masks 還有 bitwise operations。
```cpp
ldr R0, =GPIOB_MODER
movs R1, #0x1540 // Mask1
ldr R2, [R0]
and R2, #0xFFFFC03F // Mask2
orrs R2, R2, R1
str R2, [R0]
```
#### GPIO Configuration - `GPIOC` Mode and Pull Up Setting
同 `GPIOB` Mode,不過這次是要將 `PC13` 設定為 Input Mode
```cpp
// GPIOC_MODER
Port 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Mode x x x x 0 0 x x x x x x x x x x x x x x x x x x x x x x x x x x
```
- Set Pull Up
因為 spec 中有提到要將 User Button 設為 Pull Up,
所以還要改動 Register `GPIOC_PUPDR`
```cpp
ldr R0, =GPIOC_PUPDR
movs R1, #0x4000000
ldr R2, [R0]
and R2, #0xF3FFFFFF
orrs R2, R2, R1
str R2, [R0]
```
#### `GPIOB` Output Data (Turn On/Off the LED)
輸出 Port 現在要輸出什麼電位是從 Register `GPIOB_ODR` 靠近低位的 half word 控制。
如果要讓 LED 全亮就要將`GPIOB_ODR` bit3 ~ bit6 都設為 1。
```cpp
// GPIOB_ODR (half word)
Port 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Data x x x x x x x x x 1 1 1 1 x x x
```
在實作中我們將想要設定的 pattern 存在 `R2` 中,再 `strh` 到 `GPIOB_ODR` 裡面。
```cpp
ldr R0, =GPIOB_ODR
// do something to change LED pattern
strh R2, [R0]
```
#### `GPIOC` Input Data (User Button)
User Button,也就是 `PC13` 讀到的東西會存進 Register `GPIOC_IDR` 的 bit13 中。
```cpp
ldr R5, =GPIOC_IDR
ldr R6, [R5]
asr R6, #13
and R6, #1
```
- Solution to Button Bounce Problem
- 我們用一個 Register `R7` 去紀錄目前連續看到幾個 0(User Button 被按下去的狀態)
- 在看到 1 的時候去看 `R7` 的值是多少,如果超過 $2^{13}$ 的話才切換 LED 跑馬燈的狀態
#### Hardware Layout

#### Active Low 硬體接線

- 紅:`PB3`
- 橘:`PB4`
- 黃:`PB5`
- 綠:`PB6`
- 棕:`3V3`
其中棕色的線不是接在 `GND` 而是 `3V3` 的高電位,代表 `PB`s 要輸出低電位才能產生電位差讓燈泡亮起來。
### Combination Lock
```cpp=
.syntax unified
.cpu cortex-m4
.thumb
.data
.text
.global main
.equ RCC_AHB2ENR, 0x4002104C
.equ GPIOB_MODER, 0x48000400
.equ GPIOC_MODER, 0x48000800
.equ GPIOA_ODR, 0x48000014
.equ GPIOB_ODR, 0x48000414
.equ GPIOB_IDR, 0x48000410
.equ GPIOC_IDR, 0x48000810
.equ GPIOC_PUPDR, 0x4800080C
.equ password, 0xC
main:
bl GPIO_init
Start:
mov R7, #0
bl Read_Button
bl Check_Password
//ldr R0, =GPIOB_IDR
//LL:
// ldr R1, [R0]
// b LL
GPIO_init:
ldr R0, =RCC_AHB2ENR
mov R1, #0x6
str R1, [R0]
ldr R0, =GPIOB_MODER
movs R1, #0x400
ldr R2, [R0]
// 0xFFFFF3C0
//and R2, #0xFFFFF3FF // 0xF3FFFC00
//and R2, #0xFFFFFFC0
mov R2, #0
orrs R2, R2, R1
str R2, [R0]
ldr R0, =GPIOC_MODER
ldr R2, [R0]
and R2, #0xF3FFFFFF
str R2, [R0]
ldr R0, =GPIOC_PUPDR
movs R1, #0x4000000
ldr R2, [R0]
and R2, #0xF3FFFFFF
orrs R2, R2, R1
str R2, [R0]
mov R7, #0
ldr R5, =GPIOC_IDR
bx LR
Read_Button:
ldr R6, [R5]
asr R6, #13
and R6, #1
cmp R6, #0
beq Zero
One:
cmp R7, #(1<<13)
mov R7, #0
bgt Trigger
b Read_Button
Zero:
add R7, #1
b Read_Button
Trigger:
bx LR
Check_Password:
ldr R0, =GPIOB_IDR
ldr R6, [R0]
mov R7, #0
// 1, 2
mov R8, R6
and R8, #6
asr R8, #1
eor R7, R8
// 11, 12
mov R8, R6
and R8, #6144
asr R8, #9
eor R7, R8
eor R7, #password
cmp R7, #15
bne Wrong
bl light
bl light
Wrong:
bl light
b Start
light:
ldr R0, =GPIOB_ODR
// turn on
ldr R2, [R0]
orr R2, #(1<<5)
strh R2, [R0]
mov R7, #(1<<18)
LoopOn:
sub R7, #1
cmp R7, #0
bne LoopOn
// turn off
ldr R2, [R0]
and R2, #0xFFFFFFDF
strh R2, [R0]
mov R7, #(1<<18)
LoopOff:
sub R7, #1
cmp R7, #0
bne LoopOff
bx LR
L:
b L
```
### Step 1 GPIO_init
同時用到 GPIOB 與 GPIOC 所以先將 register RCC_AHB2ENR 上的 bit1 和 bit2 的位置設為 1
```c++
ldr R0, =RCC_AHB2ENR
mov R1, #0x6
str R1, [R0]
```
- GPIOB
- 用 PB5 進行輸出
- PB1, PB2, PB11, PB22 用來當密碼輸入
```
ldr R0, =GPIOB_MODER
movs R1, #0x400
ldr R2, [R0]
mov R2, #0
orrs R2, R2, R1
str R2, [R0]
```
- GPIOC
- 只有用到 PC13 的按鈕當作 input
```
ldr R0, =GPIOC_MODER
ldr R2, [R0]
and R2, #0xF3FFFFFF
str R2, [R0]
```
### Step 2 Read_Button
與前一題相同,為了避免 Buttom Bounce Problem,我們會用一個 counter 紀錄目前連續讀到幾個 0 (按鈕按下),當發現讀入的變成 1 時 counter 必須要大於 $2^13$ 我們才會當作有按下,這樣就可以解決 Buttom Bounce Problem
當我們確定的按鈕被按了一下,進入 `Step 3`
### Step 3 Read & Check password
將 PB1, PB2, PB11, PB12 的 bit 轉成整數整數表示的 password
```
// 1, 2
mov R8, R6
and R8, #6
asr R8, #1
eor R7, R8
// 11, 12
mov R8, R6
and R8, #6144
asr R8, #9
eor R7, R8
```
正確就執行亮燈三次,否則略過兩次
```
eor R7, #password
cmp R7, #15
bne Wrong
bl light
bl light
Wrong:
bl light
b Start
```
### Active low
一開始我們是做成 Active high, 直接用電路有通或是沒通來當作 1/0。
為了改成 Active low 我們先實驗了
1. 將 5v 接到 PB1, 這時顯示的是 1
2. 並聯另一個線路走到 GND, 這時 PB1 會顯示 0
上面這個效果也就是我們要的 Active low
藉著就是接上 DIP 開關, 讓開關打開了時候可以產生一條並聯走到 GND 的線路, 沒打開的時候會直接走到 PBx, 也就是上面的實驗, 接著做四個一樣的東西分別接到 PB1,2,11,12 就完成了/
### 線路本尊




## Feedback
第一次寫 code 直接控制到實際會動的機器覺得很新鮮,也複習到個國中的基本電路的觀(?
只用了短短幾行的 code 就可以控制 GPIO,讓人體會到原來寫 code 距離實體的機器其實沒有離很遠
另外寫組語愈來愈有種在寫自動機的感覺, 寫多個條件變得好麻煩, 現在覺得很感激 compiler。
還有學到不要用 LED 充當杜邦線
在 debug 的過程中, 學到不要一次把全部都拼出來, 這樣很難找到錯誤, 這時就可以用到資工人 unit test 的精神, 一步一步確定每一個環節沒有問題, 看似浪費時間實際上減少了很多 debug 找問題的時間
## Reference
- [UM1724 STM32 NUCLEO User Manual](https://hackmd.io/@REIq7VP9RZq5oslp3_iPcA/B1TbUB5BV/https%3A%2F%2Fstorage.googleapis.com%2F2019_mpsl%2Freference%2FUM1724-STM32-NUCLEO-User-Manual.pdf) p35 Figure 24. NUCLEO-L476RG
- 板子上的接口名稱
- How to turn on single LED (Lecture Slide 005-MCSL-GPIO.pdf p31)
- 控制 PA5 的 Sample Code (PA5就是GPIOA的第5個bit在控制的)
- [STM32L476](https://www.st.com/content/ccc/resource/technical/document/reference_manual/02/35/09/0c/4f/f7/40/03/DM00083560.pdf/files/DM00083560.pdf/jcr:content/translations/en.DM00083560.pdf)
- AHB2
- ==p77== Table1 各種GPIO的address
- ==p242== 6.4.11 AHB2 peripheral reset register (RCC_AHB2RSTR)
- AHB2 設定
- GPIO
- ==p311== 8.4.13 GPIO register map
- ==p303== 8.4.1 GPIO port mode register (GPIOx_MODER) (x =A to I)
- 設定某個 port 是 input 還是 output
- ==p305== 8.4.6 GPIO port output data register (GPIOx_ODR) (x = A to I)
- port 的開關
- [成大資工wiki](http://wiki.csie.ncku.edu.tw/embedded/GPIO)