src/main.cpp

Sun, 25 Oct 2020 22:45:24 +0100

author
David Douard <david.douard@sdf3.org>
date
Sun, 25 Oct 2020 22:45:24 +0100
changeset 35
b2dca6b935bb
parent 34
94ab7ff42a1b
child 36
a6c7292742a0
permissions
-rw-r--r--

Indent src/main.cpp with 2-ws

#include "stdio.h"

#include <mbed.h>
#include <rtos.h>
#include <string>

#include "Terminal6x8.h"
#include "Mono19x27.h"
#include "Mono15x22.h"
#include "Arial12x12.h"

#include "SSD1322.h"
#include "hp34comm.h"

#include "QEI.h"
#include "Keypad.h"

// Pins and device declarations
#if defined TARGET_NUCLEO_F446RE
#include "def_f446re.h"
#elif defined TARGET_HP34970_FP_F303RD
#include "def_hp34970_fp.h"
#endif

#include "platform/CircularBuffer.h"


SSD1322 *dsp;
HPSerial *hp;
volatile uint8_t must_refresh;
Thread tdsp, tloop;
Ticker blinker;
Timeout rst_delay;
Timeout splashscreen_timer;
InterruptIn rst(HP_RST);

QEI qenc(KP_ENC1, KP_ENC2, NC, 16);
volatile uint8_t knob;
bool shift;  // true when kp is shifted, cleared by command 0x01 from Unit
volatile bool splashscreen;


typedef enum {
		KEY_NONE=0,
		KEY_PRESSED,
		KEY_RELEASED
} key_event_t;

typedef struct keycode {
	uint8_t row;
	uint8_t col;
	key_event_t keyevent;
} keycode_t;

//volatile keycode_t cur_keycode;

#define KEY_BUF_SIZE 10
CircularBuffer<keycode_t, KEY_BUF_SIZE> key_buf;

#define KP_NROWS 4
#define KP_NCOLS 5
DigitalIn kp_rows[KP_NROWS] = {
		KP_R0,
		KP_R1,
		KP_R2,
		KP_R3
};

DigitalOut kp_columns[KP_NCOLS] = {
		KP_C0, KP_C1,
		KP_C2, KP_C3,
		KP_C4
};

/* mapping (RxC)
	 0x2 0x00:  View
	 0x1 0x01:  Mon
	 3x3 0x02:  Sto/Rcl
	 0x0 0x03:  Scan
	 1x2 0x04:  Alarm
	 1x1 0x05:  Mx+B
	 1x0 0x06:  Measure
	 2x0 0x07:  Interval
	 3x2 0x08:  Card Reset
	 3x1 0x09:  Close
	 3x0 0x0A:  Open
	 0x3 0x0B:  Read
	 2x3 0x0C:  Shift
	 1x3 0x0D:  Write
	 0x4 0x0E:  Left
	 1x4 0x0F:  Right
	 2x2 0x10:  Advanced
	 2x1 0x11:  Step
*/

uint8_t kp_mapping[KP_NROWS][KP_NCOLS] = { // [row][column]
		{0x03, 0x01, 0x00, 0x0B, 0x0E},
		{0x06, 0x05, 0x04, 0x0D, 0x0F},
		{0x07, 0x11, 0x10, 0x0C, 0xFF},
		{0x0A, 0x09, 0x08, 0x02, 0xFF}
};


void kp_cb(uint8_t row, uint8_t col);
void kr_cb(uint8_t row, uint8_t col);

Keypad *kpad;
uint8_t curchar;
//uint8_t curcmd;
uint8_t nchars;
char buffer[MAX_BUFF+1];

void timeout_h() {
#if defined(HAS_LED)
	led = !led;
#endif
}

typedef struct _DSP
{
	uint8_t cmd;
	uint8_t color;
	uint8_t bgcolor;
	uint8_t x0;
	uint8_t y0;
	uint8_t fmt; // 0x01=>ascii, 0x02=>hex, 0x04=>bits, 0x08=>flags, 0x80=>ignore
	uint8_t maxsize;
	uint8_t width;
	const unsigned char* font;
	char buffer[MAX_BUFF+1];
} DSP;

static DSP table[] =
{
		{ 0x00, 0xF, 0x0,  0,  0, 0x01, MAX_BUFF, 245, Mono19x27}, // main display
		{ 0x0C, 0xF, 0x0,196, 34, 0x01, 3,        45,  Mono15x22}, // channels display
		{ 0x0A, 0xF, 0x0,  0, 57, 0x08, 4,        0, Terminal6x8}, // flags + bits
};

