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