mirror of
https://github.com/isometimes/rpi4-osdev
synced 2024-11-09 20:00:40 +00:00
A rough BLE controlled Breakout game in part8
This commit is contained in:
parent
b2ae702c48
commit
d42d4b33fc
16 changed files with 1672 additions and 5 deletions
|
@ -26,7 +26,7 @@ unsigned char echo_addr[6];
|
||||||
unsigned int connected = 0;
|
unsigned int connected = 0;
|
||||||
unsigned int connection_handle = 0;
|
unsigned int connection_handle = 0;
|
||||||
|
|
||||||
void poll2(unsigned char byte)
|
void hci_poll2(unsigned char byte)
|
||||||
{
|
{
|
||||||
switch (poll_state) {
|
switch (poll_state) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -53,7 +53,7 @@ void poll2(unsigned char byte)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *poll()
|
unsigned char *hci_poll()
|
||||||
{
|
{
|
||||||
unsigned int goal = messages_received + 1;
|
unsigned int goal = messages_received + 1;
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ unsigned char *poll()
|
||||||
|
|
||||||
while (run < MAX_READ_RUN && messages_received < goal && bt_isReadByteReady()) {
|
while (run < MAX_READ_RUN && messages_received < goal && bt_isReadByteReady()) {
|
||||||
unsigned char byte = bt_readByte();
|
unsigned char byte = bt_readByte();
|
||||||
poll2(byte);
|
hci_poll2(byte);
|
||||||
run++;
|
run++;
|
||||||
}
|
}
|
||||||
if (run == MAX_READ_RUN) return 0;
|
if (run == MAX_READ_RUN) return 0;
|
||||||
|
@ -75,7 +75,7 @@ void bt_search()
|
||||||
{
|
{
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
|
|
||||||
while ( (buf = poll()) ) {
|
while ( (buf = hci_poll()) ) {
|
||||||
if (data_len >= 2) {
|
if (data_len >= 2) {
|
||||||
if (buf[0] == LE_ADREPORT_CODE) {
|
if (buf[0] == LE_ADREPORT_CODE) {
|
||||||
unsigned char numreports = buf[1];
|
unsigned char numreports = buf[1];
|
||||||
|
@ -137,7 +137,7 @@ void bt_conn()
|
||||||
{
|
{
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
|
|
||||||
while ( (buf = poll()) ) {
|
while ( (buf = hci_poll()) ) {
|
||||||
if (data_len >= 2) {
|
if (data_len >= 2) {
|
||||||
if (buf[0] == LE_CONNECT_CODE && !connected) {
|
if (buf[0] == LE_CONNECT_CODE && !connected) {
|
||||||
connected = !buf[1];
|
connected = !buf[1];
|
||||||
|
|
BIN
part8-breakout-ble/BCM4345C0.hcd
Normal file
BIN
part8-breakout-ble/BCM4345C0.hcd
Normal file
Binary file not shown.
22
part8-breakout-ble/Makefile
Normal file
22
part8-breakout-ble/Makefile
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
CFILES = $(wildcard *.c)
|
||||||
|
OFILES = $(CFILES:.c=.o)
|
||||||
|
GCCFLAGS = -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles
|
||||||
|
GCCPATH = ../../gcc-arm-9.2-2019.12-x86_64-aarch64-none-elf/bin
|
||||||
|
|
||||||
|
all: clean kernel8.img
|
||||||
|
|
||||||
|
boot.o: boot.S
|
||||||
|
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c boot.S -o boot.o
|
||||||
|
|
||||||
|
BCM4345C0.o : BCM4345C0.hcd
|
||||||
|
$(GCCPATH)/aarch64-none-elf-objcopy -I binary -O elf64-littleaarch64 -B aarch64 $< $@
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
$(GCCPATH)/aarch64-none-elf-gcc $(GCCFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
kernel8.img: boot.o $(OFILES) BCM4345C0.o
|
||||||
|
$(GCCPATH)/aarch64-none-elf-ld -nostdlib -nostartfiles boot.o $(OFILES) BCM4345C0.o -T link.ld -o kernel8.elf
|
||||||
|
$(GCCPATH)/aarch64-none-elf-objcopy -O binary kernel8.elf kernel8.img
|
||||||
|
|
||||||
|
clean:
|
||||||
|
/bin/rm kernel8.elf *.o *.img > /dev/null 2> /dev/null || true
|
30
part8-breakout-ble/boot.S
Normal file
30
part8-breakout-ble/boot.S
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
.section ".text.boot" // Make sure the linker puts this at the start of the kernel image
|
||||||
|
|
||||||
|
.global _start // Execution starts here
|
||||||
|
|
||||||
|
_start:
|
||||||
|
// Check processor ID is zero (executing on main core), else hang
|
||||||
|
mrs x1, mpidr_el1
|
||||||
|
and x1, x1, #3
|
||||||
|
cbz x1, 2f
|
||||||
|
// We're not on the main core, so hang in an infinite wait loop
|
||||||
|
1: wfe
|
||||||
|
b 1b
|
||||||
|
2: // We're on the main core!
|
||||||
|
|
||||||
|
// Set stack to start below our code
|
||||||
|
ldr x1, =_start
|
||||||
|
mov sp, x1
|
||||||
|
|
||||||
|
// Clean the BSS section
|
||||||
|
ldr x1, =__bss_start // Start address
|
||||||
|
ldr w2, =__bss_size // Size of the section
|
||||||
|
3: cbz w2, 4f // Quit loop if zero
|
||||||
|
str xzr, [x1], #8
|
||||||
|
sub w2, w2, #1
|
||||||
|
cbnz w2, 3b // Loop if non-zero
|
||||||
|
|
||||||
|
// Jump to our main() routine in C (make sure it doesn't return)
|
||||||
|
4: bl main
|
||||||
|
// In case it does return, halt the master core too
|
||||||
|
b 1b
|
319
part8-breakout-ble/bt.c
Normal file
319
part8-breakout-ble/bt.c
Normal file
|
@ -0,0 +1,319 @@
|
||||||
|
#include "io.h"
|
||||||
|
#include "fb.h"
|
||||||
|
|
||||||
|
// UART0
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARM_UART0_BASE = PERIPHERAL_BASE + 0x201000,
|
||||||
|
ARM_UART0_DR = ARM_UART0_BASE + 0x00,
|
||||||
|
ARM_UART0_FR = ARM_UART0_BASE + 0x18,
|
||||||
|
ARM_UART0_IBRD = ARM_UART0_BASE + 0x24,
|
||||||
|
ARM_UART0_FBRD = ARM_UART0_BASE + 0x28,
|
||||||
|
ARM_UART0_LCRH = ARM_UART0_BASE + 0x2C,
|
||||||
|
ARM_UART0_CR = ARM_UART0_BASE + 0x30,
|
||||||
|
ARM_UART0_IFLS = ARM_UART0_BASE + 0x34,
|
||||||
|
ARM_UART0_IMSC = ARM_UART0_BASE + 0x38,
|
||||||
|
ARM_UART0_RIS = ARM_UART0_BASE + 0x3C,
|
||||||
|
ARM_UART0_MIS = ARM_UART0_BASE + 0x40,
|
||||||
|
ARM_UART0_ICR = ARM_UART0_BASE + 0x44
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char lo(unsigned int val) { return (unsigned char)(val & 0xff); }
|
||||||
|
unsigned char hi(unsigned int val) { return (unsigned char)((val & 0xff00) >> 8); }
|
||||||
|
|
||||||
|
unsigned int bt_isReadByteReady() { return (!(mmio_read(ARM_UART0_FR) & 0x10)); }
|
||||||
|
|
||||||
|
unsigned char bt_readByte()
|
||||||
|
{
|
||||||
|
unsigned char ch = lo(mmio_read(ARM_UART0_DR));
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char bt_waitReadByte()
|
||||||
|
{
|
||||||
|
while (!bt_isReadByteReady());
|
||||||
|
return bt_readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_writeByte(char byte)
|
||||||
|
{
|
||||||
|
while ((mmio_read(ARM_UART0_FR) & 0x20) != 0);
|
||||||
|
mmio_write(ARM_UART0_DR, (unsigned int)byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_flushrx()
|
||||||
|
{
|
||||||
|
while (bt_isReadByteReady()) bt_readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_init()
|
||||||
|
{
|
||||||
|
gpio_useAsAlt3(30);
|
||||||
|
gpio_useAsAlt3(31);
|
||||||
|
gpio_useAsAlt3(32);
|
||||||
|
gpio_useAsAlt3(33);
|
||||||
|
|
||||||
|
bt_flushrx();
|
||||||
|
|
||||||
|
mmio_write(ARM_UART0_IMSC, 0x00);
|
||||||
|
mmio_write(ARM_UART0_ICR, 0x7ff);
|
||||||
|
mmio_write(ARM_UART0_IBRD, 0x1a);
|
||||||
|
mmio_write(ARM_UART0_FBRD, 0x03);
|
||||||
|
mmio_write(ARM_UART0_IFLS, 0x08);
|
||||||
|
mmio_write(ARM_UART0_LCRH, 0x70);
|
||||||
|
mmio_write(ARM_UART0_CR, 0xB01);
|
||||||
|
mmio_write(ARM_UART0_IMSC, 0x430);
|
||||||
|
|
||||||
|
wait_msec(0x100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// HOST SETUP
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OGF_HOST_CONTROL = 0x03,
|
||||||
|
OGF_LE_CONTROL = 0x08,
|
||||||
|
OGF_VENDOR = 0x3f,
|
||||||
|
|
||||||
|
COMMAND_SET_BDADDR = 0x01,
|
||||||
|
COMMAND_RESET_CHIP = 0x03,
|
||||||
|
COMMAND_SET_BAUD = 0x18,
|
||||||
|
COMMAND_LOAD_FIRMWARE = 0x2e,
|
||||||
|
|
||||||
|
HCI_COMMAND_PKT = 0x01,
|
||||||
|
HCI_ACL_PKT = 0x02,
|
||||||
|
HCI_EVENT_PKT = 0x04,
|
||||||
|
COMMAND_COMPLETE_CODE = 0x0e,
|
||||||
|
CONNECT_COMPLETE_CODE = 0x0f,
|
||||||
|
|
||||||
|
LL_SCAN_ACTIVE = 0x01,
|
||||||
|
LL_ADV_NONCONN_IND = 0x03
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char empty[] = {};
|
||||||
|
|
||||||
|
int hciCommandBytes(unsigned char *opcodebytes, unsigned char *data, unsigned char length)
|
||||||
|
{
|
||||||
|
unsigned char c=0;
|
||||||
|
|
||||||
|
bt_writeByte(HCI_COMMAND_PKT);
|
||||||
|
bt_writeByte(opcodebytes[0]);
|
||||||
|
bt_writeByte(opcodebytes[1]);
|
||||||
|
bt_writeByte(length);
|
||||||
|
|
||||||
|
while (c++<length) bt_writeByte(*data++);
|
||||||
|
|
||||||
|
if (bt_waitReadByte() != HCI_EVENT_PKT) return 1;
|
||||||
|
|
||||||
|
unsigned char code = bt_waitReadByte();
|
||||||
|
if (code == CONNECT_COMPLETE_CODE) {
|
||||||
|
if (bt_waitReadByte() != 4) return 2;
|
||||||
|
|
||||||
|
unsigned char err = bt_waitReadByte();
|
||||||
|
if (err != 0) {
|
||||||
|
uart_writeText("Saw HCI COMMAND STATUS error "); uart_hex(err); uart_writeText("\n");
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bt_waitReadByte() == 0) return 3;
|
||||||
|
if (bt_waitReadByte() != opcodebytes[0]) return 4;
|
||||||
|
if (bt_waitReadByte() != opcodebytes[1]) return 5;
|
||||||
|
} else if (code == COMMAND_COMPLETE_CODE) {
|
||||||
|
if (bt_waitReadByte() != 4) return 6;
|
||||||
|
if (bt_waitReadByte() == 0) return 7;
|
||||||
|
if (bt_waitReadByte() != opcodebytes[0]) return 8;
|
||||||
|
if (bt_waitReadByte() != opcodebytes[1]) return 9;
|
||||||
|
if (bt_waitReadByte() != 0) return 10;
|
||||||
|
} else return 11;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int hciCommand(unsigned short ogf, unsigned short ocf, unsigned char *data, unsigned char length)
|
||||||
|
{
|
||||||
|
unsigned short opcode = ogf << 10 | ocf;
|
||||||
|
unsigned char opcodebytes[2] = { lo(opcode), hi(opcode) };
|
||||||
|
|
||||||
|
return hciCommandBytes(opcodebytes, data, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_reset() {
|
||||||
|
if (hciCommand(OGF_HOST_CONTROL, COMMAND_RESET_CHIP, empty, 0)) uart_writeText("bt_reset() failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_loadfirmware()
|
||||||
|
{
|
||||||
|
if (hciCommand(OGF_VENDOR, COMMAND_LOAD_FIRMWARE, empty, 0)) uart_writeText("loadFirmware() failed\n");
|
||||||
|
|
||||||
|
extern unsigned char _binary_BCM4345C0_hcd_start[];
|
||||||
|
extern unsigned char _binary_BCM4345C0_hcd_size[];
|
||||||
|
|
||||||
|
unsigned int c=0;
|
||||||
|
unsigned int size = (long)&_binary_BCM4345C0_hcd_size;
|
||||||
|
|
||||||
|
while (c < size) {
|
||||||
|
unsigned char opcodebytes[] = { _binary_BCM4345C0_hcd_start[c], _binary_BCM4345C0_hcd_start[c+1] };
|
||||||
|
unsigned char length = _binary_BCM4345C0_hcd_start[c+2];
|
||||||
|
unsigned char *data = &(_binary_BCM4345C0_hcd_start[c+3]);
|
||||||
|
|
||||||
|
if (hciCommandBytes(opcodebytes, data, length)) {
|
||||||
|
uart_writeText("Firmware data load failed\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
c += 3 + length;
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_msec(0x100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_setbaud()
|
||||||
|
{
|
||||||
|
static unsigned char params[] = { 0, 0, 0x00, 0xc2, 0x01, 0x00 }; // little endian, 115200
|
||||||
|
if (hciCommand(OGF_VENDOR, COMMAND_SET_BAUD, params, 6)) uart_writeText("bt_setbaud() failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_setbdaddr()
|
||||||
|
{
|
||||||
|
static unsigned char params[] = { 0xee, 0xff, 0xc0, 0xee, 0xff, 0xc0 }; // reversed
|
||||||
|
if (hciCommand(OGF_VENDOR, COMMAND_SET_BDADDR, params, 6)) uart_writeText("bt_setbdaddr() failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_getbdaddr(unsigned char *bdaddr) {
|
||||||
|
bt_writeByte(HCI_COMMAND_PKT);
|
||||||
|
bt_writeByte(0x09);
|
||||||
|
bt_writeByte(0x10);
|
||||||
|
bt_writeByte(0x00);
|
||||||
|
|
||||||
|
if (bt_waitReadByte() != HCI_EVENT_PKT) return;
|
||||||
|
if (bt_waitReadByte() != COMMAND_COMPLETE_CODE) return;
|
||||||
|
if (bt_waitReadByte() != 0x0a) return;
|
||||||
|
if (bt_waitReadByte() != 1) return;
|
||||||
|
if (bt_waitReadByte() != 0x09) return;
|
||||||
|
if (bt_waitReadByte() != 0x10) return;
|
||||||
|
if (bt_waitReadByte() != 0x00) return;
|
||||||
|
|
||||||
|
for (int c=0;c<6;c++) bdaddr[c] = bt_waitReadByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendACLsubscribe(unsigned int handle)
|
||||||
|
{
|
||||||
|
bt_writeByte(HCI_ACL_PKT);
|
||||||
|
|
||||||
|
bt_writeByte(lo(handle));
|
||||||
|
bt_writeByte(hi(handle));
|
||||||
|
|
||||||
|
unsigned int length = 0x0009;
|
||||||
|
bt_writeByte(lo(length));
|
||||||
|
bt_writeByte(hi(length));
|
||||||
|
|
||||||
|
unsigned int data_length = 0x0005;
|
||||||
|
bt_writeByte(lo(data_length));
|
||||||
|
bt_writeByte(hi(data_length));
|
||||||
|
|
||||||
|
unsigned int channel = 0x0004;
|
||||||
|
bt_writeByte(lo(channel));
|
||||||
|
bt_writeByte(hi(channel));
|
||||||
|
|
||||||
|
unsigned char params[] = { 0x12, 0x2b, 0x00, 0x01, 0x00 };
|
||||||
|
|
||||||
|
unsigned int c=0;
|
||||||
|
while (c++<data_length) bt_writeByte(params[c-1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLEeventmask(unsigned char mask)
|
||||||
|
{
|
||||||
|
unsigned char params[] = { mask, 0, 0, 0, 0, 0, 0, 0 };
|
||||||
|
if (hciCommand(OGF_LE_CONTROL, 0x01, params, 8)) uart_writeText("setLEeventmask failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLEscanenable(unsigned char state, unsigned char duplicates) {
|
||||||
|
unsigned char params[] = { state, duplicates };
|
||||||
|
if (hciCommand(OGF_LE_CONTROL, 0x0c, params, 2)) uart_writeText(" setLEscanenable failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLEscanparameters(unsigned char type, unsigned char linterval, unsigned char hinterval, unsigned char lwindow, unsigned char hwindow, unsigned char own_address_type, unsigned char filter_policy) {
|
||||||
|
unsigned char params[] = { type, linterval, hinterval, lwindow, hwindow, own_address_type, filter_policy };
|
||||||
|
if (hciCommand(OGF_LE_CONTROL, 0x0b, params, 7)) uart_writeText("setLEscanparameters failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLEadvertenable(unsigned char state) {
|
||||||
|
unsigned char params[] = { state };
|
||||||
|
uart_writeText("doing the HCIcommand\n");
|
||||||
|
if (hciCommand(OGF_LE_CONTROL, 0x0a, params, 1)) uart_writeText("setLEadvertenable failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLEadvertparameters(unsigned char type, unsigned char linterval_min, unsigned char hinterval_min, unsigned char linterval_max, unsigned char hinterval_max, unsigned char own_address_type, unsigned char filter_policy) {
|
||||||
|
unsigned char params[16] = { linterval_min, hinterval_min, linterval_max, hinterval_max, type, own_address_type, 0, 0, 0, 0, 0, 0, 0, 0x07, filter_policy };
|
||||||
|
uart_writeText("doing the HCIcommand\n");
|
||||||
|
if (hciCommand(OGF_LE_CONTROL, 0x06, params, 15)) uart_writeText("setLEadvertparameters failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void setLEadvertdata() {
|
||||||
|
static unsigned char params[] = { 0x19,
|
||||||
|
0x02, 0x01, 0x06,
|
||||||
|
0x03, 0x03, 0xAA, 0xFE,
|
||||||
|
0x11, 0x16, 0xAA, 0xFE, 0x10, 0x00, 0x03, 0x69, 0x73, 0x6f, 0x6d, 0x65, 0x74, 0x69, 0x6d, 0x2e, 0x65, 0x73,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||||
|
if (hciCommand(OGF_LE_CONTROL, 0x08, params, 32)) uart_writeText("setLEadvertdata failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void createLEconnection(unsigned char a1, unsigned char a2, unsigned char a3, unsigned char a4, unsigned char a5, unsigned char a6, unsigned char linterval, unsigned char hinterval, unsigned char lwindow, unsigned char hwindow, unsigned char own_address_type, unsigned char filter_policy, unsigned char linterval_min, unsigned char hinterval_min, unsigned char linterval_max, unsigned char hinterval_max) {
|
||||||
|
unsigned char params[26] = { linterval, hinterval, lwindow, hwindow,
|
||||||
|
filter_policy,
|
||||||
|
0, a1, a2, a3, a4, a5, a6,
|
||||||
|
own_address_type,
|
||||||
|
linterval_min, hinterval_min, linterval_max, hinterval_max,
|
||||||
|
0, 0, 0x2a, 0x00, 0, 0, 0, 0 };
|
||||||
|
if (hciCommand(OGF_LE_CONTROL, 0x0d, params, 25)) uart_writeText("createLEconnection failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopScanning() {
|
||||||
|
setLEscanenable(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stopAdvertising() {
|
||||||
|
setLEadvertenable(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startActiveScanning() {
|
||||||
|
float BleScanInterval = 60; // every 60ms
|
||||||
|
float BleScanWindow = 60;
|
||||||
|
float BleScanDivisor = 0.625;
|
||||||
|
|
||||||
|
unsigned int p = BleScanInterval / BleScanDivisor;
|
||||||
|
unsigned int q = BleScanWindow / BleScanDivisor;
|
||||||
|
|
||||||
|
setLEscanparameters(LL_SCAN_ACTIVE, lo(p), hi(p), lo(q), hi(q), 0, 0);
|
||||||
|
setLEscanenable(1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startActiveAdvertising() {
|
||||||
|
float advertMinFreq = 100; // every 100ms
|
||||||
|
float advertMaxFreq = 100; // every 100ms
|
||||||
|
float bleGranularity = 0.625;
|
||||||
|
|
||||||
|
unsigned int min_interval = advertMinFreq / bleGranularity;
|
||||||
|
unsigned int max_interval = advertMaxFreq / bleGranularity;
|
||||||
|
|
||||||
|
setLEadvertparameters(LL_ADV_NONCONN_IND, lo(min_interval), hi(min_interval), lo(max_interval), hi(max_interval), 0, 0);
|
||||||
|
setLEadvertdata();
|
||||||
|
setLEadvertenable(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connect(unsigned char *addr)
|
||||||
|
{
|
||||||
|
float BleScanInterval = 60; // every 60ms
|
||||||
|
float BleScanWindow = 60;
|
||||||
|
float BleScanDivisor = 0.625;
|
||||||
|
|
||||||
|
float connMinFreq = 30; // every 30ms
|
||||||
|
float connMaxFreq = 50; // every 50ms
|
||||||
|
float BleGranularity = 1.25;
|
||||||
|
|
||||||
|
unsigned int p = BleScanInterval / BleScanDivisor;
|
||||||
|
unsigned int q = BleScanWindow / BleScanDivisor;
|
||||||
|
|
||||||
|
unsigned int min_interval = connMinFreq / BleGranularity;
|
||||||
|
unsigned int max_interval = connMaxFreq / BleGranularity;
|
||||||
|
|
||||||
|
createLEconnection(addr[5], addr[4], addr[3], addr[2], addr[1], addr[0], lo(p), hi(p), lo(q), hi(q), 0, 0, lo(min_interval), hi(min_interval), lo(max_interval), hi(max_interval));
|
||||||
|
}
|
16
part8-breakout-ble/bt.h
Normal file
16
part8-breakout-ble/bt.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
void bt_reset();
|
||||||
|
void bt_loadfirmware();
|
||||||
|
void bt_setbaud();
|
||||||
|
void bt_setbdaddr();
|
||||||
|
void bt_getbdaddr(unsigned char *bdaddr);
|
||||||
|
void bt_init();
|
||||||
|
unsigned int bt_isReadByteReady();
|
||||||
|
unsigned char bt_readByte();
|
||||||
|
unsigned char bt_waitReadByte();
|
||||||
|
void setLEeventmask(unsigned char mask);
|
||||||
|
void startActiveScanning();
|
||||||
|
void stopScanning();
|
||||||
|
void startActiveAdvertising();
|
||||||
|
void connect(unsigned char *addr);
|
||||||
|
void bt_flushrx();
|
||||||
|
void sendACLsubscribe(unsigned int handle);
|
213
part8-breakout-ble/fb.c
Normal file
213
part8-breakout-ble/fb.c
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
#include "io.h"
|
||||||
|
#include "mb.h"
|
||||||
|
#include "terminal.h"
|
||||||
|
|
||||||
|
unsigned int width, height, pitch, isrgb;
|
||||||
|
unsigned char *fb;
|
||||||
|
|
||||||
|
void fb_init()
|
||||||
|
{
|
||||||
|
mbox[0] = 35*4; // Length of message in bytes
|
||||||
|
mbox[1] = MBOX_REQUEST;
|
||||||
|
|
||||||
|
mbox[2] = MBOX_TAG_SETPHYWH; // Tag identifier
|
||||||
|
mbox[3] = 8; // Value size in bytes
|
||||||
|
mbox[4] = 8; // Value size in bytes (again!)
|
||||||
|
mbox[5] = 1920; // Value(width)
|
||||||
|
mbox[6] = 1080; // Value(height)
|
||||||
|
|
||||||
|
mbox[7] = MBOX_TAG_SETVIRTWH;
|
||||||
|
mbox[8] = 8;
|
||||||
|
mbox[9] = 8;
|
||||||
|
mbox[10] = 1920;
|
||||||
|
mbox[11] = 1080;
|
||||||
|
|
||||||
|
mbox[12] = MBOX_TAG_SETVIRTOFF;
|
||||||
|
mbox[13] = 8;
|
||||||
|
mbox[14] = 8;
|
||||||
|
mbox[15] = 0; // Value(x)
|
||||||
|
mbox[16] = 0; // Value(y)
|
||||||
|
|
||||||
|
mbox[17] = MBOX_TAG_SETDEPTH;
|
||||||
|
mbox[18] = 4;
|
||||||
|
mbox[19] = 4;
|
||||||
|
mbox[20] = 32; // Bits per pixel
|
||||||
|
|
||||||
|
mbox[21] = MBOX_TAG_SETPXLORDR;
|
||||||
|
mbox[22] = 4;
|
||||||
|
mbox[23] = 4;
|
||||||
|
mbox[24] = 1; // RGB
|
||||||
|
|
||||||
|
mbox[25] = MBOX_TAG_GETFB;
|
||||||
|
mbox[26] = 8;
|
||||||
|
mbox[27] = 8;
|
||||||
|
mbox[28] = 4096; // FrameBufferInfo.pointer
|
||||||
|
mbox[29] = 0; // FrameBufferInfo.size
|
||||||
|
|
||||||
|
mbox[30] = MBOX_TAG_GETPITCH;
|
||||||
|
mbox[31] = 4;
|
||||||
|
mbox[32] = 4;
|
||||||
|
mbox[33] = 0; // Bytes per line
|
||||||
|
|
||||||
|
mbox[34] = MBOX_TAG_LAST;
|
||||||
|
|
||||||
|
// Check call is successful and we have a pointer with depth 32
|
||||||
|
if (mbox_call(MBOX_CH_PROP) && mbox[20] == 32 && mbox[28] != 0) {
|
||||||
|
mbox[28] &= 0x3FFFFFFF; // Convert GPU address to ARM address
|
||||||
|
width = mbox[10]; // Actual physical width
|
||||||
|
height = mbox[11]; // Actual physical height
|
||||||
|
pitch = mbox[33]; // Number of bytes per line
|
||||||
|
isrgb = mbox[24]; // Pixel order
|
||||||
|
fb = (unsigned char *)((long)mbox[28]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawPixel(int x, int y, unsigned char attr)
|
||||||
|
{
|
||||||
|
int offs = (y * pitch) + (x * 4);
|
||||||
|
*((unsigned int*)(fb + offs)) = vgapal[attr & 0x0f];
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawRect(int x1, int y1, int x2, int y2, unsigned char attr, int fill)
|
||||||
|
{
|
||||||
|
int y=y1;
|
||||||
|
|
||||||
|
while (y <= y2) {
|
||||||
|
int x=x1;
|
||||||
|
while (x <= x2) {
|
||||||
|
if ((x == x1 || x == x2) || (y == y1 || y == y2)) drawPixel(x, y, attr);
|
||||||
|
else if (fill) drawPixel(x, y, (attr & 0xf0) >> 4);
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
y++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawLine(int x1, int y1, int x2, int y2, unsigned char attr)
|
||||||
|
{
|
||||||
|
int dx, dy, p, x, y;
|
||||||
|
|
||||||
|
dx = x2-x1;
|
||||||
|
dy = y2-y1;
|
||||||
|
x = x1;
|
||||||
|
y = y1;
|
||||||
|
p = 2*dy-dx;
|
||||||
|
|
||||||
|
while (x<x2) {
|
||||||
|
if (p >= 0) {
|
||||||
|
drawPixel(x,y,attr);
|
||||||
|
y++;
|
||||||
|
p = p+2*dy-2*dx;
|
||||||
|
} else {
|
||||||
|
drawPixel(x,y,attr);
|
||||||
|
p = p+2*dy;
|
||||||
|
}
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawCircle(int x0, int y0, int radius, unsigned char attr, int fill)
|
||||||
|
{
|
||||||
|
int x = radius;
|
||||||
|
int y = 0;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
while (x >= y) {
|
||||||
|
if (fill) {
|
||||||
|
drawLine(x0 - y, y0 + x, x0 + y, y0 + x, (attr & 0xf0) >> 4);
|
||||||
|
drawLine(x0 - x, y0 + y, x0 + x, y0 + y, (attr & 0xf0) >> 4);
|
||||||
|
drawLine(x0 - x, y0 - y, x0 + x, y0 - y, (attr & 0xf0) >> 4);
|
||||||
|
drawLine(x0 - y, y0 - x, x0 + y, y0 - x, (attr & 0xf0) >> 4);
|
||||||
|
}
|
||||||
|
drawPixel(x0 - y, y0 + x, attr);
|
||||||
|
drawPixel(x0 + y, y0 + x, attr);
|
||||||
|
drawPixel(x0 - x, y0 + y, attr);
|
||||||
|
drawPixel(x0 + x, y0 + y, attr);
|
||||||
|
drawPixel(x0 - x, y0 - y, attr);
|
||||||
|
drawPixel(x0 + x, y0 - y, attr);
|
||||||
|
drawPixel(x0 - y, y0 - x, attr);
|
||||||
|
drawPixel(x0 + y, y0 - x, attr);
|
||||||
|
|
||||||
|
if (err <= 0) {
|
||||||
|
y += 1;
|
||||||
|
err += 2*y + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err > 0) {
|
||||||
|
x -= 1;
|
||||||
|
err -= 2*x + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawChar(unsigned char ch, int x, int y, unsigned char attr, int zoom)
|
||||||
|
{
|
||||||
|
unsigned char *glyph = (unsigned char *)&font + (ch < FONT_NUMGLYPHS ? ch : 0) * FONT_BPG;
|
||||||
|
|
||||||
|
for (int i=1;i<=(FONT_HEIGHT*zoom);i++) {
|
||||||
|
for (int j=0;j<(FONT_WIDTH*zoom);j++) {
|
||||||
|
unsigned char mask = 1 << (j/zoom);
|
||||||
|
unsigned char col = (*glyph & mask) ? attr & 0x0f : (attr & 0xf0) >> 4;
|
||||||
|
|
||||||
|
drawPixel(x+j, y+i, col);
|
||||||
|
}
|
||||||
|
glyph += (i%zoom) ? 0 : FONT_BPL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawString(int x, int y, char *s, unsigned char attr, int zoom)
|
||||||
|
{
|
||||||
|
while (*s) {
|
||||||
|
if (*s == '\r') {
|
||||||
|
x = 0;
|
||||||
|
} else if(*s == '\n') {
|
||||||
|
x = 0; y += (FONT_HEIGHT*zoom);
|
||||||
|
} else {
|
||||||
|
drawChar(*s, x, y, attr, zoom);
|
||||||
|
x += (FONT_WIDTH*zoom);
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveRect(int oldx, int oldy, int width, int height, int shiftx, int shifty, unsigned char attr)
|
||||||
|
{
|
||||||
|
unsigned int newx = oldx + shiftx, newy = oldy + shifty;
|
||||||
|
unsigned int xcount = 0, ycount = 0;
|
||||||
|
unsigned int bitmap[width][height]; // This is very unsafe if it's too big for the stack...
|
||||||
|
unsigned int offs;
|
||||||
|
|
||||||
|
// Save the bitmap
|
||||||
|
while (xcount < width) {
|
||||||
|
while (ycount < height) {
|
||||||
|
offs = ((oldy + ycount) * pitch) + ((oldx + xcount) * 4);
|
||||||
|
|
||||||
|
bitmap[xcount][ycount] = *((unsigned int*)(fb + offs));
|
||||||
|
ycount++;
|
||||||
|
}
|
||||||
|
ycount=0;
|
||||||
|
xcount++;
|
||||||
|
}
|
||||||
|
// Wipe it out with background colour
|
||||||
|
drawRect(oldx, oldy, oldx + width, oldy + width, attr, 1);
|
||||||
|
// Draw it again
|
||||||
|
for (int i=newx;i<newx + width;i++) {
|
||||||
|
for (int j=newy;j<newy + height;j++) {
|
||||||
|
offs = (j * pitch) + (i * 4);
|
||||||
|
*((unsigned int*)(fb + offs)) = bitmap[i-newx][j-newy];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_msec(unsigned int n)
|
||||||
|
{
|
||||||
|
register unsigned long f, t, r;
|
||||||
|
|
||||||
|
// Get the current counter frequency
|
||||||
|
asm volatile ("mrs %0, cntfrq_el0" : "=r"(f));
|
||||||
|
// Read the current counter
|
||||||
|
asm volatile ("mrs %0, cntpct_el0" : "=r"(t));
|
||||||
|
// Calculate expire value for counter
|
||||||
|
t+=((f/1000)*n)/1000;
|
||||||
|
do{asm volatile ("mrs %0, cntpct_el0" : "=r"(r));}while(r<t);
|
||||||
|
}
|
9
part8-breakout-ble/fb.h
Normal file
9
part8-breakout-ble/fb.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
void fb_init();
|
||||||
|
void drawPixel(int x, int y, unsigned char attr);
|
||||||
|
void drawChar(unsigned char ch, int x, int y, unsigned char attr, int zoom);
|
||||||
|
void drawString(int x, int y, char *s, unsigned char attr, int zoom);
|
||||||
|
void drawRect(int x1, int y1, int x2, int y2, unsigned char attr, int fill);
|
||||||
|
void drawCircle(int x0, int y0, int radius, unsigned char attr, int fill);
|
||||||
|
void drawLine(int x1, int y1, int x2, int y2, unsigned char attr);
|
||||||
|
void moveRect(int oldx, int oldy, int width, int height, int shiftx, int shifty, unsigned char attr);
|
||||||
|
void wait_msec(unsigned int n);
|
193
part8-breakout-ble/io.c
Normal file
193
part8-breakout-ble/io.c
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
// GPIO
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GPFSEL0 = PERIPHERAL_BASE + 0x200000,
|
||||||
|
GPSET0 = PERIPHERAL_BASE + 0x20001C,
|
||||||
|
GPCLR0 = PERIPHERAL_BASE + 0x200028,
|
||||||
|
GPPUPPDN0 = PERIPHERAL_BASE + 0x2000E4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GPIO_MAX_PIN = 53,
|
||||||
|
GPIO_FUNCTION_OUT = 1,
|
||||||
|
GPIO_FUNCTION_ALT5 = 2,
|
||||||
|
GPIO_FUNCTION_ALT3 = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
Pull_None = 0,
|
||||||
|
Pull_Down = 1, // Are down and up the right way around?
|
||||||
|
Pull_Up = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
void mmio_write(long reg, unsigned int val) { *(volatile unsigned int *)reg = val; }
|
||||||
|
unsigned int mmio_read(long reg) { return *(volatile unsigned int *)reg; }
|
||||||
|
|
||||||
|
unsigned int gpio_call(unsigned int pin_number, unsigned int value, unsigned int base, unsigned int field_size, unsigned int field_max) {
|
||||||
|
unsigned int field_mask = (1 << field_size) - 1;
|
||||||
|
|
||||||
|
if (pin_number > field_max) return 0;
|
||||||
|
if (value > field_mask) return 0;
|
||||||
|
|
||||||
|
unsigned int num_fields = 32 / field_size;
|
||||||
|
unsigned int reg = base + ((pin_number / num_fields) * 4);
|
||||||
|
unsigned int shift = (pin_number % num_fields) * field_size;
|
||||||
|
|
||||||
|
unsigned int curval = mmio_read(reg);
|
||||||
|
curval &= ~(field_mask << shift);
|
||||||
|
curval |= value << shift;
|
||||||
|
mmio_write(reg, curval);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int gpio_set (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPSET0, 1, GPIO_MAX_PIN); }
|
||||||
|
unsigned int gpio_clear (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPCLR0, 1, GPIO_MAX_PIN); }
|
||||||
|
unsigned int gpio_pull (unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPPUPPDN0, 2, GPIO_MAX_PIN); }
|
||||||
|
unsigned int gpio_function(unsigned int pin_number, unsigned int value) { return gpio_call(pin_number, value, GPFSEL0, 3, GPIO_MAX_PIN); }
|
||||||
|
|
||||||
|
void gpio_useAsAlt3(unsigned int pin_number) {
|
||||||
|
gpio_pull(pin_number, Pull_None);
|
||||||
|
gpio_function(pin_number, GPIO_FUNCTION_ALT3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_useAsAlt5(unsigned int pin_number) {
|
||||||
|
gpio_pull(pin_number, Pull_None);
|
||||||
|
gpio_function(pin_number, GPIO_FUNCTION_ALT5);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_initOutputPinWithPullNone(unsigned int pin_number) {
|
||||||
|
gpio_pull(pin_number, Pull_None);
|
||||||
|
gpio_function(pin_number, GPIO_FUNCTION_OUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gpio_setPinOutputBool(unsigned int pin_number, unsigned int onOrOff) {
|
||||||
|
if (onOrOff) {
|
||||||
|
gpio_set(pin_number, 1);
|
||||||
|
} else {
|
||||||
|
gpio_clear(pin_number, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UART
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AUX_BASE = PERIPHERAL_BASE + 0x215000,
|
||||||
|
AUX_IRQ = AUX_BASE,
|
||||||
|
AUX_ENABLES = AUX_BASE + 4,
|
||||||
|
AUX_MU_IO_REG = AUX_BASE + 64,
|
||||||
|
AUX_MU_IER_REG = AUX_BASE + 68,
|
||||||
|
AUX_MU_IIR_REG = AUX_BASE + 72,
|
||||||
|
AUX_MU_LCR_REG = AUX_BASE + 76,
|
||||||
|
AUX_MU_MCR_REG = AUX_BASE + 80,
|
||||||
|
AUX_MU_LSR_REG = AUX_BASE + 84,
|
||||||
|
AUX_MU_MSR_REG = AUX_BASE + 88,
|
||||||
|
AUX_MU_SCRATCH = AUX_BASE + 92,
|
||||||
|
AUX_MU_CNTL_REG = AUX_BASE + 96,
|
||||||
|
AUX_MU_STAT_REG = AUX_BASE + 100,
|
||||||
|
AUX_MU_BAUD_REG = AUX_BASE + 104,
|
||||||
|
AUX_UART_CLOCK = 500000000,
|
||||||
|
UART_MAX_QUEUE = 16 * 1024
|
||||||
|
};
|
||||||
|
|
||||||
|
#define AUX_MU_BAUD(baud) ((AUX_UART_CLOCK/(baud*8))-1)
|
||||||
|
|
||||||
|
unsigned char uart_output_queue[UART_MAX_QUEUE];
|
||||||
|
unsigned int uart_output_queue_write = 0;
|
||||||
|
unsigned int uart_output_queue_read = 0;
|
||||||
|
|
||||||
|
void uart_init() {
|
||||||
|
mmio_write(AUX_ENABLES, 1); //enable UART1
|
||||||
|
mmio_write(AUX_MU_IER_REG, 0);
|
||||||
|
mmio_write(AUX_MU_CNTL_REG, 0);
|
||||||
|
mmio_write(AUX_MU_LCR_REG, 3); //8 bits
|
||||||
|
mmio_write(AUX_MU_MCR_REG, 0);
|
||||||
|
mmio_write(AUX_MU_IER_REG, 0);
|
||||||
|
mmio_write(AUX_MU_IIR_REG, 0xC6); //disable interrupts
|
||||||
|
mmio_write(AUX_MU_BAUD_REG, AUX_MU_BAUD(115200));
|
||||||
|
gpio_useAsAlt5(14);
|
||||||
|
gpio_useAsAlt5(15);
|
||||||
|
mmio_write(AUX_MU_CNTL_REG, 3); //enable RX/TX
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int uart_isOutputQueueEmpty() {
|
||||||
|
return uart_output_queue_read == uart_output_queue_write;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int uart_isReadByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x01; }
|
||||||
|
unsigned int uart_isWriteByteReady() { return mmio_read(AUX_MU_LSR_REG) & 0x20; }
|
||||||
|
|
||||||
|
unsigned char uart_readByte() {
|
||||||
|
while (!uart_isReadByteReady());
|
||||||
|
return (unsigned char)mmio_read(AUX_MU_IO_REG);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_writeByteBlockingActual(unsigned char ch) {
|
||||||
|
while (!uart_isWriteByteReady());
|
||||||
|
mmio_write(AUX_MU_IO_REG, (unsigned int)ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_loadOutputFifo() {
|
||||||
|
while (!uart_isOutputQueueEmpty() && uart_isWriteByteReady()) {
|
||||||
|
uart_writeByteBlockingActual(uart_output_queue[uart_output_queue_read]);
|
||||||
|
uart_output_queue_read = (uart_output_queue_read + 1) & (UART_MAX_QUEUE - 1); // Don't overrun
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_writeByteBlocking(unsigned char ch) {
|
||||||
|
unsigned int next = (uart_output_queue_write + 1) & (UART_MAX_QUEUE - 1); // Don't overrun
|
||||||
|
|
||||||
|
while (next == uart_output_queue_read) uart_loadOutputFifo();
|
||||||
|
|
||||||
|
uart_output_queue[uart_output_queue_write] = ch;
|
||||||
|
uart_output_queue_write = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_writeText(char *buffer) {
|
||||||
|
while (*buffer) {
|
||||||
|
if (*buffer == '\n') uart_writeByteBlockingActual('\r');
|
||||||
|
uart_writeByteBlockingActual(*buffer++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_drainOutputQueue() {
|
||||||
|
while (!uart_isOutputQueueEmpty()) uart_loadOutputFifo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_update() {
|
||||||
|
uart_loadOutputFifo();
|
||||||
|
|
||||||
|
if (uart_isReadByteReady()) {
|
||||||
|
unsigned char ch = uart_readByte();
|
||||||
|
if (ch == '\r') uart_writeText("\n"); else uart_writeByteBlocking(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_hex(unsigned int d) {
|
||||||
|
unsigned int n;
|
||||||
|
int c;
|
||||||
|
for(c=28;c>=0;c-=4) {
|
||||||
|
// get highest tetrad
|
||||||
|
n=(d>>c)&0xF;
|
||||||
|
// 0-9 => '0'-'9', 10-15 => 'A'-'F'
|
||||||
|
n+=n>9?0x37:0x30;
|
||||||
|
|
||||||
|
uart_writeByteBlockingActual(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_byte(unsigned char b) {
|
||||||
|
unsigned int n;
|
||||||
|
int c;
|
||||||
|
for(c=4;c>=0;c-=4) {
|
||||||
|
// get highest tetrad
|
||||||
|
n=(b>>c)&0xF;
|
||||||
|
// 0-9 => '0'-'9', 10-15 => 'A'-'F'
|
||||||
|
n+=n>9?0x37:0x30;
|
||||||
|
|
||||||
|
uart_writeByteBlockingActual(n);
|
||||||
|
}
|
||||||
|
uart_writeByteBlockingActual(' ');
|
||||||
|
}
|
14
part8-breakout-ble/io.h
Normal file
14
part8-breakout-ble/io.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#define PERIPHERAL_BASE 0xFE000000
|
||||||
|
|
||||||
|
void uart_init();
|
||||||
|
void uart_writeText(char *buffer);
|
||||||
|
void uart_loadOutputFifo();
|
||||||
|
unsigned char uart_readByte();
|
||||||
|
unsigned int uart_isReadByteReady();
|
||||||
|
void uart_writeByteBlockingActual(unsigned char ch);
|
||||||
|
void uart_update();
|
||||||
|
void mmio_write(long reg, unsigned int val);
|
||||||
|
unsigned int mmio_read(long reg);
|
||||||
|
void gpio_useAsAlt3(unsigned int pin_number);
|
||||||
|
void uart_hex(unsigned int d);
|
||||||
|
void uart_byte(unsigned char b);
|
462
part8-breakout-ble/kernel.c
Normal file
462
part8-breakout-ble/kernel.c
Normal file
|
@ -0,0 +1,462 @@
|
||||||
|
#include "fb.h"
|
||||||
|
#include "io.h"
|
||||||
|
#include "bt.h"
|
||||||
|
|
||||||
|
// The BLE stuff
|
||||||
|
#define memcmp __builtin_memcmp
|
||||||
|
#define MAX_MSG_LEN 50
|
||||||
|
#define MAX_READ_RUN 100
|
||||||
|
|
||||||
|
unsigned char data_buf[MAX_MSG_LEN];
|
||||||
|
unsigned int data_len;
|
||||||
|
unsigned int messages_received = 0;
|
||||||
|
unsigned int poll_state = 0;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
LE_EVENT_CODE = 0x3e,
|
||||||
|
LE_CONNECT_CODE = 0x01,
|
||||||
|
LE_ADREPORT_CODE = 0x02,
|
||||||
|
HCI_ACL_PKT = 0x02,
|
||||||
|
HCI_EVENT_PKT = 0x04
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int got_echo_sid = 0;
|
||||||
|
unsigned int got_echo_name = 0;
|
||||||
|
unsigned char echo_addr[6];
|
||||||
|
unsigned int connected = 0;
|
||||||
|
unsigned int connection_handle = 0;
|
||||||
|
unsigned char dir = 1;
|
||||||
|
|
||||||
|
void hci_poll2(unsigned char byte)
|
||||||
|
{
|
||||||
|
switch (poll_state) {
|
||||||
|
case 0:
|
||||||
|
if (byte != HCI_EVENT_PKT) poll_state = 0;
|
||||||
|
else poll_state = 1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (byte != LE_EVENT_CODE) poll_state = 0;
|
||||||
|
else poll_state = 2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (byte > MAX_MSG_LEN) poll_state = 0;
|
||||||
|
else {
|
||||||
|
poll_state = 3;
|
||||||
|
data_len = byte;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data_buf[poll_state - 3] = byte;
|
||||||
|
if (poll_state == data_len + 3 - 1) {
|
||||||
|
messages_received++;
|
||||||
|
poll_state = 0;
|
||||||
|
} else poll_state++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *hci_poll()
|
||||||
|
{
|
||||||
|
unsigned int goal = messages_received + 1;
|
||||||
|
|
||||||
|
if (bt_isReadByteReady()) {
|
||||||
|
unsigned int run = 0;
|
||||||
|
|
||||||
|
while (run < MAX_READ_RUN && messages_received < goal && bt_isReadByteReady()) {
|
||||||
|
unsigned char byte = bt_readByte();
|
||||||
|
hci_poll2(byte);
|
||||||
|
run++;
|
||||||
|
}
|
||||||
|
if (run == MAX_READ_RUN) return 0;
|
||||||
|
else return data_buf;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_search()
|
||||||
|
{
|
||||||
|
unsigned char *buf;
|
||||||
|
|
||||||
|
while ( (buf = hci_poll()) ) {
|
||||||
|
if (data_len >= 2) {
|
||||||
|
if (buf[0] == LE_ADREPORT_CODE) {
|
||||||
|
unsigned char numreports = buf[1];
|
||||||
|
|
||||||
|
if (numreports == 1) {
|
||||||
|
unsigned char event_type = buf[2];
|
||||||
|
|
||||||
|
if (event_type == 0x00) {
|
||||||
|
unsigned char buf_len = buf[10];
|
||||||
|
unsigned char ad_len = buf[11];
|
||||||
|
|
||||||
|
if (ad_len < data_len && buf_len + 11 == data_len - 1) {
|
||||||
|
for (int c=9;c>=4;c--) echo_addr[9-c] = buf[c];
|
||||||
|
buf += 11;
|
||||||
|
|
||||||
|
got_echo_sid = 0; got_echo_name = 0; // Reset the search state machine
|
||||||
|
do {
|
||||||
|
ad_len = buf[0];
|
||||||
|
unsigned char ad_type = buf[1];
|
||||||
|
buf += 2;
|
||||||
|
|
||||||
|
if (ad_len >= 2) {
|
||||||
|
if (ad_type == 0x03) {
|
||||||
|
unsigned int sid=0;
|
||||||
|
|
||||||
|
for (int d=0;d<ad_len - 1;d+=2) {
|
||||||
|
sid = buf[d] | (buf[d+1] << 8);
|
||||||
|
if (sid == 0xEC00) {
|
||||||
|
uart_hex(sid); uart_writeText(" ");
|
||||||
|
got_echo_sid = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (ad_type == 0x09) {
|
||||||
|
char remote_name[ad_len - 1];
|
||||||
|
unsigned int d=0;
|
||||||
|
|
||||||
|
while (d<ad_len - 1) {
|
||||||
|
remote_name[d] = buf[d];
|
||||||
|
d++;
|
||||||
|
}
|
||||||
|
if (!memcmp(remote_name,"echo",4)) {
|
||||||
|
uart_writeText(remote_name); uart_writeText(" ");
|
||||||
|
got_echo_name = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf += ad_len - 1;
|
||||||
|
} while (buf[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bt_conn()
|
||||||
|
{
|
||||||
|
unsigned char *buf;
|
||||||
|
|
||||||
|
while ( (buf = hci_poll()) ) {
|
||||||
|
if (data_len >= 2) {
|
||||||
|
if (buf[0] == LE_CONNECT_CODE && !connected) {
|
||||||
|
connected = !buf[1];
|
||||||
|
uart_hex(connected); uart_writeText(" ");
|
||||||
|
connection_handle = buf[2] | (buf[3] << 8);
|
||||||
|
uart_hex(connection_handle); uart_writeText(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void acl_poll()
|
||||||
|
{
|
||||||
|
while (bt_isReadByteReady()) {
|
||||||
|
unsigned char byte = bt_readByte();
|
||||||
|
|
||||||
|
if (byte == HCI_EVENT_PKT) {
|
||||||
|
unsigned char opcode = bt_waitReadByte();
|
||||||
|
unsigned char length = bt_waitReadByte();
|
||||||
|
for (int i=0;i<length;i++) bt_waitReadByte();
|
||||||
|
} else if (byte == HCI_ACL_PKT) {
|
||||||
|
unsigned char h1 = bt_waitReadByte();
|
||||||
|
unsigned char h2 = bt_waitReadByte();
|
||||||
|
|
||||||
|
unsigned int handle = h1 | (h2 & 0x0f);
|
||||||
|
unsigned char flags = (h2 & 0xf0) >> 4;
|
||||||
|
|
||||||
|
h1 = bt_waitReadByte();
|
||||||
|
h2 = bt_waitReadByte();
|
||||||
|
|
||||||
|
unsigned int length = h1 | (h2 << 8);
|
||||||
|
unsigned char data[length];
|
||||||
|
|
||||||
|
for (int i=0;i<length;i++) data[i] = bt_waitReadByte();
|
||||||
|
|
||||||
|
length = data[0] | (data[1] << 8);
|
||||||
|
|
||||||
|
unsigned int channel = data[2] | (data[3] << 8);
|
||||||
|
unsigned char opcode = data[4];
|
||||||
|
|
||||||
|
if (opcode == 0x1b) {
|
||||||
|
unsigned int from_handle = data[5] | (data[6] << 8);
|
||||||
|
if (length == 4) {
|
||||||
|
dir = data[7];
|
||||||
|
uart_byte(dir);
|
||||||
|
}
|
||||||
|
uart_writeText("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The screen
|
||||||
|
#define WIDTH 1920
|
||||||
|
#define HEIGHT 1080
|
||||||
|
#define MARGIN 30
|
||||||
|
#define VIRTWIDTH (WIDTH-(2*MARGIN))
|
||||||
|
#define FONT_BPG 8
|
||||||
|
|
||||||
|
// For the bricks
|
||||||
|
#define ROWS 5
|
||||||
|
#define COLS 10
|
||||||
|
unsigned int bricks = ROWS * COLS;
|
||||||
|
|
||||||
|
// Gameplay
|
||||||
|
#define NUM_LIVES 3
|
||||||
|
|
||||||
|
// OBJECT TRACKING
|
||||||
|
|
||||||
|
struct Object
|
||||||
|
{
|
||||||
|
unsigned int type;
|
||||||
|
unsigned int x;
|
||||||
|
unsigned int y;
|
||||||
|
unsigned int width;
|
||||||
|
unsigned int height;
|
||||||
|
unsigned char alive;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
OBJ_NONE = 0,
|
||||||
|
OBJ_BRICK = 1,
|
||||||
|
OBJ_PADDLE = 2,
|
||||||
|
OBJ_BALL = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int numobjs = 0;
|
||||||
|
struct Object objects[(ROWS * COLS) + (2 * NUM_LIVES)];
|
||||||
|
struct Object *ball;
|
||||||
|
struct Object *paddle;
|
||||||
|
|
||||||
|
void removeObject(struct Object *object)
|
||||||
|
{
|
||||||
|
drawRect(object->x, object->y, object->x + object->width, object->y + object->height, 0, 1);
|
||||||
|
object->alive = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void moveObject(struct Object *object, int xoff, int yoff)
|
||||||
|
{
|
||||||
|
moveRect(object->x, object->y, object->width, object->height, xoff, yoff, 0x00);
|
||||||
|
object->x = object->x + xoff;
|
||||||
|
object->y = object->y + yoff;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Object *detectCollision(struct Object *with, int xoff, int yoff)
|
||||||
|
{
|
||||||
|
for (int i=0; i<numobjs;i++) {
|
||||||
|
if (&objects[i] != with && objects[i].alive == 1) {
|
||||||
|
if (with->x + xoff > objects[i].x + objects[i].width || objects[i].x > with->x + xoff + with->width) {
|
||||||
|
// with is too far left or right to ocllide
|
||||||
|
} else if (with->y + yoff > objects[i].y + objects[i].height || objects[i].y > with->y + yoff + with->height) {
|
||||||
|
// with is too far up or down to ocllide
|
||||||
|
} else {
|
||||||
|
// Collision!
|
||||||
|
return &objects[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// KEY HANDLER
|
||||||
|
|
||||||
|
unsigned char getUart()
|
||||||
|
{
|
||||||
|
unsigned char ch = 0;
|
||||||
|
|
||||||
|
if (uart_isReadByteReady()) ch = uart_readByte();
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// OBJECT INITIALISERS
|
||||||
|
|
||||||
|
void initBricks()
|
||||||
|
{
|
||||||
|
int brickwidth = 32;
|
||||||
|
int brickheight = 8;
|
||||||
|
int brickspacer = 20;
|
||||||
|
int brickcols[5] = { 0x11, 0x22, 0xEE, 0x44, 0x66 };
|
||||||
|
|
||||||
|
int ybrick = MARGIN + brickheight;
|
||||||
|
|
||||||
|
for (int i=0; i<ROWS; i++) {
|
||||||
|
int xbrick = MARGIN + (VIRTWIDTH/COLS/2) - (brickwidth/2);
|
||||||
|
|
||||||
|
for (int j = 0; j<COLS; j++) {
|
||||||
|
drawRect(xbrick, ybrick, xbrick+brickwidth, ybrick+brickheight, brickcols[i], 1);
|
||||||
|
|
||||||
|
objects[numobjs].type = OBJ_BRICK;
|
||||||
|
objects[numobjs].x = xbrick;
|
||||||
|
objects[numobjs].y = ybrick;
|
||||||
|
objects[numobjs].width = brickwidth;
|
||||||
|
objects[numobjs].height = brickheight;
|
||||||
|
objects[numobjs].alive = 1;
|
||||||
|
numobjs++;
|
||||||
|
|
||||||
|
xbrick += (VIRTWIDTH/COLS);
|
||||||
|
}
|
||||||
|
ybrick = ybrick + brickspacer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initBall()
|
||||||
|
{
|
||||||
|
int ballradius = 15;
|
||||||
|
|
||||||
|
drawCircle(WIDTH/2, HEIGHT/2, ballradius, 0x55, 1);
|
||||||
|
|
||||||
|
objects[numobjs].type = OBJ_BALL;
|
||||||
|
objects[numobjs].x = (WIDTH/2) - ballradius;
|
||||||
|
objects[numobjs].y = (HEIGHT/2) - ballradius;
|
||||||
|
objects[numobjs].width = ballradius * 2;
|
||||||
|
objects[numobjs].height = ballradius * 2;
|
||||||
|
objects[numobjs].alive = 1;
|
||||||
|
ball = &objects[numobjs];
|
||||||
|
numobjs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initPaddle()
|
||||||
|
{
|
||||||
|
int paddlewidth = 80;
|
||||||
|
int paddleheight = 20;
|
||||||
|
|
||||||
|
drawRect((WIDTH-paddlewidth)/2, (HEIGHT-MARGIN-paddleheight), (WIDTH-paddlewidth)/2 + paddlewidth, (HEIGHT-MARGIN), 0x11, 1);
|
||||||
|
|
||||||
|
objects[numobjs].type = OBJ_PADDLE;
|
||||||
|
objects[numobjs].x = (WIDTH-paddlewidth)/2;
|
||||||
|
objects[numobjs].y = (HEIGHT-MARGIN-paddleheight);
|
||||||
|
objects[numobjs].width = paddlewidth;
|
||||||
|
objects[numobjs].height = paddleheight;
|
||||||
|
objects[numobjs].alive = 1;
|
||||||
|
paddle = &objects[numobjs];
|
||||||
|
numobjs++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawScoreboard(int score, int lives)
|
||||||
|
{
|
||||||
|
char tens = score / 10; score -= (10 * tens);
|
||||||
|
char ones = score;
|
||||||
|
|
||||||
|
char string[] = "Score: 0xx Lives: x\0\0";
|
||||||
|
|
||||||
|
string[8] = tens + 0x30;
|
||||||
|
string[9] = ones + 0x30;
|
||||||
|
string[20] = (char)lives + 0x30;
|
||||||
|
|
||||||
|
drawString((WIDTH/2)-252, MARGIN-25, string, 0x0f, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
struct Object *foundObject;
|
||||||
|
|
||||||
|
int lives = NUM_LIVES;
|
||||||
|
int points = 0;
|
||||||
|
|
||||||
|
int velocity_x = 1;
|
||||||
|
int velocity_y = 3;
|
||||||
|
|
||||||
|
uart_init();
|
||||||
|
|
||||||
|
bt_init();
|
||||||
|
uart_writeText("Initialising Bluetooth: ");
|
||||||
|
bt_reset();
|
||||||
|
bt_loadfirmware();
|
||||||
|
bt_setbaud();
|
||||||
|
bt_setbdaddr();
|
||||||
|
|
||||||
|
fb_init();
|
||||||
|
initBricks();
|
||||||
|
initBall();
|
||||||
|
initPaddle();
|
||||||
|
drawScoreboard(points, lives);
|
||||||
|
|
||||||
|
// Print the BD_ADDR
|
||||||
|
unsigned char local_addr[6];
|
||||||
|
bt_getbdaddr(local_addr);
|
||||||
|
for (int c=5;c>=0;c--) uart_byte(local_addr[c]);
|
||||||
|
uart_writeText("\n");
|
||||||
|
|
||||||
|
// Start scanning for echo
|
||||||
|
setLEeventmask(0xff);
|
||||||
|
startActiveScanning();
|
||||||
|
uart_writeText("Waiting for echo: ");
|
||||||
|
while (!(got_echo_sid && got_echo_name)) bt_search();
|
||||||
|
stopScanning();
|
||||||
|
for (int c=0;c<=5;c++) uart_byte(echo_addr[c]);
|
||||||
|
uart_writeText("\n");
|
||||||
|
|
||||||
|
// Ask to connect to the echo
|
||||||
|
uart_writeText("Connecting to echo: ");
|
||||||
|
connect(echo_addr);
|
||||||
|
while (!connected) bt_conn();
|
||||||
|
uart_writeText("\n");
|
||||||
|
|
||||||
|
// Subscribe to updates
|
||||||
|
uart_writeText("Sending subscribe request: ");
|
||||||
|
uart_hex(connection_handle); uart_writeText("\n");
|
||||||
|
sendACLsubscribe(connection_handle);
|
||||||
|
|
||||||
|
// Begin the game
|
||||||
|
uart_writeText("Let the game commence...\n");
|
||||||
|
|
||||||
|
while (lives > 0 && bricks > 0) {
|
||||||
|
acl_poll();
|
||||||
|
|
||||||
|
// Get any waiting input and flush the buffer
|
||||||
|
if (dir != 1) {
|
||||||
|
if (dir == 2) if (paddle->x + paddle->width + (paddle->width / 2) <= WIDTH-MARGIN) moveObject(paddle, paddle->width / 2, 0);
|
||||||
|
if (dir == 0) if (paddle->x >= MARGIN+(paddle->width / 2)) moveObject(paddle, -(paddle->width / 2), 0);
|
||||||
|
}
|
||||||
|
uart_loadOutputFifo();
|
||||||
|
|
||||||
|
// Are we going to hit anything?
|
||||||
|
foundObject = detectCollision(ball, velocity_x, velocity_y);
|
||||||
|
|
||||||
|
if (foundObject) {
|
||||||
|
if (foundObject == paddle) {
|
||||||
|
velocity_y = -velocity_y;
|
||||||
|
// Are we going to hit the side of the paddle
|
||||||
|
if (ball->x + ball->width + velocity_x == paddle->x || ball->x + velocity_x == paddle->x + paddle->width) velocity_x = -velocity_x;
|
||||||
|
} else if (foundObject->type == OBJ_BRICK) {
|
||||||
|
removeObject(foundObject);
|
||||||
|
velocity_y = -velocity_y;
|
||||||
|
bricks--;
|
||||||
|
points++;
|
||||||
|
drawScoreboard(points, lives);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_msec(4000); // Wait a little...
|
||||||
|
moveObject(ball, velocity_x, velocity_y);
|
||||||
|
|
||||||
|
// Check we're in the game arena still
|
||||||
|
if (ball->x + ball->width >= WIDTH-MARGIN) {
|
||||||
|
velocity_x = -velocity_x;
|
||||||
|
} else if (ball->x <= MARGIN) {
|
||||||
|
velocity_x = -velocity_x;
|
||||||
|
} else if (ball->y + ball->height >= HEIGHT-MARGIN) {
|
||||||
|
lives--;
|
||||||
|
|
||||||
|
removeObject(ball);
|
||||||
|
removeObject(paddle);
|
||||||
|
|
||||||
|
initBall();
|
||||||
|
initPaddle();
|
||||||
|
drawScoreboard(points, lives);
|
||||||
|
} else if (ball->y <= MARGIN) {
|
||||||
|
velocity_y = -velocity_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int zoom = WIDTH/192;
|
||||||
|
int strwidth = 10 * FONT_BPG * zoom;
|
||||||
|
int strheight = FONT_BPG * zoom;
|
||||||
|
|
||||||
|
if (bricks == 0) drawString((WIDTH/2)-(strwidth/2), (HEIGHT/2)-(strheight/2), "Well done!", 0x02, zoom);
|
||||||
|
else drawString((WIDTH/2)-(strwidth/2), (HEIGHT/2)-(strheight/2), "Game over!", 0x04, zoom);
|
||||||
|
|
||||||
|
while (1) acl_poll();
|
||||||
|
}
|
19
part8-breakout-ble/link.ld
Normal file
19
part8-breakout-ble/link.ld
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x80000; /* Kernel load address for AArch64 */
|
||||||
|
.text : { KEEP(*(.text.boot)) *(.text .text.* .gnu.linkonce.t*) }
|
||||||
|
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r*) }
|
||||||
|
PROVIDE(_data = .);
|
||||||
|
.data : { *(.data .data.* .gnu.linkonce.d*) }
|
||||||
|
.bss (NOLOAD) : {
|
||||||
|
. = ALIGN(16);
|
||||||
|
__bss_start = .;
|
||||||
|
*(.bss .bss.*)
|
||||||
|
*(COMMON)
|
||||||
|
__bss_end = .;
|
||||||
|
}
|
||||||
|
_end = .;
|
||||||
|
|
||||||
|
/DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) }
|
||||||
|
}
|
||||||
|
__bss_size = (__bss_end - __bss_start)>>3;
|
44
part8-breakout-ble/main
Normal file
44
part8-breakout-ble/main
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
uart_init();
|
||||||
|
bt_init();
|
||||||
|
|
||||||
|
uart_writeText("Initialising Bluetooth: ");
|
||||||
|
bt_reset();
|
||||||
|
bt_loadfirmware();
|
||||||
|
bt_setbaud();
|
||||||
|
bt_setbdaddr();
|
||||||
|
|
||||||
|
// Print the BD_ADDR
|
||||||
|
unsigned char local_addr[6];
|
||||||
|
bt_getbdaddr(local_addr);
|
||||||
|
for (int c=5;c>=0;c--) uart_byte(local_addr[c]);
|
||||||
|
uart_writeText("\n");
|
||||||
|
|
||||||
|
// Start scanning for echo
|
||||||
|
setLEeventmask(0xff);
|
||||||
|
startActiveScanning();
|
||||||
|
uart_writeText("Waiting for echo: ");
|
||||||
|
while (!(got_echo_sid && got_echo_name)) bt_search();
|
||||||
|
stopScanning();
|
||||||
|
for (int c=0;c<=5;c++) uart_byte(echo_addr[c]);
|
||||||
|
uart_writeText("\n");
|
||||||
|
|
||||||
|
// Ask to connect to the echo
|
||||||
|
uart_writeText("Connecting to echo: ");
|
||||||
|
connect(echo_addr);
|
||||||
|
while (!connected) bt_conn();
|
||||||
|
uart_writeText("\n");
|
||||||
|
|
||||||
|
// Get the characteristic value
|
||||||
|
uart_writeText("Sending read request: ");
|
||||||
|
uart_hex(connection_handle); uart_writeText("\n");
|
||||||
|
sendACLsubscribe(connection_handle);
|
||||||
|
|
||||||
|
// Into the main infinite loop
|
||||||
|
uart_writeText("Waiting for input...\n");
|
||||||
|
while (1) {
|
||||||
|
acl_poll();
|
||||||
|
uart_update();
|
||||||
|
}
|
||||||
|
}
|
39
part8-breakout-ble/mb.c
Normal file
39
part8-breakout-ble/mb.c
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
#include "io.h"
|
||||||
|
|
||||||
|
// The buffer must be 16-byte aligned as only the upper 28 bits of the address can be passed via the mailbox
|
||||||
|
volatile unsigned int __attribute__((aligned(16))) mbox[36];
|
||||||
|
|
||||||
|
enum {
|
||||||
|
VIDEOCORE_MBOX = (PERIPHERAL_BASE + 0x0000B880),
|
||||||
|
MBOX_READ = (VIDEOCORE_MBOX + 0x0),
|
||||||
|
MBOX_POLL = (VIDEOCORE_MBOX + 0x10),
|
||||||
|
MBOX_SENDER = (VIDEOCORE_MBOX + 0x14),
|
||||||
|
MBOX_STATUS = (VIDEOCORE_MBOX + 0x18),
|
||||||
|
MBOX_CONFIG = (VIDEOCORE_MBOX + 0x1C),
|
||||||
|
MBOX_WRITE = (VIDEOCORE_MBOX + 0x20),
|
||||||
|
MBOX_RESPONSE = 0x80000000,
|
||||||
|
MBOX_FULL = 0x80000000,
|
||||||
|
MBOX_EMPTY = 0x40000000
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int mbox_call(unsigned char ch)
|
||||||
|
{
|
||||||
|
// 28-bit address (MSB) and 4-bit value (LSB)
|
||||||
|
unsigned int r = ((unsigned int)((long) &mbox) &~ 0xF) | (ch & 0xF);
|
||||||
|
|
||||||
|
// Wait until we can write
|
||||||
|
while (mmio_read(MBOX_STATUS) & MBOX_FULL);
|
||||||
|
|
||||||
|
// Write the address of our buffer to the mailbox with the channel appended
|
||||||
|
mmio_write(MBOX_WRITE, r);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
// Is there a reply?
|
||||||
|
while (mmio_read(MBOX_STATUS) & MBOX_EMPTY);
|
||||||
|
|
||||||
|
// Is it a reply to our message?
|
||||||
|
if (r == mmio_read(MBOX_READ)) return mbox[1]==MBOX_RESPONSE; // Is it successful?
|
||||||
|
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
34
part8-breakout-ble/mb.h
Normal file
34
part8-breakout-ble/mb.h
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
extern volatile unsigned int mbox[36];
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MBOX_REQUEST = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MBOX_CH_POWER = 0,
|
||||||
|
MBOX_CH_FB = 1,
|
||||||
|
MBOX_CH_VUART = 2,
|
||||||
|
MBOX_CH_VCHIQ = 3,
|
||||||
|
MBOX_CH_LEDS = 4,
|
||||||
|
MBOX_CH_BTNS = 5,
|
||||||
|
MBOX_CH_TOUCH = 6,
|
||||||
|
MBOX_CH_COUNT = 7,
|
||||||
|
MBOX_CH_PROP = 8 // Request from ARM for response by VideoCore
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MBOX_TAG_SETPOWER = 0x28001,
|
||||||
|
MBOX_TAG_SETCLKRATE = 0x38002,
|
||||||
|
|
||||||
|
MBOX_TAG_SETPHYWH = 0x48003,
|
||||||
|
MBOX_TAG_SETVIRTWH = 0x48004,
|
||||||
|
MBOX_TAG_SETVIRTOFF = 0x48009,
|
||||||
|
MBOX_TAG_SETDEPTH = 0x48005,
|
||||||
|
MBOX_TAG_SETPXLORDR = 0x48006,
|
||||||
|
MBOX_TAG_GETFB = 0x40001,
|
||||||
|
MBOX_TAG_GETPITCH = 0x40008,
|
||||||
|
|
||||||
|
MBOX_TAG_LAST = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned int mbox_call(unsigned char ch);
|
253
part8-breakout-ble/terminal.h
Normal file
253
part8-breakout-ble/terminal.h
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
unsigned int vgapal[] = {
|
||||||
|
0x000000,
|
||||||
|
0x0000AA,
|
||||||
|
0x00AA00,
|
||||||
|
0x00AAAA,
|
||||||
|
0xAA0000,
|
||||||
|
0xAA00AA,
|
||||||
|
0xAA5500,
|
||||||
|
0xAAAAAA,
|
||||||
|
0x555555,
|
||||||
|
0x5555FF,
|
||||||
|
0x55FF55,
|
||||||
|
0x55FFFF,
|
||||||
|
0xFF5555,
|
||||||
|
0xFF55FF,
|
||||||
|
0xFFFF55,
|
||||||
|
0xFFFFFF
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FONT_WIDTH = 8,
|
||||||
|
FONT_HEIGHT = 8,
|
||||||
|
FONT_BPG = 8, // Bytes per glyph
|
||||||
|
FONT_BPL = 1, // Bytes per line
|
||||||
|
FONT_NUMGLYPHS = 224
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned char font[FONT_NUMGLYPHS][FONT_BPG] = {
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0000 (nul)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0001
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0002
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0003
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0004
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0005
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0006
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0007
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0008
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0009
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000A
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000B
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000C
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000D
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000E
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+000F
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0010
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0011
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0012
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0013
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0014
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0015
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0016
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0017
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0018
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0019
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001A
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001B
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001C
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001D
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001E
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+001F
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space)
|
||||||
|
{ 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00}, // U+0021 (!)
|
||||||
|
{ 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0022 (")
|
||||||
|
{ 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, // U+0023 (#)
|
||||||
|
{ 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00}, // U+0024 ($)
|
||||||
|
{ 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00}, // U+0025 (%)
|
||||||
|
{ 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00}, // U+0026 (&)
|
||||||
|
{ 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0027 (')
|
||||||
|
{ 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00}, // U+0028 (()
|
||||||
|
{ 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00}, // U+0029 ())
|
||||||
|
{ 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00}, // U+002A (*)
|
||||||
|
{ 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00}, // U+002B (+)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+002C (,)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.)
|
||||||
|
{ 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/)
|
||||||
|
{ 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0)
|
||||||
|
{ 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1)
|
||||||
|
{ 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2)
|
||||||
|
{ 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3)
|
||||||
|
{ 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4)
|
||||||
|
{ 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5)
|
||||||
|
{ 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6)
|
||||||
|
{ 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7)
|
||||||
|
{ 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8)
|
||||||
|
{ 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9)
|
||||||
|
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+003A (:)
|
||||||
|
{ 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06}, // U+003B (;)
|
||||||
|
{ 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00}, // U+003C (<)
|
||||||
|
{ 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00}, // U+003D (=)
|
||||||
|
{ 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00}, // U+003E (>)
|
||||||
|
{ 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00}, // U+003F (?)
|
||||||
|
{ 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00}, // U+0040 (@)
|
||||||
|
{ 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A)
|
||||||
|
{ 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B)
|
||||||
|
{ 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C)
|
||||||
|
{ 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D)
|
||||||
|
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E)
|
||||||
|
{ 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F)
|
||||||
|
{ 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G)
|
||||||
|
{ 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H)
|
||||||
|
{ 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I)
|
||||||
|
{ 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J)
|
||||||
|
{ 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K)
|
||||||
|
{ 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L)
|
||||||
|
{ 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M)
|
||||||
|
{ 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N)
|
||||||
|
{ 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O)
|
||||||
|
{ 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P)
|
||||||
|
{ 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q)
|
||||||
|
{ 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R)
|
||||||
|
{ 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S)
|
||||||
|
{ 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T)
|
||||||
|
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U)
|
||||||
|
{ 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V)
|
||||||
|
{ 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W)
|
||||||
|
{ 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X)
|
||||||
|
{ 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y)
|
||||||
|
{ 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z)
|
||||||
|
{ 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00}, // U+005B ([)
|
||||||
|
{ 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00}, // U+005C (\)
|
||||||
|
{ 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00}, // U+005D (])
|
||||||
|
{ 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00}, // U+005E (^)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, // U+005F (_)
|
||||||
|
{ 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0060 (`)
|
||||||
|
{ 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a)
|
||||||
|
{ 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b)
|
||||||
|
{ 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c)
|
||||||
|
{ 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d)
|
||||||
|
{ 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e)
|
||||||
|
{ 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f)
|
||||||
|
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g)
|
||||||
|
{ 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h)
|
||||||
|
{ 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i)
|
||||||
|
{ 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j)
|
||||||
|
{ 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k)
|
||||||
|
{ 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l)
|
||||||
|
{ 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m)
|
||||||
|
{ 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n)
|
||||||
|
{ 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o)
|
||||||
|
{ 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p)
|
||||||
|
{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q)
|
||||||
|
{ 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r)
|
||||||
|
{ 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s)
|
||||||
|
{ 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t)
|
||||||
|
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u)
|
||||||
|
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v)
|
||||||
|
{ 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w)
|
||||||
|
{ 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x)
|
||||||
|
{ 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y)
|
||||||
|
{ 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z)
|
||||||
|
{ 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00}, // U+007B ({)
|
||||||
|
{ 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00}, // U+007C (|)
|
||||||
|
{ 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00}, // U+007D (})
|
||||||
|
{ 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007E (~)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+007F
|
||||||
|
{ 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x18, 0x30, 0x1E}, // U+00C7 (C cedille)
|
||||||
|
{ 0x00, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FC (u umlaut)
|
||||||
|
{ 0x38, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E9 (e aigu)
|
||||||
|
{ 0x7E, 0xC3, 0x3C, 0x60, 0x7C, 0x66, 0xFC, 0x00}, // U+00E2 (a circumflex)
|
||||||
|
{ 0x33, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E4 (a umlaut)
|
||||||
|
{ 0x07, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E0 (a grave)
|
||||||
|
{ 0x0C, 0x0C, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E5 (a ring)
|
||||||
|
{ 0x00, 0x00, 0x1E, 0x03, 0x03, 0x1E, 0x30, 0x1C}, // U+00E7 (c cedille)
|
||||||
|
{ 0x7E, 0xC3, 0x3C, 0x66, 0x7E, 0x06, 0x3C, 0x00}, // U+00EA (e circumflex)
|
||||||
|
{ 0x33, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00EB (e umlaut)
|
||||||
|
{ 0x07, 0x00, 0x1E, 0x33, 0x3F, 0x03, 0x1E, 0x00}, // U+00E8 (e grave)
|
||||||
|
{ 0x33, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EF (i umlaut)
|
||||||
|
{ 0x3E, 0x63, 0x1C, 0x18, 0x18, 0x18, 0x3C, 0x00}, // U+00EE (i circumflex)
|
||||||
|
{ 0x07, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00EC (i grave)
|
||||||
|
{ 0x63, 0x1C, 0x36, 0x63, 0x7F, 0x63, 0x63, 0x00}, // U+00C4 (A umlaut)
|
||||||
|
{ 0x0C, 0x0C, 0x00, 0x1E, 0x33, 0x3F, 0x33, 0x00}, // U+00C5 (A ring)
|
||||||
|
{ 0x07, 0x00, 0x3F, 0x06, 0x1E, 0x06, 0x3F, 0x00}, // U+00C8 (E grave)
|
||||||
|
{ 0x00, 0x00, 0xFE, 0x30, 0xFE, 0x33, 0xFE, 0x00}, // U+00E6 (ae)
|
||||||
|
{ 0x7C, 0x36, 0x33, 0x7F, 0x33, 0x33, 0x73, 0x00}, // U+00C6 (AE)
|
||||||
|
{ 0x1E, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F4 (o circumflex)
|
||||||
|
{ 0x00, 0x33, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F6 (o umlaut)
|
||||||
|
{ 0x00, 0x07, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F2 (o grave)
|
||||||
|
{ 0x1E, 0x33, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FB (u circumflex)
|
||||||
|
{ 0x00, 0x07, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00F9 (u grave)
|
||||||
|
{ 0x00, 0x33, 0x00, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+00FF (y umlaut)
|
||||||
|
{ 0xC3, 0x18, 0x3C, 0x66, 0x66, 0x3C, 0x18, 0x00}, // U+00D6 (O umlaut)
|
||||||
|
{ 0x33, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+00DC (U umlaut)
|
||||||
|
{ 0x18, 0x18, 0x7E, 0x03, 0x03, 0x7E, 0x18, 0x18}, // U+00A2 (dollarcents)
|
||||||
|
{ 0x1C, 0x36, 0x26, 0x0F, 0x06, 0x67, 0x3F, 0x00}, // U+00A3 (pound sterling)
|
||||||
|
{ 0x33, 0x33, 0x1E, 0x3F, 0x0C, 0x3F, 0x0C, 0x0C}, // U+00A5 (yen)
|
||||||
|
{ 0x7C, 0xC6, 0x1C, 0x36, 0x36, 0x1C, 0x33, 0x1E}, // U+00A7 (paragraph)
|
||||||
|
{ 0x70, 0xD8, 0x18, 0x3C, 0x18, 0x18, 0x1B, 0x0E}, // U+0192 (dutch florijn)
|
||||||
|
{ 0x38, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x7E, 0x00}, // U+00E1 (a aigu)
|
||||||
|
{ 0x1C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+00ED (i augu)
|
||||||
|
{ 0x00, 0x38, 0x00, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+00F3 (o aigu)
|
||||||
|
{ 0x00, 0x38, 0x00, 0x33, 0x33, 0x33, 0x7E, 0x00}, // U+00FA (u aigu)
|
||||||
|
{ 0x00, 0x1F, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x00}, // U+00F1 (n ~)
|
||||||
|
{ 0x3F, 0x00, 0x33, 0x37, 0x3F, 0x3B, 0x33, 0x00}, // U+00D1 (N ~)
|
||||||
|
{ 0x3C, 0x36, 0x36, 0x7C, 0x00, 0x00, 0x00, 0x00}, // U+00AA (superscript a)
|
||||||
|
{ 0x1C, 0x36, 0x36, 0x1C, 0x00, 0x00, 0x00, 0x00}, // U+00BA (superscript 0)
|
||||||
|
{ 0x0C, 0x00, 0x0C, 0x06, 0x03, 0x33, 0x1E, 0x00}, // U+00BF (inverted ?)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x3F, 0x03, 0x03, 0x00, 0x00}, // U+2310 (gun pointing right)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x3F, 0x30, 0x30, 0x00, 0x00}, // U+00AC (gun pointing left)
|
||||||
|
{ 0xC3, 0x63, 0x33, 0x7B, 0xCC, 0x66, 0x33, 0xF0}, // U+00BD (1/2)
|
||||||
|
{ 0xC3, 0x63, 0x33, 0xBD, 0xEC, 0xF6, 0xF3, 0x03}, // U+00BC (1/4)
|
||||||
|
{ 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00}, // U+00A1 (inverted !)
|
||||||
|
{ 0x00, 0xCC, 0x66, 0x33, 0x66, 0xCC, 0x00, 0x00}, // U+00AB (<<)
|
||||||
|
{ 0x00, 0x33, 0x66, 0xCC, 0x66, 0x33, 0x00, 0x00}, // U+00BB (>>)
|
||||||
|
{ 0x55, 0x00, 0xAA, 0x00, 0x55, 0x00, 0xAA, 0x00}, // U+2591 (25% solid)
|
||||||
|
{ 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA}, // U+2592 (50% solid)
|
||||||
|
{ 0xFF, 0xAA, 0xFF, 0x55, 0xFF, 0xAA, 0xFF, 0x55}, // U+2593 (75% solid)
|
||||||
|
{ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, // U+2502 (thin vertical)
|
||||||
|
{ 0x08, 0x08, 0x08, 0x08, 0x0f, 0x08, 0x08, 0x08}, // U+2524 (down L, left L, up L)
|
||||||
|
{ 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2561 (up L, down L, left D)
|
||||||
|
{ 0x14, 0x14, 0x14, 0x14, 0x17, 0x14, 0x14, 0x14}, // U+2562 (up D, down D, left L)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x1F, 0x14, 0x14, 0x14}, // U+2556 (down D, left L)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x0F, 0x08, 0x0F, 0x08, 0x08}, // U+2555 (down L, left D)
|
||||||
|
{ 0x14, 0x14, 0x14, 0x17, 0x10, 0x17, 0x14, 0x14}, // U+2563 (up D, down D, left D)
|
||||||
|
{ 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14}, // U+2551 (double vertical)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x1F, 0x10, 0x17, 0x14, 0x14}, // U+2557 (down D, left D)
|
||||||
|
{ 0x14, 0x14, 0x14, 0x17, 0x10, 0x1F, 0x00, 0x00}, // U+255D (up D, left D)
|
||||||
|
{ 0x14, 0x14, 0x14, 0x14, 0x1F, 0x00, 0x00, 0x00}, // U+255C (up D, left L)
|
||||||
|
{ 0x08, 0x08, 0x08, 0x0F, 0x08, 0x0F, 0x00, 0x00}, // U+255B (up L, left D)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0x0f, 0x08, 0x08, 0x08}, // U+2510 (down L, left L)
|
||||||
|
{ 0x08, 0x08, 0x08, 0x08, 0xf8, 0x00, 0x00, 0x00}, // U+2514 (up L, right L)
|
||||||
|
{ 0x08, 0x08, 0x08, 0x08, 0xff, 0x00, 0x00, 0x00}, // U+2534 (up L, right L, left L)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0xff, 0x08, 0x08, 0x08}, // U+252C (down L, right L, left L)
|
||||||
|
{ 0x08, 0x08, 0x08, 0x08, 0xf8, 0x08, 0x08, 0x08}, // U+251C (down L, right L, up L)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00}, // U+2500 (thin horizontal)
|
||||||
|
{ 0x08, 0x08, 0x08, 0x08, 0xff, 0x08, 0x08, 0x08}, // U+253C (up L, right L, left L, down L)
|
||||||
|
{ 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+255E (up L, down L, right D)
|
||||||
|
{ 0x14, 0x14, 0x14, 0x14, 0xF4, 0x14, 0x14, 0x14}, // U+255F (up D, down D, right L)
|
||||||
|
{ 0x14, 0x14, 0x14, 0xF4, 0x04, 0xFC, 0x00, 0x00}, // U+255A (up D, right D)
|
||||||
|
{ 0x00, 0x00, 0x00, 0xFC, 0x04, 0xF4, 0x14, 0x14}, // U+2554 (down D, right D)
|
||||||
|
{ 0x14, 0x14, 0x14, 0xF7, 0x00, 0xFF, 0x00, 0x00}, // U+2569 (left D, right D, up D)
|
||||||
|
{ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xF7, 0x14, 0x14}, // U+2566 (left D, right D, down D)
|
||||||
|
{ 0x14, 0x14, 0x14, 0xF4, 0x04, 0xF4, 0x14, 0x14}, // U+2560 (up D, down D, right D)
|
||||||
|
{ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2550 (double horizontal)
|
||||||
|
{ 0x14, 0x14, 0x14, 0xF7, 0x00, 0xF7, 0x14, 0x14}, // U+256C (left D, right D, down D, up D)
|
||||||
|
{ 0x08, 0x08, 0x08, 0xFF, 0x00, 0xFF, 0x00, 0x00}, // U+2567 (left D, right D, up L)
|
||||||
|
{ 0x14, 0x14, 0x14, 0x14, 0xFF, 0x00, 0x00, 0x00}, // U+2568 (left L, right L, up D)
|
||||||
|
{ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x08, 0x08}, // U+2564 (left D, right D, down L)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x14, 0x14, 0x14}, // U+2565 (left L, right L, down D)
|
||||||
|
{ 0x14, 0x14, 0x14, 0x14, 0xFC, 0x00, 0x00, 0x00}, // U+2559 (up D, right L)
|
||||||
|
{ 0x08, 0x08, 0x08, 0xF8, 0x08, 0xF8, 0x00, 0x00}, // U+2558 (up L, right D)
|
||||||
|
{ 0x00, 0x00, 0x00, 0xF8, 0x08, 0xF8, 0x08, 0x08}, // U+2552 (down L, right D)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0xFC, 0x14, 0x14, 0x14}, // U+2553 (down D, right L)
|
||||||
|
{ 0x14, 0x14, 0x14, 0x14, 0xFF, 0x14, 0x14, 0x14}, // U+256B (left L, right L, down D, up D)
|
||||||
|
{ 0x08, 0x08, 0x08, 0xFF, 0x08, 0xFF, 0x08, 0x08}, // U+256A (left D, right D, down L, up L)
|
||||||
|
{ 0x08, 0x08, 0x08, 0x08, 0x0f, 0x00, 0x00, 0x00}, // U+2518 (up L, left L)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0xf8, 0x08, 0x08, 0x08}, // U+250C (down L, right L)
|
||||||
|
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2588 (solid)
|
||||||
|
{ 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF}, // U+2584 (bottom half)
|
||||||
|
{ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, // U+258C (left half)
|
||||||
|
{ 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, // U+2590 (right half)
|
||||||
|
{ 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}, // U+2580 (top half)
|
||||||
|
};
|
Loading…
Reference in a new issue