# 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

- 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.