166 Commits

Author SHA1 Message Date
40cb04bde5 fix wording 2024-03-29 18:08:37 +01:00
a1457e6a69 lengthCnt must be 16 Bit, one full tone for 160/4 has 375 2024-03-29 15:52:12 +01:00
0303bbdc3c corrects of tones 2024-03-29 15:11:38 +01:00
162bdaefee beat marks 2024-03-29 15:10:04 +01:00
b9fd0099a8 variable names 2024-03-29 14:22:02 +01:00
d09f8d240f Merge branch 'main' of gitea.hottis.de:wn/tetris 2024-03-29 14:13:10 +01:00
ed6da383de docs 2024-03-29 14:13:05 +01:00
9328e22425 pace calculation works 2024-03-29 14:11:36 +01:00
48b9fc7578 some docs 2024-03-29 13:33:39 +01:00
d4d494ae7b sync marks work 2024-03-29 13:29:53 +01:00
1ebf85cb9d sync marks 2024-03-29 13:04:05 +01:00
5a8323bddb up to three melodies will be played by one sequencer instance 2024-03-29 12:41:44 +01:00
97dc6ede70 dreistimmig 2024-03-29 11:15:38 +01:00
a4563cd393 third melody 2024-03-29 00:39:21 +01:00
b435396d67 other melody 2024-03-29 00:28:15 +01:00
38ea1a7fdb register name 2024-03-28 17:05:35 +01:00
6632630303 register names 2024-03-28 17:03:56 +01:00
fdb524c8d4 separate SPI 2024-03-28 16:53:19 +01:00
2c3bccd147 fix wrong initial state 2024-03-28 16:53:08 +01:00
ee98ba12a3 spi separated 2024-03-28 16:48:29 +01:00
33242a09c1 docs 2024-03-28 16:25:16 +01:00
2097280f2b add docs 2024-03-28 16:07:50 +01:00
70b3b25a9d Merge branch 'main' of gitea.hottis.de:wn/tetris 2024-03-26 23:22:06 +01:00
5e1e9dfa92 sequencer init state 2024-03-26 23:21:24 +01:00
f397d0737b docs 2024-03-26 23:15:56 +01:00
1c2414463b sounds good 2024-03-26 23:13:59 +01:00
2e629f12aa sequencer states 2024-03-26 22:12:46 +01:00
9989a52c38 legato 2024-03-26 21:05:48 +01:00
73d2bbc730 tetris melody 2024-03-26 16:56:46 +01:00
5e2f120432 Tonleiter is running 2024-03-26 16:47:42 +01:00
152f171c66 sequencer 2024-03-26 15:58:45 +01:00
9ddb747f16 refactored 2024-03-26 15:05:39 +01:00
bd11d12620 psg and notes 2024-03-26 14:43:02 +01:00
e5c6669284 notes refactored 2024-03-26 13:30:42 +01:00
202e91bfb6 playsommething 2024-03-26 12:23:44 +01:00
48c83f0b2d scheduler added, tuned for 1ms 2024-03-26 10:44:23 +01:00
d8e34ec209 note definitions 2024-03-25 23:13:33 +01:00
5a491140c7 register numbering is octal! 2024-03-25 22:11:15 +01:00
aaf709b0c9 begin sound driver 2024-03-25 13:28:33 +01:00
32bb08696f score reset 2024-03-22 23:03:31 +01:00
85d243551e score counter 2024-03-22 22:49:43 +01:00
a32ef8fa5b prepare display 2024-03-22 18:38:30 +01:00
10a09e3ad3 game over 2024-03-22 12:00:23 +01:00
611c56b329 game over 2024-03-22 11:59:28 +01:00
5b1dfde819 updated schematics 2024-03-21 21:14:05 +01:00
1cc4785ddb refactor SPI handling and introduce SPI CS, forgotten files 2024-03-21 20:55:41 +01:00
9acd56b79b refactor SPI handling and introduce SPI CS 2024-03-21 20:55:26 +01:00
474fce2278 mini canvas 2024-03-21 19:30:40 +01:00
8597a9f736 schematics 2024-03-21 16:02:24 +01:00
da21fac130 motion definition 2024-03-21 15:40:16 +01:00
5f9fab5f2c more to the center 2024-03-21 15:28:36 +01:00
f524a08687 introduce next shape 2024-03-21 12:24:12 +01:00
5c03232855 wipe full lines 2024-03-21 11:58:42 +01:00
f69c5bc59e random is working 2024-03-21 11:34:20 +01:00
50486f6ec0 adc for random number generation not yet working 2024-03-20 21:56:19 +01:00
8c995f66ff J done 2024-03-20 21:00:22 +01:00
ebe958ac49 start of J 2024-03-20 17:35:31 +01:00
fadb80c362 L done 2024-03-20 17:05:13 +01:00
d1e13dc16a S done 2024-03-20 15:19:26 +01:00
90a9294da6 Z done 2024-03-20 14:48:25 +01:00
cfc06ddb2d placeholders for all stones 2024-03-20 13:54:57 +01:00
5e5c616bde T done 2024-03-20 13:48:06 +01:00
2ca1d2e4ad T, move right fixed 2024-03-19 22:18:42 +01:00
a28cdcf6af T, move left fixed 2024-03-19 21:19:34 +01:00
7f6d027d1a fix T, rotate left 2024-03-19 18:14:03 +01:00
97b1d19da8 T 2024-03-19 17:42:25 +01:00
8b2f18415d buttons 2024-03-19 16:46:52 +01:00
5615c80e8f all leds 2024-03-19 14:57:41 +01:00
a68f74559f refactoring 2024-03-18 20:50:10 +01:00
326d0f66b0 remove unused code 2024-03-18 13:01:23 +01:00
feed11f977 change 2024-03-18 12:54:23 +01:00
fd3df973ec code beautifying 2024-03-18 12:51:57 +01:00
58ae9a641a move displayTests to disabled folder 2024-03-18 12:18:18 +01:00
e0aae175b0 debug option in Makefile separated 2024-03-15 23:27:10 +01:00
7e4285f280 move down, left, right for T done 2024-03-15 14:23:19 +01:00
007548efbb O done 2024-03-15 12:54:32 +01:00
14f6018f5a motion table and next orientation table down 2024-03-15 10:22:11 +01:00
f4b614bf0f first motion table 2024-03-14 17:25:45 +01:00
a7ea698f9b move and rotate of i 2024-03-14 14:46:31 +01:00
1959a3f578 game-ctrl merged 2024-03-14 11:10:18 +01:00
349c93b68f Merge remote-tracking branch 'repo-game-ctrl/shapes' 2024-03-14 11:09:26 +01:00
9c1a8a6e21 moved 2024-03-14 11:07:10 +01:00
9003528897 colors.h added 2024-03-14 10:57:09 +01:00
2b34a3e51d start game 2024-03-13 17:35:46 +01:00
e5381a8c9d Merge branch 'main' into shapes 2024-03-13 14:55:04 +01:00
8bd6197c02 spi timing 2024-03-13 14:54:53 +01:00
f5b0e67056 shapes 2024-03-13 14:52:46 +01:00
19be1a6e48 more tests 2024-03-12 23:00:17 +01:00
05de9326d7 some more display tests 2024-03-12 18:18:26 +01:00
633fc814b2 display tests 2024-03-12 17:53:42 +01:00
88e0cff853 11 rows 2024-03-12 10:19:16 +01:00
6020118fc2 110 leds 2024-03-12 10:19:02 +01:00
d3846c3e08 up and down 2024-03-08 15:54:34 +01:00
30ab5287da rename isr 2024-03-08 15:09:05 +01:00
fb16cfaa6f clean up cleaned up 2024-03-08 15:06:35 +01:00
cf62e0c0a0 Merge branch 'main' of gitea.hottis.de:wn/game-ctrl-01 2024-03-08 15:01:33 +01:00
044779681b more images 2024-03-08 15:01:01 +01:00
167617ad2c faster spi 2024-03-08 14:13:21 +01:00
26db6bf03d a funny light show 2024-03-08 14:07:09 +01:00
a4adf6ac27 synchronize scheduler, now it works 2024-03-08 13:12:55 +01:00
8da88e96c2 1 second 2024-03-08 11:27:11 +01:00
da3c2a5ffb image 2024-03-08 08:23:51 +01:00
a5438cef14 accept multiple address/data octets 2024-03-08 08:16:56 +01:00
d295a36598 receivedata added 2024-03-07 21:50:09 +01:00
3eaf90dbd6 works again 2024-03-07 15:43:27 +01:00
25ad1ad7df try math 2024-03-06 13:17:08 +01:00
6536dc5534 try math 2024-03-06 13:16:18 +01:00
730ac0df30 try math 2024-03-06 13:15:56 +01:00
152c9f60d4 try math 2024-03-06 13:14:43 +01:00
2f07e37b5d try math 2024-03-06 13:12:59 +01:00
d61986630c try math 2024-03-06 13:12:13 +01:00
2e19371fac try math 2024-03-06 13:08:18 +01:00
53e33bf4ff try math 2024-03-06 13:08:04 +01:00
01c645d8df try math 2024-03-06 13:07:38 +01:00
75acdd3171 try math 2024-03-06 13:04:32 +01:00
e6db62c9a5 try math 2024-03-06 13:04:14 +01:00
2ccef81175 try math 2024-03-06 13:03:49 +01:00
ad37bb2280 fix for new image 2024-03-06 12:57:37 +01:00
fd6eb582eb new image 2024-03-06 12:55:36 +01:00
8292d59cf0 doc on reset circuit 2024-03-06 10:44:31 +01:00
1e77cdaddf Merge branch 'main' of gitea.hottis.de:wn/rgb-driver-01 2024-03-06 10:39:23 +01:00
d0219a3c77 violet 2024-03-06 10:39:10 +01:00
2bef7bf2c2 Merge branch 'main' of gitea.hottis.de:wn/rgb-driver-01 2024-03-06 10:37:12 +01:00
9f50f6010b reset circuit 2024-03-06 10:36:43 +01:00
c35ec0991c dimm factor introduced 2024-03-05 17:01:53 +01:00
584e834b0d comment 2024-03-05 16:21:56 +01:00
fbe779d3b8 code beautified 2024-03-05 16:18:19 +01:00
49d78662e2 rolling led 2024-03-05 16:14:13 +01:00
c502303a36 use of subroutines work 2024-03-05 15:27:34 +01:00
2d532a6403 this is working 2024-03-05 15:06:42 +01:00
86fb74bd49 adjust color offsets 2024-02-29 18:13:58 +01:00
42c06f0da4 drop unnecessary .extern statements 2024-02-28 12:10:22 +01:00
b4ebe4978b readme 2024-02-27 20:53:13 +01:00
a5536e63b7 six leds 2024-02-27 20:49:06 +01:00
4f70ea392b readme 2024-02-27 20:39:27 +01:00
9c493d515e image 2024-02-27 20:35:48 +01:00
201a371a57 names refactor 2024-02-27 19:11:36 +01:00
40c49b6809 names refactor 2024-02-27 19:09:50 +01:00
c9b8fbba52 readme 2024-02-27 19:03:57 +01:00
d8755709cd readme 2024-02-27 19:02:02 +01:00
785810924e timing images 2024-02-27 19:00:19 +01:00
a0794dd9c1 refactor names, 6 2024-02-27 18:59:57 +01:00
4104cbf3d6 refactor names, 5 2024-02-27 18:30:08 +01:00
c829a76273 refactor names, 4 2024-02-27 17:48:22 +01:00
e1b7c328f8 refactor names, 3 2024-02-27 17:45:40 +01:00
2435cee771 refactor names, 2 2024-02-27 17:34:11 +01:00
75598f973a refactor names, 1 2024-02-27 17:32:16 +01:00
155b7153e9 refactor 2024-02-27 16:58:43 +01:00
5c2a6c42d2 hold data in ram 2024-02-27 15:33:45 +01:00
b390b8ec8f hold data in ram 2024-02-27 15:32:35 +01:00
cbf8a3cf86 color wheel from https://learn.sparkfun.com/tutorials/lilypad-protosnap-plus-activity-guide/3-custom-color-mixing 2024-02-27 15:11:27 +01:00
f0e8bf7616 color definitions 2024-02-27 15:10:38 +01:00
8f143104f6 works with leds attached 2024-02-27 14:23:32 +01:00
80801e185b readme 2024-02-27 12:09:39 +01:00
68490a4f83 schematics 2024-02-27 12:08:47 +01:00
5d7c75b358 fix 2024-02-27 11:58:02 +01:00
4e6fd76b08 Merge branch 'main' of gitea.hottis.de:wn/msp430-experiment-02 2024-02-27 11:56:31 +01:00
cea174197f add images 2024-02-27 11:56:06 +01:00
df9faabc7f readme 2024-02-27 11:54:19 +01:00
5128d36a75 readme fixed 2024-02-27 11:42:54 +01:00
b3ee547b64 remove dead code 2024-02-27 11:38:44 +01:00
92fb90983f cycles work now 2024-02-27 11:25:57 +01:00
fd801003a8 seems to work so far 2024-02-23 21:20:16 +01:00
491a544919 changes 2024-02-23 10:39:57 +01:00
b411a05d0e change Makefile 2024-02-20 13:05:32 +01:00
29c2c88c71 initial 2024-02-20 12:50:23 +01:00
86 changed files with 3769 additions and 239 deletions

