# 2019-MPSL Lab4
> 2019 Fall Microprocessor System Lab
> Lab4 7-Seg LED
> [name=0516009 吳宗達、0616015 劉姿利]
## What to Do
- 了解 `MAX7219` 七段顯示器的設定
## How to Do
### MAX7219 7-Seg Configuration
#### Registers
14 個 registers,每個有 8 bits 的空間

- `Digit i`: 顯示器上第 i 個 7-Seg 的
- number(Decode Mode)

- pattern(No-Decode Mode)

- `Decode Mode`: 第 i 個 bit 代表 Digit i 是否要用 Decode Mode
- `Intensity`: 亮度
- `Scan Limit`: data = i 代表要顯示 Digit 0~i
- `Shutdown`: 當 data 為 0 時關機
#### Set Command
```
bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|-- address --|
|-------- data ---------|
```
將 `address` 上的值設為 `data`。
#### Pass Command into MAX7219 7-Seg
- Three inputs: `DIN`, `CLK`, `CS`
- Steps:
- 每次 `CLK` 由低電位至高電位時,讀入 `DIN` 的值到 buffer 的最低位上並往左 shift
- 等到 buffer 上的值是想要的 Command 後,讓 `CS` 由低電位變成高電位,就會改動目標 Register 的值了
實作的部分我們將 `R0`、`R1` 當作 parameters,分別存了目標 address 和 目標 data,當執行以下 `pass_data` 的時候就會將 data 傳到 7-Seg 中。
```cpp
pass_data:
push {R0, R1, R2, R3, R4, R5, LR}
// R0: address
// R1: data
lsl R0, #8
add R0, R1
// R2: counter
// R3: GPIOB_ODR address
// R4: GPIOB_ODR data
// R5: mask
mov R2, #16
ldr R3, =GPIOB_ODR
pass_data_loop:
// data
mov R5, #1
sub R2, #1
lsl R5, R2
tst R0, R5
ldrh R4, [R3]
itee ne
orrne R4, #(1<<4)
ldreq R5, =mask
andeq R4, R5
strh R4, [R3]
// clock down and up
eor R4, #(1<<3)
strh R4, [R3]
eor R4, #(1<<3)
strh R4, [R3]
cmp R2, #0
bne pass_data_loop
eor R4, #(1<<5)
strh R4, [R3]
eor R4, #(1<<5)
strh R4, [R3]
pop {R0, R1, R2, R3, R4, R5, PC}
```
#### MAX7219 Initialization
```cpp
MAX7219_init:
push {LR}
mov R0, #MAX7219_DisplayTest
mov R1, #1
bl pass_data
mov R0, #MAX7219_DisplayTest
mov R1, #0
bl pass_data
mov R0, #MAX7219_ScanLimit
mov R1, #0
bl pass_data
mov R0, #MAX7219_DecodeMode
mov R1, #0
bl pass_data
mov R0, #MAX7219_Shutdown
mov R1, #1
bl pass_data
pop {PC}
```
因為前面已經寫好需要的 `pass_data` function 了,操作的時候只要把 `R0`、`R1` 設定好再 `bl pass_data` 就完成了。
#### 硬體接法

