---
# System prepended metadata

title: 2019-MPSL Lab5

---


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