// 9x10
const unsigned char icon_alarm[] __attribute__((aligned (2))) =
{
		0x1c, 0x0,
		0x3e, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0x7f, 0x0,
		0xff, 0x80,
		0x10, 0x0
};

const unsigned char icon_curve[] __attribute__((aligned (2))) =
{
		0x80, 0x0,
		0x80, 0x0,
		0x80, 0x80,
		0x81, 0x0,
		0x9e, 0x0,
		0xa0, 0x0,
		0xc0, 0x0,
		0x80, 0x0,
		0x80, 0x0,
		0xff, 0x80
};

typedef struct _FLAG
{
	uint8_t flag;
	uint8_t x;
	uint8_t y;
	bool reverse;
	const char* msg;
	const unsigned char* icon;
} FLAG;

typedef struct _FRAME
{
	uint16_t flag;
	uint8_t x0;
	uint8_t y0;
	uint8_t x1;
	uint8_t y1;
} FRAME;

static const FLAG flags[] =
{
    // flag, zone, x0, y0, reverse, msg, icon
    // right-side icons area
		{ 0x00, 246, 0,  false, NULL, icon_alarm}, // F1.0
    { 0x01, 246, 11, false, NULL, icon_curve}, // F1.1

    // F1.2 == Channel frame
    { 0x03, 197, 27, false, "Channel"},  // F1.3
    // F1.7 == Alarm frame

    { 0x34, 0,  28+8,  false, "MON"},    // F4.4
    { 0x33, 0,  28+16, false, "VIEW"},   // F4.3
    { 0x35, 0,  28,    true, "SCAN"},   // F4.5
    { 0x36, 0,  28+25, true, "CONFIG"},    // F4.6

    { 0x32, 40, 52,    false, "*"},      // F4.2
    { 0x31, 50, 52,    false, "ADRS"},   // F4.1
    { 0x30, 80, 52,    false, "RMT"},    // F4.0
    { 0x27, 104, 52,   true, "ERROR"},    // F3.7

    { 0x26, 140, 52,   false, "EXT"},    // F3.6
    { 0x25, 164, 52,   false, "ONCE"},   // F3.5

    { 0x23, 104, 28+16, false, "MEM"},    // F3.3


    // col 5
    { 0x14, 244, 22,  false, "4W"},     // F2.4
    { 0x15, 244, 30,  false, "OC"},     // F2.5
    { 0x22, 129, 28+16, false, "LAST"},   // F3.2
    { 0x21, 129, 28+16, false, "MIN"},    // F3.1
    { 0x20, 129, 28+16, false, "MAX"},    // F3.0
    { 0x17, 129, 28+16, false, "AVG"},    // F2.7

    { 0x05, 154+0,  17+10, false, "Alarm"},  // F1.5
    { 0x06, 154+0,  17+20, false, "H"},      // F1.6
    { 0x13, 154+6,  17+20, false, "1"},      // F2.3
    { 0x10, 154+12, 17+20, false, "2"},      // F2.0
    { 0x12, 154+18, 17+20, false, "3"},      // F2.2
    { 0x11, 154+24, 17+20, false, "4"},      // F2.1
    { 0x04, 154+30, 17+20, false, "L"},      // F1.4

};

static const FRAME zones[] =
{ // flag, x0, y0, x1, y1
    { 0x001, 0,   0, 245, 27}, // main display area
    { 0x002, 246, 0, 255, 27}, // right notif area
    { 0x004, 208, 35, 254, 62}, // channels display area
    { 0x008, 160, 28, 202, 54}, // alarms area
    { 0x010, 0,   28, 32,  54}, // flags col1
    { 0x020, 33,  28, 70,  54}, // flags col2
    { 0x040, 71,  28, 103, 54}, // flags col3
    { 0x080, 104, 28, 128, 54}, // flags col4
    { 0x100, 129, 28, 159, 54}, // flags col5

//    { 0x8000, 0, 55, 255, 63}, // flags bits display area
};

static const FRAME frames[] =
{
    { 0x02, 194, 30, 243, 53}, // F1.2 - channel frame
    { 0x07, 151, 30, 192, 46}, // F1.7 - alarm frame
};


#ifdef DEBUG

