---
# System prepended metadata

title: 2019-MPSL Lab4

---

# 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 的空間

![Minion](https://i.imgur.com/buZHz87.png =400x)

- `Digit i`: 顯示器上第 i 個 7-Seg 的
    - number(Decode Mode)
      ![Minion](https://i.imgur.com/sWysj1j.png =300x)
    - pattern(No-Decode Mode)
      ![Minion](https://i.imgur.com/gi4lD8M.png =300x)
- `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` 就完成了。

#### 硬體接法

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

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