4
.gitignore vendored
View File

@ -1,3 +1,5 @@
*.o
firmware.*
firmware.txt
firmware.elf
firmware.map

View File

@ -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();
}

View File

@ -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_

View File

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

Binary file not shown.

Binary file not shown.

131
display-driver/main.c Normal file
View 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 = 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();
}
}

View File

@ -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;
}

View File

@ -1,9 +0,0 @@
#ifndef _DISPLAY_DRIVER_H_
#define _DISPLAY_DRIVER_H_
void displayDriverInit();
void displayDriverTransferCanvas();
#endif // _DISPLAY_DRIVER_H_

View File

@ -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/schematics.pdf Executable file

Binary file not shown.

31
game-ctrl/Makefile Normal file
View 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
$(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"

86
game-ctrl/buttons.c Normal file
View File

@ -0,0 +1,86 @@
#include <stddef.h>
#include <stdint.h>
#include <msp430g2553.h>
#include "buttons.h"
#include "scheduler.h"
#include "shapes.h"
#include "canvas.h"
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;
}
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;
}
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;
}
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;
}
static uint8_t buttonsMoveDownPressed() {
return P2IN & BIT2;
}
void buttonsExec(void *handle) {
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();
}
}
void buttonsInit() {
P2DIR &= ~(BIT0|BIT1|BIT2|BIT3|BIT4);
schAdd(buttonsExec, NULL, 0, 25);
}