DigitalOut dbgpin(DBGPIN);
inline void pulse(uint8_t count=1, bool stayup=false)
{
	dbgpin = 0;
	wait_us(2);
	while (count--)
	{
		dbgpin = 1;
		wait_us(2);
		dbgpin = 0;
		wait_us(2);
	}
	if (stayup)
		dbgpin = 1;
}
#else
inline void pulse(uint8_t count=1, bool stayup=false)
{}
#endif

// callbacks & thread functions
void loop();
void copy_to_lcd(void);
void test_dsp();
void reset(void);
void reset_irq(void);
void qei_cb(int dir);
void end_splashscreen(void);
void show(uint8_t, const char*, uint8_t);

/*
	#if defined(HAVE_PC)
	FileHandle *mbed::mbed_override_console(int fd)
	{
	return static_cast<FileHandle*> (&pc);
	}
	#endif
*/

void setup() {
#if defined(HAVE_PC)
#if   defined(TARGET_NUCLEO_F446RE)
	pc.set_baud(115200);
#endif

/*
	#if  defined(TARGET_HP34970_FP_F303RD)
  pc.init();
  pc.connect();
	#endif
*/
#endif

	printf("\n\nSETUP\n");
	printf("  System Core Clock = %.3f MHZ\r\n",
				 (float)SystemCoreClock/1000000);

	/*
		#if defined(HAS_LED)
		printf("Attaching Led 1: %d\n", LED1);
		blinker.attach(callback(timeout_h), 0.5f);
		#endif
	*/

	hp = NULL;
#if defined(TARGET_NUCLEO_F446RE)
	printf("Serial communication pins\r\n");
	printf("     USBRX=%d\r\n", USBRX);
	printf("     USBTX=%d\r\n", USBTX);
#endif
#if defined(TARGET_HP34970_FP_F303RD)
	printf("Serial communication via USB\r\n");
#endif

	printf("Setup HP communication pins\r\n");
	printf("     HP_RX=%d\r\n", HP_RX);
	DigitalIn(HP_RX).mode(PullDown);
	printf("     HP_TX=%d\r\n", HP_TX);
	DigitalOut(HP_TX).write(1);
	printf("     HP_RST=%d\r\n", HP_RST);
	DigitalIn(HP_RST).mode(PullDown);

	printf("  setup QEI pins\r\n");
	printf("     ENC1=%d\r\n", KP_ENC1);
	DigitalIn(KP_ENC1).mode(PullDown);
	printf("     ENC2=%d\r\n", KP_ENC2);
	DigitalIn(KP_ENC2).mode(PullDown);
	qenc.attach(&qei_cb);

	printf("  setup Keypad\r\n");
	//cur_keycode.keyevent = KEY_NONE;

	shift = false;
	kpad = new Keypad(KP_NROWS, kp_rows, KP_NCOLS, kp_columns);
	printf("  attach Keypad callbacks\r\n");
	kpad->attach(&kp_cb, &kr_cb);
	printf("  start Keypad\r\n");
	kpad->start();

	printf("Setup OLED display\r\n");
	// init the LCD
	printf("     DSP_MOSI=%d\r\n", DSP_MOSI);
	printf("     DSP_MISO=%d\r\n", DSP_MISO);
	printf("     DSP_SCLK=%d\r\n", DSP_SCLK);
	printf("     DSP_CS=%d\r\n", DSP_CS);
	printf("     DSP_RST=%d\r\n", DSP_RST);
	printf("     DSP_DC=%d\r\n", DSP_DC);
	dsp = new SSD1322(20000000, DSP_MOSI, DSP_MISO, DSP_SCLK, DSP_CS,
										DSP_RST, DSP_DC, "SSD1322");

	printf("  configure DSP\r\n");
	dsp->background(Black);    // set background to black
	dsp->foreground(0xF);
	dsp->cls();

	//curcmd = 0xFF;
	curchar = 0;
	nchars = 0;

	for (uint8_t i=0; i<sizeof(table)/sizeof(table[0]); ++i)
		memset(table[i].buffer, 0, MAX_BUFF+1);

	printf("  display splash screen\r\n");
	dsp->locate(30, 10);
	dsp->set_font((unsigned char*)Mono19x27);
	dsp->printf("HP34970A");
	dsp->set_font((unsigned char*)Terminal6x8);
	dsp->locate(90, 40);
	dsp->printf("David Douard");
	dsp->locate(0, 52);
	dsp->printf("Clock = %d ", SystemCoreClock);

	RCC_OscInitTypeDef cfg;
	HAL_RCC_GetOscConfig(&cfg);
	if (cfg.HSEState == RCC_HSE_BYPASS)
		dsp->printf("HSE:EXT ");
	else if (cfg.HSEState == RCC_HSE_ON)
		dsp->printf("HSE:XTAL ");
	else
		dsp->printf("HSE:OFF ");

	if (cfg.HSIState == RCC_HSI_ON)
		dsp->printf("HSI:ON ");
	else
		dsp->printf("HSI:OFF ");

	dsp->copy_to_lcd();

	printf("Starting LCD thread\r\n");
	tdsp.start(&copy_to_lcd);

	printf("Starting Event thread\r\n");
	tloop.start(&loop);

	/*
		dsp->clrbuff();
		show(0x00, "HH:MM:\tSS\t:mmmm", 15); // main dsp
		show(0x0C, "888", 3); // channel dsp
		show(0x0A, "\xFF\xFF\xFF\xFF", 4); // all flags
	*/

	printf("Attaching timers\r\n");
	splashscreen = true;
	splashscreen_timer.attach(callback(&end_splashscreen), 2);
	rst.fall(&reset_irq);

	printf("SETUP DONE\r\n");
}

