---
# System prepended metadata

title: 2019-MPSL Lab3

---

# 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`

![](https://i.imgur.com/Ltpm4YE.png)

想要 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

![](https://i.imgur.com/6E9SwUA.png)


#### Active Low 硬體接線

![](https://i.imgur.com/gho99EX.jpg)

- 紅：`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 就完成了/

### 線路本尊
![](https://i.imgur.com/9TmMStR.jpg)
![](https://i.imgur.com/zNUHi6x.jpg)
![](https://i.imgur.com/z6mrLCt.jpg)
![](https://i.imgur.com/03NmXxd.jpg)


## 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)