# 2019-MPSL Lab5 > 2019 Fall Microprocessor System Lab > Lab5 STM32 Keypad Scanning > [name=0516009 吳宗達、0616015 劉姿利] ## What to Do - Knowing how to c program on the STM32 - Knowing how to use STM32 keypad scanning ## How to Do ### Max7219 Displayer > Modify GPIO_init(), max7219_init() and max7219_send() finished in Lab4 to make it callable by C. Add a C file to complete code below. Finally, display your student ID on 7-Seg LED. ```asm= .syntax unified .cpu cortex-m4 .thumb .data student_id: .byte 0, 6, 1, 6, 0, 1, 5 .text .global GPIO_init .global max7219_init .global max7219_send .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 max7219_send: 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 max7219_send_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 max7219_send_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 max7219_send mov R0, #MAX7219_DisplayTest mov R1, #0 bl max7219_send mov R0, #MAX7219_ScanLimit mov R1, #6 bl max7219_send mov R0, #MAX7219_DecodeMode mov R1, #(1<<8) sub R1, #1 bl max7219_send mov R0, #MAX7219_Shutdown mov R1, #1 bl max7219_send 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} ``` ```cpp= extern void GPIO_init(); extern void max7219_init(); extern void max7219_send(unsigned char address, unsigned char data); int display(int data, int num_digs) { const int base = 10; if (num_digs > 8){ return -1; } max7219_send(0xb, num_digs-1); for (int i = 0; i < num_digs; i++) { int d = data % base; data /= base; max7219_send(i+1, d); } return 0; } int main() { int student_id = 616015; GPIO_init(); max7219_init(); display(student_id, 7); } ``` #### Configuration of C Programming on Windows 1. put `stm32L476xx.h` under `project/inc` 2. project -> properties -> C/C++ Build -> Settings 3. MCU GCC Complier -> includes 4. add a new path to `${workspace_loc:/project/inc}` #### Declarations of Functions Written in Assembly in assembly file ```asm .text .global GPIO_init .global max7219_init .global max7219_send ``` in c file ```c extern void GPIO_init(); extern void max7219_init(); extern void max7219_send(unsigned char address, unsigned char data); ``` #### Parameters Passing - First 4 parameters will be copied to `R0` ~ `R3` - Others will be pushed into stack ```c extern void max7219_send(unsigned char address, unsigned char data); ``` - The `address` will be in `R0` - The `data` will be in `R1` ### Keypad Scanning > Use 4 input GPIO pins and 4 output GPIO pins to connect with keypad. Show the corresponding number of pressed button on 7-Seg LED. Don’t show number when released button. ```c= #include "stm32l476xx.h" extern void max7219_init(); extern void max7219_send(unsigned char address, unsigned char data); //TODO: define your gpio pin GPIO_TypeDef* GPIO[16] = {[0xA]=GPIOA, [0xB]=GPIOB, [0xC]=GPIOC}; const unsigned int X[4] = {0xC7, 0xA9, 0xA8, 0xBA}; const unsigned int Y[4] = {0xA5, 0xA6, 0xA7, 0xB6}; const unsigned int M[3] = {3, 4, 5}; const char mapping[16] = { 1, 2, 3, 10, 4, 5, 6, 11, 7, 8, 9, 12, 15, 0, 14, 13 }; const char reverse[16] = { 1, 4, 7, 15, 2, 5, 8, 0, 3, 6, 9, 14, 10, 11, 12, 13 }; void max7219_gpio_init() { RCC->AHB2ENR = 7; for (int i = 0; i < 4; i++) { GPIOB->MODER &= ~(3 << (2*M[i])); GPIOB->MODER |= (1 << (2*M[i])); } max7219_init(); } /* initial keypad gpio pin, X as output and Y as input */ void keypad_init() { for (int i = 0; i < 4; i++) { int x = X[i] >> 4, k = X[i] & 0xF; GPIO[x]->MODER &= ~(3 << (2*k)); GPIO[x]->MODER |= (1 << (2*k)); } for (int i = 0; i < 4; i++) { int x = Y[i] >> 4, k = Y[i] & 0xF;; GPIO[x]->MODER &= ~(3 << (2*k)); GPIO[x]->PUPDR &= ~(3 << (2*k)); GPIO[x]->PUPDR |= (2 << (2*k)); } } void keypad_init_reverse() { for (int i = 0; i < 4; i++) { int x = Y[i] >> 4, k = Y[i] & 0xF; GPIO[x]->MODER &= ~(3 << (2*k)); GPIO[x]->MODER |= (1 << (2*k)); } for (int i = 0; i < 4; i++) { int x = X[i] >> 4, k = X[i] & 0xF;; GPIO[x]->MODER &= ~(3 << (2*k)); GPIO[x]->PUPDR &= ~(3 << (2*k)); GPIO[x]->PUPDR |= (2 << (2*k)); } } /* TODO: scan keypad value return: >=0: key pressedvalue -1: no keypress */ int keypad_scan(const char *mapping, const unsigned int *X, const unsigned int *Y) { int result = 0; char nil = 1; for (int i=0; i<4; i++){ int x = X[i] >> 4, k = X[i] & 0xF; GPIO[x]->ODR &= ~(1 << k); GPIO[x]->ODR |= (1 << k); for (int j=0; j<4; j++){ int y = Y[j] >> 4, l = Y[j] & 0xF; if (GPIO[y]->IDR & (1<<l)){ result += mapping[j*4 + i]; nil = 0; } } GPIO[x]->ODR ^= (1 << k); } if (nil) return -1; return result; } int display(int data) { int num_digs = 0; if (0<=data && data<10){ num_digs = 1; } else if (10<=data && data <=29){ num_digs = 2; } else { num_digs = 4; data = 0; } const int base = 10; max7219_send(0xb, num_digs-1); for (int i = 0; i < num_digs; i++) { int d = data % base; data /= base; max7219_send(i+1, d); } return 0; } void blank() { max7219_send(0x1, 15); max7219_send(0xb, 0); } int main() { max7219_gpio_init(); keypad_init(); max7219_send(0xb, 0); while (1){ keypad_init(); int c1 = keypad_scan(mapping, X, Y); keypad_init_reverse(); int c2 = keypad_scan(reverse, Y, X); int c; if (c1 > c2) { c = c1; } else { c = c2; } if (c >= 0) { display(c); } else if (c == -1) { blank(); } for (int i=0; i<(1<<18); i++); } } ``` #### GPIO Init in C - Output ```c GPIO[x]->MODER &= ~(3 << (2*k)); GPIO[x]->MODER |= (1 << (2*k)); ``` - Input ```c GPIO[x]->MODER &= ~(3 << (2*k)); GPIO[x]->PUPDR &= ~(3 << (2*k)); GPIO[x]->PUPDR |= (2 << (2*k)); ``` Where `k` is the port number and `x` is the registers set (A/B/C). #### Keypad Scanning ![](https://i.imgur.com/zNgFjih.png) - The Idea of Implementing Keypad Scanning - Where `mapping` is a matrix which store the actual values on the keypad ```c keypad_scan: result = 0 nil = 1 for i from 0 to 3: send (1 << i) to the output for j from 0 to 3: if input[j] is 1: result += mapping[i][j] nil = 0 if nil is 1: return -1 else return result ``` - The Code ```c int keypad_scan(const char *mapping, const unsigned int *X, const unsigned int *Y) { int result = 0; char nil = 1; for (int i=0; i<4; i++){ int x = X[i] >> 4, k = X[i] & 0xF; GPIO[x]->ODR &= ~(1 << k); GPIO[x]->ODR |= (1 << k); for (int j=0; j<4; j++){ int y = Y[j] >> 4, l = Y[j] & 0xF; if (GPIO[y]->IDR & (1<<l)){ result += mapping[j*4 + i]; nil = 0; } } GPIO[x]->ODR ^= (1 << k); } if (nil) return -1; return result; } ``` #### Pressing 2 Buttons We found that because the keypad scan a whole row a time, there are problems when trying to press 2 buttons in the same row. Belows are the steps we solved the problems. 1. Change the input and output 2. Scan again with the transpose of matrix `mapping` (Because the rows and columns are exchanged) ``` mapping: transpose: 1 2 3 10 1 4 7 15 4 5 6 11 2 5 8 0 7 8 9 12 3 6 9 14 15 0 14 13 10 11 12 13 ``` 3. Get the max of the 2 values above and print ## Feedback After this lab, we totally understand that how important a compiler is. With C programming, we can finish our tasks in fewer and even more readable lines. While we still appreciate that we are more familiar in assembly language because of previous labs.