Compare commits
242 Commits
Author | SHA1 | Date | |
---|---|---|---|
d68066014a | |||
6e99595c00 | |||
2c87b2814d | |||
95ad5a9e9f | |||
9a0e3be4fe | |||
2827046b8b | |||
faf75e158a | |||
f9b63c06fe | |||
1cbba5c8b3 | |||
ebb0f5846d | |||
1beb7ef04a | |||
70467cf2a7 | |||
d9769c6b28 | |||
d9fd18d799 | |||
7894359f30 | |||
4cb0a10617 | |||
a92a3beb96 | |||
ccd395d6ab | |||
eb75e31577 | |||
ba4248ff24 | |||
2cc5a6a4f3 | |||
5c86d55458 | |||
b9e5813223 | |||
1b4a93d9e1 | |||
30d50dcc5e | |||
1607dc62dd | |||
78df7eee66 | |||
09fe302e63 | |||
d28e62fdd3 | |||
01d4fe3f85 | |||
5de2761fde | |||
1f807cdb7c | |||
735599ee7f | |||
7a2c9f96d4 | |||
5d7a94c3b2 | |||
1d915baf77 | |||
08b96a6617 | |||
ff95034605 | |||
ac4801c7cf | |||
a4cb8129c5 | |||
f5fa1e5e22 | |||
1d96cee661 | |||
7a560959c1 | |||
8d8a818cf9 | |||
b400431607 | |||
7933aa46ae | |||
076c0f3f1a | |||
b49665512f | |||
36d3b2f735 | |||
d68dae167d | |||
3412197cae | |||
8dc0569407 | |||
ac6ca860cb | |||
2711f5fb4b | |||
9674bc8ef5 | |||
c0ee849cec | |||
78c906ef26 | |||
0c4533bfac | |||
3eed2e30eb | |||
3cebb5d351 | |||
a29525ac9e | |||
83e6227581 | |||
010c493b90 | |||
2404910870 | |||
53e538b112 | |||
1232d0b884 | |||
a1d6422897 | |||
761de5a94d | |||
9a58eedcc4 | |||
2fb12f8af0 | |||
fef3f69f63 | |||
5b9194caae | |||
2f8e2937c1 | |||
3769b3eb05 | |||
8f777c9ac4 | |||
aeb7aeb7f2 | |||
20c01f2efa | |||
40cb04bde5 | |||
a1457e6a69 | |||
0303bbdc3c | |||
162bdaefee | |||
b9fd0099a8 | |||
d09f8d240f | |||
ed6da383de | |||
9328e22425 | |||
48b9fc7578 | |||
d4d494ae7b | |||
1ebf85cb9d | |||
5a8323bddb | |||
97dc6ede70 | |||
a4563cd393 | |||
b435396d67 | |||
38ea1a7fdb | |||
6632630303 | |||
fdb524c8d4 | |||
2c3bccd147 | |||
ee98ba12a3 | |||
33242a09c1 | |||
2097280f2b | |||
70b3b25a9d | |||
5e1e9dfa92 | |||
f397d0737b | |||
1c2414463b | |||
2e629f12aa | |||
9989a52c38 | |||
73d2bbc730 | |||
5e2f120432 | |||
152f171c66 | |||
9ddb747f16 | |||
bd11d12620 | |||
e5c6669284 | |||
202e91bfb6 | |||
48c83f0b2d | |||
d8e34ec209 | |||
5a491140c7 | |||
aaf709b0c9 | |||
32bb08696f | |||
85d243551e | |||
a32ef8fa5b | |||
10a09e3ad3 | |||
611c56b329 | |||
5b1dfde819 | |||
1cc4785ddb | |||
9acd56b79b | |||
474fce2278 | |||
8597a9f736 | |||
da21fac130 | |||
5f9fab5f2c | |||
f524a08687 | |||
5c03232855 | |||
f69c5bc59e | |||
50486f6ec0 | |||
8c995f66ff | |||
ebe958ac49 | |||
fadb80c362 | |||
d1e13dc16a | |||
90a9294da6 | |||
cfc06ddb2d | |||
5e5c616bde | |||
2ca1d2e4ad | |||
a28cdcf6af | |||
7f6d027d1a | |||
97b1d19da8 | |||
8b2f18415d | |||
5615c80e8f | |||
a68f74559f | |||
326d0f66b0 | |||
feed11f977 | |||
fd3df973ec | |||
58ae9a641a | |||
e0aae175b0 | |||
7e4285f280 | |||
007548efbb | |||
14f6018f5a | |||
f4b614bf0f | |||
a7ea698f9b | |||
1959a3f578 | |||
349c93b68f | |||
9c1a8a6e21 | |||
9003528897 | |||
2b34a3e51d | |||
e5381a8c9d | |||
8bd6197c02 | |||
f5b0e67056 | |||
19be1a6e48 | |||
05de9326d7 | |||
633fc814b2 | |||
88e0cff853 | |||
6020118fc2 | |||
d3846c3e08 | |||
30ab5287da | |||
fb16cfaa6f | |||
cf62e0c0a0 | |||
044779681b | |||
167617ad2c | |||
26db6bf03d | |||
a4adf6ac27 | |||
da3c2a5ffb | |||
a5438cef14 | |||
d295a36598 | |||
3eaf90dbd6 | |||
25ad1ad7df | |||
6536dc5534 | |||
730ac0df30 | |||
152c9f60d4 | |||
2f07e37b5d | |||
d61986630c | |||
2e19371fac | |||
53e33bf4ff | |||
01c645d8df | |||
75acdd3171 | |||
e6db62c9a5 | |||
2ccef81175 | |||
ad37bb2280 | |||
fd6eb582eb | |||
8292d59cf0 | |||
1e77cdaddf | |||
d0219a3c77 | |||
2bef7bf2c2 | |||
9f50f6010b | |||
c35ec0991c | |||
584e834b0d | |||
fbe779d3b8 | |||
49d78662e2 | |||
c502303a36 | |||
2d532a6403 | |||
86fb74bd49 | |||
42c06f0da4 | |||
b4ebe4978b | |||
a5536e63b7 | |||
4f70ea392b | |||
9c493d515e | |||
201a371a57 | |||
40c49b6809 | |||
c9b8fbba52 | |||
d8755709cd | |||
785810924e | |||
a0794dd9c1 | |||
4104cbf3d6 | |||
c829a76273 | |||
e1b7c328f8 | |||
2435cee771 | |||
75598f973a | |||
155b7153e9 | |||
5c2a6c42d2 | |||
b390b8ec8f | |||
cbf8a3cf86 | |||
f0e8bf7616 | |||
8f143104f6 | |||
80801e185b | |||
68490a4f83 | |||
5d7c75b358 | |||
4e6fd76b08 | |||
cea174197f | |||
df9faabc7f | |||
5128d36a75 | |||
b3ee547b64 | |||
92fb90983f | |||
fd801003a8 | |||
491a544919 | |||
b411a05d0e | |||
29c2c88c71 |
4
.gitignore
vendored
@ -1,3 +1,5 @@
|
||||
*.o
|
||||
firmware.*
|
||||
firmware.txt
|
||||
firmware.elf
|
||||
firmware.map
|
||||
|
||||
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
* PontCoopScheduler.c
|
||||
*
|
||||
* Created on: 29.08.2016
|
||||
* Author: wn
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#include "PontCoopScheduler.h"
|
||||
|
||||
tTask tasks[MAX_NUM_OF_TASKS];
|
||||
|
||||
|
||||
void schInit() {
|
||||
for (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
tasks[i].delay = 0;
|
||||
tasks[i].period = 0;
|
||||
tasks[i].run = 0;
|
||||
tasks[i].exec = NULL;
|
||||
tasks[i].handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void schAdd(void (*exec)(void *), void *handle, uint32_t delay, uint32_t period) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
if (tasks[i].exec == NULL) {
|
||||
tasks[i].delay = delay;
|
||||
tasks[i].period = period;
|
||||
if (delay == 0 && period == 0) {
|
||||
tasks[i].run = 1;
|
||||
} else {
|
||||
tasks[i].run = 0;
|
||||
}
|
||||
tasks[i].exec = exec;
|
||||
tasks[i].handle = handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void schDel(void (*exec)(void *), void *handle) {
|
||||
for (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
if ((tasks[i].exec == exec) && (tasks[i].handle == handle)) {
|
||||
tasks[i].exec = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void schExec() {
|
||||
for (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
if (tasks[i].exec != NULL && tasks[i].run > 0) {
|
||||
tasks[i].run--;
|
||||
tasks[i].exec(tasks[i].handle);
|
||||
if (tasks[i].period == 0) {
|
||||
tasks[i].exec = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void schUpdate() {
|
||||
for (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
if (tasks[i].exec != NULL) {
|
||||
if (tasks[i].delay == 0) {
|
||||
tasks[i].delay = tasks[i].period;
|
||||
tasks[i].run++;
|
||||
} else {
|
||||
tasks[i].delay--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t schTaskCnt() {
|
||||
uint8_t cnt = 0;
|
||||
for (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
if (tasks[i].exec != NULL){
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
return cnt;
|
||||
}
|
31
canvas.c
@ -1,31 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "canvas.h"
|
||||
#include "displayDriver.h"
|
||||
|
||||
|
||||
static canvas_t canvas;
|
||||
static uint8_t canvasStorage[CANVAS_WIDTH * CANVAS_HEIGHT];
|
||||
|
||||
void canvasInit() {
|
||||
canvas.height = CANVAS_HEIGHT;
|
||||
canvas.width = CANVAS_WIDTH;
|
||||
canvas.size = CANVAS_HEIGHT * CANVAS_WIDTH;
|
||||
canvas.canvas = canvasStorage;
|
||||
|
||||
// Bit7 signals need to be transfered for octet
|
||||
memset(canvasStorage, 0x80, canvas.size);
|
||||
|
||||
displayDriverTransferCanvas();
|
||||
}
|
||||
|
||||
canvas_t *canvasGet() {
|
||||
return &canvas;
|
||||
}
|
||||
|
||||
void canvasShow() {
|
||||
displayDriverTransferCanvas();
|
||||
}
|
||||
|
||||
|
23
canvas.h
@ -1,23 +0,0 @@
|
||||
#ifndef _CANVAS_H_
|
||||
#define _CANVAS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#define CANVAS_WIDTH 10
|
||||
#define CANVAS_HEIGHT 6
|
||||
|
||||
typedef struct {
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
uint8_t size;
|
||||
uint8_t *canvas;
|
||||
} canvas_t;
|
||||
|
||||
void canvasInit();
|
||||
canvas_t *canvasGet();
|
||||
void canvasShow();
|
||||
|
||||
|
||||
|
||||
#endif // _CANVAS_H_
|
@ -4,14 +4,14 @@ OBJDUMP=$(TOOLCHAIN_PREFIX)/bin/msp430-elf-objdump
|
||||
|
||||
ARTIFACT=firmware
|
||||
MCU=msp430g2553
|
||||
CFLAGS=-Wall -mmcu=$(MCU) -std=gnu99 -I $(TOOLCHAIN_PREFIX)/include -O3 -g0
|
||||
CFLAGS=-Wall -mmcu=$(MCU) -std=gnu99 -I $(TOOLCHAIN_PREFIX)/include -O1 -g0
|
||||
|
||||
# for debugging
|
||||
#CFLAGS=-Wall -mmcu=$(MCU) -std=gnu99 -I $(TOOLCHAIN_PREFIX)/include -g3 -ggdb -gdwarf-2
|
||||
#CFLAGS+= -g3 -ggdb -gdwarf-2
|
||||
|
||||
LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include
|
||||
|
||||
$(ARTIFACT).elf: main.o led.o time.o PontCoopScheduler.o displayDriver.o canvas.o displayTest.o
|
||||
$(ARTIFACT).elf: main.o
|
||||
$(CC) -o $@ $(LDFLAGS) $^
|
||||
$(OBJDUMP) -D $(ARTIFACT).elf > $(ARTIFACT).txt
|
||||
|
||||
@ -24,7 +24,7 @@ all: $(ARTIFACT).elf
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm -f *.o *.elf
|
||||
-rm -f *.o $(ARTIFACT).elf $(ARTIFACT).txt
|
||||
|
||||
.PHONY: upload
|
||||
upload: $(ARTIFACT).elf
|
BIN
display-driver/docs/MCU-Connection and Pattern.pdf
Executable file
BIN
display-driver/docs/Pinout-Display.pdf
Executable file
131
display-driver/main.c
Normal file
@ -0,0 +1,131 @@
|
||||
#include <msp430g2553.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
const uint8_t patterns[] = {
|
||||
0b00111111, // 0
|
||||
0b00000110, // 1
|
||||
0b01011011, // 2
|
||||
0b01001111, // 3
|
||||
0b01100110, // 4
|
||||
0b01101101, // 5
|
||||
0b01111101, // 6
|
||||
0b00100111, // 7
|
||||
0b01111111, // 8
|
||||
0b01101111, // 9
|
||||
};
|
||||
|
||||
|
||||
|
||||
volatile union {
|
||||
uint16_t value;
|
||||
uint8_t receiveBuffer[2];
|
||||
} value;
|
||||
|
||||
|
||||
static void delay() {
|
||||
asm volatile (
|
||||
"push r12\n"
|
||||
"mov.w #1000, r12\n"
|
||||
"loop:\n"
|
||||
"dec.w r12\n"
|
||||
"jnz loop\n"
|
||||
"pop r12\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void __attribute__ ((interrupt (USCIAB0RX_VECTOR))) receive() {
|
||||
static uint8_t octetNumber = 0;
|
||||
if (UC0IFG & UCB0RXIFG) {
|
||||
value.receiveBuffer[octetNumber] = UCB0RXBUF;
|
||||
octetNumber++;
|
||||
if (octetNumber > 1) {
|
||||
octetNumber = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mask 0x0f: digit value
|
||||
// mask 0x10: set dp
|
||||
// mask 0x20: digit off
|
||||
static void setDigit(uint16_t bit, uint8_t value) {
|
||||
P1OUT |= (BIT0 | BIT1 | BIT2 | BIT3);
|
||||
P2OUT = ((value & 0x20) ? 0 : patterns[value & 0x0f]) | ((value & 0x10) ? 0x80 : 0x00);
|
||||
P1OUT &= ~bit;
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
WDTCTL = WDTPW | WDTHOLD;
|
||||
|
||||
__disable_interrupt();
|
||||
|
||||
// highest possible system clock
|
||||
DCOCTL = DCO0 | DCO1 | DCO2;
|
||||
BCSCTL1 = XT2OFF | RSEL0 | RSEL1 | RSEL2 | RSEL3;
|
||||
BCSCTL2 = 0;
|
||||
BCSCTL3 = 0;
|
||||
|
||||
// SPI slave
|
||||
// BIT4: UCB0STE
|
||||
// BIT5: UCB0CLK
|
||||
// BIT6: UCB0SOMI
|
||||
// BIT7: UCB0SIMO
|
||||
P1SEL |= BIT4 | BIT5 | BIT6 | BIT7;
|
||||
P1SEL2 |= BIT4 | BIT5 | BIT6 | BIT7;
|
||||
// most significant bit first, enable STE
|
||||
UCB0CTL0 = UCCKPH | UCSYNC | UCMSB | UCMODE_2;
|
||||
UCB0CTL1 = 0x00;
|
||||
// enable RX interrupt
|
||||
UC0IE |= UCB0RXIE;
|
||||
|
||||
// digit driver
|
||||
P1DIR |= BIT0 | BIT1 | BIT2 | BIT3;
|
||||
P1SEL &= ~(BIT0 | BIT1 | BIT2 | BIT3);
|
||||
P1SEL2 &= ~(BIT0 | BIT1 | BIT2 | BIT3);
|
||||
|
||||
// segment driver
|
||||
P2DIR = 0xff;
|
||||
P2SEL = 0x00;
|
||||
P2SEL2 = 0x00;
|
||||
|
||||
// all digits off
|
||||
P1OUT |= (BIT0 | BIT1 | BIT2 | BIT3);
|
||||
|
||||
// all segments off
|
||||
P2OUT &= ~(BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7);
|
||||
|
||||
// reset value to 0
|
||||
value.value = 0;
|
||||
|
||||
__enable_interrupt();
|
||||
|
||||
while (1) {
|
||||
__disable_interrupt();
|
||||
uint16_t shadowValue = value.value;
|
||||
__enable_interrupt();
|
||||
|
||||
uint8_t digit0 = shadowValue % 10;
|
||||
uint8_t digit1 = (shadowValue / 10) % 10;
|
||||
uint8_t digit2 = (shadowValue / 100) % 10;
|
||||
uint8_t digit3 = (shadowValue / 1000) % 10;
|
||||
|
||||
digit1 += (!((digit3 & 0x0f) | (digit2 & 0x0f) | (digit1 & 0x0f))) ? 0x20 : 0x00;
|
||||
digit2 += (!((digit3 & 0x0f) | (digit2 & 0x0f))) ? 0x20 : 0x00;
|
||||
digit3 += (!(digit3 & 0x0f)) ? 0x20 : 0x00;
|
||||
|
||||
setDigit(BIT0, digit0);
|
||||
delay();
|
||||
|
||||
setDigit(BIT1, digit1);
|
||||
delay();
|
||||
|
||||
setDigit(BIT2, digit2);
|
||||
delay();
|
||||
|
||||
setDigit(BIT3, digit3);
|
||||
delay();
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#include "displayDriver.h"
|
||||
#include "led.h"
|
||||
#include "canvas.h"
|
||||
#include <msp430g2553.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
|
||||
static void spiSendOctet(uint8_t v) {
|
||||
// wait for TX buffer empty
|
||||
while (!(UC0IFG & UCB0TXIFG));
|
||||
// load octet into TX buffer
|
||||
UCB0TXBUF = v;
|
||||
}
|
||||
|
||||
void displayDriverTransferCanvas() {
|
||||
// wait for signal waiting for data
|
||||
while ((P1IN & BIT3) == 0);
|
||||
|
||||
canvas_t *canvas = canvasGet();
|
||||
for (uint8_t i = 0; i < canvas->size; i++) {
|
||||
if ((*((canvas->canvas)+i) & 0x80) != 0) {
|
||||
*((canvas->canvas)+i) &= ~0x80;
|
||||
spiSendOctet(i);
|
||||
spiSendOctet(*((canvas->canvas)+i));
|
||||
}
|
||||
}
|
||||
spiSendOctet(0xfe);
|
||||
}
|
||||
|
||||
void displayDriverInit() {
|
||||
// SPI in master mode
|
||||
UCB0CTL0 = UCMST;
|
||||
// SPI timing config
|
||||
UCB0CTL1 = UCSSEL_3;
|
||||
UCB0BR0 = 8;
|
||||
UCB0BR1 = 0;
|
||||
|
||||
// BIT5: UCB0CLK
|
||||
// BIT6: UCB0SOMI
|
||||
// BIT7: UCB0SIMO
|
||||
P1SEL |= BIT5 | BIT6 | BIT7;
|
||||
P1SEL2 |= BIT5 | BIT6 | BIT7;
|
||||
P1DIR |= BIT5 | BIT7;
|
||||
|
||||
// P1.3 is signal line
|
||||
P1DIR &= ~BIT3;
|
||||
|
||||
// enable SPI module
|
||||
UCB0CTL1 &= ~UCSWRST;
|
||||
}
|
||||
|
||||
|
@ -1,9 +0,0 @@
|
||||
#ifndef _DISPLAY_DRIVER_H_
|
||||
#define _DISPLAY_DRIVER_H_
|
||||
|
||||
void displayDriverInit();
|
||||
void displayDriverTransferCanvas();
|
||||
|
||||
|
||||
|
||||
#endif // _DISPLAY_DRIVER_H_
|
@ -1,29 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include "canvas.h"
|
||||
#include "PontCoopScheduler.h"
|
||||
|
||||
|
||||
static canvas_t *canvas;
|
||||
static uint8_t lastPixel = 0xff;
|
||||
static uint8_t currentPixel = 0;
|
||||
|
||||
void displayTestExec(void *args) {
|
||||
if (lastPixel != 0xff) {
|
||||
*((canvas->canvas)+lastPixel) = 0x80;
|
||||
}
|
||||
|
||||
lastPixel = currentPixel;
|
||||
*((canvas->canvas)+currentPixel) = 0x81;
|
||||
currentPixel++;
|
||||
if (currentPixel >= canvas->size) {
|
||||
currentPixel = 0;
|
||||
}
|
||||
|
||||
canvasShow();
|
||||
}
|
||||
|
||||
void displayTestInit() {
|
||||
canvas = canvasGet();
|
||||
schAdd(displayTestExec, NULL, 0, 50);
|
||||
}
|
||||
|
BIN
docs/IMG_4936.jpg
Normal file
After Width: | Height: | Size: 2.0 MiB |
BIN
docs/IMG_4941.jpg
Normal file
After Width: | Height: | Size: 603 KiB |
BIN
docs/IMG_4958.jpeg
Normal file
After Width: | Height: | Size: 262 KiB |
BIN
docs/Tetris Technical Documentation.pdf
Executable file
BIN
docs/display-driver.jpg
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
docs/game-ctrl.jpg
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
docs/logo.jpg
Normal file
After Width: | Height: | Size: 75 KiB |
BIN
docs/reset-ctrl.jpg
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
docs/rgb-driver.jpg
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
docs/schematics.pdf
Executable file
BIN
docs/sound-driver-1.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
docs/sound-driver-2.png
Normal file
After Width: | Height: | Size: 183 KiB |
BIN
docs/sound-driver-3.jpg
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
docs/sound-driver-4.jpg
Normal file
After Width: | Height: | Size: 74 KiB |
31
game-ctrl/Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
TOOLCHAIN_PREFIX=/opt/msp430-gcc
|
||||
CC=$(TOOLCHAIN_PREFIX)/bin/msp430-elf-gcc
|
||||
OBJDUMP=$(TOOLCHAIN_PREFIX)/bin/msp430-elf-objdump
|
||||
|
||||
ARTIFACT=firmware
|
||||
MCU=msp430g2553
|
||||
CFLAGS=-Wall -mmcu=$(MCU) -std=gnu99 -I $(TOOLCHAIN_PREFIX)/include -O1 -g0
|
||||
|
||||
# for debugging
|
||||
#CFLAGS+= -g3 -ggdb -gdwarf-2
|
||||
|
||||
LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include
|
||||
|
||||
$(ARTIFACT).elf: main.o spi.o scheduler.o canvas.o shapes.o game.o buttons.o myrand.o display.o sound.o eeprom.o config.o
|
||||
$(CC) -o $@ $(LDFLAGS) $^
|
||||
$(OBJDUMP) -D $(ARTIFACT).elf > $(ARTIFACT).txt
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
|
||||
.PHONY: all
|
||||
all: $(ARTIFACT).elf
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm -f *.o $(ARTIFACT).elf $(ARTIFACT).txt
|
||||
|
||||
.PHONY: upload
|
||||
upload: $(ARTIFACT).elf
|
||||
mspdebug rf2500 "prog $(ARTIFACT).elf"
|
135
game-ctrl/buttons.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <msp430g2553.h>
|
||||
|
||||
#include "buttons.h"
|
||||
#include "scheduler.h"
|
||||
#include "shapes.h"
|
||||
#include "canvas.h"
|
||||
#include "sound.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
|
||||
bool mutedFlag = true;
|
||||
|
||||
static uint8_t buttonsMoveLeftPressed() {
|
||||
static uint8_t last = 0;
|
||||
uint8_t current = (P2IN & BIT4);
|
||||
uint8_t res = (current != 0) && (current != last);
|
||||
last = current;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool buttonsConfig1Pressed() {
|
||||
return buttonsMoveLeftPressed();
|
||||
}
|
||||
|
||||
static uint8_t buttonsMoveRightPressed() {
|
||||
static uint8_t last = 0;
|
||||
uint8_t current = (P2IN & BIT0);
|
||||
uint8_t res = (current != 0) && (current != last);
|
||||
last = current;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool buttonsConfig4Pressed() {
|
||||
return buttonsMoveRightPressed();
|
||||
}
|
||||
|
||||
static uint8_t buttonsRotateLeftPressed() {
|
||||
static uint8_t last = 0;
|
||||
uint8_t current = (P2IN & BIT3);
|
||||
uint8_t res = (current != 0) && (current != last);
|
||||
last = current;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool buttonsConfig2Pressed() {
|
||||
return buttonsRotateLeftPressed();
|
||||
}
|
||||
|
||||
static uint8_t buttonsRotateRightPressed() {
|
||||
static uint8_t last = 0;
|
||||
uint8_t current = (P2IN & BIT1);
|
||||
uint8_t res = (current != 0) && (current != last);
|
||||
last = current;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool buttonsConfig3Pressed() {
|
||||
return buttonsRotateRightPressed();
|
||||
}
|
||||
|
||||
|
||||
static uint8_t buttonsMoveDownPressed() {
|
||||
return P2IN & BIT2;
|
||||
}
|
||||
|
||||
bool isConfigMode() {
|
||||
return (P2IN & BIT2);
|
||||
}
|
||||
|
||||
|
||||
void buttonsExec(void *handle) {
|
||||
static uint32_t unmuteTimestamp;
|
||||
uint32_t currentTimestamp = getSeconds();
|
||||
|
||||
|
||||
if (! stoneIsValid()) {
|
||||
// don't do anything, the stone has not been initialized
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t buttonPressed = 0;
|
||||
|
||||
if (buttonsMoveLeftPressed()) {
|
||||
stoneMoveLeft();
|
||||
buttonPressed = 1;
|
||||
}
|
||||
if (buttonsMoveRightPressed()) {
|
||||
stoneMoveRight();
|
||||
buttonPressed = 1;
|
||||
}
|
||||
if (buttonsRotateLeftPressed()) {
|
||||
stoneRotateLeft();
|
||||
buttonPressed = 1;
|
||||
}
|
||||
if (buttonsRotateRightPressed()) {
|
||||
stoneRotateRight();
|
||||
buttonPressed = 1;
|
||||
}
|
||||
if (buttonsMoveDownPressed()) {
|
||||
stoneMoveDown();
|
||||
buttonPressed = 1;
|
||||
}
|
||||
|
||||
if (buttonPressed == 1) {
|
||||
canvasShow();
|
||||
|
||||
if (mutedFlag) {
|
||||
eepromIncGameCounter();
|
||||
soundCtrl(SOUND_UNMUTE);
|
||||
mutedFlag = false;
|
||||
}
|
||||
unmuteTimestamp = currentTimestamp;
|
||||
}
|
||||
|
||||
if ((! mutedFlag) && (unmuteTimestamp + MUTE_DELAY < currentTimestamp)) {
|
||||
soundCtrl(SOUND_MUTE);
|
||||
mutedFlag = true;
|
||||
}
|
||||
}
|
||||
|
||||
void buttonsInit() {
|
||||
P2DIR &= ~(BIT0|BIT1|BIT2|BIT3|BIT4);
|
||||
}
|
||||
|
||||
void buttonsStart() {
|
||||
schAdd(buttonsExec, NULL, 0, 25);
|
||||
}
|
||||
|
||||
bool isGameActive() {
|
||||
return ! mutedFlag;
|
||||
}
|
||||
|
16
game-ctrl/buttons.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef _BUTTONS_H_
|
||||
#define _BUTTONS_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void buttonsInit();
|
||||
void buttonsStart();
|
||||
bool isGameActive();
|
||||
bool isConfigMode();
|
||||
bool buttonsConfig1Pressed();
|
||||
bool buttonsConfig2Pressed();
|
||||
bool buttonsConfig3Pressed();
|
||||
bool buttonsConfig4Pressed();
|
||||
|
||||
|
||||
#endif // _BUTTONS_H_
|
113
game-ctrl/canvas.c
Normal file
@ -0,0 +1,113 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <msp430g2553.h>
|
||||
|
||||
#include "canvas.h"
|
||||
#include "spi.h"
|
||||
#include "eeprom.h"
|
||||
#include "../rgb-driver/colors.h"
|
||||
|
||||
|
||||
static uint8_t canvasStorage[CANVAS_WIDTH * CANVAS_HEIGHT];
|
||||
const canvas_t canvas = {
|
||||
.height = CANVAS_HEIGHT,
|
||||
.width = CANVAS_WIDTH,
|
||||
.canvas = canvasStorage
|
||||
};
|
||||
|
||||
static uint8_t miniCanvasStorage[MINI_CANVAS_WIDTH * MINI_CANVAS_HEIGHT];
|
||||
const canvas_t miniCanvas = {
|
||||
.height = MINI_CANVAS_HEIGHT,
|
||||
.width = MINI_CANVAS_WIDTH,
|
||||
.canvas = miniCanvasStorage
|
||||
};
|
||||
|
||||
void canvasShow() {
|
||||
uint8_t brightness_offset = _brightness_offset * eepromReadBrightness();
|
||||
|
||||
// wait for signal waiting for data
|
||||
while (!(P1IN & BIT3));
|
||||
|
||||
spiSendBegin(e_SPI_CANVAS);
|
||||
|
||||
for (uint8_t i = 0; i < (CANVAS_WIDTH*CANVAS_HEIGHT); i++) {
|
||||
if ((*((canvas.canvas)+i) & 0x80) != 0) {
|
||||
*((canvas.canvas)+i) &= ~0x80;
|
||||
spiSendOctet(i);
|
||||
spiSendOctet((*((canvas.canvas)+i) == 0) ? 0 : (*((canvas.canvas)+i) + brightness_offset));
|
||||
}
|
||||
}
|
||||
for (uint8_t i = 0; i < (MINI_CANVAS_WIDTH*MINI_CANVAS_HEIGHT); i++) {
|
||||
if ((*((miniCanvas.canvas)+i) & 0x80) != 0) {
|
||||
*((miniCanvas.canvas)+i) &= ~0x80;
|
||||
spiSendOctet(i + (CANVAS_HEIGHT*CANVAS_WIDTH));
|
||||
spiSendOctet((*((miniCanvas.canvas)+i) == 0) ? 0 : (*((miniCanvas.canvas)+i) + brightness_offset));
|
||||
}
|
||||
}
|
||||
spiSendOctet(0xfe);
|
||||
|
||||
spiSendEnd(e_SPI_CANVAS);
|
||||
}
|
||||
|
||||
void canvasInit() {
|
||||
// P1.3 is signal line
|
||||
P1DIR &= ~BIT3;
|
||||
|
||||
canvasClear();
|
||||
miniCanvasClear();
|
||||
canvasShow();
|
||||
}
|
||||
|
||||
void canvasClear() {
|
||||
memset(canvas.canvas, 0x80, CANVAS_WIDTH*CANVAS_HEIGHT);
|
||||
}
|
||||
|
||||
void miniCanvasClear() {
|
||||
memset(miniCanvas.canvas, 0x80, MINI_CANVAS_WIDTH*MINI_CANVAS_HEIGHT);
|
||||
}
|
||||
|
||||
//void canvasSetAll(uint8_t color) {
|
||||
// memset(canvas.canvas, color + 0x80, CANVAS_WIDTH*CANVAS_HEIGHT);
|
||||
//}
|
||||
|
||||
void canvasSetPixel(uint8_t column, uint8_t row, uint8_t color) {
|
||||
*((canvas.canvas) + (row * canvas.width + column)) = (color + 0x80);
|
||||
}
|
||||
|
||||
void miniCanvasSetPixel(uint8_t column, uint8_t row, uint8_t color) {
|
||||
*((miniCanvas.canvas) + (row * miniCanvas.width + column)) = (color + 0x80);
|
||||
}
|
||||
|
||||
void canvasWipeRow(uint8_t row) {
|
||||
memmove(((canvas.canvas)+canvas.width), canvas.canvas, canvas.width*row);
|
||||
for (uint8_t i = 10; i < canvas.width*(row+1); i++) {
|
||||
*((canvas.canvas)+i) += 0x80;
|
||||
}
|
||||
memset(canvas.canvas, 0x80, canvas.width);
|
||||
}
|
||||
|
||||
void canvasFillRow(uint8_t row, uint8_t color) {
|
||||
for (uint8_t c = 0; c < canvas.width; c++) {
|
||||
canvasSetPixel(c, row, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t canvasIsRowFilled(uint8_t row) {
|
||||
uint8_t res = 1;
|
||||
for (uint8_t column = 0; column < canvas.width; column++) {
|
||||
if (*((canvas.canvas) + (row * canvas.width + column)) == 0) {
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
uint8_t canvasIsPixelFree(uint8_t column, uint8_t row) {
|
||||
return (*((canvas.canvas) + (row * canvas.width + column)) == 0) &&
|
||||
(column < canvas.width) &&
|
||||
(row < canvas.height);
|
||||
}
|
||||
|
30
game-ctrl/canvas.h
Normal file
@ -0,0 +1,30 @@
|
||||
#ifndef _CANVAS_H_
|
||||
#define _CANVAS_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Definition of CANVAS_WIDTH and CANVAS_HEIGHT
|
||||
// imported from rgb-driver
|
||||
#include "../rgb-driver/canvasSize.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
const uint8_t width;
|
||||
const uint8_t height;
|
||||
const uint8_t size;
|
||||
uint8_t * const canvas;
|
||||
} canvas_t;
|
||||
|
||||
void canvasInit();
|
||||
void canvasClear();
|
||||
void miniCanvasClear();
|
||||
//void canvasSetAll(uint8_t color);
|
||||
void canvasShow();
|
||||
void canvasSetPixel(uint8_t column, uint8_t row, uint8_t color);
|
||||
void miniCanvasSetPixel(uint8_t column, uint8_t row, uint8_t color);
|
||||
uint8_t canvasIsPixelFree(uint8_t column, uint8_t row);
|
||||
void canvasWipeRow(uint8_t row);
|
||||
void canvasFillRow(uint8_t row, uint8_t color);
|
||||
uint8_t canvasIsRowFilled(uint8_t row);
|
||||
|
||||
#endif // _CANVAS_H_
|
135
game-ctrl/config.c
Normal file
@ -0,0 +1,135 @@
|
||||
#include <stddef.h>
|
||||
#include "config.h"
|
||||
#include "canvas.h"
|
||||
#include "../rgb-driver/colors.h"
|
||||
#include "scheduler.h"
|
||||
#include "buttons.h"
|
||||
#include "eeprom.h"
|
||||
#include "display.h"
|
||||
#include "shapes.h"
|
||||
#include "sound.h"
|
||||
|
||||
|
||||
static bool configChanged = false;
|
||||
static bool muted = false;
|
||||
|
||||
static void configHandleFlash() {
|
||||
uint8_t color = eepromReadFlashColor();
|
||||
|
||||
canvasFillRow(CANVAS_HEIGHT-1, color);
|
||||
displaySetValue(color);
|
||||
|
||||
if (buttonsConfig2Pressed()) {
|
||||
configChanged = true;
|
||||
color += 1;
|
||||
if (color > _color_end) {
|
||||
color = 0;
|
||||
}
|
||||
eepromSetFlashColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
static void configHandleResetHighScore() {
|
||||
displaySetValue(eepromReadHighScore());
|
||||
|
||||
if (buttonsConfig2Pressed()) {
|
||||
configChanged = true;
|
||||
eepromSetHighScore(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void configHandleResetGameCounter() {
|
||||
displaySetValue(eepromReadGameCounter());
|
||||
|
||||
if (buttonsConfig2Pressed()) {
|
||||
configChanged = true;
|
||||
eepromClearGameCounter(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void configHandleBrightness() {
|
||||
displaySetValue(eepromReadBrightness());
|
||||
stoneDrawConfigPattern();
|
||||
|
||||
if (buttonsConfig2Pressed()) {
|
||||
configChanged = true;
|
||||
uint8_t brightness = eepromReadBrightness() + 1;
|
||||
if (brightness > _brightness_shifts) {
|
||||
brightness = 0;
|
||||
}
|
||||
eepromSetBrightness(brightness);
|
||||
}
|
||||
}
|
||||
|
||||
static void configHandleAmplitude() {
|
||||
displaySetValue(eepromReadAmplitude());
|
||||
if (muted) {
|
||||
muted = false;
|
||||
soundCtrl(SOUND_START);
|
||||
soundCtrl(SOUND_UNMUTE);
|
||||
}
|
||||
|
||||
if (buttonsConfig2Pressed()) {
|
||||
configChanged = true;
|
||||
uint8_t amplitude = eepromReadAmplitude() + 1;
|
||||
if (amplitude > 15) {
|
||||
amplitude = 0;
|
||||
}
|
||||
eepromSetAmplitude(amplitude);
|
||||
soundCtrl(SOUND_COMMAND + SOUND_SUBCMD_AMPLITUDE + amplitude);
|
||||
}
|
||||
}
|
||||
|
||||
void (*configHandler[])(void) = {
|
||||
configHandleResetHighScore,
|
||||
configHandleResetGameCounter,
|
||||
configHandleFlash,
|
||||
configHandleBrightness,
|
||||
configHandleAmplitude
|
||||
};
|
||||
|
||||
void configExec(void *handle) {
|
||||
static uint8_t configState = 0;
|
||||
static uint8_t lastConfigState = 255;
|
||||
|
||||
if (configState != lastConfigState) {
|
||||
lastConfigState = configState;
|
||||
|
||||
miniCanvasClear();
|
||||
canvasClear();
|
||||
if (! muted) {
|
||||
muted = true;
|
||||
soundCtrl(SOUND_MUTE);
|
||||
}
|
||||
|
||||
uint8_t row = configState / 3;
|
||||
uint8_t column = configState % 3;
|
||||
miniCanvasSetPixel(column, row, _red);
|
||||
}
|
||||
|
||||
if (buttonsConfig1Pressed()) {
|
||||
configState += 1;
|
||||
if (configState >= sizeof(configHandler) / sizeof(configHandler[0])) {
|
||||
configState = 0;
|
||||
}
|
||||
}
|
||||
|
||||
configHandler[configState]();
|
||||
|
||||
if (configChanged) {
|
||||
miniCanvasSetPixel(0, 3, _red);
|
||||
if (buttonsConfig4Pressed()) {
|
||||
eepromCommit();
|
||||
configChanged = false;
|
||||
}
|
||||
} else {
|
||||
miniCanvasSetPixel(0, 3, _green);
|
||||
}
|
||||
|
||||
canvasShow();
|
||||
}
|
||||
|
||||
|
||||
void configInit() {
|
||||
schAdd(configExec, NULL, 0, 100);
|
||||
}
|
8
game-ctrl/config.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
|
||||
|
||||
void configInit();
|
||||
|
||||
|
||||
#endif // _CONFIG_H_
|
184
game-ctrl/disabled/displayTest.c
Normal file
@ -0,0 +1,184 @@
|
||||
#include <stdlib.h>
|
||||
#include "canvas.h"
|
||||
#include "PontCoopScheduler.h"
|
||||
|
||||
|
||||
#define MAX_COLOR 0x0d
|
||||
|
||||
typedef enum {
|
||||
e_WIPE_LAST_COLUMN_DOWN,
|
||||
e_PIXELS_UP,
|
||||
e_WIPE_LAST_PIXEL_UP,
|
||||
e_PIXELS_DOWN,
|
||||
e_WIPE_LAST_PIXEL_DOWN,
|
||||
e_ROWS_UP,
|
||||
e_WIPE_LAST_ROW_UP,
|
||||
e_ROWS_DOWN,
|
||||
e_WIPE_LAST_ROW_DOWN,
|
||||
e_COLUMNS_UP,
|
||||
e_WIPE_LAST_COLUMN_UP,
|
||||
e_COLUMNS_DOWN
|
||||
} t_State;
|
||||
|
||||
void displayTestExec(void *args) {
|
||||
static int16_t last = 0xff;
|
||||
static int16_t current = 0;
|
||||
static uint8_t color = 0x01;
|
||||
static t_State state = e_PIXELS_UP;
|
||||
|
||||
switch (state) {
|
||||
// wipe last column
|
||||
case e_WIPE_LAST_COLUMN_DOWN:
|
||||
for (uint16_t i = 0; i < canvas.height; i++) {
|
||||
canvasSetPixel(last, i, 0);
|
||||
}
|
||||
last = 0xff;
|
||||
state = e_PIXELS_UP;
|
||||
// pixels up
|
||||
case e_PIXELS_UP:
|
||||
if (last != 0xff) {
|
||||
*((canvas.canvas)+last) = 0x80;
|
||||
}
|
||||
|
||||
last = current;
|
||||
*((canvas.canvas)+current) = (color + 0x80);
|
||||
current++;
|
||||
if (current >= canvas.size) {
|
||||
current = 0;
|
||||
state = e_WIPE_LAST_PIXEL_UP;
|
||||
}
|
||||
break;
|
||||
// wipe last pixel
|
||||
case e_WIPE_LAST_PIXEL_UP:
|
||||
*((canvas.canvas)+last) = 0x80;
|
||||
last = 0xff;
|
||||
current = canvas.size - 1;
|
||||
state = e_PIXELS_DOWN;
|
||||
// pixels down
|
||||
case e_PIXELS_DOWN:
|
||||
if (last != 0xff) {
|
||||
*((canvas.canvas)+last) = 0x80;
|
||||
}
|
||||
|
||||
last = current;
|
||||
*((canvas.canvas)+current) = (color + 0x80);
|
||||
current--;
|
||||
if (current < 0) {
|
||||
current = 0;
|
||||
state = e_WIPE_LAST_PIXEL_DOWN;
|
||||
}
|
||||
break;
|
||||
// wipe last pixel
|
||||
case e_WIPE_LAST_PIXEL_DOWN:
|
||||
*((canvas.canvas)+last) = 0x80;
|
||||
last = 0xff;
|
||||
state = e_ROWS_UP;
|
||||
// rows up
|
||||
case e_ROWS_UP:
|
||||
if (last != 0xff) {
|
||||
for (uint16_t i = 0; i < canvas.width; i++) {
|
||||
canvasSetPixel(i, last, 0);
|
||||
}
|
||||
}
|
||||
|
||||
last = current;
|
||||
for (uint16_t i = 0; i < canvas.width; i++) {
|
||||
canvasSetPixel(i, current, color);
|
||||
}
|
||||
current++;
|
||||
if (current >= canvas.height) {
|
||||
current = 0;
|
||||
state = e_WIPE_LAST_ROW_UP;
|
||||
}
|
||||
break;
|
||||
// wipe last row
|
||||
case e_WIPE_LAST_ROW_UP:
|
||||
for (uint16_t i = 0; i < canvas.width; i++) {
|
||||
canvasSetPixel(i, last, 0);
|
||||
}
|
||||
last = 0xff;
|
||||
current = canvas.height - 1;
|
||||
state = e_ROWS_DOWN;
|
||||
// rows down
|
||||
case e_ROWS_DOWN:
|
||||
if (last != 0xff) {
|
||||
for (uint16_t i = 0; i < canvas.width; i++) {
|
||||
canvasSetPixel(i, last, 0);
|
||||
}
|
||||
}
|
||||
|
||||
last = current;
|
||||
for (uint16_t i = 0; i < canvas.width; i++) {
|
||||
canvasSetPixel(i, current, color);
|
||||
}
|
||||
current--;
|
||||
if (current < 0) {
|
||||
current = 0;
|
||||
state = e_WIPE_LAST_ROW_DOWN;
|
||||
}
|
||||
break;
|
||||
// wipe last row
|
||||
case e_WIPE_LAST_ROW_DOWN:
|
||||
for (uint16_t i = 0; i < canvas.width; i++) {
|
||||
canvasSetPixel(i, last, 0);
|
||||
}
|
||||
last = 0xff;
|
||||
state = e_COLUMNS_UP;
|
||||
// columns up
|
||||
case e_COLUMNS_UP:
|
||||
if (last != 0xff) {
|
||||
for (uint16_t i = 0; i < canvas.height; i++) {
|
||||
canvasSetPixel(last, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
last = current;
|
||||
for (uint16_t i = 0; i < canvas.height; i++) {
|
||||
canvasSetPixel(current, i, color);
|
||||
}
|
||||
current++;
|
||||
if (current >= canvas.width) {
|
||||
current = 0;
|
||||
state = e_WIPE_LAST_COLUMN_UP;
|
||||
}
|
||||
break;
|
||||
// wipe last column
|
||||
case e_WIPE_LAST_COLUMN_UP:
|
||||
for (uint16_t i = 0; i < canvas.height; i++) {
|
||||
canvasSetPixel(last, i, 0);
|
||||
}
|
||||
last = 0xff;
|
||||
current = canvas.width - 1;
|
||||
state = e_COLUMNS_DOWN;
|
||||
// columns down
|
||||
case e_COLUMNS_DOWN:
|
||||
if (last != 0xff) {
|
||||
for (uint16_t i = 0; i < canvas.height; i++) {
|
||||
canvasSetPixel(last, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
last = current;
|
||||
for (uint16_t i = 0; i < canvas.height; i++) {
|
||||
canvasSetPixel(current, i, color);
|
||||
}
|
||||
current--;
|
||||
if (current < 0) {
|
||||
current = 0;
|
||||
state = e_WIPE_LAST_COLUMN_DOWN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
color++;
|
||||
if (color > MAX_COLOR) {
|
||||
color = 1;
|
||||
}
|
||||
|
||||
canvasShow();
|
||||
}
|
||||
|
||||
void displayTestInit() {
|
||||
schAdd(displayTestExec, NULL, 0, 50);
|
||||
}
|
||||
|
75
game-ctrl/disabled/displayTest2.c
Normal file
@ -0,0 +1,75 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "canvas.h"
|
||||
#include "PontCoopScheduler.h"
|
||||
#include "displayTest2.h"
|
||||
|
||||
|
||||
|
||||
|
||||
typedef enum { e_idle, e_start, e_fillArea, e_placeObject, e_wipeLines_7, e_wipeLines_8, e_wipeLines_9, e_wipeLines_10 } t_state;
|
||||
|
||||
void displayTest2Exec(void *args) {
|
||||
static t_state state = e_start;
|
||||
|
||||
switch (state) {
|
||||
case e_idle:
|
||||
break;
|
||||
|
||||
case e_start:
|
||||
state = e_fillArea;
|
||||
break;
|
||||
|
||||
case e_fillArea:
|
||||
memset((canvas.canvas)+(canvas.width*7), 0x81, canvas.width);
|
||||
memset((canvas.canvas)+(canvas.width*8), 0x8d, canvas.width);
|
||||
memset((canvas.canvas)+(canvas.width*9), 0x82, canvas.width);
|
||||
memset((canvas.canvas)+(canvas.width*10), 0x88, canvas.width);
|
||||
for (uint8_t i = 0; i < canvas.width; i++) {
|
||||
if (i != 4 && i != 5) {
|
||||
canvasSetPixel(i, 6, 0x05);
|
||||
}
|
||||
}
|
||||
state = e_placeObject;
|
||||
break;
|
||||
|
||||
case e_placeObject:
|
||||
canvasSetPixel(4, 0, 0x04);
|
||||
canvasSetPixel(5, 0, 0x04);
|
||||
canvasSetPixel(6, 0, 0x04);
|
||||
canvasSetPixel(5, 1, 0x04);
|
||||
state = e_wipeLines_7;
|
||||
break;
|
||||
|
||||
case e_wipeLines_7:
|
||||
canvasWipeRow(7);
|
||||
state = e_wipeLines_8;
|
||||
break;
|
||||
|
||||
case e_wipeLines_8:
|
||||
canvasWipeRow(8);
|
||||
state = e_wipeLines_9;
|
||||
break;
|
||||
|
||||
case e_wipeLines_9:
|
||||
canvasWipeRow(9);
|
||||
state = e_wipeLines_10;
|
||||
break;
|
||||
|
||||
case e_wipeLines_10:
|
||||
canvasWipeRow(10);
|
||||
state = e_idle;
|
||||
break;
|
||||
|
||||
default:
|
||||
state = e_idle;
|
||||
break;
|
||||
}
|
||||
|
||||
canvasShow();
|
||||
}
|
||||
|
||||
void displayTest2Init() {
|
||||
schAdd(displayTest2Exec, NULL, 0, 5000);
|
||||
}
|
||||
|
11
game-ctrl/disabled/displayTest2.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _DISPLAY_TEST_2_H_
|
||||
#define _DISPLAY_TEST_2_H_
|
||||
|
||||
|
||||
void displayTest2Init();
|
||||
void displayTest2Exec();
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // _DISPLAY_TEST_2_H_
|
78
game-ctrl/disabled/displayTest3.c
Normal file
@ -0,0 +1,78 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "canvas.h"
|
||||
#include "PontCoopScheduler.h"
|
||||
#include "displayTest3.h"
|
||||
#include "led.h"
|
||||
|
||||
|
||||
typedef enum { e_idle, e_delay, e_start, e_fillArea, e_placeObject, e_preWipeLines, e_wipeLines } t_state;
|
||||
|
||||
void displayTest3Exec(void *args) {
|
||||
static t_state state = e_start;
|
||||
static uint8_t wipeLineHelper = 0;
|
||||
|
||||
ledBlueToggle();
|
||||
|
||||
switch (state) {
|
||||
case e_idle:
|
||||
state = e_delay;
|
||||
break;
|
||||
|
||||
case e_delay:
|
||||
state = e_start;
|
||||
break;
|
||||
|
||||
case e_start:
|
||||
canvasClear();
|
||||
state = e_fillArea;
|
||||
break;
|
||||
|
||||
case e_fillArea:
|
||||
memset((canvas.canvas)+(canvas.width*7), 0x82, canvas.width);
|
||||
memset((canvas.canvas)+(canvas.width*8), 0x83, canvas.width);
|
||||
memset((canvas.canvas)+(canvas.width*9), 0x84, canvas.width);
|
||||
memset((canvas.canvas)+(canvas.width*10), 0x85, canvas.width);
|
||||
for (uint8_t i = 0; i < canvas.width; i++) {
|
||||
if (i != 4 && i != 5) {
|
||||
canvasSetPixel(i, 6, 0x01);
|
||||
}
|
||||
}
|
||||
state = e_placeObject;
|
||||
break;
|
||||
|
||||
case e_placeObject:
|
||||
canvasSetPixel(4, 0, 0x04);
|
||||
canvasSetPixel(5, 0, 0x04);
|
||||
canvasSetPixel(6, 0, 0x04);
|
||||
canvasSetPixel(5, 1, 0x04);
|
||||
state = e_preWipeLines;
|
||||
break;
|
||||
|
||||
case e_preWipeLines:
|
||||
wipeLineHelper = 0;
|
||||
state = e_wipeLines;
|
||||
// no break
|
||||
|
||||
case e_wipeLines:
|
||||
if (canvasIsRowFilled(wipeLineHelper) == 1) {
|
||||
canvasWipeRow(wipeLineHelper);
|
||||
}
|
||||
wipeLineHelper++;
|
||||
if (wipeLineHelper >= canvas.height) {
|
||||
state = e_idle;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
state = e_idle;
|
||||
break;
|
||||
}
|
||||
|
||||
canvasShow();
|
||||
}
|
||||
|
||||
void displayTest3Init() {
|
||||
schAdd(displayTest3Exec, NULL, 0, 1000);
|
||||
}
|
||||
|
11
game-ctrl/disabled/displayTest3.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _DISPLAY_TEST_3_H_
|
||||
#define _DISPLAY_TEST_3_H_
|
||||
|
||||
|
||||
void displayTest3Init();
|
||||
void displayTest3Exec();
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // _DISPLAY_TEST_3_H_
|
@ -1,6 +1,6 @@
|
||||
#include "led.h"
|
||||
#include <msp430g2553.h>
|
||||
#include "PontCoopScheduler.h"
|
||||
#include "scheduler.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
@ -12,6 +12,10 @@ void ledGreenOff() {
|
||||
P1OUT &= ~BIT0;
|
||||
}
|
||||
|
||||
void ledGreenToggle() {
|
||||
P1OUT ^= BIT0;
|
||||
}
|
||||
|
||||
void ledBlueOn() {
|
||||
P1OUT |= BIT1;
|
||||
}
|
||||
@ -20,8 +24,12 @@ void ledBlueOff() {
|
||||
P1OUT &= ~BIT1;
|
||||
}
|
||||
|
||||
void ledBlueToggle() {
|
||||
P1OUT ^= BIT1;
|
||||
}
|
||||
|
||||
void ledExec(void *args) {
|
||||
static int i = 0;
|
||||
static uint16_t i = 0;
|
||||
|
||||
if (i == 0) {
|
||||
ledGreenOff();
|
||||
@ -41,7 +49,7 @@ void ledInit() {
|
||||
ledBlueOff();
|
||||
|
||||
|
||||
// schAdd(ledExec, NULL, 0, 50);
|
||||
// schAdd(ledExec, NULL, 0, 500);
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,10 @@
|
||||
|
||||
void ledBlueOff();
|
||||
void ledBlueOn();
|
||||
void ledBlueToggle();
|
||||
void ledGreenOff();
|
||||
void ledGreenOn();
|
||||
void ledGreenToggle();
|
||||
|
||||
void ledInit();
|
||||
void ledExec();
|
26
game-ctrl/display.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "display.h"
|
||||
#include "spi.h"
|
||||
|
||||
|
||||
|
||||
void displayInit() {
|
||||
}
|
||||
|
||||
|
||||
void displaySetValue(uint16_t v) {
|
||||
union {
|
||||
uint16_t value;
|
||||
uint8_t sendBuffer[2];
|
||||
} value;
|
||||
|
||||
value.value = v;
|
||||
|
||||
spiSendBegin(e_SPI_DISPLAY);
|
||||
|
||||
spiSendOctet(value.sendBuffer[0]);
|
||||
spiSendOctet(value.sendBuffer[1]);
|
||||
|
||||
spiSendEnd(e_SPI_DISPLAY);
|
||||
}
|
13
game-ctrl/display.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef _DISPLAY_H_
|
||||
#define _DISPLAY_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
void displayInit();
|
||||
void displaySetValue(uint16_t v);
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // _DISPLAY_H_
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
BIN
game-ctrl/docs/motion-definitions.pdf
Executable file
BIN
game-ctrl/docs/one-color.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
game-ctrl/docs/working-transfer.png
Normal file
After Width: | Height: | Size: 7.0 KiB |
153
game-ctrl/eeprom.c
Normal file
@ -0,0 +1,153 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
#include "eeprom.h"
|
||||
#include "spi.h"
|
||||
#include "scheduler.h"
|
||||
#include "display.h"
|
||||
#include "canvas.h"
|
||||
#include "../rgb-driver/colors.h"
|
||||
|
||||
|
||||
#define MAGIC 0xb003
|
||||
#define HIGHSCORE_ADDR 0x00
|
||||
#define DUMMY 0x00
|
||||
#define CMD_READ 0b00000011
|
||||
#define CMD_WRITE 0b00000010
|
||||
#define CMD_WRDI 0b00000100
|
||||
#define CMD_WREN 0b00000110
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint16_t magic;
|
||||
uint16_t highScore;
|
||||
uint16_t gameCounter;
|
||||
uint8_t flashColor;
|
||||
uint8_t brightness;
|
||||
uint8_t amplitude;
|
||||
} t_configBlock;
|
||||
|
||||
typedef union {
|
||||
t_configBlock v;
|
||||
uint8_t buffer[sizeof(t_configBlock)];
|
||||
} eepromBuf_t;
|
||||
|
||||
eepromBuf_t buf;
|
||||
|
||||
static void writeBuf() {
|
||||
spiSendBegin(e_SPI_EEPROM);
|
||||
spiSendOctet(CMD_WREN);
|
||||
spiSendEnd(e_SPI_EEPROM);
|
||||
|
||||
spiSendBegin(e_SPI_EEPROM);
|
||||
spiSendOctet(CMD_WRITE);
|
||||
spiSendOctet(HIGHSCORE_ADDR);
|
||||
for (uint8_t i = 0; i < sizeof(t_configBlock); i++) {
|
||||
spiSendOctet(buf.buffer[i]);
|
||||
}
|
||||
spiSendEnd(e_SPI_EEPROM);
|
||||
}
|
||||
|
||||
static void readBuf() {
|
||||
spiSendBegin(e_SPI_EEPROM);
|
||||
spiSendOctet(CMD_READ);
|
||||
spiReceiveOctet();
|
||||
spiSendOctet(HIGHSCORE_ADDR);
|
||||
spiReceiveOctet();
|
||||
for (uint8_t i = 0; i < sizeof(t_configBlock); i++) {
|
||||
spiSendOctet(DUMMY);
|
||||
buf.buffer[i] = spiReceiveOctet();
|
||||
}
|
||||
spiSendEnd(e_SPI_EEPROM);
|
||||
}
|
||||
|
||||
void eepromInit() {
|
||||
readBuf();
|
||||
|
||||
if (buf.v.magic != MAGIC) {
|
||||
memset(buf.buffer, 0, sizeof(t_configBlock));
|
||||
buf.v.magic = MAGIC;
|
||||
writeBuf();
|
||||
}
|
||||
}
|
||||
|
||||
void eepromCommit() {
|
||||
writeBuf();
|
||||
}
|
||||
|
||||
|
||||
void eepromShowValues() {
|
||||
canvasClear();
|
||||
canvasFillRow(0, _green);
|
||||
canvasShow();
|
||||
displaySetValue(buf.v.highScore);
|
||||
wait(2);
|
||||
canvasClear();
|
||||
canvasFillRow(1, _green);
|
||||
canvasShow();
|
||||
displaySetValue(MIN(buf.v.gameCounter, 9999));
|
||||
wait(2);
|
||||
canvasClear();
|
||||
canvasFillRow(2, _green);
|
||||
canvasShow();
|
||||
displaySetValue(buf.v.flashColor);
|
||||
wait(2);
|
||||
canvasClear();
|
||||
canvasFillRow(3, _green);
|
||||
canvasShow();
|
||||
displaySetValue(buf.v.brightness);
|
||||
wait(2);
|
||||
canvasClear();
|
||||
canvasFillRow(4, _green);
|
||||
canvasShow();
|
||||
displaySetValue(buf.v.amplitude);
|
||||
wait(2);
|
||||
}
|
||||
|
||||
uint16_t eepromReadHighScore() {
|
||||
return buf.v.highScore;
|
||||
}
|
||||
|
||||
void eepromSetHighScore(uint16_t v) {
|
||||
buf.v.highScore = v;
|
||||
writeBuf();
|
||||
}
|
||||
|
||||
uint8_t eepromReadFlashColor() {
|
||||
return buf.v.flashColor;
|
||||
}
|
||||
|
||||
void eepromSetFlashColor(uint8_t v) {
|
||||
buf.v.flashColor = v;
|
||||
}
|
||||
|
||||
uint8_t eepromReadBrightness() {
|
||||
return buf.v.brightness;
|
||||
}
|
||||
|
||||
void eepromSetBrightness(uint8_t v) {
|
||||
buf.v.brightness = v;
|
||||
}
|
||||
|
||||
uint8_t eepromReadAmplitude() {
|
||||
return buf.v.amplitude;
|
||||
}
|
||||
|
||||
void eepromSetAmplitude(uint8_t v) {
|
||||
buf.v.amplitude = v;
|
||||
}
|
||||
|
||||
uint16_t eepromReadGameCounter() {
|
||||
return buf.v.gameCounter;
|
||||
}
|
||||
|
||||
void eepromIncGameCounter() {
|
||||
buf.v.gameCounter += 1;
|
||||
writeBuf();
|
||||
}
|
||||
|
||||
void eepromClearGameCounter() {
|
||||
buf.v.gameCounter = 0;
|
||||
}
|
||||
|
||||
|
24
game-ctrl/eeprom.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef _EEPROM_H_
|
||||
#define _EEPROM_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
void eepromInit();
|
||||
void eepromCommit();
|
||||
void eepromShowValues();
|
||||
uint16_t eepromReadHighScore();
|
||||
void eepromSetHighScore(uint16_t v);
|
||||
uint8_t eepromReadFlashColor();
|
||||
void eepromSetFlashColor(uint8_t v);
|
||||
uint8_t eepromReadBrightness();
|
||||
void eepromSetBrightness(uint8_t v);
|
||||
uint8_t eepromReadAmplitude();
|
||||
void eepromSetAmplitude(uint8_t v);
|
||||
void eepromIncGameCounter();
|
||||
uint16_t eepromReadGameCounter();
|
||||
void eepromClearGameCounter();
|
||||
|
||||
|
||||
|
||||
#endif // _EEPROM_H_
|
2
game-ctrl/firmware.gdb
Normal file
@ -0,0 +1,2 @@
|
||||
target remote localhost:2000
|
||||
file firmware.elf
|
196
game-ctrl/game.c
Normal file
@ -0,0 +1,196 @@
|
||||
// #define STATE_DEBUGGING
|
||||
|
||||
#include "stddef.h"
|
||||
#include "stdint.h"
|
||||
|
||||
#include "game.h"
|
||||
#include "scheduler.h"
|
||||
#include "shapes.h"
|
||||
#include "canvas.h"
|
||||
#include "../rgb-driver/colors.h"
|
||||
#include "display.h"
|
||||
#include "sound.h"
|
||||
#include "eeprom.h"
|
||||
#include "buttons.h"
|
||||
|
||||
|
||||
#define GAME_CYCLE_TIME 10
|
||||
#define GAMEOVER_DELAY 10
|
||||
#define MAX_LEVEL 100
|
||||
|
||||
|
||||
static uint16_t delayFactor(uint16_t level) {
|
||||
return MAX_LEVEL + 1 - level;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
e_BootWait,
|
||||
e_Start, e_NewStone, e_Down, e_DownDelay,
|
||||
e_ClearRowInit, e_ClearRowNext, e_ClearRowCheck, e_ClearRowFlash, e_ClearRowFlashDelay, e_ClearRowWipe,
|
||||
e_GameOver, e_GameOverFill, e_GameOverWipe, e_GameOverDelay
|
||||
} state_t;
|
||||
|
||||
void gameExec(void *handle) {
|
||||
static state_t state = e_BootWait;
|
||||
static uint16_t bootWaitTime = 2500 / GAME_CYCLE_TIME;
|
||||
static uint8_t gameOverDelay;
|
||||
static uint8_t rowIndex;
|
||||
static uint16_t proceedDelay;
|
||||
static uint16_t level;
|
||||
static uint16_t filledLines;
|
||||
static uint16_t score;
|
||||
static bool newHighScoreAchieved;
|
||||
|
||||
static uint8_t clearCheckCnt;
|
||||
|
||||
#ifdef STATE_DEBUGGING
|
||||
displaySetValue(state);
|
||||
#endif
|
||||
// --- engine begin -------------------------------------------------------
|
||||
switch (state) {
|
||||
case e_BootWait:
|
||||
bootWaitTime -= 1;
|
||||
if (bootWaitTime == 0) {
|
||||
state = e_Start;
|
||||
}
|
||||
break;
|
||||
// --- phase: game --------------------------------------------------------
|
||||
case e_Start:
|
||||
canvasClear();
|
||||
soundCtrl(SOUND_START);
|
||||
level = 1;
|
||||
filledLines = 0;
|
||||
score = 0;
|
||||
newHighScoreAchieved = false;
|
||||
state = e_NewStone;
|
||||
break;
|
||||
|
||||
case e_NewStone:
|
||||
stoneCreate();
|
||||
if (stoneDraw()) {
|
||||
proceedDelay = delayFactor(level);
|
||||
state = e_DownDelay;
|
||||
} else {
|
||||
state = e_GameOver;
|
||||
}
|
||||
break;
|
||||
|
||||
case e_DownDelay:
|
||||
proceedDelay--;
|
||||
if (proceedDelay == 0) {
|
||||
state = e_Down;
|
||||
}
|
||||
break;
|
||||
|
||||
case e_Down:
|
||||
if (! stoneMoveDown()) {
|
||||
soundCtrl(SOUND_LOCK);
|
||||
stoneLock();
|
||||
state = e_ClearRowInit;
|
||||
} else {
|
||||
proceedDelay = delayFactor(level);
|
||||
state = e_DownDelay;
|
||||
}
|
||||
break;
|
||||
|
||||
// --- phase: clear rows --------------------------------------------------
|
||||
case e_ClearRowInit:
|
||||
clearCheckCnt = 0;
|
||||
state = e_ClearRowCheck;
|
||||
break;
|
||||
|
||||
case e_ClearRowNext:
|
||||
if (clearCheckCnt >= CANVAS_HEIGHT) {
|
||||
state = e_NewStone;
|
||||
} else {
|
||||
clearCheckCnt += 1;
|
||||
state = e_ClearRowCheck;
|
||||
}
|
||||
break;
|
||||
|
||||
case e_ClearRowCheck:
|
||||
if (canvasIsRowFilled(clearCheckCnt)) {
|
||||
score += level;
|
||||
if (score > eepromReadHighScore()) {
|
||||
newHighScoreAchieved = true;
|
||||
eepromSetHighScore(score);
|
||||
eepromCommit();
|
||||
}
|
||||
state = e_ClearRowFlash;
|
||||
} else {
|
||||
state = e_ClearRowNext;
|
||||
}
|
||||
break;
|
||||
|
||||
case e_ClearRowFlash:
|
||||
canvasFillRow(clearCheckCnt, eepromReadFlashColor());
|
||||
state = e_ClearRowFlashDelay;
|
||||
break;
|
||||
|
||||
case e_ClearRowFlashDelay:
|
||||
state = e_ClearRowWipe;
|
||||
break;
|
||||
|
||||
case e_ClearRowWipe:
|
||||
canvasWipeRow(clearCheckCnt);
|
||||
filledLines += 1;
|
||||
|
||||
if ((filledLines > 0) && ((filledLines % 10) == 0)) {
|
||||
if (level < MAX_LEVEL) {
|
||||
level += 1;
|
||||
}
|
||||
soundCtrl(SOUND_FANFARE);
|
||||
} else {
|
||||
soundCtrl(SOUND_PLING);
|
||||
}
|
||||
state = e_ClearRowNext;
|
||||
break;
|
||||
|
||||
// --- phase: game over ---------------------------------------------------
|
||||
case e_GameOver:
|
||||
soundCtrl(SOUND_GAMEOVER);
|
||||
rowIndex = CANVAS_HEIGHT;
|
||||
state = e_GameOverFill;
|
||||
break;
|
||||
|
||||
case e_GameOverFill:
|
||||
rowIndex--;
|
||||
canvasFillRow(rowIndex, newHighScoreAchieved ? _green : _red);
|
||||
if (rowIndex == 0) {
|
||||
state = e_GameOverWipe;
|
||||
}
|
||||
break;
|
||||
|
||||
case e_GameOverWipe:
|
||||
canvasWipeRow(rowIndex);
|
||||
rowIndex++;
|
||||
if (rowIndex == CANVAS_HEIGHT) {
|
||||
gameOverDelay = GAMEOVER_DELAY;
|
||||
state = e_GameOverDelay;
|
||||
}
|
||||
break;
|
||||
|
||||
case e_GameOverDelay:
|
||||
gameOverDelay--;
|
||||
if (gameOverDelay == 0) {
|
||||
state = e_Start;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// --- engine end ---------------------------------------------------------
|
||||
|
||||
canvasShow();
|
||||
|
||||
#ifndef STATE_DEBUGGING
|
||||
if (isGameActive()) {
|
||||
displaySetValue(score);
|
||||
} else {
|
||||
displaySetValue(eepromReadHighScore());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gameInit() {
|
||||
schAdd(gameExec, NULL, 0, GAME_CYCLE_TIME);
|
||||
}
|
||||
|
7
game-ctrl/game.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef _GAME_H_
|
||||
#define _GAME_H_
|
||||
|
||||
void gameInit();
|
||||
|
||||
|
||||
#endif // _GAME_H_
|
@ -4,11 +4,17 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "time.h"
|
||||
#include "PontCoopScheduler.h"
|
||||
#include "led.h"
|
||||
#include "displayDriver.h"
|
||||
#include "scheduler.h"
|
||||
#include "canvas.h"
|
||||
#include "displayTest.h"
|
||||
#include "game.h"
|
||||
#include "buttons.h"
|
||||
#include "shapes.h"
|
||||
#include "myrand.h"
|
||||
#include "spi.h"
|
||||
#include "display.h"
|
||||
#include "eeprom.h"
|
||||
#include "config.h"
|
||||
#include "sound.h"
|
||||
|
||||
|
||||
int main() {
|
||||
@ -22,15 +28,25 @@ int main() {
|
||||
BCSCTL2 = 0;
|
||||
BCSCTL3 = 0;
|
||||
|
||||
|
||||
timeInit();
|
||||
schInit();
|
||||
|
||||
ledInit();
|
||||
displayDriverInit();
|
||||
spiInit();
|
||||
eepromInit();
|
||||
displayInit();
|
||||
myRandInit();
|
||||
canvasInit();
|
||||
displayTestInit();
|
||||
soundInit();
|
||||
buttonsInit();
|
||||
|
||||
eepromShowValues();
|
||||
|
||||
if (isConfigMode()) {
|
||||
configInit();
|
||||
} else {
|
||||
shapesInit();
|
||||
gameInit();
|
||||
buttonsStart();
|
||||
}
|
||||
|
||||
__enable_interrupt();
|
||||
|
26
game-ctrl/myrand.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include <stdint.h>
|
||||
#include <msp430g2553.h>
|
||||
|
||||
#include "myrand.h"
|
||||
|
||||
void myRandInit() {
|
||||
ADC10CTL1 = INCH_10;
|
||||
ADC10CTL0 = SREF_1 | ADC10SHT_1 | REFON | ADC10ON;
|
||||
}
|
||||
|
||||
uint16_t myRandGet() {
|
||||
uint16_t res = 0;
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
ADC10CTL0 |= ENC | ADC10SC;
|
||||
|
||||
while ((ADC10CTL1 & ADC10BUSY));
|
||||
|
||||
res <<= 1;
|
||||
res |= ADC10MEM & 0x0001;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
8
game-ctrl/myrand.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef _MYRAND_H_
|
||||
#define _MYRAND_H_
|
||||
|
||||
void myRandInit();
|
||||
uint16_t myRandGet();
|
||||
|
||||
|
||||
#endif // _MYRAND_H_
|
117
game-ctrl/scheduler.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* PontCoopScheduler.c
|
||||
*
|
||||
* Originally created on: 29.08.2016
|
||||
* Author: wn
|
||||
*/
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <msp430g2553.h>
|
||||
#include "scheduler.h"
|
||||
|
||||
tTask tasks[MAX_NUM_OF_TASKS];
|
||||
|
||||
uint32_t seconds;
|
||||
|
||||
void schInit() {
|
||||
TACCR0 = 32;
|
||||
TACCTL0 = CCIE;
|
||||
TACTL = MC_1 | ID_0 | TASSEL_1 | TACLR;
|
||||
|
||||
for (uint16_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
tasks[i].delay = 0;
|
||||
tasks[i].period = 0;
|
||||
tasks[i].run = 0;
|
||||
tasks[i].exec = NULL;
|
||||
tasks[i].handle = NULL;
|
||||
}
|
||||
|
||||
seconds = 0;
|
||||
}
|
||||
|
||||
void __attribute__ ((interrupt (TIMER0_A0_VECTOR))) schUpdate() {
|
||||
static uint16_t milliSeconds = 0;
|
||||
if (milliSeconds >= 1000) {
|
||||
seconds += 1;
|
||||
milliSeconds = 0;
|
||||
}
|
||||
milliSeconds += 1;
|
||||
|
||||
for (uint16_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
if (tasks[i].exec != NULL) {
|
||||
if (tasks[i].delay == 0) {
|
||||
tasks[i].delay = tasks[i].period;
|
||||
tasks[i].run++;
|
||||
} else {
|
||||
tasks[i].delay--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void schAdd(void (*exec)(void *), void *handle, uint32_t delay, uint32_t period) {
|
||||
for (uint16_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
if (tasks[i].exec == NULL) {
|
||||
tasks[i].delay = delay;
|
||||
tasks[i].period = period;
|
||||
if (delay == 0 && period == 0) {
|
||||
tasks[i].run = 1;
|
||||
} else {
|
||||
tasks[i].run = 0;
|
||||
}
|
||||
tasks[i].exec = exec;
|
||||
tasks[i].handle = handle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void schDel(void (*exec)(void *), void *handle) {
|
||||
for (uint16_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
if ((tasks[i].exec == exec) && (tasks[i].handle == handle)) {
|
||||
tasks[i].exec = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void schExec() {
|
||||
for (uint16_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
|
||||
// synchronize access to tasks[].run
|
||||
__disable_interrupt();
|
||||
if (tasks[i].exec != NULL && tasks[i].run > 0) {
|
||||
tasks[i].run--;
|
||||
// synchronize access to tasks[].run
|
||||
// reenable interrupts before actually executing task
|
||||
__enable_interrupt();
|
||||
tasks[i].exec(tasks[i].handle);
|
||||
if (tasks[i].period == 0) {
|
||||
tasks[i].exec = NULL;
|
||||
}
|
||||
} else {
|
||||
// synchronize access to tasks[].run
|
||||
// reenable interrupts in case task is not yet executable
|
||||
__enable_interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t getSeconds() {
|
||||
uint32_t s;
|
||||
|
||||
__disable_interrupt();
|
||||
s = seconds;
|
||||
__enable_interrupt();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void wait(uint8_t t) {
|
||||
uint8_t startTime = getSeconds();
|
||||
|
||||
while (getSeconds() < (startTime + t));
|
||||
}
|
||||
|
@ -13,12 +13,12 @@
|
||||
|
||||
|
||||
|
||||
#define MAX_NUM_OF_TASKS 4
|
||||
#define MAX_NUM_OF_TASKS 2
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint32_t delay;
|
||||
uint32_t period;
|
||||
uint16_t delay;
|
||||
uint16_t period;
|
||||
uint8_t run;
|
||||
void (*exec)(void *handle);
|
||||
void *handle;
|
||||
@ -31,6 +31,7 @@ void schDel(void (*exec)(void *), void *handle);
|
||||
void schExec();
|
||||
void schUpdate();
|
||||
uint8_t schTaskCnt();
|
||||
|
||||
uint32_t getSeconds();
|
||||
void wait(uint8_t t);
|
||||
|
||||
#endif /* PONTCOOPSCHEDULER_H_ */
|
529
game-ctrl/shapes.c
Normal file
@ -0,0 +1,529 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "shapes.h"
|
||||
#include "myrand.h"
|
||||
#include "canvas.h"
|
||||
#include "../rgb-driver/colors.h"
|
||||
|
||||
|
||||
|
||||
|
||||
typedef enum { e_I=0, e_O, e_T, e_Z, e_S, e_L, e_J, e_ShapeInvalid } shape_t;
|
||||
typedef enum { e_MoveDown, e_MoveLeft, e_MoveRight, e_RotateLeft, e_RotateRight } direction_t;
|
||||
typedef enum { e_0, e_90, e_180, e_270, e_Keep } orientation_t;
|
||||
|
||||
typedef struct {
|
||||
shape_t shape;
|
||||
orientation_t orientation;
|
||||
uint8_t x; // column
|
||||
uint8_t y; // row
|
||||
bool locked;
|
||||
} stone_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
} pixel_t;
|
||||
|
||||
typedef struct {
|
||||
int8_t x;
|
||||
int8_t y;
|
||||
} offset_t;
|
||||
|
||||
typedef struct {
|
||||
offset_t set[4];
|
||||
offset_t reset[4];
|
||||
offset_t offset;
|
||||
} motion_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t color;
|
||||
uint8_t nullRotation;
|
||||
pixel_t draw[4];
|
||||
motion_t motion[5][4];
|
||||
} motionTable_t;
|
||||
|
||||
const motionTable_t motions[7] = { // size = number of implemented stones
|
||||
{ // I
|
||||
.color = _cyan,
|
||||
.nullRotation = 0,
|
||||
.draw = { { 0, 0}, { 0, 1}, { 0, 2}, { 0, 3} },
|
||||
.motion = {
|
||||
{
|
||||
// move down
|
||||
{ .set = { { 0, 4}, { 0, 4}, { 0, 4}, { 0, 4} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 1} }, // 0
|
||||
{ .set = { { 0, 1}, { 1, 1}, { 2, 1}, { 3, 1} }, .reset = { { 0, 0}, { 1, 0}, { 2, 0}, { 3, 0} }, .offset = { 0, 1} }, // 90
|
||||
{ .set = { { 0, 4}, { 0, 4}, { 0, 4}, { 0, 4} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 1} }, // 180
|
||||
{ .set = { { 0, 1}, { 1, 1}, { 2, 1}, { 3, 1} }, .reset = { { 0, 0}, { 1, 0}, { 2, 0}, { 3, 0} }, .offset = { 0, 1} } // 270
|
||||
},
|
||||
{
|
||||
// move left
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3} }, .reset = { { 0, 0}, { 0, 1}, { 0, 2}, { 0, 3} }, .offset = {-1, 0} }, // 0
|
||||
{ .set = { {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0} }, .reset = { { 3, 0}, { 3, 0}, { 3, 0}, { 3, 0} }, .offset = {-1, 0} }, // 90
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 2}, {-1, 3} }, .reset = { { 0, 0}, { 0, 1}, { 0, 2}, { 0, 3} }, .offset = {-1, 0} }, // 180
|
||||
{ .set = { {-1, 0}, {-1, 0}, {-1, 0}, {-1, 0} }, .reset = { { 3, 0}, { 3, 0}, { 3, 0}, { 3, 0} }, .offset = {-1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// move right
|
||||
{ .set = { { 1, 0}, { 1, 1}, { 1, 2}, { 1, 3} }, .reset = { { 0, 0}, { 0, 1}, { 0, 2}, { 0, 3} }, .offset = { 1, 0} }, // 0
|
||||
{ .set = { { 4, 0}, { 4, 0}, { 4, 0}, { 4, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 1, 0} }, // 90
|
||||
{ .set = { { 1, 0}, { 1, 1}, { 1, 2}, { 1, 3} }, .reset = { { 0, 0}, { 0, 1}, { 0, 2}, { 0, 3} }, .offset = { 1, 0} }, // 180
|
||||
{ .set = { { 4, 0}, { 4, 0}, { 4, 0}, { 4, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate left
|
||||
{ .set = { {-1, 1}, { 1, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 0}, { 0, 2}, { 0, 3}, { 0, 3} }, .offset = {-1, 1} }, // 0
|
||||
{ .set = { { 1,-2}, { 1,-1}, { 1, 1}, { 1, 1} }, .reset = { { 0, 0}, { 2, 0}, { 3, 0}, { 3, 0} }, .offset = { 1,-2} }, // 90
|
||||
{ .set = { {-1, 1}, { 1, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 0}, { 0, 2}, { 0, 3}, { 0, 3} }, .offset = {-1, 1} }, // 180
|
||||
{ .set = { { 1,-2}, { 1,-1}, { 1, 1}, { 1, 1} }, .reset = { { 0, 0}, { 2, 0}, { 3, 0}, { 3, 0} }, .offset = { 1,-2} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate right
|
||||
{ .set = { {-2, 1}, {-1, 1}, { 1, 1}, { 1, 1} }, .reset = { { 0, 0}, { 0, 2}, { 0, 3}, { 0, 3} }, .offset = {-2, 1} }, // 0
|
||||
{ .set = { { 1,-1}, { 1, 1}, { 1, 2}, { 1, 2} }, .reset = { { 0, 0}, { 2, 0}, { 3, 0}, { 3, 0} }, .offset = { 1,-1} }, // 90
|
||||
{ .set = { {-2, 1}, {-1, 1}, { 1, 1}, { 1, 1} }, .reset = { { 0, 0}, { 0, 2}, { 0, 3}, { 0, 3} }, .offset = {-2, 1} }, // 180
|
||||
{ .set = { { 1,-1}, { 1, 1}, { 1, 2}, { 1, 2} }, .reset = { { 0, 0}, { 2, 0}, { 3, 0}, { 3, 0} }, .offset = { 1,-1} }, // 270
|
||||
},
|
||||
}
|
||||
},
|
||||
{ // O
|
||||
.color = _yellow,
|
||||
.nullRotation = 1,
|
||||
.draw = { { 0, 0}, { 0, 1}, { 1, 0}, { 1, 1} },
|
||||
.motion = {
|
||||
{
|
||||
// move down
|
||||
{ .set = { { 0, 2}, { 1, 2}, { 1, 2}, { 1, 2} }, .reset = { { 0, 0}, { 1, 0}, { 1, 0}, { 1, 0} }, .offset = { 0, 1} }, // 0
|
||||
{ .set = { { 0, 2}, { 1, 2}, { 1, 2}, { 1, 2} }, .reset = { { 0, 0}, { 1, 0}, { 1, 0}, { 1, 0} }, .offset = { 0, 1} }, // 90
|
||||
{ .set = { { 0, 2}, { 1, 2}, { 1, 2}, { 1, 2} }, .reset = { { 0, 0}, { 1, 0}, { 1, 0}, { 1, 0} }, .offset = { 0, 1} }, // 180
|
||||
{ .set = { { 0, 2}, { 1, 2}, { 1, 2}, { 1, 2} }, .reset = { { 0, 0}, { 1, 0}, { 1, 0}, { 1, 0} }, .offset = { 0, 1} }, // 270
|
||||
},
|
||||
{
|
||||
// move left
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 1, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = {-1, 0} }, // 0
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 1, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = {-1, 0} }, // 90
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 1, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = {-1, 0} }, // 180
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 1, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = {-1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// move right
|
||||
{ .set = { { 2, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 1, 0} }, // 0
|
||||
{ .set = { { 2, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 1, 0} }, // 90
|
||||
{ .set = { { 2, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 1, 0} }, // 180
|
||||
{ .set = { { 2, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate left
|
||||
{ .set = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 0} }, // 0
|
||||
{ .set = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 0} }, // 90
|
||||
{ .set = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 0} }, // 180
|
||||
{ .set = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate right
|
||||
{ .set = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 0} }, // 0
|
||||
{ .set = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 0} }, // 90
|
||||
{ .set = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 0} }, // 180
|
||||
{ .set = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 0, 0} }, // 270
|
||||
},
|
||||
}
|
||||
},
|
||||
{ // T
|
||||
.color = _violet,
|
||||
.nullRotation = 0,
|
||||
.draw = { { 0, 0}, { 1, 0}, { 2, 0}, { 1, 1} },
|
||||
.motion = {
|
||||
{
|
||||
// move down
|
||||
{ .set = { { 0, 1}, { 2, 1}, { 1, 2}, { 1, 2} }, .reset = { { 0, 0}, { 1, 0}, { 2, 0}, { 2, 0} }, .offset = { 0, 1} }, // 0
|
||||
{ .set = { { 0, 2}, { 1, 3}, { 1, 3}, { 1, 3} }, .reset = { { 1, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 0, 1} }, // 90
|
||||
{ .set = { { 0, 2}, { 1, 2}, { 2, 2}, { 2, 2} }, .reset = { { 1, 0}, { 0, 1}, { 2, 1}, { 2, 1} }, .offset = { 0, 1} }, // 180
|
||||
{ .set = { { 0, 3}, { 1, 2}, { 1, 2}, { 1, 2} }, .reset = { { 0, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 0, 1} }, // 270
|
||||
},
|
||||
{
|
||||
// move left
|
||||
{ .set = { {-1, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .reset = { { 1, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = {-1, 0} }, // 0
|
||||
{ .set = { { 0, 0}, {-1, 1}, { 0, 2}, { 0, 2} }, .reset = { { 1, 0}, { 1, 1}, { 1, 2}, { 1, 2} }, .offset = {-1, 0} }, // 90
|
||||
{ .set = { {-1, 1}, { 0, 0}, { 0, 0}, { 0, 0} }, .reset = { { 1, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = {-1, 0} }, // 180
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 2}, {-1, 2} }, .reset = { { 0, 0}, { 0, 2}, { 1, 1}, { 1, 1} }, .offset = {-1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// move right
|
||||
{ .set = { { 3, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 1, 0} }, // 0
|
||||
{ .set = { { 2, 0}, { 2, 1}, { 2, 2}, { 2, 2} }, .reset = { { 0, 1}, { 1, 0}, { 1, 2}, { 1, 2} }, .offset = { 1, 0} }, // 90
|
||||
{ .set = { { 3, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .reset = { { 0, 0}, { 0, 1}, { 1, 0}, { 1, 0} }, .offset = { 1, 0} }, // 180
|
||||
{ .set = { { 1, 0}, { 1, 2}, { 2, 1}, { 2, 1} }, .reset = { { 0, 0}, { 0, 1}, { 0, 2}, { 0, 2} }, .offset = { 1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate left
|
||||
{ .set = { { 1,-1}, { 1,-1}, { 1,-1}, { 1,-1} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = { 1,-1} }, // 0
|
||||
{ .set = { { 2, 1}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 1, 0}, { 1, 0}, { 1, 0}, { 1, 0} }, .offset = { 0, 1} }, // 90
|
||||
{ .set = { { 1, 2}, { 1, 2}, { 1, 2}, { 1, 2} }, .reset = { { 2, 1}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = { 0, 0} }, // 180
|
||||
{ .set = { {-1, 1}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 0, 2}, { 0, 2}, { 0, 2}, { 0, 2} }, .offset = {-1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate right
|
||||
{ .set = { { 1,-1}, { 1,-1}, { 1,-1}, { 1,-1} }, .reset = { { 2, 0}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = { 0,-1} }, // 0
|
||||
{ .set = { { 2, 1}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 1, 2}, { 1, 2}, { 1, 2}, { 1, 2} }, .offset = { 0, 0} }, // 90
|
||||
{ .set = { { 1, 2}, { 1, 2}, { 1, 2}, { 1, 2} }, .reset = { { 0, 1}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 1, 0} }, // 180
|
||||
{ .set = { {-1, 1}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 0, 0}, { 0, 0}, { 0, 0}, { 0, 0} }, .offset = {-1, 1} }, // 270
|
||||
},
|
||||
}
|
||||
},
|
||||
{ // Z
|
||||
.color = _red,
|
||||
.nullRotation = 0,
|
||||
.draw = { { 0, 0}, { 1, 0}, { 1, 1}, { 2, 1} },
|
||||
.motion = {
|
||||
{
|
||||
// move down
|
||||
{ .set = { { 0, 1}, { 1, 2}, { 2, 2}, { 2, 2} }, .reset = { { 0, 0}, { 1, 0}, { 2, 1}, { 2, 1} }, .offset = { 0, 1} }, // 0
|
||||
{ .set = { { 1, 2}, { 0, 3}, { 0, 3}, { 0, 3} }, .reset = { { 1, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 0, 1} }, // 90
|
||||
{ .set = { { 0, 1}, { 1, 2}, { 2, 2}, { 2, 2} }, .reset = { { 0, 0}, { 1, 0}, { 2, 1}, { 2, 1} }, .offset = { 0, 1} }, // 180
|
||||
{ .set = { { 1, 2}, { 0, 3}, { 0, 3}, { 0, 3} }, .reset = { { 1, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 0, 1} }, // 270
|
||||
},
|
||||
{
|
||||
// move left
|
||||
{ .set = { {-1, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .reset = { { 1, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = {-1, 0} }, // 0
|
||||
{ .set = { {-1, 1}, {-1, 2}, { 0, 0}, { 0, 0} }, .reset = { { 1, 0}, { 1, 1}, { 0, 2}, { 0, 2} }, .offset = {-1, 0} }, // 90
|
||||
{ .set = { {-1, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .reset = { { 1, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = {-1, 0} }, // 180
|
||||
{ .set = { {-1, 1}, {-1, 2}, { 0, 0}, { 0, 0} }, .reset = { { 1, 0}, { 1, 1}, { 0, 2}, { 0, 2} }, .offset = {-1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// move right
|
||||
{ .set = { { 3, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .reset = { { 0, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 1, 0} }, // 0
|
||||
{ .set = { { 2, 0}, { 2, 1}, { 1, 2}, { 1, 2} }, .reset = { { 0, 1}, { 0, 2}, { 1, 0}, { 1, 0} }, .offset = { 1, 0} }, // 90
|
||||
{ .set = { { 3, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .reset = { { 0, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 1, 0} }, // 180
|
||||
{ .set = { { 2, 0}, { 2, 1}, { 1, 2}, { 1, 2} }, .reset = { { 0, 1}, { 0, 2}, { 1, 0}, { 1, 0} }, .offset = { 1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate left
|
||||
{ .set = { { 1,-1}, { 0, 1}, { 0, 1}, { 0, 1} }, .reset = { { 2, 1}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 0,-1} }, // 0
|
||||
{ .set = { { 0, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 2}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 0, 0} }, // 90
|
||||
{ .set = { { 1,-1}, { 0, 1}, { 0, 1}, { 0, 1} }, .reset = { { 2, 1}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 0,-1} }, // 180
|
||||
{ .set = { { 0, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 2}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 0, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate right
|
||||
{ .set = { { 1,-1}, { 0, 1}, { 0, 1}, { 0, 1} }, .reset = { { 2, 1}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 0,-1} }, // 0
|
||||
{ .set = { { 0, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 2}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 0, 0} }, // 90
|
||||
{ .set = { { 1,-1}, { 0, 1}, { 0, 1}, { 0, 1} }, .reset = { { 2, 1}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 0,-1} }, // 180
|
||||
{ .set = { { 0, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .reset = { { 0, 2}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 0, 0} }, // 270
|
||||
},
|
||||
}
|
||||
},
|
||||
{ // S
|
||||
.color = _green,
|
||||
.nullRotation = 0,
|
||||
.draw = { { 0, 1}, { 1, 1}, { 1, 0}, { 2, 0} },
|
||||
.motion = {
|
||||
{
|
||||
// move down
|
||||
{ .set = { { 0, 2}, { 1, 2}, { 2, 1}, { 2, 1} }, .reset = { { 0, 1}, { 1, 0}, { 2, 0}, { 2, 0} }, .offset = { 0, 1} }, // 0
|
||||
{ .set = { { 0, 2}, { 1, 3}, { 1, 3}, { 1, 3} }, .reset = { { 0, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 0, 1} }, // 90
|
||||
{ .set = { { 0, 2}, { 1, 2}, { 2, 1}, { 2, 1} }, .reset = { { 0, 1}, { 1, 0}, { 2, 0}, { 2, 0} }, .offset = { 0, 1} }, // 180
|
||||
{ .set = { { 0, 2}, { 1, 3}, { 1, 3}, { 1, 3} }, .reset = { { 0, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 0, 1} }, // 270
|
||||
},
|
||||
{
|
||||
// move left
|
||||
{ .set = { { 0, 0}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 2, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = {-1, 0} }, // 0
|
||||
{ .set = { {-1, 0}, {-1, 1}, { 0, 2}, { 0, 2} }, .reset = { { 0, 0}, { 1, 1}, { 1, 2}, { 1, 2} }, .offset = {-1, 0} }, // 90
|
||||
{ .set = { { 0, 0}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 2, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = {-1, 0} }, // 180
|
||||
{ .set = { {-1, 0}, {-1, 1}, { 0, 2}, { 0, 2} }, .reset = { { 0, 0}, { 1, 1}, { 1, 2}, { 1, 2} }, .offset = {-1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// move right
|
||||
{ .set = { { 2, 1}, { 3, 0}, { 3, 0}, { 3, 0} }, .reset = { { 1, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 1, 0} }, // 0
|
||||
{ .set = { { 1, 0}, { 2, 1}, { 2, 2}, { 2, 2} }, .reset = { { 0, 0}, { 0, 1}, { 1, 2}, { 1, 2} }, .offset = { 1, 0} }, // 90
|
||||
{ .set = { { 2, 1}, { 3, 0}, { 3, 0}, { 3, 0} }, .reset = { { 1, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 1, 0} }, // 180
|
||||
{ .set = { { 1, 0}, { 2, 1}, { 2, 2}, { 2, 2} }, .reset = { { 0, 0}, { 0, 1}, { 1, 2}, { 1, 2} }, .offset = { 1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate left
|
||||
{ .set = { { 0, 0}, { 0,-1}, { 0,-1}, { 0,-1} }, .reset = { { 0, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = { 0,-1} }, // 0
|
||||
{ .set = { { 1, 0}, { 2, 0}, { 2, 0}, { 2, 0} }, .reset = { { 0, 0}, { 1, 2}, { 1, 2}, { 1, 2} }, .offset = { 0, 0} }, // 90
|
||||
{ .set = { { 0, 0}, { 0,-1}, { 0,-1}, { 0,-1} }, .reset = { { 0, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = { 0,-1} }, // 180
|
||||
{ .set = { { 1, 0}, { 2, 0}, { 2, 0}, { 2, 0} }, .reset = { { 0, 0}, { 1, 2}, { 1, 2}, { 1, 2} }, .offset = { 0, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate right
|
||||
{ .set = { { 0, 0}, { 0,-1}, { 0,-1}, { 0,-1} }, .reset = { { 0, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = { 0,-1} }, // 0
|
||||
{ .set = { { 1, 0}, { 2, 0}, { 2, 0}, { 2, 0} }, .reset = { { 0, 0}, { 1, 2}, { 1, 2}, { 1, 2} }, .offset = { 0, 0} }, // 90
|
||||
{ .set = { { 0, 0}, { 0,-1}, { 0,-1}, { 0,-1} }, .reset = { { 0, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = { 0,-1} }, // 180
|
||||
{ .set = { { 1, 0}, { 2, 0}, { 2, 0}, { 2, 0} }, .reset = { { 0, 0}, { 1, 2}, { 1, 2}, { 1, 2} }, .offset = { 0, 0} }, // 270
|
||||
},
|
||||
}
|
||||
},
|
||||
{ // L
|
||||
.color = _orange,
|
||||
.nullRotation = 0,
|
||||
.draw = { { 0, 0}, { 0, 1}, { 0, 2}, { 1, 2} },
|
||||
.motion = {
|
||||
{
|
||||
// move down
|
||||
{ .set = { { 0, 3}, { 1, 3}, { 1, 3}, { 1, 3} }, .reset = { { 0, 0}, { 1, 2}, { 1, 2}, { 1, 2} }, .offset = { 0, 1} }, // 0
|
||||
{ .set = { { 1, 1}, { 2, 1}, { 0, 2}, { 0, 2} }, .reset = { { 0, 0}, { 1, 0}, { 2, 0}, { 2, 0} }, .offset = { 0, 1} }, // 90
|
||||
{ .set = { { 0, 1}, { 1, 3}, { 1, 3}, { 1, 3} }, .reset = { { 0, 0}, { 1, 0}, { 1, 0}, { 1, 0} }, .offset = { 0, 1} }, // 180
|
||||
{ .set = { { 0, 2}, { 1, 2}, { 2, 2}, { 2, 2} }, .reset = { { 2, 0}, { 0, 1}, { 1, 1}, { 1, 1} }, .offset = { 0, 1} }, // 270
|
||||
},
|
||||
{
|
||||
// move left
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 2}, {-1, 2} }, .reset = { { 0, 0}, { 0, 1}, { 1, 2}, { 0, 0} }, .offset = {-1, 0} }, // 0
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 0, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = {-1, 0} }, // 90
|
||||
{ .set = { { 0, 1}, { 0, 2}, {-1, 0}, {-1, 0} }, .reset = { { 1, 0}, { 1, 1}, { 1, 2}, { 1, 2} }, .offset = {-1, 0} }, // 180
|
||||
{ .set = { {-1, 1}, { 1, 0}, { 1, 0}, { 1, 0} }, .reset = { { 2, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = {-1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// move right
|
||||
{ .set = { { 1, 0}, { 1, 1}, { 2, 2}, { 2, 2} }, .reset = { { 0, 0}, { 0, 1}, { 0, 2}, { 0, 2} }, .offset = { 1, 0} }, // 0
|
||||
{ .set = { { 1, 1}, { 3, 0}, { 3, 0}, { 3, 0} }, .reset = { { 0, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 1, 0} }, // 90
|
||||
{ .set = { { 2, 0}, { 2, 1}, { 2, 2}, { 2, 2} }, .reset = { { 0, 0}, { 1, 1}, { 1, 2}, { 1, 2} }, .offset = { 1, 0} }, // 180
|
||||
{ .set = { { 3, 0}, { 3, 1}, { 3, 1}, { 3, 1} }, .reset = { { 0, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = { 1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate left
|
||||
{ .set = { {-1, 2}, {-2, 2}, {-2, 2}, {-2, 2} }, .reset = { { 0, 0}, { 1, 2}, { 1, 2}, { 1, 2} }, .offset = {-2, 1} }, // 0
|
||||
{ .set = { { 0,-1}, { 0,-2}, { 0,-2}, { 0,-2} }, .reset = { { 0, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = { 0,-2} }, // 90
|
||||
{ .set = { { 2, 0}, { 3, 0}, { 3, 0}, { 3, 0} }, .reset = { { 0, 0}, { 1, 2}, { 1, 2}, { 1, 2} }, .offset = { 1, 0} }, // 180
|
||||
{ .set = { { 2, 2}, { 2, 3}, { 2, 3}, { 2, 3} }, .reset = { { 0, 1}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = { 1, 1} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate right
|
||||
{ .set = { { 0, 3}, { 2, 2}, { 2, 2}, { 2, 2} }, .reset = { { 0, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 0, 2} }, // 0
|
||||
{ .set = { {-1, 0}, { 0, 2}, { 0, 2}, { 0, 2} }, .reset = { { 1, 0}, { 2, 0}, { 2, 0}, { 2, 0} }, .offset = {-1, 0} }, // 90
|
||||
{ .set = { {-1, 0}, { 1,-1}, { 1,-1}, { 1,-1} }, .reset = { { 1, 1}, { 1, 2}, { 1, 2}, { 1, 2} }, .offset = {-1,-1} }, // 180
|
||||
{ .set = { { 2,-1}, { 3, 1}, { 3, 1}, { 3, 1} }, .reset = { { 0, 1}, { 1, 1}, { 1, 1}, { 1, 1} }, .offset = { 2,-1} }, // 270
|
||||
},
|
||||
}
|
||||
},
|
||||
{ // J
|
||||
.color = _blue,
|
||||
.nullRotation = 0,
|
||||
.draw = { { 0, 2}, { 1, 0}, { 1, 1}, { 1, 2} },
|
||||
.motion = {
|
||||
{
|
||||
// move down
|
||||
{ .set = { { 0, 3}, { 1, 3}, { 1, 3}, { 1, 3} }, .reset = { { 0, 2}, { 1, 0}, { 1, 0}, { 1, 0} }, .offset = { 0, 1} }, // 0
|
||||
{ .set = { { 0, 2}, { 1, 2}, { 2, 2}, { 2, 2} }, .reset = { { 0, 0}, { 1, 1}, { 2, 1}, { 2, 1} }, .offset = { 0, 1} }, // 90
|
||||
{ .set = { { 0, 3}, { 1, 1}, { 1, 1}, { 1, 1} }, .reset = { { 0, 0}, { 1, 0}, { 1, 0}, { 1, 0} }, .offset = { 0, 1} }, // 180
|
||||
{ .set = { { 0, 1}, { 1, 1}, { 2, 2}, { 2, 2} }, .reset = { { 0, 0}, { 1, 0}, { 2, 0}, { 2, 0} }, .offset = { 0, 1} }, // 270
|
||||
},
|
||||
{
|
||||
// move left
|
||||
{ .set = { { 0, 0}, { 0, 1}, {-1, 2}, {-1, 2} }, .reset = { { 1, 0}, { 1, 1}, { 1, 2}, { 1, 2} }, .offset = {-1, 0} }, // 0
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 1}, {-1, 1} }, .reset = { { 0, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = {-1, 0} }, // 90
|
||||
{ .set = { {-1, 0}, {-1, 1}, {-1, 2}, {-1, 2} }, .reset = { { 1, 0}, { 0, 1}, { 0, 2}, { 0, 2} }, .offset = {-1, 0} }, // 180
|
||||
{ .set = { {-1, 0}, { 1, 1}, { 1, 1}, { 1, 1} }, .reset = { { 2, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = {-1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// move right
|
||||
{ .set = { { 2, 0}, { 2, 1}, { 2, 2}, { 2, 2} }, .reset = { { 1, 0}, { 1, 1}, { 0, 2}, { 0, 2} }, .offset = { 1, 0} }, // 0
|
||||
{ .set = { { 1, 0}, { 3, 1}, { 3, 1}, { 3, 1} }, .reset = { { 0, 0}, { 0, 1}, { 0, 1}, { 0, 1} }, .offset = { 1, 0} }, // 90
|
||||
{ .set = { { 1, 1}, { 1, 2}, { 2, 0}, { 2, 0} }, .reset = { { 0, 0}, { 0, 1}, { 0, 2}, { 0, 2} }, .offset = { 1, 0} }, // 180
|
||||
{ .set = { { 3, 0}, { 3, 1}, { 3, 1}, { 3, 1} }, .reset = { { 0, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = { 1, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate left
|
||||
{ .set = { { 1, 3}, {-1, 2}, {-1, 2}, {-1, 2} }, .reset = { { 1, 0}, { 1, 1}, { 0, 0}, { 0, 0} }, .offset = {-1, 2} }, // 0
|
||||
{ .set = { {-1, 1}, { 0,-1}, { 0,-1}, { 0,-1} }, .reset = { { 1, 1}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = {-1,-1} }, // 90
|
||||
{ .set = { { 2, 0}, { 0,-1}, { 0,-1}, { 0,-1} }, .reset = { { 0, 1}, { 0, 2}, { 0, 2}, { 0, 2} }, .offset = { 0,-1} }, // 180
|
||||
{ .set = { { 2, 2}, { 3, 0}, { 3, 0}, { 3, 0} }, .reset = { { 0, 0}, { 1, 0}, { 1, 0}, { 1, 0} }, .offset = { 2, 0} }, // 270
|
||||
},
|
||||
{
|
||||
// rotate right
|
||||
{ .set = { { 2, 2}, { 3, 2}, { 3, 2}, { 3, 2} }, .reset = { { 1, 0}, { 0, 2}, { 0, 2}, { 0, 2} }, .offset = { 1, 1} }, // 0
|
||||
{ .set = { { 0, 2}, { 0, 3}, { 0, 3}, { 0, 3} }, .reset = { { 0, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = { 0, 1} }, // 90
|
||||
{ .set = { {-1, 0}, {-2, 0}, {-2, 0}, {-2, 0} }, .reset = { { 1, 0}, { 0, 2}, { 0, 2}, { 0, 2} }, .offset = {-2, 0} }, // 180
|
||||
{ .set = { { 2,-1}, { 2,-2}, { 2,-2}, { 2,-2} }, .reset = { { 0, 0}, { 2, 1}, { 2, 1}, { 2, 1} }, .offset = { 1,-2} }, // 270
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const orientation_t nextOrientation[5][4] = { // 5 = number of directions to move, 4 = number of orientation a stone can have
|
||||
{ e_Keep, e_Keep, e_Keep, e_Keep }, // move down, current orientation: 0, 90, 180, 270
|
||||
{ e_Keep, e_Keep, e_Keep, e_Keep }, // move left
|
||||
{ e_Keep, e_Keep, e_Keep, e_Keep }, // move right
|
||||
{ e_270, e_0, e_90, e_180 }, // rotate left
|
||||
{ e_90, e_180, e_270, e_0 } // rotate right
|
||||
};
|
||||
|
||||
stone_t stone;
|
||||
shape_t nextShape;
|
||||
|
||||
static shape_t randomNextShape() {
|
||||
return ((shape_t[]){ e_I, e_O, e_T, e_Z, e_S, e_L, e_J })[myRandGet() % e_ShapeInvalid];
|
||||
}
|
||||
|
||||
void shapesInit() {
|
||||
stone.shape = e_ShapeInvalid;
|
||||
nextShape = randomNextShape();
|
||||
}
|
||||
|
||||
void stoneCreate() {
|
||||
stone.shape = nextShape;
|
||||
nextShape = randomNextShape();
|
||||
stone.orientation = e_0;
|
||||
stone.x = 4;
|
||||
stone.y = 0;
|
||||
stone.locked = false;
|
||||
}
|
||||
|
||||
void stoneLock() {
|
||||
stone.locked = true;
|
||||
}
|
||||
|
||||
uint8_t stoneIsValid() {
|
||||
return stone.shape != e_ShapeInvalid;
|
||||
}
|
||||
|
||||
// all of them return 1 in case of success and 0 in case of error
|
||||
static uint8_t move(direction_t direction) {
|
||||
// if this is a rotation and the shape is marked with nullRotation (just the O), do nothing
|
||||
// and return success
|
||||
if (motions[stone.shape].nullRotation && (direction == e_RotateLeft || direction == e_RotateRight)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// if the stone is already locked, do nothing
|
||||
if (stone.locked) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// check whether the pixels to move to are free
|
||||
if (canvasIsPixelFree(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[0].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].set[0].y) &&
|
||||
canvasIsPixelFree(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[1].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].set[1].y) &&
|
||||
canvasIsPixelFree(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[2].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].set[2].y) &&
|
||||
canvasIsPixelFree(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[3].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].set[3].y)) {
|
||||
// if so, reset the pixels the shape moves away from
|
||||
canvasSetPixel(stone.x + motions[stone.shape].motion[direction][stone.orientation].reset[0].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].reset[0].y,
|
||||
_off);
|
||||
canvasSetPixel(stone.x + motions[stone.shape].motion[direction][stone.orientation].reset[1].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].reset[1].y,
|
||||
_off);
|
||||
canvasSetPixel(stone.x + motions[stone.shape].motion[direction][stone.orientation].reset[2].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].reset[2].y,
|
||||
_off);
|
||||
canvasSetPixel(stone.x + motions[stone.shape].motion[direction][stone.orientation].reset[3].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].reset[3].y,
|
||||
_off);
|
||||
// and set the pixels the shape moves to to the shape's color
|
||||
canvasSetPixel(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[0].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].set[0].y,
|
||||
motions[stone.shape].color);
|
||||
canvasSetPixel(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[1].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].set[1].y,
|
||||
motions[stone.shape].color);
|
||||
canvasSetPixel(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[2].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].set[2].y,
|
||||
motions[stone.shape].color);
|
||||
canvasSetPixel(stone.x + motions[stone.shape].motion[direction][stone.orientation].set[3].x,
|
||||
stone.y + motions[stone.shape].motion[direction][stone.orientation].set[3].y,
|
||||
motions[stone.shape].color);
|
||||
// set the new origin of the shape
|
||||
stone.x += motions[stone.shape].motion[direction][stone.orientation].offset.x;
|
||||
stone.y += motions[stone.shape].motion[direction][stone.orientation].offset.y;
|
||||
// set the new orientation of the shape, if required
|
||||
stone.orientation = (nextOrientation[direction][stone.orientation] == e_Keep) ?
|
||||
stone.orientation :
|
||||
nextOrientation[direction][stone.orientation];
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nextStoneDraw() {
|
||||
miniCanvasClear();
|
||||
miniCanvasSetPixel(motions[nextShape].draw[0].x,
|
||||
motions[nextShape].draw[0].y,
|
||||
motions[nextShape].color);
|
||||
miniCanvasSetPixel(motions[nextShape].draw[1].x,
|
||||
motions[nextShape].draw[1].y,
|
||||
motions[nextShape].color);
|
||||
miniCanvasSetPixel(motions[nextShape].draw[2].x,
|
||||
motions[nextShape].draw[2].y,
|
||||
motions[nextShape].color);
|
||||
miniCanvasSetPixel(motions[nextShape].draw[3].x,
|
||||
motions[nextShape].draw[3].y,
|
||||
motions[nextShape].color);
|
||||
}
|
||||
|
||||
static void stoneJustDraw(uint8_t x, uint8_t y, shape_t shape) {
|
||||
canvasSetPixel(x + motions[shape].draw[0].x,
|
||||
y + motions[shape].draw[0].y,
|
||||
motions[shape].color);
|
||||
canvasSetPixel(x + motions[shape].draw[1].x,
|
||||
y + motions[shape].draw[1].y,
|
||||
motions[shape].color);
|
||||
canvasSetPixel(x + motions[shape].draw[2].x,
|
||||
y + motions[shape].draw[2].y,
|
||||
motions[shape].color);
|
||||
canvasSetPixel(x + motions[shape].draw[3].x,
|
||||
y + motions[shape].draw[3].y,
|
||||
motions[shape].color);
|
||||
}
|
||||
|
||||
uint8_t stoneDraw() {
|
||||
nextStoneDraw();
|
||||
|
||||
uint8_t res = 0;
|
||||
// check if the pixels the shape should be drawn at are free
|
||||
if (canvasIsPixelFree(stone.x + motions[stone.shape].draw[0].x,
|
||||
stone.y + motions[stone.shape].draw[0].y) &&
|
||||
canvasIsPixelFree(stone.x + motions[stone.shape].draw[1].x,
|
||||
stone.y + motions[stone.shape].draw[1].y) &&
|
||||
canvasIsPixelFree(stone.x + motions[stone.shape].draw[2].x,
|
||||
stone.y + motions[stone.shape].draw[2].y) &&
|
||||
canvasIsPixelFree(stone.x + motions[stone.shape].draw[3].x,
|
||||
stone.y + motions[stone.shape].draw[3].y)) {
|
||||
// if so, draw the shape
|
||||
stoneJustDraw(stone.x, stone.y, stone.shape);
|
||||
res = 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void stoneDrawConfigPattern() {
|
||||
stoneJustDraw(1, 0, e_I);
|
||||
stoneJustDraw(3, 4, e_O);
|
||||
stoneJustDraw(4, 7, e_T);
|
||||
stoneJustDraw(5, 10, e_Z);
|
||||
stoneJustDraw(1, 12, e_S);
|
||||
stoneJustDraw(5, 15, e_L);
|
||||
stoneJustDraw(1, 17, e_J);
|
||||
}
|
||||
|
||||
uint8_t stoneMoveDown() {
|
||||
return move(e_MoveDown);
|
||||
}
|
||||
|
||||
uint8_t stoneMoveLeft() {
|
||||
return move(e_MoveLeft);
|
||||
}
|
||||
|
||||
uint8_t stoneMoveRight() {
|
||||
return move(e_MoveRight);
|
||||
}
|
||||
|
||||
uint8_t stoneRotateLeft() {
|
||||
return move(e_RotateLeft);
|
||||
}
|
||||
|
||||
uint8_t stoneRotateRight() {
|
||||
return move(e_RotateRight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
19
game-ctrl/shapes.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef _SHAPES_H_
|
||||
#define _SHAPES_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void shapesInit();
|
||||
void stoneCreate();
|
||||
void stoneLock();
|
||||
uint8_t stoneIsValid();
|
||||
uint8_t stoneDraw();
|
||||
uint8_t stoneMoveDown();
|
||||
uint8_t stoneMoveLeft();
|
||||
uint8_t stoneMoveRight();
|
||||
uint8_t stoneRotateLeft();
|
||||
uint8_t stoneRotateRight();
|
||||
|
||||
void stoneDrawConfigPattern();
|
||||
|
||||
#endif // _SHAPES_H_
|
20
game-ctrl/sound.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include <stdint.h>
|
||||
#include "sound.h"
|
||||
#include "spi.h"
|
||||
#include "eeprom.h"
|
||||
|
||||
|
||||
|
||||
void soundInit() {
|
||||
soundCtrl(SOUND_COMMAND + SOUND_SUBCMD_AMPLITUDE + eepromReadAmplitude());
|
||||
}
|
||||
|
||||
|
||||
void soundCtrl(uint8_t cmd) {
|
||||
spiSendBegin(e_SPI_SOUND);
|
||||
|
||||
spiSendOctet(cmd);
|
||||
|
||||
spiSendEnd(e_SPI_SOUND);
|
||||
}
|
||||
|
15
game-ctrl/sound.h
Normal file
@ -0,0 +1,15 @@
|
||||
#ifndef _SOUND_H_
|
||||
#define _SOUND_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define MUTE_DELAY 30 // seconds
|
||||
|
||||
#include "../sound-driver/soundCodes.h"
|
||||
|
||||
void soundInit();
|
||||
void soundCtrl(uint8_t cmd);
|
||||
|
||||
|
||||
|
||||
#endif // _SOUND_H_
|
56
game-ctrl/spi.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include <msp430g2553.h>
|
||||
#include "spi.h"
|
||||
|
||||
void spiInit() {
|
||||
// SPI in master mode, most significant bit first
|
||||
UCB0CTL0 = UCCKPH | UCMST | UCMSB;
|
||||
// SPI timing config
|
||||
UCB0CTL1 = UCSSEL_3;
|
||||
// Faster than 8 ends up in strange communication errors
|
||||
// between the both MCUs.
|
||||
// With 8 the transfer of a complete 110 pixel canvas takes
|
||||
// about 720us.
|
||||
// 8 was still too fast and caused problems.
|
||||
UCB0BR0 = 16;
|
||||
UCB0BR1 = 0;
|
||||
|
||||
// BIT5: UCB0CLK
|
||||
// BIT6: UCB0SOMI
|
||||
// BIT7: UCB0SIMO
|
||||
P1SEL |= BIT5 | BIT6 | BIT7;
|
||||
P1SEL2 |= BIT5 | BIT6 | BIT7;
|
||||
// P1DIR |= BIT5 | BIT7;
|
||||
|
||||
// Device Select Lines: 0: Canvas, 1: Display, 2: Sound, 4: EEPROM
|
||||
P1DIR |= BIT0 | BIT1 | BIT2 | BIT4;
|
||||
// Disable all of them
|
||||
P1OUT |= BIT0 | BIT1 | BIT2 | BIT4;
|
||||
|
||||
// enable SPI module
|
||||
UCB0CTL1 &= ~UCSWRST;
|
||||
}
|
||||
|
||||
void spiSendBegin(t_SpiDeviceSelector d) {
|
||||
uint16_t bit = ((uint16_t[]){ BIT0, BIT1, BIT2, BIT4 })[d];
|
||||
P1OUT &= ~bit;
|
||||
}
|
||||
|
||||
void spiSendEnd(t_SpiDeviceSelector d) {
|
||||
while (UCB0STAT & UCBUSY);
|
||||
uint16_t bit = ((uint16_t[]){ BIT0, BIT1, BIT2, BIT4 })[d];
|
||||
P1OUT |= bit;
|
||||
}
|
||||
|
||||
void spiSendOctet(uint8_t v) {
|
||||
// wait for TX buffer empty
|
||||
while (!(UC0IFG & UCB0TXIFG));
|
||||
// load octet into TX buffer
|
||||
UCB0TXBUF = v;
|
||||
}
|
||||
|
||||
uint8_t spiReceiveOctet() {
|
||||
while (!(UC0IFG & UCB0RXIFG));
|
||||
uint8_t v = UCB0RXBUF;
|
||||
return v;
|
||||
}
|
||||
|
17
game-ctrl/spi.h
Normal file
@ -0,0 +1,17 @@
|
||||
#ifndef _SPI_H_
|
||||
#define _SPI_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
typedef enum { e_SPI_CANVAS, e_SPI_DISPLAY, e_SPI_SOUND, e_SPI_EEPROM } t_SpiDeviceSelector;
|
||||
|
||||
void spiInit();
|
||||
void spiSendBegin(t_SpiDeviceSelector d);
|
||||
void spiSendEnd(t_SpiDeviceSelector d);
|
||||
void spiSendOctet(uint8_t v);
|
||||
uint8_t spiReceiveOctet();
|
||||
|
||||
|
||||
|
||||
#endif // _SPI_H_
|
70
readme.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Tetris - Hardware and Software
|
||||
|
||||

|
||||
|
||||
Update Amplifier (separate input circuitry per PSG, it appears, that a silent PSG has a DC level on its output which is summarized to the AC output of the working PSG, so two input circuits with individual couping capacitor):
|
||||
|
||||

|
||||
|
||||
Update of the power switch of the amplifier (at appears, that the small transistor couldn't deliver enough current):
|
||||
|
||||

|
||||
|
||||
This Tetris implementation consists of a hardware and a software (running on that hardware).
|
||||
|
||||
The hardware utilizes four MSP430 microcontrollers for 1.) the game play, 2.) the play ground canvas, 3.) the score display and 4.) the sound effects.
|
||||
|
||||
Further documentation including calculations and drawing can be found in the `docs` subdirs of the four main subdirs.
|
||||
|
||||
## Game Play
|
||||
|
||||
Code is in subdir `game-ctrl` (https://gitea.hottis.de/wn/tetris/src/branch/main/game-ctrl).
|
||||
|
||||
In the firmware for this MSP430 microcontroller the whole game mechanics, reading the buttons, reading and writing the highscore EEPROM and the control of the peripherial microcontrollers are implemented.
|
||||
|
||||
The buttons are debounced using RC circuitry and Schmitt triggers and connected to GPIOs of the microcontroller.
|
||||
|
||||
The peripherial microcontrollers and the EEPROM are connected via SPI including individual chip select lines.
|
||||
|
||||

|
||||
|
||||
|
||||
## Play Ground Canvas
|
||||
|
||||
Code is in subdir `rgb-driver` (https://gitea.hottis.de/wn/tetris/src/branch/main/rgb-driver).
|
||||
|
||||
The play ground is implemented using a 10 * 20 matrix of PL9823 RGB LEDs which are controlled by another MSP430 microcontroller. The firmware for this microcontroller is implemented for performance and real time requirements in assembly code. Through some discret logic the signals for PL9823 LEDs are generated. Major challenge was to generated the signals according the datasheet of all 200 (including a mini canvas for the stone preview: 212) LEDs in real time without interrupts.
|
||||
|
||||
The communcation with the game play controller is implemented as a sequences of tuples of LED address (0 to 211) and color code. A single octet of 253 where the LED address is expected is taken as the end-of-telegram mark. Readiness to receive a telegram is signaled to the game play controller via a single line connected to a GPIO of the game play controller.
|
||||
|
||||

|
||||
|
||||
Details are here https://gitea.hottis.de/wn/tetris/src/branch/main/rgb-driver/readme.md
|
||||
|
||||
|
||||
## Score Display
|
||||
|
||||
Code is in subdir `display-driver` (https://gitea.hottis.de/wn/tetris/src/branch/main/display-driver).
|
||||
|
||||
In the first place, a MAX7221 was meant to be used for connecting a multiple digit seven-segment display. However, it appears, that the MAX7221 requires 3.5V as minimum voltage for the high-level, which caan't be provided by the MSP430 (which runs on 3.3V) and level-shifters haven't been around. Thus, the minimal required amount of functionality of the MAX7221 has been implemented in C on an MSP430. Just four digits are supported.
|
||||
|
||||
Communication with the game play controller is just a 16 bit number to be displayed.
|
||||
|
||||

|
||||
|
||||
|
||||
## Sound Effects
|
||||
|
||||
Code is in subdir `sound-driver` (https://gitea.hottis.de/wn/tetris/src/branch/main/sound-driver).
|
||||
|
||||
An MSP430 microcontroller and two mediaeval AY-3-8913 sound chips are deployed. The sound chips themselve run on 5V, their 8-bit-address/data bus is connected to the port 2 (bit 0 to 7) of the microcontroller. The bus control signal `_CS`, `BC1` and `BDIR` are generated in software and provided via GPIOs.
|
||||
|
||||
An amplifier following the proposal of the AY-3-8913 datasheet is implemented using a LM386 chip. A MOSFET BS108 controlled via a GPIO is use the shortcut the input of the amplifier to ground to mute sound effects.
|
||||
|
||||
The clock generator proposed by the AY-3-8913 does not work reliably, so an alternative design from "The Art of Electronics" has been used.
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
36
rgb-driver/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
TOOLCHAIN_PREFIX=/opt/msp430-gcc
|
||||
CC=$(TOOLCHAIN_PREFIX)/bin/msp430-elf-gcc
|
||||
OBJDUMP=$(TOOLCHAIN_PREFIX)/bin/msp430-elf-objdump
|
||||
|
||||
MCU=msp430g2553
|
||||
ARTIFACT=firmware
|
||||
COMMON=-Wall -mmcu=$(MCU) -std=gnu99 -I $(TOOLCHAIN_PREFIX)/include -Os -g0 -fdata-sections -ffunction-sections
|
||||
#COMMON+= -ggdb -gdwarf-2 # debug
|
||||
CFLAGS=$(COMMON)
|
||||
ASFLAGS=$(COMMON) -D__ASSEMBLER__
|
||||
|
||||
LDFLAGS=-L $(TOOLCHAIN_PREFIX)/include -Wl,-Map,firmware.map -nostdlib -nostartfiles -T $(MCU).ld
|
||||
|
||||
$(ARTIFACT).elf: colors.o main.o
|
||||
$(CC) -o $@ $(LDFLAGS) $^
|
||||
$(OBJDUMP) -D $(ARTIFACT).elf > $(ARTIFACT).txt
|
||||
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
.S.o:
|
||||
$(CC) $(ASFLAGS) -c $<
|
||||
|
||||
|
||||
.PHONY: all
|
||||
all: $(ARTIFACT).elf
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm -f *.o *.elf *.map *.txt
|
||||
|
||||
.PHONY: upload
|
||||
upload: $(ARTIFACT).elf
|
||||
mspdebug rf2500 "prog $(ARTIFACT).elf"
|
||||
|
10
rgb-driver/canvasSize.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef _CANVAS_SIZE_H_
|
||||
#define _CANVAS_SIZE_H_
|
||||
|
||||
#define CANVAS_WIDTH 10
|
||||
#define CANVAS_HEIGHT 20
|
||||
|
||||
#define MINI_CANVAS_WIDTH 3
|
||||
#define MINI_CANVAS_HEIGHT 4
|
||||
|
||||
#endif // _CANVAS_SIZE_H_
|
55
rgb-driver/colors.S
Normal file
@ -0,0 +1,55 @@
|
||||
#include "colors.h"
|
||||
|
||||
.section ".rodata","a"
|
||||
;; color definitions according to
|
||||
;; https://learn.sparkfun.com/tutorials/lilypad-protosnap-plus-activity-guide/3-custom-color-mixing
|
||||
colors:
|
||||
.global colors
|
||||
;; red, green, blue, padding
|
||||
.byte 0x00, 0x00, 0x00, 0 ;; off
|
||||
|
||||
.byte 0x00>>5, 0x00>>5, 0xff>>5, 0 ;; blue
|
||||
.byte 0x00>>5, 0xff>>5, 0x00>>5, 0 ;; green
|
||||
.byte 0xff>>5, 0x80>>5, 0x00>>5, 0 ;; orange
|
||||
.byte 0x80>>5, 0x00>>5, 0xff>>5, 0 ;; violet
|
||||
.byte 0x00>>5, 0xff>>5, 0xff>>5, 0 ;; cyan
|
||||
.byte 0xff>>5, 0xff>>5, 0x00>>5, 0 ;; yellow
|
||||
.byte 0xff>>5, 0x00>>5, 0x00>>5, 0 ;; red
|
||||
.byte 0xff>>5, 0xff>>5, 0xff>>5, 0 ;; white
|
||||
|
||||
.byte 0x00>>4, 0x00>>4, 0xff>>4, 0 ;; blue
|
||||
.byte 0x00>>4, 0xff>>4, 0x00>>4, 0 ;; green
|
||||
.byte 0xff>>4, 0x80>>4, 0x00>>4, 0 ;; orange
|
||||
.byte 0x80>>4, 0x00>>4, 0xff>>4, 0 ;; violet
|
||||
.byte 0x00>>4, 0xff>>4, 0xff>>4, 0 ;; cyan
|
||||
.byte 0xff>>4, 0xff>>4, 0x00>>4, 0 ;; yellow
|
||||
.byte 0xff>>4, 0x00>>4, 0x00>>4, 0 ;; red
|
||||
.byte 0xff>>4, 0xff>>4, 0xff>>4, 0 ;; white
|
||||
|
||||
.byte 0x00>>3, 0x00>>3, 0xff>>3, 0 ;; blue
|
||||
.byte 0x00>>3, 0xff>>3, 0x00>>3, 0 ;; green
|
||||
.byte 0xff>>3, 0x80>>3, 0x00>>3, 0 ;; orange
|
||||
.byte 0x80>>3, 0x00>>3, 0xff>>3, 0 ;; violet
|
||||
.byte 0x00>>3, 0xff>>3, 0xff>>3, 0 ;; cyan
|
||||
.byte 0xff>>3, 0xff>>3, 0x00>>3, 0 ;; yellow
|
||||
.byte 0xff>>3, 0x00>>3, 0x00>>3, 0 ;; red
|
||||
.byte 0xff>>3, 0xff>>3, 0xff>>3, 0 ;; white
|
||||
|
||||
.byte 0x00>>2, 0x00>>2, 0xff>>2, 0 ;; blue
|
||||
.byte 0x00>>2, 0xff>>2, 0x00>>2, 0 ;; green
|
||||
.byte 0xff>>2, 0x80>>2, 0x00>>2, 0 ;; orange
|
||||
.byte 0x80>>2, 0x00>>2, 0xff>>2, 0 ;; violet
|
||||
.byte 0x00>>2, 0xff>>2, 0xff>>2, 0 ;; cyan
|
||||
.byte 0xff>>2, 0xff>>2, 0x00>>2, 0 ;; yellow
|
||||
.byte 0xff>>2, 0x00>>2, 0x00>>2, 0 ;; red
|
||||
.byte 0xff>>2, 0xff>>2, 0xff>>2, 0 ;; white
|
||||
|
||||
.byte 0x00>>1, 0x00>>1, 0xff>>1, 0 ;; blue
|
||||
.byte 0x00>>1, 0xff>>1, 0x00>>1, 0 ;; green
|
||||
.byte 0xff>>1, 0x80>>1, 0x00>>1, 0 ;; orange
|
||||
.byte 0x80>>1, 0x00>>1, 0xff>>1, 0 ;; violet
|
||||
.byte 0x00>>1, 0xff>>1, 0xff>>1, 0 ;; cyan
|
||||
.byte 0xff>>1, 0xff>>1, 0x00>>1, 0 ;; yellow
|
||||
.byte 0xff>>1, 0x00>>1, 0x00>>1, 0 ;; red
|
||||
.byte 0xff>>1, 0xff>>1, 0xff>>1, 0 ;; white
|
||||
|
21
rgb-driver/colors.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef _COLORS_H_
|
||||
#define _COLORS_H_
|
||||
|
||||
|
||||
#define _off 0
|
||||
|
||||
#define _blue 1
|
||||
#define _green 2
|
||||
#define _orange 3
|
||||
#define _violet 4
|
||||
#define _cyan 5
|
||||
#define _yellow 6
|
||||
#define _red 7
|
||||
#define _white 8
|
||||
|
||||
#define _brightness_offset 8
|
||||
#define _brightness_shifts 5
|
||||
#define _color_end (_brightness_offset * _brightness_shifts)
|
||||
|
||||
|
||||
#endif
|
BIN
rgb-driver/docs/74hc74-function-table.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
rgb-driver/docs/TertiaryColorWheel_Chart.png
Normal file
After Width: | Height: | Size: 180 KiB |
BIN
rgb-driver/docs/comm_game_ctrl_01.png
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
rgb-driver/docs/cycler_working_first_octets.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
rgb-driver/docs/cycler_working_last_octets.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
rgb-driver/docs/five_leds.png
Normal file
After Width: | Height: | Size: 8.5 KiB |
BIN
rgb-driver/docs/pulse_complete.png
Normal file
After Width: | Height: | Size: 8.2 KiB |
BIN
rgb-driver/docs/pulse_long.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
rgb-driver/docs/pulse_short.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
rgb-driver/docs/reset-circuit.jpeg
Normal file
After Width: | Height: | Size: 726 KiB |
BIN
rgb-driver/docs/reset-signal.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
rgb-driver/docs/schematics.jpeg
Normal file
After Width: | Height: | Size: 899 KiB |
BIN
rgb-driver/docs/signals01.png
Normal file
After Width: | Height: | Size: 326 KiB |
BIN
rgb-driver/docs/six_leds.png
Normal file
After Width: | Height: | Size: 8.6 KiB |
BIN
rgb-driver/docs/timing.png
Normal file
After Width: | Height: | Size: 49 KiB |
334
rgb-driver/main.S
Normal file
@ -0,0 +1,334 @@
|
||||
#include <msp430g2553.h>
|
||||
#include "colors.h"
|
||||
#include "canvasSize.h"
|
||||
|
||||
|
||||
#define PC r0
|
||||
#define SP r1
|
||||
#define SR r2
|
||||
|
||||
|
||||
;; ----------------------------------------------------
|
||||
;; --- r4, r5 and r6 must not be used for any other ---
|
||||
;; --- purpose ---
|
||||
;; required for communication between drawscreen and isr
|
||||
#define SIGNAL_REGISTER r4
|
||||
#define SIGNAL_OCTET_DONE 0x01
|
||||
#define SIGNAL_ISR_ENABLE 0x02
|
||||
#define SIGNAL_ALL_DATA_DONE 0x04
|
||||
#define SIGNAL_INIT_VALUE SIGNAL_OCTET_DONE
|
||||
|
||||
;; required for handover of data between drawscreen and isr
|
||||
#define DATA_REGISTER r5
|
||||
|
||||
;; required for sequencing of isr
|
||||
#define BIT_COUNTER_REGISTER r6
|
||||
#define BIT_COUNTER_INIT_VALUE 0x01
|
||||
;; ----------------------------------------------------
|
||||
|
||||
|
||||
;; 2.48us
|
||||
#define TIMER_COMPLETE 45
|
||||
;; 1.18us
|
||||
#define TIMER_LONG 22
|
||||
;; 550ns
|
||||
#define TIMER_SHORT 10
|
||||
|
||||
.macro set_data_bit
|
||||
bis #BIT0, &P1OUT
|
||||
.endm
|
||||
.macro clear_data_bit
|
||||
bic #BIT0, &P1OUT
|
||||
.endm
|
||||
.macro set_output_enable
|
||||
bis #BIT1, &P1OUT
|
||||
.endm
|
||||
.macro clear_output_enable
|
||||
bic #BIT1, &P1OUT
|
||||
.endm
|
||||
.macro set_debug
|
||||
bis #BIT2, &P1OUT
|
||||
.endm
|
||||
.macro clear_debug
|
||||
bic #BIT2, &P1OUT
|
||||
.endm
|
||||
.macro set_signal_waiting_for_data
|
||||
bis #BIT3, &P1OUT
|
||||
.endm
|
||||
.macro clear_signal_waiting_for_data
|
||||
bic #BIT3, &P1OUT
|
||||
.endm
|
||||
|
||||
.section ".data"
|
||||
screendata:
|
||||
.rept (CANVAS_HEIGHT*CANVAS_WIDTH) + (MINI_CANVAS_HEIGHT*MINI_CANVAS_WIDTH) ;; number of leds in hardward
|
||||
.byte 0
|
||||
.endr
|
||||
screendataend:
|
||||
.byte 0xff
|
||||
|
||||
|
||||
;; .text is the name of the section, it is a hint for the linker to
|
||||
;; allocate the section
|
||||
;; ax: a means allocatable by linker, x means executable
|
||||
;; @progbits is a hint for the linker to allocate this section into
|
||||
;; program memory (flash)
|
||||
.section ".text","ax",@progbits
|
||||
;; ----------------------------------------------------
|
||||
_start:
|
||||
;; disable watchdog
|
||||
mov.w #WDTPW|WDTHOLD,&WDTCTL
|
||||
|
||||
;; configure clock system to the highest frequency
|
||||
mov.b #DCO0|DCO1|DCO2,&DCOCTL
|
||||
mov.b #XT2OFF|RSEL0|RSEL1|RSEL2|RSEL3,&BCSCTL1
|
||||
mov.b #0,&BCSCTL2
|
||||
mov.b #0,&BCSCTL3
|
||||
|
||||
;; initialize stack pointer with value from linker
|
||||
mov.w #__stack, SP
|
||||
|
||||
init:
|
||||
;; configuration of GPIO Ports
|
||||
;; BIT0: data bit
|
||||
;; BIT1: output enable
|
||||
;; BIT2: debug
|
||||
;; BIT3: Signal waiting for data
|
||||
mov.b #BIT0|BIT1|BIT2|BIT3, &P1DIR
|
||||
mov.b #0,&P1OUT
|
||||
;; BIT4: spi, UCB0STE
|
||||
;; BIT5: spi, UCB0CLK
|
||||
;; BIT6: spi, UCB0SOMI
|
||||
;; BIT7: spi, UCB0SIMO
|
||||
mov.b #BIT4|BIT5|BIT6|BIT7, &P1SEL
|
||||
mov.b #BIT4|BIT5|BIT6|BIT7, &P1SEL2
|
||||
;; BIT4: long pulse
|
||||
;; BIT1: short pulse
|
||||
mov.b #BIT1|BIT4,&P2DIR
|
||||
mov.b #BIT1|BIT4,&P2SEL
|
||||
|
||||
;; timer configuration
|
||||
;; configure and stop timer
|
||||
;; cycle time is 56.25ns
|
||||
mov.w #ID_0|MC_0|TACLR|TASSEL_2,&TA1CTL
|
||||
;; 2.0us
|
||||
mov.w #TIMER_COMPLETE,&TA1CCR0
|
||||
;; a bit less
|
||||
mov.w #TIMER_SHORT,&TA1CCR1
|
||||
mov.w #TIMER_LONG,&TA1CCR2
|
||||
;; configure output mode for TA0.1
|
||||
mov.w #CCIE,&TA1CCTL0
|
||||
mov.w #OUTMOD_7,&TA1CCTL1
|
||||
mov.w #OUTMOD_7,&TA1CCTL2
|
||||
|
||||
;; spi configuration
|
||||
;; USCI B to slave mode, enable STE and most significant bit first
|
||||
mov.b #UCCKPH|UCSYNC|UCMODE_2|UCMSB, &UCB0CTL0
|
||||
mov.b #0x00, &UCB0CTL1
|
||||
|
||||
;; make sure the isr will not immediately start
|
||||
mov.b #SIGNAL_INIT_VALUE, SIGNAL_REGISTER
|
||||
|
||||
;; start timer in up mode
|
||||
bis.w #MC0,&TA1CTL
|
||||
;; enable interrupts
|
||||
eint
|
||||
|
||||
|
||||
;; ----------------------------------------------------
|
||||
mainloop:
|
||||
call #resetscreen
|
||||
|
||||
mainloop_draw:
|
||||
call #drawscreen
|
||||
|
||||
set_signal_waiting_for_data
|
||||
|
||||
call #receivedata
|
||||
|
||||
clear_signal_waiting_for_data
|
||||
|
||||
jmp mainloop_draw
|
||||
|
||||
|
||||
;; ----------------------------------------------------
|
||||
resetscreen:
|
||||
push r7
|
||||
push r8
|
||||
|
||||
;; reset screen data
|
||||
mov.w #screendata, r7
|
||||
mov.w #screendataend, r8
|
||||
resetscreen_continue:
|
||||
mov.b #_off, @r7
|
||||
inc.w r7
|
||||
cmp.w r7, r8
|
||||
jnz resetscreen_continue
|
||||
|
||||
pop r8
|
||||
pop r7
|
||||
ret
|
||||
|
||||
|
||||
;; ----------------------------------------------------
|
||||
receivedata:
|
||||
push r9
|
||||
push r10
|
||||
|
||||
;; wait for first octet
|
||||
receivedata_wait_for_control_octet:
|
||||
bit #UCB0RXIFG, &UC0IFG
|
||||
jz receivedata_wait_for_control_octet
|
||||
|
||||
;; get control or address octet from buffer register
|
||||
mov.b UCB0RXBUF, r9
|
||||
;; check whether value == 0xfe (no more data)
|
||||
cmp.b #0xfe, r9
|
||||
;; no more data
|
||||
jz receivedata_end
|
||||
|
||||
;; it is an address octet
|
||||
receivedata_wait_for_octet:
|
||||
bit #UCB0RXIFG, &UC0IFG
|
||||
jz receivedata_wait_for_octet
|
||||
|
||||
;; get data octet from buffer register
|
||||
mov.b UCB0RXBUF, r10
|
||||
;; move it to the destination
|
||||
mov.b r10, screendata(r9)
|
||||
;; next address/control octet
|
||||
jmp receivedata_wait_for_control_octet
|
||||
|
||||
receivedata_end:
|
||||
pop r10
|
||||
pop r9
|
||||
ret
|
||||
|
||||
|
||||
;; ----------------------------------------------------
|
||||
drawscreen:
|
||||
push r7
|
||||
push r8
|
||||
push r9
|
||||
|
||||
#define DATA_NEXT_ADDRESS_REGISTER r7
|
||||
#define DATA_END_ADDRESS_REGISTER r8
|
||||
#define NEXT_DATA_REGISTER r9
|
||||
|
||||
;; initialize bit-counter for isr
|
||||
mov.b #BIT_COUNTER_INIT_VALUE, BIT_COUNTER_REGISTER
|
||||
;; initialize isr-sync register, signal BYTE_DONE for the first start
|
||||
mov.b #SIGNAL_OCTET_DONE, SIGNAL_REGISTER
|
||||
|
||||
;; screen data start/next into r7
|
||||
mov.w #screendata, DATA_NEXT_ADDRESS_REGISTER
|
||||
;; screen data end into r8
|
||||
mov.w #screendataend, DATA_END_ADDRESS_REGISTER
|
||||
|
||||
drawscreen_continue:
|
||||
;; prepare next byte to handle by isr
|
||||
cmp.w DATA_NEXT_ADDRESS_REGISTER, DATA_END_ADDRESS_REGISTER
|
||||
jz drawscreen_data_done
|
||||
|
||||
;; load next data byte
|
||||
mov.b @DATA_NEXT_ADDRESS_REGISTER, NEXT_DATA_REGISTER
|
||||
inc.w DATA_NEXT_ADDRESS_REGISTER
|
||||
|
||||
;; multiple color code by four to get color data
|
||||
rla.b NEXT_DATA_REGISTER
|
||||
rla.b NEXT_DATA_REGISTER
|
||||
|
||||
;; enable isr
|
||||
bis #SIGNAL_ISR_ENABLE, SIGNAL_REGISTER
|
||||
|
||||
drawscreen_wait_for_isr_0:
|
||||
;; check bit0 in sync register
|
||||
bit #SIGNAL_OCTET_DONE, SIGNAL_REGISTER
|
||||
jz drawscreen_wait_for_isr_0
|
||||
;; load data
|
||||
mov.b colors(NEXT_DATA_REGISTER), DATA_REGISTER
|
||||
;; clear BYTE_DONE
|
||||
bic #SIGNAL_OCTET_DONE, SIGNAL_REGISTER
|
||||
drawscreen_wait_for_isr_1:
|
||||
;; check bit0 in sync register
|
||||
bit #SIGNAL_OCTET_DONE, SIGNAL_REGISTER
|
||||
jz drawscreen_wait_for_isr_1
|
||||
;; load data
|
||||
mov.b colors+1(NEXT_DATA_REGISTER), DATA_REGISTER
|
||||
;; clear BYTE_DONE
|
||||
bic #SIGNAL_OCTET_DONE, SIGNAL_REGISTER
|
||||
drawscreen_wait_for_isr_2:
|
||||
;; check bit0 in sync register
|
||||
bit #SIGNAL_OCTET_DONE, SIGNAL_REGISTER
|
||||
jz drawscreen_wait_for_isr_2
|
||||
;; load data
|
||||
mov.b colors+2(NEXT_DATA_REGISTER), DATA_REGISTER
|
||||
;; clear BYTE_DONE
|
||||
bic #SIGNAL_OCTET_DONE, SIGNAL_REGISTER
|
||||
|
||||
;; continue
|
||||
jmp drawscreen_continue
|
||||
|
||||
drawscreen_data_done:
|
||||
;; signal all data processed, isr finish
|
||||
bis #SIGNAL_ALL_DATA_DONE, SIGNAL_REGISTER
|
||||
|
||||
pop r9
|
||||
pop r8
|
||||
pop r7
|
||||
ret
|
||||
|
||||
|
||||
;; ----------------------------------------------------
|
||||
; --- timer isr ---
|
||||
;; r6: exclusively used by isr as bit-counter
|
||||
shifter_isr:
|
||||
;; check isr enable bit
|
||||
bit #SIGNAL_ISR_ENABLE, SIGNAL_REGISTER
|
||||
jz shifter_isr_exit
|
||||
|
||||
;; shift msb of data register r5 into carry flag and set or reset P1.0 accordingly
|
||||
rla.b DATA_REGISTER
|
||||
jnc shifter_isr_false_bit
|
||||
set_data_bit
|
||||
jmp shifter_isr_end
|
||||
shifter_isr_false_bit:
|
||||
clear_data_bit
|
||||
|
||||
shifter_isr_end:
|
||||
;; enable output
|
||||
set_output_enable
|
||||
|
||||
;; roll bit-counter
|
||||
rla.b BIT_COUNTER_REGISTER
|
||||
jnc shifter_isr_exit
|
||||
|
||||
;; reset bit-counter
|
||||
mov.b #BIT_COUNTER_INIT_VALUE, BIT_COUNTER_REGISTER
|
||||
;; signal byte done
|
||||
bis #SIGNAL_OCTET_DONE, SIGNAL_REGISTER
|
||||
|
||||
;; check whether all data are processed
|
||||
bit #SIGNAL_ALL_DATA_DONE, SIGNAL_REGISTER
|
||||
jz shifter_isr_exit
|
||||
;; disable isr
|
||||
bic #SIGNAL_ISR_ENABLE, SIGNAL_REGISTER
|
||||
;; disable output
|
||||
clear_output_enable
|
||||
|
||||
shifter_isr_exit:
|
||||
reti
|
||||
|
||||
|
||||
;; ----------------------------------------------------
|
||||
.section "__interrupt_vector_14","ax",@progbits
|
||||
.word shifter_isr
|
||||
|
||||
;; .resetvec comes from linker
|
||||
.section ".resetvec","ax",@progbits
|
||||
.word _start
|
||||
|
||||
.end
|
||||
|
||||
|
||||
|
128
rgb-driver/readme.md
Normal file
@ -0,0 +1,128 @@
|
||||
## Debugging
|
||||
|
||||
```
|
||||
mspdebug rf2500 gdb
|
||||
|
||||
msp430-gdb -x firmware.gdb
|
||||
```
|
||||
|
||||
Attention: the gdb in the TI toolchain package is broken, use the one from Debian
|
||||
|
||||
|
||||
|
||||
## Signals Working Cycler
|
||||
|
||||
These signals are related to code under tag `cycler_works_include_output_stage`.
|
||||
|
||||
First octets:
|
||||
|
||||

|
||||
|
||||
Last octets:
|
||||
|
||||

|
||||
|
||||
Schematics and legend for signals:
|
||||
|
||||

|
||||
|
||||
### Some more explanations
|
||||
|
||||
Consider above schematics and the screen shot "Last octets" from the oscilloscope.
|
||||
|
||||

|
||||
|
||||
Timer TA1 is running in "up mode" to the value 45 set in compare register `TA1CCR0`. The compare registers `TA1CCR1` is set to 10, `TA1CCR2` is set to 22.
|
||||
The output mode of the timer is set to "Reset/Set", which means the GPIO associated with `TA1CCR1` (P2.1) and `TA1CCR2` (P2.4) are set at the overflow and
|
||||
restart of the counter and reset when the counter matches the associated compare value.
|
||||
|
||||
So, on P2.1 (D1 on the oscilloscope) we have a long pulse and at P2.4 (D0 on the oscilloscope) we have a short pulse, with synchronous raising edge.
|
||||
|
||||

|
||||
|
||||
The inverted signal P2.4 is connected to the Clock input of a 74HC74 D-flipflop, the data input of the flipflop is connected to GPIO P1.0 (D2 on the oscilloscope).
|
||||
|
||||
The interrupt service routine `shifter_isr` is triggered by the overflow and restart of the timer, this interrupt service routine provides the next bit to be
|
||||
signaled on P1.0. This bit is stored at the falling edge of P2.4 (long pulse) in the flipflop.
|
||||
|
||||
The short pulse (P2.1, D1) is ANDed using a 74HC08 with the inverted output of the flipflop, the long pulse (P2.4, D0) is ANDed with the non-inverted output of
|
||||
the flipflop, the ANDed results are ORed using a 74HC32.
|
||||
|
||||
So, at the output of the OR gate (yellow on the oscilloscope) we get a long pulse for a 1 at P1.0 provided by the ISR and a short pulse for a 0 at P1.0.
|
||||
|
||||
The routine `drawscreen` takes color values from the "frame buffer" beginning at `screendata` and translated them into the red, green and blue values and provides these values, first red, then green and finally blue to the ISR via the `DATA_REGISTER`.
|
||||
|
||||
The ISR cycles over the `DATA_REGISTER` and presents the bits at P1.0.
|
||||
|
||||
Additionally, when the first bit of a full draw screen cycle is presented at P1.0 by the ISR, it also sets the data enable signal at P1.1 and when the last bit has been provided it disabled the data enable signal. This signal is also synchronized using a flipflop and used to enable the short/long pulses using an AND gate.
|
||||
|
||||
|
||||
|
||||
## Timing
|
||||
|
||||
Complete cycle: 2.48us
|
||||
|
||||

|
||||
|
||||
Short pulse: 550ns
|
||||
|
||||

|
||||
|
||||
Long pulse: 1.18us
|
||||
|
||||

|
||||
|
||||
|
||||
## Load Time
|
||||
|
||||
During of loading data into five LEDs: 297us
|
||||
|
||||

|
||||
|
||||
During of loading data into six LEDs: 297us
|
||||
|
||||

|
||||
|
||||
|
||||
| # of LEDs | Load Time measured | calculated |
|
||||
| --------- | ------------------ | ---------- |
|
||||
| 5 | 297us | |
|
||||
| 6 | 354us | 356.4us |
|
||||
| 10 | | 594us |
|
||||
| 100 | | 5.9ms |
|
||||
| 200 | | 11.8ms |
|
||||
|
||||
|
||||
## Reset Circuitry
|
||||
|
||||
It appears that the output voltage of the power supply raises that slow, that the MCU
|
||||
will not handle the reset correctly.
|
||||
|
||||
The following circuitry should generate a valid reset signal far enough from the raise
|
||||
of the supply voltage:
|
||||
|
||||

|
||||
|
||||
The circuit generates the following signals:
|
||||
|
||||

|
||||
|
||||
#### Reference voltage (green):
|
||||
|
||||
```math
|
||||
U_ref = 3.3V \frac{22k\Omega}{22k\Omega + 10k\Omega} = 2.2V
|
||||
```
|
||||
|
||||
|
||||
#### Trigger voltage (purple):
|
||||
|
||||
```math
|
||||
U_trigg = 3.3V \frac{330k\Omega}{330k\Omega + 82k\Omega} = 2.64V
|
||||
```
|
||||
|
||||
|
||||
#### RC constant:
|
||||
|
||||
```math
|
||||
\tau = 82k\Omega \cdot 100nF = 8.2ms
|
||||
```
|
35
sound-driver/Makefile
Normal file
@ -0,0 +1,35 @@
|
||||
TOOLCHAIN_PREFIX=/opt/msp430-gcc
|
||||
CC=$(TOOLCHAIN_PREFIX)/bin/msp430-elf-gcc
|
||||
OBJDUMP=$(TOOLCHAIN_PREFIX)/bin/msp430-elf-objdump
|
||||
|
||||
ARTIFACT=firmware
|
||||
MCU=msp430g2553
|
||||
DEBUGFLAGS=
|
||||
# DEBUGFLAGS+= -g3 -ggdb -gdwarf-2
|
||||
COMMONFLAGS=-Wall -mmcu=$(MCU) -I $(TOOLCHAIN_PREFIX)/include -O0 -g0 $(DEBUGFLAGS)
|
||||
CFLAGS=$(COMMONFLAGS) -std=gnu99
|
||||
ASFLAGS=$(COMMONFLAGS) -D__ASSEMBLER__
|
||||
|
||||
LDFLAGS=-mmcu=$(MCU) -L $(TOOLCHAIN_PREFIX)/include
|
||||
|
||||
$(ARTIFACT).elf: main.o scheduler.o spi.o spi_init.o sequencer.o melody_tetris.o melody_tusch1.o psg.o mute.o melody_pling.o config.o
|
||||
$(CC) -o $@ $(LDFLAGS) $^
|
||||
$(OBJDUMP) -D $(ARTIFACT).elf > $(ARTIFACT).txt
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) -c $<
|
||||
|
||||
.S.o:
|
||||
$(CC) $(ASFLAGS) -c $<
|
||||
|
||||
|
||||
.PHONY: all
|
||||
all: $(ARTIFACT).elf
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
-rm -f *.o $(ARTIFACT).elf $(ARTIFACT).txt
|
||||
|
||||
.PHONY: upload
|
||||
upload: $(ARTIFACT).elf
|
||||
mspdebug rf2500 "prog $(ARTIFACT).elf"
|
26
sound-driver/config.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/param.h>
|
||||
#include "config.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint8_t melodyAmplitude;
|
||||
uint8_t effectsAmplitude;
|
||||
} config_t;
|
||||
|
||||
config_t config;
|
||||
|
||||
|
||||
void configSetAmplitude(uint8_t v) {
|
||||
config.melodyAmplitude = MIN(v, 15);
|
||||
config.effectsAmplitude = MIN(v+4, 15);
|
||||
}
|
||||
|
||||
uint8_t *configGetMelodyAmplitudePtr() {
|
||||
return &(config.melodyAmplitude);
|
||||
}
|
||||
|
||||
uint8_t *configGetEffectsAmplitudePtr() {
|
||||
return &(config.effectsAmplitude);
|
||||
}
|
||||
|
12
sound-driver/config.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef _CONFIG_H_
|
||||
#define _CONFIG_H_
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void configSetAmplitude(uint8_t v);
|
||||
uint8_t *configGetMelodyAmplitudePtr();
|
||||
uint8_t *configGetEffectsAmplitudePtr();
|
||||
|
||||
|
||||
#endif // _CONFIG_H_
|
BIN
sound-driver/docs/Audio-Amplifier.pdf
Executable file
BIN
sound-driver/docs/Clock-Generation.pdf
Executable file
BIN
sound-driver/docs/Frequency-Codes-76489AN.xlsx
Normal file
BIN
sound-driver/docs/Frequency-Codes.pdf
Executable file
BIN
sound-driver/docs/Korobeiniki.svg.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
sound-driver/docs/MCU-Connection.pdf
Executable file
BIN
sound-driver/docs/Oktavskala.pdf
Executable file
BIN
sound-driver/docs/Registers.pdf
Executable file
BIN
sound-driver/docs/TetrisThemeThreeVoices.pdf
Executable file
BIN
sound-driver/docs/ay3-8910.pdf
Normal file
BIN
sound-driver/docs/lengths.xlsx
Normal file
2
sound-driver/firmware.gdb
Normal file
@ -0,0 +1,2 @@
|
||||
target remote localhost:2000
|
||||
file firmware.elf
|
38
sound-driver/main.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include <msp430g2553.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "spi.h"
|
||||
#include "psg.h"
|
||||
#include "scheduler.h"
|
||||
#include "sequencer.h"
|
||||
#include "melody_tetris.h"
|
||||
#include "melody_tusch1.h"
|
||||
#include "mute.h"
|
||||
|
||||
int main() {
|
||||
WDTCTL = WDTPW | WDTHOLD;
|
||||
|
||||
__disable_interrupt();
|
||||
|
||||
// highest possible system clock
|
||||
DCOCTL = DCO0 | DCO1 | DCO2;
|
||||
BCSCTL1 = XT2OFF | RSEL0 | RSEL1 | RSEL2 | RSEL3;
|
||||
BCSCTL2 = 0;
|
||||
BCSCTL3 = 0;
|
||||
|
||||
|
||||
schInit();
|
||||
|
||||
spiInit();
|
||||
psgInit();
|
||||
muteInit();
|
||||
sequencerInit();
|
||||
|
||||
__enable_interrupt();
|
||||
|
||||
|
||||
while (1) {
|
||||
schExec();
|
||||
}
|
||||
}
|