void end_splashscreen(void)
{
	// print is forbidden here because we are in an ISR context here
	//printf("End of splash screen CB\r\n");
	splashscreen = false;
}

void reset_irq(void)
{
	rst_delay.attach(callback(&reset), 0.1);
}

void reset(void)
{
	if (DigitalIn(HP_RST).read() == 0) {
		if (hp == NULL) {
			printf("setup HP communication handler\r\n");
			hp = new HPSerial(HP_TX, HP_RX);
		}

		printf("!! RST !! (gstate=%d, state=%d)\r\n",
					 hp->gstate(), hp->state());
		//printf("Value is ... %X\n", hp->search());
		hp->startup();
	}
}

void copy_to_lcd(void) {
	//uint8_t mask=1;

	while(1) {
		pulse(0, true);
		if ((splashscreen == false) && (must_refresh)) {
			must_refresh = 0;
			//Thread::wait(20); // give a bit of time for some more cmds
			dsp->copy_to_lcd();
		}

		/*
			if (must_refresh & mask) {
			for(uint8_t i=0; i<sizeof(zones)/sizeof(zones[0]); i++)
			if (zones[i].flag == mask) {
			dsp->copy_to_lcd(zones[i].x0/4, (zones[i].x1+3)/4,
			zones[i].y0,   zones[i].y1);
			must_refresh &= ~mask;
			break;
			}
			}
			mask = mask << 1;
			if (mask == 0) {
			mask = 1;
			}
		*/
		pulse(0, false);

		ThisThread::sleep_for(30);
	}
}