7
game-ctrl/buttons.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef _BUTTONS_H_
#define _BUTTONS_H_
void buttonsInit();
#endif // _BUTTONS_H_

109
game-ctrl/canvas.c Normal file
View File

@ -0,0 +1,109 @@
#include <string.h>
#include <stdint.h>
#include <msp430g2553.h>
#include "canvas.h"
#include "spi.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() {
// 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));
}
}
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));
}
}
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
View 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_

View 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);
}

View 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);
}

View File

@ -0,0 +1,11 @@
#ifndef _DISPLAY_TEST_2_H_
#define _DISPLAY_TEST_2_H_
void displayTest2Init();
void displayTest2Exec();
#endif // _DISPLAY_TEST_2_H_

View 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);
}

View File

@ -0,0 +1,11 @@
#ifndef _DISPLAY_TEST_3_H_
#define _DISPLAY_TEST_3_H_
void displayTest3Init();
void displayTest3Exec();
#endif // _DISPLAY_TEST_3_H_

View File

@ -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);
}

View File

@ -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
View 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
View File

@ -0,0 +1,13 @@
#ifndef _DISPLAY_H_
#define _DISPLAY_H_
#include <stdint.h>
void displayInit();
void displaySetValue(uint16_t v);
#endif // _DISPLAY_H_