- `VCC`: `5V`
- `GND`: `GND`
- `DIN`: `PB4`
- `CS`: `PB5`
- `CLK`: `PB3`
### Code for Lab 4-1
> 由 0 顯示到 F
```cpp=
.syntax unified
.cpu cortex-m4
.thumb
.data
arr: .byte 0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B, 0x77, 0x1F, 0x4E, 0x3D, 0x4F, 0x47
.text
.global main
.equ RCC_AHB2ENR, 0x4002104C
.equ GPIOB_MODER, 0x48000400
.equ GPIOB_ODR, 0x48000414
.equ MAX7219_Digit0, 0x1
.equ MAX7219_DecodeMode, 0x9
.equ MAX7219_ScanLimit, 0xB
.equ MAX7219_DisplayTest, 0xF
.equ MAX7219_Shutdown, 0xC
.equ mask, 0xFFEF
// PB3: CLK
// PB4: DIN
// PB5: CS
main:
bl GPIO_init
bl MAX7219_init
ldr R0, =MAX7219_Digit0
ldr R3, =arr
mov R2, #0
display_loop:
// R0: digit0
// R1: display pattern
// R2: display number
// R3: pattern address
ldrb R1, [R3, R2]
bl pass_data
add R2, #1
cmp R2, #16
it eq
moveq R2, #0
bl delay
b display_loop
pass_data:
push {R0, R1, R2, R3, R4, R5, LR}
// R0: address
// R1: data
lsl R0, #8
add R0, R1
// R2: counter
// R3: GPIOB_ODR address
// R4: GPIOB_ODR data
// R5: mask
mov R2, #16
ldr R3, =GPIOB_ODR
pass_data_loop:
// data
mov R5, #1
sub R2, #1
lsl R5, R2
tst R0, R5
ldrh R4, [R3]
itee ne
orrne R4, #(1<<4)
ldreq R5, =mask
andeq R4, R5
strh R4, [R3]
// clock down and up
eor R4, #(1<<3)
strh R4, [R3]
eor R4, #(1<<3)
strh R4, [R3]
cmp R2, #0
bne pass_data_loop
eor R4, #(1<<5)
strh R4, [R3]
eor R4, #(1<<5)
strh R4, [R3]
pop {R0, R1, R2, R3, R4, R5, PC}
MAX7219_init:
push {LR}
mov R0, #MAX7219_DisplayTest
mov R1, #1
bl pass_data
mov R0, #MAX7219_DisplayTest
mov R1, #0
bl pass_data
mov R0, #MAX7219_ScanLimit
mov R1, #0
bl pass_data
mov R0, #MAX7219_DecodeMode
mov R1, #0
bl pass_data
mov R0, #MAX7219_Shutdown
mov R1, #1
bl pass_data
pop {PC}
GPIO_init:
push {R0, R1, R2, LR}
ldr R0, =RCC_AHB2ENR
mov R1, #0x6
str R1, [R0]
ldr R0, =GPIOB_MODER
movs R1, #0x540
ldr R2, [R0]
and R2, #0xFFFFF03F
orrs R2, R2, R1
str R2, [R0]
ldr R0, =GPIOB_ODR
movs R1, #0
strh R1, [R0]
pop {R0, R1, R2, PC}
delay:
push {R1, LR}
mov R1, #(1<<20)
delay_loop:
sub R1, #1
cmp R1, #0
bne delay_loop
pop {R1, PC}
```
### Code for Lab 4-2
> 顯示學號
```cpp=
.syntax unified
.cpu cortex-m4
.thumb
.data
// arr: .byte 0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B, 0x77, 0x1F, 0x4E, 0x3D, 0x4F, 0x47
student_id: .byte 0, 6, 1, 6, 0, 1, 5
// student_id: .byte 1, 2, 3, 4, 5, 6, 7
.text
.global main
.equ RCC_AHB2ENR, 0x4002104C
.equ GPIOB_MODER, 0x48000400
.equ GPIOB_ODR, 0x48000414
.equ MAX7219_Digit0, 0x1
.equ MAX7219_DecodeMode, 0x9
.equ MAX7219_ScanLimit, 0xB
.equ MAX7219_DisplayTest, 0xF
.equ MAX7219_Shutdown, 0xC
.equ mask, 0xFFEF
// PB3: CLK
// PB4: DIN
// PB5: CS
main:
bl GPIO_init
bl MAX7219_init
L:
bl Set_display
b L
Set_display:
// R0: digit0
// R1: display number
// R2: display idx
// R3: pattern address
push {R0, R1, R2, R3, LR}
ldr R3, =student_id
mov R2, #0
Loop:
mov R0, #7
sub R0, R2
ldrb R1, [R3, R2]
bl pass_data
add R2, #1
cmp R2, #7
bne Loop
pop {R0, R1, R2, R3, PC}
pass_data:
push {R0, R1, R2, R3, R4, R5, LR}
// R0: address
// R1: data
lsl R0, #8
add R0, R1
// R2: counter
// R3: GPIOB_ODR address
// R4: GPIOB_ODR data
// R5: mask
mov R2, #16
ldr R3, =GPIOB_ODR
pass_data_loop:
// data
mov R5, #1
sub R2, #1
lsl R5, R2
tst R0, R5
ldrh R4, [R3]
itee ne
orrne R4, #(1<<4)
ldreq R5, =mask
andeq R4, R5
strh R4, [R3]
// clock down and up
eor R4, #(1<<3)
strh R4, [R3]
eor R4, #(1<<3)
strh R4, [R3]
cmp R2, #0
bne pass_data_loop
eor R4, #(1<<5)
strh R4, [R3]
eor R4, #(1<<5)
strh R4, [R3]
pop {R0, R1, R2, R3, R4, R5, PC}
MAX7219_init:
push {LR}
mov R0, #MAX7219_DisplayTest
mov R1, #1
bl pass_data
mov R0, #MAX7219_DisplayTest
mov R1, #0
bl pass_data
mov R0, #MAX7219_ScanLimit
mov R1, #6
bl pass_data
mov R0, #MAX7219_DecodeMode
mov R1, #(1<<8)
sub R1, #1
bl pass_data
mov R0, #MAX7219_Shutdown
mov R1, #1
bl pass_data
pop {PC}
GPIO_init:
push {R0, R1, R2, LR}
ldr R0, =RCC_AHB2ENR
mov R1, #0x6
str R1, [R0]
ldr R0, =GPIOB_MODER
movs R1, #0x540
ldr R2, [R0]
and R2, #0xFFFFF03F
orrs R2, R2, R1
str R2, [R0]
ldr R0, =GPIOB_ODR
movs R1, #0
strh R1, [R0]
pop {R0, R1, R2, PC}
```
### Code for Lab 4-3
> 按 user button 顯示下一位的費波那契數列,overflow 顯示 -1,長按 reset
```cpp=
.syntax unified
.cpu cortex-m4
.thumb
.data
// arr: .byte 0x7E, 0x30, 0x6D, 0x79, 0x33, 0x5B, 0x5F, 0x70, 0x7F, 0x7B, 0x77, 0x1F, 0x4E, 0x3D, 0x4F, 0x47
student_id: .byte 0, 6, 1, 6, 0, 1, 5
// student_id: .byte 1, 2, 3, 4, 5, 6, 7
.text
.global main
.equ RCC_AHB2ENR, 0x4002104C
.equ GPIOB_MODER, 0x48000400
.equ GPIOC_MODER, 0x48000800
.equ GPIOB_ODR, 0x48000414
.equ GPIOC_IDR, 0x48000810
.equ GPIOC_PUPDR, 0x4800080C
.equ MAX7219_Digit0, 0x1
.equ MAX7219_DecodeMode, 0x9
.equ MAX7219_ScanLimit, 0xB
.equ MAX7219_DisplayTest, 0xF
.equ MAX7219_Shutdown, 0xC
.equ mask, 0xFFEF
// PB3: CLK
// PB4: DIN
// PB5: CS
main:
bl GPIO_init
bl MAX7219_init
// R8: start state
mov R8, #1
bl Start
L:
b L
Start:
mov R2, #0
bl Display_number
bl Read_Button
mov R8, #1
bl Fibo
bl Overflow
b Start
Overflow:
mov R2, #-1
bl Display_number
Overflow_loop:
bl Read_Button
b Overflow_loop
Read_Button:
push {R5, R6, R7}
ldr R5, =GPIOC_IDR
mov R7, #0
Read_Button_loop:
ldr R6, [R5]
asr R6, #13
and R6, #1
cmp R6, #0
beq Zero
One:
cmp R7, #(1<<13)
bgt Trigger
mov R7, #0
b NotChange
Zero:
cmp R7, #(1<<18)
bgt Reset
add R7, #1
b NotChange
Trigger:
cmp R8, #0
mov R8, #1
mov R7, #0
beq NotChange
pop {R5, R6, R7}
bx LR
Reset:
mov R8, #0
pop {R5, R6, R7}
b Start
NotChange:
b Read_Button_loop
Fibo:
// R3: f(n-2)
// R4: f(n-1)
// R5: f(n)
push {R2, R3, R4, R5}
mov R3, #0
mov R4, #1
mov R5, #(1<<26)
Fibo_L:
add R2, R3, R4
cmp R2, R5
bge End_Fibo
bl Display_number
mov R4, R3
mov R3, R2
bl Read_Button
b Fibo_L
End_Fibo:
pop {R2, R3, R4, R5}
b Overflow
bx LR
Display_number:
// R2: The number
push {R0, R1, R2, R3, R4, LR}
// R4 = R2, R2 = |R2|
mov R4, R2
cmp R2, #0
itt lt
sublt R2, #1
mvnlt R2, R2
// R0: pos
// R1: value
// R3: ten
// R4: original R2
mov R0, #1
mov R3, #10
Display_number_loop:
sdiv R1, R2, R3
mls R1, R1, R3, R2
bl pass_data
sdiv R2, R3
add R0, #1
cmp R2, #0
bne Display_number_loop
cmp R4, #0
bge End_display_number_loop
mov R1, #10
bl pass_data
add R0, #1
End_display_number_loop:
sub R0, #2
mov R1, R0
mov R0, #MAX7219_ScanLimit
bl pass_data
pop {R0, R1, R2, R3, R4, PC}
pass_data:
push {R0, R1, R2, R3, R4, R5, LR}
// R0: address
// R1: data
lsl R0, #8
add R0, R1
// R2: counter
// R3: GPIOB_ODR address
// R4: GPIOB_ODR data
// R5: mask
mov R2, #16
ldr R3, =GPIOB_ODR
pass_data_loop:
// data
mov R5, #1
sub R2, #1
lsl R5, R2
tst R0, R5
ldrh R4, [R3]
itee ne
orrne R4, #(1<<4)
ldreq R5, =mask
andeq R4, R5
strh R4, [R3]
// clock down and up
eor R4, #(1<<3)
strh R4, [R3]
eor R4, #(1<<3)
strh R4, [R3]
cmp R2, #0
bne pass_data_loop
eor R4, #(1<<5)
strh R4, [R3]
eor R4, #(1<<5)
strh R4, [R3]
pop {R0, R1, R2, R3, R4, R5, PC}
MAX7219_init:
push {LR}
mov R0, #MAX7219_DisplayTest
mov R1, #1
bl pass_data
mov R0, #MAX7219_DisplayTest
mov R1, #0
bl pass_data
mov R0, #MAX7219_ScanLimit
mov R1, #6
bl pass_data
mov R0, #MAX7219_DecodeMode
mov R1, #(1<<8)
sub R1, #1
bl pass_data
mov R0, #MAX7219_Shutdown
mov R1, #1
bl pass_data
pop {PC}
GPIO_init:
push {R0, R1, R2, LR}
ldr R0, =RCC_AHB2ENR
mov R1, #0x6
str R1, [R0]
ldr R0, =GPIOB_MODER
movs R1, #0x540
ldr R2, [R0]
and R2, #0xFFFFF03F
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
movs R1, #0
strh R1, [R0]
pop {R0, R1, R2, PC}
```
### Feedback
在這次作業之前沒有想過一個完整的指令原來是一個 bit 一個 bit shift 進去,透過 `CLK` 和 `CS` 決定 bits 和完整指令傳進去的時間點。
再來就是這次嘗試著在所有 functions 前後都加 push 跟 pop,之前覺得是 recursion 才需要 push 跟 pop,可是嘗試之後發現所有都加更可以有效管理全域跟區域的變數,registers 感覺也比較夠用了。
## Reference
- [使用 MAX7219 驅動七段顯示器](https://docs.labs.mediatek.com/resource/linkit7697-arduino/zh_tw/tutorial/driving-7-segment-displays-with-max7219)