void show(uint8_t cmd, const char *intxt, uint8_t nchar=0)
{
	uint8_t i;
	// uint8_t len;
	uint16_t bgcolor, fgcolor;
	char *oldv;
	// char *txt;
	char *txtp;
	static char txt[256];


	if (cmd == 0xFF) // cls
	{
		dsp->clrbuff();
	}
	else
	{
		//txt = (char *)malloc(strlen(intxt)+1);
		strcpy(txt, intxt);
		txtp = txt;

		pulse(1, true);

		// len = MAX_BUFF;

		for (i=0; i<sizeof(table)/sizeof(table[0]); ++i) {
			if (table[i].cmd == cmd) {
				bgcolor = table[i].bgcolor;
				fgcolor = table[i].color;
				dsp->background(bgcolor);
				dsp->foreground(fgcolor);
				dsp->set_font((unsigned char*) table[i].font);
				oldv = table[i].buffer;

				dsp->locate(table[i].x0, table[i].y0);

				if (table[i].fmt & 0x01)  // ASCII text
				{
					if (strncmp(oldv, txt, table[i].maxsize) != 0)
					{
						if (table[i].width > 0)
							dsp->fillrect(table[i].x0,
														table[i].y0,
														table[i].x0 + table[i].width,
														table[i].y0 + table[i].font[2],
														bgcolor);
						for (uint8_t k=0; ;k++)
						{
							if (txtp[k] == 0x00)
							{
								dsp->printf(txtp);
								break;
							}
							if (txtp[k] == 0x09)
							{ // \t is a special char for 'unselected' display value
								txtp[k] = 0x00;
								dsp->printf(txtp);

								if (fgcolor == table[i].color)
									fgcolor /= 2;
								else
									fgcolor = table[i].color;
								dsp->foreground(fgcolor);
								txtp = &(txtp[k+1]);
								k = 0;
							}
						}
						if (cmd == 0x00) // main area
							must_refresh |= 0x01;
						if (cmd == 0x0C) // channels area
							must_refresh |= 0x04;
					}
				}
				/*
					if (table[i].fmt & 0x02 ) {
					// hex
					for (uint8_t j=0;; j++) {
					if (txt[j] == 0x00)
					break;
					dsp->printf("%02X ", txt[j]);
					}
					for (uint8_t j=3*strlen(txt); j<table[i].maxsize; j++)
					dsp->printf(" ");
					}
				*/
				if (table[i].fmt & 0x08 )  // flag indicators
				{
					// flags
					for (uint8_t j=0; j<max(nchar, table[i].maxsize) ; j++)
					{
						for (uint8_t k=0; k<8; k++)
						{
							if (1)
							{ //(txt[j] & (1 << k) ) != (oldv[j] & (1 << k))) {
								for (uint8_t l=0;
										 l<(sizeof(flags)/sizeof(flags[0])); ++l)
								{
									if (flags[l].flag == ((j<<4) + k)) {
										if (txtp[j] & (1 << k))
										{
											dsp->foreground(flags[l].reverse ? bgcolor : fgcolor);
											dsp->background(flags[l].reverse ? fgcolor : bgcolor);
										}
										else
										{
											dsp->foreground(bgcolor);
											dsp->background(bgcolor);
										}
										if (flags[l].msg != NULL)
										{ // a string
											dsp->locate(flags[l].x, flags[l].y);
											dsp->printf(flags[l].msg);}
										else
										{ // an icon
											Bitmap_s pic = {9, 10, 2, (char*) flags[l].icon};
											dsp->Bitmap_BW(pic, flags[l].x, flags[l].y);
										}
										must_refresh = 1; //|= zones[m].flag;
										break;
									}
								}
							}
						}
					}

					// draw frames (Alarm and Channel)
					for (uint8_t l=0;
							 l<(sizeof(frames)/sizeof(frames[0])); ++l)
					{
						uint16_t color;
						if (frames[l].flag & txt[0]) // frame flags are on the 1st byte only
							color = fgcolor/6;
						else
							color = bgcolor;
						dsp->hline(frames[l].x0+1, frames[l].x0+3, frames[l].y0, color);
						dsp->hline(frames[l].x1-3, frames[l].x1-1, frames[l].y0, color);
						dsp->hline(frames[l].x0+1, frames[l].x1-1, frames[l].y1, color);

						dsp->vline(frames[l].x0, frames[l].y0+1, frames[l].y1-1, color);
						dsp->vline(frames[l].x1, frames[l].y0+1, frames[l].y1-1, color);
					}
				}

				for(uint8_t j=0; j<table[i].maxsize; j++)
					oldv[j] = txt[j];
				break;
			}
		}
		//free(txt);
		//dsp->copy_to_lcd();
		pulse(1, false);
	}
}

void test_dsp()
{
	const FRAME *z;
	printf("TEST DSP\r\n");
	dsp->cls();
	printf("TEST DSP #2\r\n");
	show(0x00, "8g8g8g8g8g8g8", 13); // main dsp
	show(0x0C, "888", 3); // channel dsp
	show(0x0A, "\xFF\xFF\xFF\xFF", 4); // all flags
	dsp->copy_to_lcd();
	ThisThread::sleep_for(3);
	dsp->cls();
	printf("TEST DSP #3\r\n");

	for (uint8_t i=0; i<(sizeof(zones)/sizeof(zones[0])); i++)
	{
		z = &zones[i];
		dsp->fillrect(z->x0, z->y0, z->x1, z->y1, 4+i);
		dsp->locate(z->x0+1, z->y0+1);
		dsp->printf("%d", i);
	}

	/*
		for (uint8_t i=0; i<(sizeof(zones)/sizeof(zones[0])); i++)
		{
		z = &zones[i];
		printf("Zone %d [%x]: %d, %d, %d, %d\n", i, z->flag,
		z->x0, z->y0, z->x1, z->y1);
		must_refresh = z->flag;
		wait(1);
		}
		printf("Done\n");
		wait(2);
		printf("Copy ALL\n");
		dsp->copy_to_lcd();
	*/
	ThisThread::sleep_for(2);
	dsp->cls();
	printf("TEST DSP DONE\r\n");
}