BIN
game-ctrl/docs/1second.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

2
game-ctrl/firmware.gdb Normal file
View File

@ -0,0 +1,2 @@
target remote localhost:2000
file firmware.elf

129
game-ctrl/game.c Normal file
View File

@ -0,0 +1,129 @@
#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"
#define GAME_CYCLE_TIME 100
#define GAMEOVER_DELAY 10
static uint8_t delayFactor(uint8_t level) {
return 11 - level;
}
typedef enum {
e_Phase_Game, e_Phase_GameOver
} phase_t;
typedef enum {
e_Start, e_NewStone, e_Down, e_DownDelay, e_ClearRows,
e_GameOver, e_GameOverFill, e_GameOverWipe, e_GameOverDelay
} state_t;
void gameExec(void *handle) {
static phase_t phase;
static state_t state = e_Start;
static uint8_t gameOverDelay;
static uint8_t rowIndex;
static uint8_t proceedDelay;
static uint8_t level;
static uint16_t score;
// --- engine begin -------------------------------------------------------
switch (state) {
// --- phase: game --------------------------------------------------------
case e_Start:
canvasClear();
level = 1;
score = 0;
displaySetValue(score);
phase = e_Phase_Game;
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) {
rowIndex = 0;
state = e_ClearRows;
}
break;
case e_ClearRows:
state = e_Down;
break;
case e_Down:
if (! stoneMoveDown()) {
state = e_NewStone;
} else {
proceedDelay = delayFactor(level);
state = e_DownDelay;
}
break;
// --- phase: game over ---------------------------------------------------
case e_GameOver:
rowIndex = CANVAS_HEIGHT;
phase = e_Phase_GameOver;
state = e_GameOverFill;
break;
case e_GameOverFill:
rowIndex--;
canvasFillRow(rowIndex, _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();
if (phase == e_Phase_Game) {
for (uint8_t r = 0; r < CANVAS_HEIGHT; r++) {
if (canvasIsRowFilled(r)) {
score += level;
displaySetValue(score);
canvasWipeRow(r);
canvasShow();
}
}
}
}
void gameInit() {
schAdd(gameExec, NULL, 0, GAME_CYCLE_TIME);
}

7
game-ctrl/game.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef _GAME_H_
#define _GAME_H_
void gameInit();
#endif // _GAME_H_

View File

@ -4,11 +4,14 @@
#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"
int main() {
@ -22,15 +25,16 @@ int main() {
BCSCTL2 = 0;
BCSCTL3 = 0;
timeInit();
schInit();
ledInit();
displayDriverInit();
spiInit();
displayInit();
myRandInit();
canvasInit();
displayTestInit();
shapesInit();
gameInit();
buttonsInit();
__enable_interrupt();

26
game-ctrl/myrand.c Normal file
View 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
View File

@ -0,0 +1,8 @@
#ifndef _MYRAND_H_
#define _MYRAND_H_
void myRandInit();
uint16_t myRandGet();
#endif // _MYRAND_H_

View File

@ -1,21 +1,24 @@
/*
* PontCoopScheduler.c
*
* Created on: 29.08.2016
* Originally created on: 29.08.2016
* Author: wn
*/
#include <stdlib.h>
#include "PontCoopScheduler.h"
#include <msp430g2553.h>
#include "scheduler.h"
tTask tasks[MAX_NUM_OF_TASKS];
void schInit() {
for (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
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;
@ -24,8 +27,21 @@ void schInit() {
}
}
void __attribute__ ((interrupt (TIMER0_A0_VECTOR))) schUpdate() {
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 (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
for (uint16_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
if (tasks[i].exec == NULL) {
tasks[i].delay = delay;
tasks[i].period = period;
@ -41,48 +57,36 @@ void schAdd(void (*exec)(void *), void *handle, uint32_t delay, uint32_t period)
}
}
/*
void schDel(void (*exec)(void *), void *handle) {
for (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
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 (uint8_t i = 0; i < MAX_NUM_OF_TASKS; i++) {
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();
}
}
}
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;
}

View File

@ -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;

498
game-ctrl/shapes.c Normal file
View File

@ -0,0 +1,498 @@
#include <stdint.h>
#include <stddef.h>
#include <stdlib.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
} 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;
void shapesInit() {
stone.shape = e_ShapeInvalid;
nextShape = e_Z;
}
void stoneCreate() {
stone.shape = nextShape;
nextShape = ((shape_t[]){ e_I, e_O, e_T, e_Z, e_S, e_L, e_J })[myRandGet() % e_ShapeInvalid];
stone.orientation = e_0;
stone.x = 4;
stone.y = 0;
}
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;
}
// 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);
}
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
canvasSetPixel(stone.x + motions[stone.shape].draw[0].x,
stone.y + motions[stone.shape].draw[0].y,
motions[stone.shape].color);
canvasSetPixel(stone.x + motions[stone.shape].draw[1].x,
stone.y + motions[stone.shape].draw[1].y,
motions[stone.shape].color);
canvasSetPixel(stone.x + motions[stone.shape].draw[2].x,
stone.y + motions[stone.shape].draw[2].y,
motions[stone.shape].color);
canvasSetPixel(stone.x + motions[stone.shape].draw[3].x,
stone.y + motions[stone.shape].draw[3].y,
motions[stone.shape].color);
res = 1;
}
return res;
}
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);
}

17
game-ctrl/shapes.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef _SHAPES_H_
#define _SHAPES_H_
#include <stdint.h>
void shapesInit();
void stoneCreate();
uint8_t stoneIsValid();
uint8_t stoneDraw();
uint8_t stoneMoveDown();
uint8_t stoneMoveLeft();
uint8_t stoneMoveRight();
uint8_t stoneRotateLeft();
uint8_t stoneRotateRight();
#endif // _SHAPES_H_

50
game-ctrl/spi.c Normal file
View File

@ -0,0 +1,50 @@
#include <msp430g2553.h>
#include "spi.h"
void spiInit() {
// SPI in master mode, most significant bit first
UCB0CTL0 = 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
P1DIR |= BIT0 | BIT1 | BIT2;
// Disable all of them
P1OUT |= BIT0 | BIT1 | BIT2;
// enable SPI module
UCB0CTL1 &= ~UCSWRST;
}
void spiSendBegin(t_SpiDeviceSelector d) {
uint16_t bit = ((uint16_t[]){ BIT0, BIT1, BIT2 })[d];
P1OUT &= ~bit;
}
void spiSendEnd(t_SpiDeviceSelector d) {
while (UCB0STAT & UCBUSY);
uint16_t bit = ((uint16_t[]){ BIT0, BIT1, BIT2 })[d];
P1OUT |= bit;
}
void spiSendOctet(uint8_t v) {
// wait for TX buffer empty
while (!(UC0IFG & UCB0TXIFG));
// load octet into TX buffer
UCB0TXBUF = v;
}

16
game-ctrl/spi.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef _SPI_H_
#define _SPI_H_
#include <stdint.h>
typedef enum { e_SPI_CANVAS, e_SPI_DISPLAY, e_SPI_SOUND } t_SpiDeviceSelector;
void spiInit();
void spiSendBegin(t_SpiDeviceSelector d);
void spiSendEnd(t_SpiDeviceSelector d);
void spiSendOctet(uint8_t v);
#endif // _SPI_H_

36
rgb-driver/Makefile Normal file
View 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
View 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_

38
rgb-driver/colors.S Normal file
View File

@ -0,0 +1,38 @@
#include "colors.h"
#define DIMM_FACTOR 3
.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
off:
.byte 0x00, 0x00, 0x00, 0
blue:
.byte 0x00>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
green:
.byte 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0
orange:
.byte 0xff>>DIMM_FACTOR, 0x80>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0
rose:
.byte 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0x80>>DIMM_FACTOR, 0
magenta:
.byte 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
violet:
.byte 0x80>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
azure:
.byte 0x00>>DIMM_FACTOR, 0x80>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
cyan:
.byte 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
springgreen:
.byte 0x00>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0x80>>DIMM_FACTOR, 0
chartreuse:
.byte 0x80>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0
yellow:
.byte 0xff>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0
white:
.byte 0xff>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0xff>>DIMM_FACTOR, 0
red:
.byte 0xff>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0x00>>DIMM_FACTOR, 0

22
rgb-driver/colors.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef _COLORS_H_
#define _COLORS_H_
#define _off 0x00
#define _blue 0x01
#define _green 0x02
#define _orange 0x03
#define _rose 0x04
#define _magenta 0x05
#define _violet 0x06
#define _azure 0x07
#define _cyan 0x08
#define _springgreen 0x09
#define _chartreuse 0x0a
#define _yellow 0x0b
#define _white 0x0c
#define _red 0x0d
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 899 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

334
rgb-driver/main.S Normal file
View 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 #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

97
rgb-driver/readme.md Normal file
View File

@ -0,0 +1,97 @@
## 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:
![](./docs/cycler_working_first_octets.png)
Last octets:
![](./docs/cycler_working_last_octets.png)
Schematics and legend for signals:
![](./docs/schematics.jpeg)
## Timing
Complete cycle: 2.48us
![](./docs/pulse_complete.png)
Short pulse: 550ns
![](./docs/pulse_short.png)
Long pulse: 1.18us
![](./docs/pulse_long.png)
## Load Time
During of loading data into five LEDs: 297us
![](./docs/five_leds.png)
During of loading data into six LEDs: 297us
![](./docs/six_leds.png)
| # 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:
![](./docs/reset-circuit.jpeg)
The circuit generates the following signals:
![](./docs/reset-signal.png)
#### 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
```

31
sound-driver/Makefile Normal file
View 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 scheduler.o spi.o psg.o sequencer.o melody.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"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

BIN
sound-driver/docs/Oktavskala.pdf Executable file

Binary file not shown.

BIN
sound-driver/docs/Registers.pdf Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,2 @@
target remote localhost:2000
file firmware.elf

37
sound-driver/main.c Normal file
View File

@ -0,0 +1,37 @@
#include <msp430g2553.h>
#include <stdint.h>
#include <stdlib.h>
#include "spi.h"
#include "psg.h"
#include "scheduler.h"
#include "sequencer.h"
#include "melody.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();
sequencerInit();
__enable_interrupt();
melodyInit();
while (1) {
schExec();
}
}

1004
sound-driver/melody.c Normal file

File diff suppressed because it is too large Load Diff

8
sound-driver/melody.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef _MELODY_H_
#define _MELODY_H_
void melodyInit();
#endif // _MELODY_H_

163
sound-driver/psg.c Normal file
View File

@ -0,0 +1,163 @@
#include <msp430g2553.h>
#include <stdint.h>
#include <stdlib.h>
#include "psg.h"
#include "scheduler.h"
const uint16_t frequencyCodes[8][12] = {
// C, Cis, D, Dis, E, F, Fis, G, Gis, A, Ais, H
{ 06535, 06234, 05747, 05474, 05233, 05002, 04563, 04353, 04153, 03762, 03600, 03424 }, // Octave 1
{ 03256, 03116, 02764, 02636, 02515, 02401, 02271, 02165, 02065, 01771, 01700, 01612 }, // Octave 2
{ 01527, 01447, 01372, 01317, 01247, 01201, 01135, 01073, 01033, 00774, 00740, 00705 }, // Octave 3
{ 00654, 00624, 00575, 00550, 00523, 00500, 00456, 00435, 00415, 00376, 00360, 00342 }, // Octave 4
{ 00326, 00312, 00276, 00264, 00252, 00240, 00227, 00217, 00207, 00177, 00170, 00161 }, // Octave 5
{ 00153, 00145, 00137, 00132, 00125, 00120, 00114, 00107, 00103, 00100, 00074, 00071 }, // Octave 6
{ 00065, 00062, 00060, 00055, 00052, 00050, 00046, 00044, 00042, 00040, 00036, 00034 }, // Octave 7
{ 00033, 00031, 00030, 00026, 00025, 00024, 00023, 00022, 00021, 00020, 00017, 00016 } // Octave 8
};
#define ADDR_DATA_REG P2OUT
#define BUS_CTRL_REG P1OUT
#define BC1 BIT3
#define BDIR BIT1
#define R0 0
#define CHANNEL_A_TONE_PERIOD_FINE_REG R0
#define R1 1
#define CHANNEL_A_TONE_PERIOD_COARSE_REG R1
#define R2 2
#define CHANNEL_B_TONE_PERIOD_FINE_REG R2
#define R3 3
#define CHANNEL_B_TONE_PERIOD_COARSE_REG R3
#define R4 4
#define CHANNEL_C_TONE_PERIOD_FINE_REG R4
#define R5 5
#define CHANNEL_C_TONE_PERIOD_COARSE_REG R5
#define R6 6
#define NOISE_PERIOD_REG R6
#define R7 7
#define _ENABLE_REG R7
#define R10 010
#define CHANNEL_A_AMPLITUDE_REG R10
#define R11 011
#define CHANNEL_B_AMPLITUDE_REG R11
#define R12 012
#define CHANNEL_C_AMPLITUDE_REG R12
#define R13 013
#define ENVELOPE_PERIOD_FINE_REG R13
#define R14 014
#define ENVELOPE_PERIOD_COARSE_REG R13
#define R15 015
#define ENVELOPE_SHAPE_REG R15
uint8_t psgShadowRegisters[14];
inline static void BUS_OP_NACT() {
BUS_CTRL_REG &= ~(BDIR | BC1);
}
inline static void BUS_OP_INTAK() {
BUS_CTRL_REG |= BDIR | BC1;
}
inline static void BUS_OP_DWS() {
BUS_CTRL_REG |= BDIR;
BUS_CTRL_REG &= ~BC1;
}
static void delay() {
asm volatile (
"push r12\n"
"mov.w #5, r12\n"
"loop:\n"
"dec.w r12\n"
"jnz loop\n"
"pop r12\n"
);
}
uint8_t psgReadShadow(uint8_t address) {
return psgShadowRegisters[address];
}
void psgWrite(uint8_t address, uint8_t data) {
psgShadowRegisters[address] = data;
// according to "State Timing" (p. 15) of datasheet
// put bus into inactive state
BUS_OP_NACT();
// put address on bus
ADDR_DATA_REG = address;
// address latch mode
BUS_OP_INTAK();
// latch address
BUS_OP_NACT();
// put data on bus
ADDR_DATA_REG = data;
// set write to psg
BUS_OP_DWS();
// set inactive again
BUS_OP_NACT();
}
void psgWriteFrequency(uint8_t channel, uint16_t frequencyCode) {
psgWrite(CHANNEL_A_TONE_PERIOD_FINE_REG + (channel * 2), (frequencyCode & 0x00ff));
psgWrite(CHANNEL_A_TONE_PERIOD_COARSE_REG + (channel * 2), ((frequencyCode >> 8) & 0x000f));
}
void psgPlayTone(uint8_t channel, t_octave octave, t_note note) {
if (note == e_Pause) {
psgWrite(_ENABLE_REG, psgReadShadow(_ENABLE_REG) | (1 << channel));
} else {
psgWrite(_ENABLE_REG, psgReadShadow(_ENABLE_REG) & ~(1 << channel));
psgWriteFrequency(channel, frequencyCodes[octave][note]);
}
}
void psgAmplitude(uint8_t channel, uint8_t volume) {
psgWrite(CHANNEL_A_AMPLITUDE_REG + channel, volume);
}
void psgInit() {
// address/data bus
P2DIR = 0xff;
P2SEL = 0;
P2SEL2 = 0;
// sound chip reset
// BIT2: /RST
P1DIR |= BIT2;
// put sound chip into reset state
P1OUT &= ~BIT2;
delay();
delay();
delay();
// bus control lines
// BIT3: BC1
// BIT1: BDIR
P1DIR |= BIT1 | BIT3;
// put bus into inactive state
BUS_CTRL_REG &= ~(BDIR | BC1);
// release sound chip from reset state
P1OUT |= BIT2;
delay();
delay();
delay();
// disable everything
psgWrite(_ENABLE_REG, 0xff);
}

52
sound-driver/psg.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef _PSG_H_
#define _PSG_H_
#include <stdint.h>
typedef enum {
e_O_1 = 0,
e_O_2,
e_O_3,
e_O_4,
e_O_5,
e_O_6,
e_O_7,
e_O_8,
e_O_Null
} t_octave;
typedef enum {
e_C = 0,
e_Cis,
e_D,
e_Dis,
e_E,
e_F,
e_Fis,
e_G,
e_Gis,
e_A,
e_Ais,
e_H,
e_Pause,
e_Null
} t_note;
#define e_Es e_Dis
#define e_As e_Gis
#define e_B e_Ais
void psgInit();
void psgPlayTone(uint8_t channel, t_octave octave, t_note note);
void psgAmplitude(uint8_t channel, uint8_t volume);
// low level
void psgWriteFrequency(uint8_t channel, uint16_t frequencyCode);
// very low level
void psgWrite(uint8_t address, uint8_t data);
uint8_t psgReadShadow(uint8_t address);
#endif // _PSG_H_

92
sound-driver/scheduler.c Normal file
View File

@ -0,0 +1,92 @@
#include <stdlib.h>
#include <msp430g2553.h>
#include "scheduler.h"
tTask tasks[MAX_NUM_OF_TASKS];
void schInit() {
P1DIR |= BIT0;
TACCR0 = 19600;
TACCTL0 = CCIE;
TACTL = MC_1 | ID_0 | TASSEL_2 | 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;
}
}
void __attribute__ ((interrupt (TIMER0_A0_VECTOR))) schUpdate() {
P1OUT ^= BIT0;
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--;
}
}
}
}
uint16_t schAdd(void (*exec)(void *), void *handle, uint32_t delay, uint32_t period) {
uint16_t taskId = 0xffff;
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;
taskId = i;
break;
}
}
return taskId;
}
/*
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 schDel(uint16_t taskId) {
tasks[taskId].exec = NULL;
}
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();
}
}
}

28
sound-driver/scheduler.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef PONTCOOPSCHEDULER_H_
#define PONTCOOPSCHEDULER_H_
#include <stdint.h>
#define MAX_NUM_OF_TASKS 8
typedef struct {
uint16_t delay;
uint16_t period;
uint8_t run;
void (*exec)(void *handle);
void *handle;
} tTask;
void schInit();
uint16_t schAdd(void (*exec)(void *), void *handle, uint32_t delay, uint32_t period);
// void schDel(void (*exec)(void *), void *handle);
void schDel(uint16_t taskId);
void schExec();
#endif /* PONTCOOPSCHEDULER_H_ */

100
sound-driver/sequencer.c Normal file
View File

@ -0,0 +1,100 @@
#include <stdint.h>
#include <stdbool.h>
#include <sys/param.h>
#include "sequencer.h"
#include "scheduler.h"
#include "psg.h"
void sequencerInit() {
}
void sequencerExec(void *handle) {
static uint16_t lengths[e_L_LengthEnd];
t_melodies *melodies = (t_melodies*) handle;
if (melodies->firstRun) {
melodies->firstRun = false;
lengths[e_L_1_4] = 60000 / melodies->pace; // duration of a 1/4 tone in ms
lengths[e_L_1_2] = lengths[e_L_1_4] << 1;
lengths[e_L_1] = lengths[e_L_1_4] << 2;
lengths[e_L_1_8] = lengths[e_L_1_4] >> 1;
lengths[e_L_1_16] = lengths[e_L_1_4] >> 2;
lengths[e_L_1_32] = lengths[e_L_1_4] >> 4;
for (uint8_t i = 0; i < e_L_LengthEnd; i++) {
lengths[i] /= SEQUENCER_PERIOD;
}
}
for (uint8_t channel = 0; channel < melodies->numOfMelodies; channel++) {
t_melody *melody = &(melodies->melodies[channel]);
switch (melody->state) {
case e_Init:
psgAmplitude(channel, melody->amplitude);
melody->state = e_PlayTone;
break;
case e_PlayTone:
if (melody->tones[melody->idx].length == e_L_SyncMark) {
if (melodies->sync == 0) {
melodies->sync = melodies->numOfMelodies;
}
melodies->sync -= 1;
melody->state = e_Sync;
} else {
if (melody->tones[melody->idx].length == e_L_EndMark) {
melody->idx = 0;
}
psgPlayTone(channel, melody->tones[melody->idx].octave, melody->tones[melody->idx].note);
melody->lengthCnt = (melody->tones[melody->idx].staccato) ? (lengths[melody->tones[melody->idx].length] / 2) : lengths[melody->tones[melody->idx].length];
melody->state = e_HoldTone;
}
break;
case e_Sync:
if (melodies->sync == 0) {
melody->state = e_SeparateTone;
}
break;
case e_HoldTone:
melody->lengthCnt -= 1;
if (melody->lengthCnt == 0) {
melody->state = (melody->tones[melody->idx].staccato) ? e_StaccatoBreak : e_SeparateTone;
}
break;
case e_StaccatoBreak:
psgPlayTone(channel, e_O_Null, e_Pause);
melody->lengthCnt = lengths[melody->tones[melody->idx].length] / 2;
melody->state = e_HoldStaccatoBreak;
break;
case e_HoldStaccatoBreak:
melody->lengthCnt -= 1;
if (melody->lengthCnt == 0) {
melody->state = e_SeparateTone;
}
break;
case e_SeparateTone:
if (! (melody->tones[melody->idx].legato)) {
psgPlayTone(channel, e_O_Null, e_Pause);
}
melody->idx += 1;
melody->state = e_PlayTone;
break;
}
}
}
uint16_t sequencerPlayMelodies(t_melodies *melodies) {
for (uint8_t i = 0; i < NUM_OF_CHANNELS; i++) {
melodies->melodies[i].idx = 0;
melodies->melodies[i].lengthCnt = 0;
melodies->melodies[i].state = e_Init;
}
melodies->sync = 0;
melodies->firstRun = true;
return schAdd(sequencerExec, (void*) melodies, 0, SEQUENCER_PERIOD);
}

61
sound-driver/sequencer.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef _SEQUENCER_H_
#define _SEQUENCER_H_
#include <stdint.h>
#include <stdbool.h>
#include "psg.h"
typedef enum {
e_L_1 = 0,
e_L_1_2 = 1,
e_L_1_4 = 2,
e_L_1_8 = 3,
e_L_1_16 = 4,
e_L_1_32 = 5,
e_L_LengthEnd = 6,
e_L_EndMark = 254,
e_L_SyncMark = 255,
} t_noteLength;
typedef struct {
t_octave octave;
t_note note;
t_noteLength length;
bool legato;
bool staccato;
} t_tone;
typedef enum {
e_Init,
e_PlayTone,
e_Sync,
e_HoldTone,
e_StaccatoBreak,
e_HoldStaccatoBreak,
e_SeparateTone
} t_sequencerState;
typedef struct {
uint16_t idx;
uint16_t lengthCnt;
t_sequencerState state;
uint8_t amplitude;
const t_tone *tones;
} t_melody;
#define SEQUENCER_PERIOD 4 // ms
#define NUM_OF_CHANNELS 3
typedef struct {
uint8_t numOfMelodies;
bool firstRun;
uint8_t pace; // quarter notes per minute
uint8_t sync;
t_melody melodies[NUM_OF_CHANNELS];
} t_melodies;
void sequencerInit();
uint16_t sequencerPlayMelodies(t_melodies *melodies);
#endif // _SEQUENCER_H_

29
sound-driver/spi.c Normal file
View File

@ -0,0 +1,29 @@
#include <msp430g2553.h>
#include "spi.h"
void __attribute__ ((interrupt (USCIAB0RX_VECTOR))) receive() {
if (UC0IFG & UCB0RXIFG) {
// receive an octet
}
}
void spiInit() {
// 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 = UCSYNC | UCMSB | UCMODE_2;
UCB0CTL1 = 0x00;
// enable RX interrupt
UC0IE |= UCB0RXIE;
}

9
sound-driver/spi.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef _SPI_H_
#define _SPI_H_
void spiInit();
#endif // _SPI_H_

30
time.c
View File

@ -1,30 +0,0 @@
#include <msp430g2553.h>
#include <stdint.h>
#include "time.h"
#include "PontCoopScheduler.h"
volatile uint32_t timestamp;
void __attribute__ ((interrupt (TIMER0_A0_VECTOR))) ta0_isr() {
timestamp++;
schUpdate();
}
void timeInit() {
timestamp = 0;
TACCR0 = 32;
TACCTL0 = CCIE;
TACTL = MC_1 | ID_0 | TASSEL_1 | TACLR;
}
uint32_t getMillis() {
return timestamp;
}
void ms_active_delay(uint16_t delay) {
uint32_t start = timestamp;
while (start + delay > timestamp);
}

12
time.h
View File

@ -1,12 +0,0 @@
#ifndef TIME_H_
#define TIME_H_
#include <stdint.h>
void timeInit();
uint32_t getMillis();
void ms_active_delay(uint16_t delay);
#endif /* TIME_H_ */