void loop()
{ // run over and over
	keycode_t key;

	unsigned int err[8];
	for (uint8_t i=0; i<8; i++)
		err[i] = 0;
	int p, pp;  // rot encoder pulse counters
	p = 0;
	pp = 0;

	while(1) {
		p = qenc.getPulses();
		if (p != pp)
		{
			dsp->locate(0, 0);
			dsp->printf("Pulses = %d  ", p);
			dsp->copy_to_lcd();
			pp = p;
		}

		if (knob != 0)
		{
			if (hp != NULL)
			{
				printf("Sending keycode %X\r\n", knob);
				hp->sendkey(knob);
				printf("   DONE\r\n");
			}
			else
			{
				dsp->locate(70, 0);
				dsp->printf("Knob = %X  ", knob);
				dsp->copy_to_lcd();
			}
			knob = 0;
		}

		if (!key_buf.empty()) //cur_keycode.keyevent != KEY_NONE)
		{
			key_buf.pop(key);
			printf("Keycode %dx%d: %s\r\n",
						 key.row, key.col, key.keyevent==KEY_PRESSED?"pressed":"released");
			if (hp != NULL) {
				uint8_t keycode = kp_mapping[key.row][key.col];
				if (key.keyevent == KEY_RELEASED)
					keycode |= 0x40;  // bit 6: key relased
				if (shift)
					keycode |= 0x20;  // bit 5: key shifted

				hp->sendkey(kp_mapping[key.row][key.col]);
			}
			else
			{
				dsp->locate(140, 0);
				dsp->printf("KC: %dx%d[0x%s%X] %s",
										key.row, key.col,
										kp_mapping[key.row][key.col] <= 0x0F ? "0" : "",
										kp_mapping[key.row][key.col],
										key.keyevent==KEY_PRESSED ? "PRE" : "REL");
				dsp->copy_to_lcd();
			}
			// cur_keycode.keyevent = KEY_NONE;
		}


		if ((hp != NULL) && (hp->cmd_available()))
		{
			HPSerial::CMD cmd;
			if (hp->pop(cmd))
			{
#if defined(HAS_LED)
				led = 1;
#endif
				for (uint8_t i=0; i<7; i++)
					if (hp->nerrors(i) > err[i]) {
						printf("ERR: %d/%d/%d/%d/%d/%d/%d\r\n",
									 hp->nerrors(0),
									 hp->nerrors(1),
									 hp->nerrors(2),
									 hp->nerrors(3),
									 hp->nerrors(4),
									 hp->nerrors(5),
									 hp->nerrors(6)
									 );
						break;
					}
				for (uint8_t i=0; i<7; i++)
					err[i] = hp->nerrors(i);

				printf("CMD[%d] %02X", (int)cmd.id, cmd.cmd);

				if ((cmd.cmd == 0x00) || (cmd.cmd == 0x0C))
					printf(": '%s'\r\n", cmd.value);
				else {
					printf(":");
					for (uint8_t i=0; i<cmd.size; i++)
						printf("%02x ", cmd.value[i]);
					printf("\r\n");
				}
				show(cmd.cmd, cmd.value, cmd.size);
#if defined(HAS_LED)
				led = 0;
#endif
			}
		}
		//else
		ThisThread::sleep_for(1);
	}
}

void qei_cb(int dir)
{
	if(dir == 1) // turn right
		knob = 0x80;
	else         // turn left
		knob = 0x81;  // 83?
}

void kp_cb(uint8_t row, uint8_t col)
{
	keycode_t key;
	key.row = row;
	key.col = col;
	key.keyevent = KEY_PRESSED;
	if(!key_buf.full())
		key_buf.push(key);
}

void kr_cb(uint8_t row, uint8_t col)
{
	keycode_t key;
	key.row = row;
	key.col = col;
	key.keyevent = KEY_RELEASED;
	if(!key_buf.full())
		key_buf.push(key);
}

int main()
{
	setup();
	printf("Main loop (noop)\r\n");
	while(1)
	{
		timeout_h();
		ThisThread::sleep_for(1);
	}
}

mercurial