First working prototype waveform generator. LCR part is still to be done
main
ArjanteMarvelde 2022-02-18 17:39:48 +01:00
rodzic b6477d6a0e
commit 2d0e4cc947
14 zmienionych plików z 5966 dodań i 256 usunięć

Wyświetl plik

@ -1,4 +1,7 @@
# Generated Cmake Pico project file
# Cmake Pico project file
# After changing this file, empty build folder and execute from there:
# cmake -G "NMake Makefiles" ..
cmake_minimum_required(VERSION 3.13)
@ -19,7 +22,7 @@ project(uWFG C CXX ASM)
pico_sdk_init()
# Add executable. Default name is the project name, version 0.1
add_executable(uWFG uWFG.c gen.c monitor.c)
add_executable(uWFG uWFG.c gen.c waveform.c monitor.c lcd.c hmi.c lcdfont.c lcdlogo.c)
pico_set_program_name(uWFG "uWFG")
pico_set_program_version(uWFG "0.1")

193
gen.c
Wyświetl plik

@ -46,69 +46,63 @@
* CH1: 0x003f800f (IRQ_QUIET=0x1, TREQ_SEL=0x3f, CHAIN_TO=0, INCR_WRITE=0, INCR_READ=0, DATA_SIZE=2, HIGH_PRIORITY=1, EN=1)
* CH2: 0x0020981f (IRQ_QUIET=0x1, TREQ_SEL=0x01, CHAIN_TO=3, INCR_WRITE=0, INCR_READ=1, DATA_SIZE=2, HIGH_PRIORITY=1, EN=1)
* CH3: 0x003f900f (IRQ_QUIET=0x1, TREQ_SEL=0x3f, CHAIN_TO=2, INCR_WRITE=0, INCR_READ=0, DATA_SIZE=2, HIGH_PRIORITY=1, EN=1)
*/
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "pico/stdlib.h"
#include "pico/sem.h"
#include "hardware/i2c.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "hardware/irq.h"
#include "hardware/pio.h"
#include "hardware/dma.h"
#include "hardware/pll.h"
#include "wfgout.pio.h"
#include "gen.h"
#define FSYS 1.25e8f
float _fsys; // System clock frequency
/*
* Global variables that hold the active parameters for both channels
* DMA pipe definitions, assume channels 0, 1, 2 and 3 for A-DATA, A-CTRL, B-DATA and B-CTRL
*/
uint a_sm = 0, b_sm = 1; // PIO0 state machine numbers
uint32_t *a_buffer, *b_buffer; // Buffer address
uint32_t a_buflen, b_buflen; // Buffer length (in 32bit words)
float a_freq, b_freq; // Buffer frequency
#define DMA_DC(i) (((i)==0) ? 0x0020081f : 0x0020981f) // See derivation above
#define DMA_CC(i) (((i)==0) ? 0x003f800f : 0x003f900f)
/*
* DMA channel definitions
* Global variables that hold the active parameters for both output channels
*/
#define DMA_ADATA 0
#define DMA_ACTRL 1
#define DMA_BDATA 2
#define DMA_BCTRL 3
#define PINA 0 // PIO channe A and B start pin numbers
#define PINB 8
#define GEN_MINBUFLEN 20 // Minimum nr of samples (10msec - 160nsec)
#define GEN_MAXBUFLEN 2000 // Maximum buffer size (1sec - 16usec)
wfg_t wfg_ctrl[2]; // Active
// Allocate maximum size samplebuffers for channel A and B
uint8_t a_buf[GEN_MAXBUFLEN] __attribute__((aligned(4))); // DMA requires to align on 32 bit boundary
uint8_t b_buf[GEN_MAXBUFLEN] __attribute__((aligned(4)));
#define DMA_ADATA_C 0x0020081f
#define DMA_ACTRL_C 0x003f800f
#define DMA_BDATA_C 0x0020981f
#define DMA_BCTRL_C 0x003f900f
/*
* Define initial waveforms
*/
#define A_FREQ 1.0e6f
#define A_BUFLEN 4
uint32_t a_buf[A_BUFLEN] = {0x00000000, 0x00000000, 0xffffffff, 0xffffffff};
#define B_FREQ 1.0e6f
#define B_BUFLEN 4
uint32_t b_buf[B_BUFLEN] = {0x00000000, 0x00000000, 0xffffffff, 0xffffffff};
/*
* Unit initialization, only call this once!
* Unit initialization, !only call this once!
* This function initializes the WFG parameters, the PIO statemachines and teh DMA channels.
*/
void wfg_init()
void gen_init()
{
float fsam;
float div;
uint i;
uint offset;
uint i, offset;
int ch;
for (i=0; i<8; i++) // Initialize the used pins
/* Retrieve system clock frequency */
_fsys = 1.2e7; // Assume 12MHz XOSC
_fsys *= pll_sys_hw->fbdiv_int&0xfff; // Feedback divider
_fsys /= (pll_sys_hw->prim&0x00070000)>>16; // Primary divider 1
_fsys /= (pll_sys_hw->prim&0x00007000)>>12; // Primary divider 2
/* Set GPIO pin behaviour */
for (i=0; i<8; i++) // Initialize the channel A and B pins
{
gpio_set_function(PINA+i, GPIO_FUNC_PIO0); // Function: PIO
gpio_set_slew_rate(PINA+i, GPIO_SLEW_RATE_FAST); // No slewrate limiting
@ -118,94 +112,79 @@ void wfg_init()
gpio_set_drive_strength(PINB+i, GPIO_DRIVE_STRENGTH_8MA);
}
a_buffer = &a_buf[0]; // Initialize active parameters
a_buflen = A_BUFLEN;
a_freq = A_FREQ;
b_buffer = &b_buf[0];
b_buflen = B_BUFLEN;
b_freq = B_FREQ;
/* Initialize the buffers and channel control structures */
for (i= 0; i<64; i++) {a_buf[i] = 0x00; a_buf[i+64] = 0xff;} // Square wave
wfg_ctrl[0].buf = a_buf; // in A sample buffer
wfg_ctrl[0].len = 128; // of 128 samples
wfg_ctrl[0].dur = 1.0e-6; // and 1 usec duration
for (i= 0; i<64; i++) {b_buf[i] = i*4; b_buf[i+64] = 0xff-(i*4);} // Triangle wave
wfg_ctrl[1].buf = b_buf; // in B sample buffer
wfg_ctrl[1].len = 128; // of 128 samples
wfg_ctrl[1].dur = 1.0e-6; // and 1usec duration
offset = pio_add_program(pio0, &wfgout_program); // Move program to PIO space and obtain offset
/* Initialize PIO and channel A and B statemachines */
offset = pio_add_program(pio0, &wfgout_program); // Move program to PIO space and obtain its offset
fsam = a_freq * 4.0 * (float)a_buflen; // Required sample rate = samples in buffer * freq
div = FSYS / fsam; // Ratio FSYS and sampleclock
if (div < 1.0) div=1.0; // Cannot get higher than FSYS
wfgout_program_init(pio0, a_sm, offset, (uint)PINA, (uint)8, div); // Invoke PIO initializer (see wfgout.pio.h)
fsam = b_freq * 4.0 * (float)b_buflen; // Required sample rate = samples in buffer * freq
div = FSYS / fsam; // Ratio FSYS and sampleclock
if (div < 1.0) div=1.0; // Cannot get higher than FSYS
wfgout_program_init(pio0, b_sm, offset, (uint)PINB, (uint)8, div); // Invoke PIO initializer (see wfgout.pio.h)
for (ch=0; ch<2; ch++)
{
div = _fsys * wfg_ctrl[ch].dur / wfg_ctrl[ch].len; // Ratio of fsys and channel B sampleclock
if (div < 1.0) div=1.0; // Cannot get higher than FSYS
wfgout_program_init(pio0, ch, offset, (uint)(ch*PINB), (uint)8, div); // Invoke PIO initializer for channel B
dma_hw->ch[DMA_ADATA].read_addr = (io_rw_32)a_buffer; // Read from waveform buffer
dma_hw->ch[DMA_ADATA].write_addr = (io_rw_32)&pio0->txf[a_sm]; // Write to PIO TX fifo
dma_hw->ch[DMA_ADATA].transfer_count = a_buflen; // Nr of 32 bit words to transfer
dma_hw->ch[DMA_ADATA].al1_ctrl = DMA_ADATA_C; // Write ctrl word without starting the DMA
dma_hw->ch[DMA_ACTRL].read_addr = (io_rw_32)&a_buffer; // Read from waveform buffer address reference
dma_hw->ch[DMA_ACTRL].write_addr = (io_rw_32)&dma_hw->ch[DMA_ADATA].read_addr; // Write to data channel read address
dma_hw->ch[DMA_ACTRL].transfer_count = 1; // One word to transfer
dma_hw->ch[DMA_ACTRL].ctrl_trig = DMA_ACTRL_C; // Write ctrl word and start DMA
dma_hw->ch[DMA_BDATA].read_addr = (io_rw_32)b_buffer; // Read from waveform buffer
dma_hw->ch[DMA_BDATA].write_addr = (io_rw_32)&pio0->txf[b_sm]; // Write to PIO TX fifo
dma_hw->ch[DMA_BDATA].transfer_count = b_buflen; // Nr of 32 bit words to transfer
dma_hw->ch[DMA_BDATA].al1_ctrl = DMA_BDATA_C; // Write ctrl word without starting the DMA
dma_hw->ch[DMA_BCTRL].read_addr = (io_rw_32)&b_buffer; // Read from waveform buffer address reference
dma_hw->ch[DMA_BCTRL].write_addr = (io_rw_32)&dma_hw->ch[DMA_BDATA].read_addr; // Write to data channel read address
dma_hw->ch[DMA_BCTRL].transfer_count = 1; // One word to transfer
dma_hw->ch[DMA_BCTRL].ctrl_trig = DMA_BCTRL_C; // Write ctrl word and start DMA
dma_hw->ch[2*ch].read_addr = (io_rw_32)wfg_ctrl[ch].buf; // Read from waveform buffer
dma_hw->ch[2*ch].write_addr = (io_rw_32)&pio0->txf[ch]; // Write to PIO TX fifo
dma_hw->ch[2*ch].transfer_count = wfg_ctrl[ch].len/4; // Nr of 32 bit words to transfer
dma_hw->ch[2*ch].al1_ctrl = DMA_DC(ch); // Write ctrl word without starting the DMA
dma_hw->ch[2*ch+1].read_addr = (io_rw_32)&(wfg_ctrl[ch].buf); // Read from waveform buffer address reference
dma_hw->ch[2*ch+1].write_addr = (io_rw_32)&dma_hw->ch[2*ch].read_addr; // Write to data channel read address
dma_hw->ch[2*ch+1].transfer_count = 1; // One word to transfer
dma_hw->ch[2*ch+1].ctrl_trig = DMA_CC(ch); // Write ctrl word and start DMA
}
}
/*
* This function is the main API of the generator.
* This function is the main API of the generator on channel ch.
* Parameters are a waveform samples buffer, its length and a desired frequency.
* It is assumed that the buffer contains one wave, and the frequency will be maximized at Fsys/buflen
* It is assumed that the buffer contains one wave, and the frequency will be maximized at fsys/buflen
*/
void wfg_play(int output, wfg_t *wave)
void gen_play(int ch, wfg_t *wave)
{
uint32_t clkdiv; // 31:16 int part, 15:8 frac part (in 1/256)
uint32_t len;
float div;
ch &= 1; // Truncate channel into range
len = (uint32_t)wave->len; len &= ~3; // Force multiple of 4
if (len<GEN_MINBUFLEN) return; // Insufficient samples
if (len>GEN_MAXBUFLEN) len = GEN_MAXBUFLEN; // Truncate to maximum
div = FSYS/((wave->freq)*(wave->len)*4.0); // Calculate divider
if (div < 1.0) div=1.0;
/* Calculate PIO clock divider */
div = _fsys * wave->dur / len; // Sample rate to fsys ratio
if (div < 1.0) div=1.0; // Sample rate too high: top off
clkdiv = (uint32_t)div; // Extract integer part
div = (div - clkdiv)*256; // Fraction x 256
clkdiv = (clkdiv << 8) + (uint32_t)div; // Add 8bit integer part of fraction
clkdiv = clkdiv << 8; // Final shift to match required format
if (output)
{
b_buffer = wave->buf;
b_buflen = wave->len;
b_freq = wave->freq;
dma_hw->ch[DMA_BDATA].read_addr = (io_rw_32)b_buffer; // Read from waveform buffer
dma_hw->ch[DMA_BDATA].write_addr = (io_rw_32)&pio0->txf[b_sm]; // Write to PIO TX fifo
dma_hw->ch[DMA_BDATA].transfer_count = b_buflen; // Nr of 32 bit words to transfer
dma_hw->ch[DMA_BDATA].al1_ctrl = DMA_BDATA_C; // Write ctrl word without starting the DMA
dma_hw->ch[DMA_BCTRL].read_addr = (io_rw_32)&b_buffer; // Read from waveform buffer address reference
dma_hw->ch[DMA_BCTRL].write_addr = (io_rw_32)&dma_hw->ch[DMA_BDATA].read_addr; // Write to data channel read address
dma_hw->ch[DMA_BCTRL].transfer_count = 1; // One word to transfer
dma_hw->ch[DMA_BCTRL].ctrl_trig = DMA_BCTRL_C; // Write ctrl word and start DMA
pio0_hw->sm[b_sm].clkdiv = (io_rw_32)clkdiv; // Set new value
pio_sm_clkdiv_restart(pio0, b_sm); // Restart clock
}
else
{
a_buffer = wave->buf;
a_buflen = wave->len;
a_freq = wave->freq;
dma_hw->ch[DMA_ADATA].read_addr = (io_rw_32)a_buffer; // Read from waveform buffer
dma_hw->ch[DMA_ADATA].write_addr = (io_rw_32)&pio0->txf[a_sm]; // Write to PIO TX fifo
dma_hw->ch[DMA_ADATA].transfer_count = a_buflen; // Nr of 32 bit words to transfer
dma_hw->ch[DMA_ADATA].al1_ctrl = DMA_ADATA_C; // Write ctrl word without starting the DMA
dma_hw->ch[DMA_ACTRL].read_addr = (io_rw_32)&a_buffer; // Read from waveform buffer address reference
dma_hw->ch[DMA_ACTRL].write_addr = (io_rw_32)&dma_hw->ch[DMA_ADATA].read_addr; // Write to data channel read address
dma_hw->ch[DMA_ACTRL].transfer_count = 1; // One word to transfer
dma_hw->ch[DMA_ACTRL].ctrl_trig = DMA_ACTRL_C; // Write ctrl word and start DMA
pio0_hw->sm[a_sm].clkdiv = (io_rw_32)clkdiv; // Set new value
pio_sm_clkdiv_restart(pio0, a_sm); // Restart clock
}
/* Store waveform */
dma_channel_abort(2*ch); // Stop DMA transfers, to prevent collisions
dma_channel_abort(2*ch+1);
memcpy(wfg_ctrl[ch].buf, wave->buf, len); // Copy samples from input
wfg_ctrl[ch].len = len;
wfg_ctrl[ch].dur = wave->dur;
/* Re-program PIO */
pio0_hw->sm[ch].clkdiv = (io_rw_32)clkdiv; // Set new value
pio_sm_clkdiv_restart(pio0, ch); // Restart clock
/* Re-program DMA */
dma_hw->ch[2*ch].read_addr = (io_rw_32)wfg_ctrl[ch].buf; // Read from waveform buffer
dma_hw->ch[2*ch].write_addr = (io_rw_32)&pio0->txf[ch]; // Write to PIO TX fifo
dma_hw->ch[2*ch].transfer_count = wfg_ctrl[ch].len/4; // Nr of 32 bit words to transfer
dma_hw->ch[2*ch].al1_ctrl = DMA_DC(ch); // Write ctrl word without starting the DMA
dma_hw->ch[2*ch+1].read_addr = (io_rw_32)&(wfg_ctrl[ch].buf); // Read from waveform buffer address reference
dma_hw->ch[2*ch+1].write_addr = (io_rw_32)&dma_hw->ch[2*ch].read_addr; // Write to data channel read address
dma_hw->ch[2*ch+1].transfer_count = 1; // One word to transfer
dma_hw->ch[2*ch+1].ctrl_trig = DMA_CC(ch); // Write ctrl word and start DMA
}

26
gen.h
Wyświetl plik

@ -10,23 +10,31 @@
*/
#define OUTA 0 // Channel A indicator
#define PINA 0 // LSB pin Channel A
#define OUTB 1 // Channel B indicator
#define PINB 8 // LSB pin Channel B
#define GEN_MINBUFLEN 20 // Minimum nr of byte samples
#define GEN_MAXBUFLEN 2000 // Maximum buffer size (byte samples)
extern float _fsys;
/*
* The structure that defines the waveform input to wfg_play.
* Note that the buffer needs to contain 4N bytes, i.e. needs to be 32bit aligned.
* For high frequencies, the minimum buffer length is in the order of 32 bytes (buflen=8).
*/
typedef struct wfg
{
uint32_t *buf; // Points to waveform buffer
uint32_t len; // Buffer length in 32 bit words
float freq; // Buffer repetition rate
uint8_t *buf; // Points to waveform buffer
uint32_t len; // Buffer length in byte samples
float dur; // Duration of buffer, in seconds
} wfg_t;
/* Initialize channel indicated by output */
void wfg_init(void);
/* Initialize both channels */
void gen_init(void);
/* Play a waveform on the channel indicate by output */
void wfg_play(int output, wfg_t *wave);
/* Play a waveform on the channel indicated by output */
void gen_play(int output, wfg_t *wave);
#endif

414
hmi.c
Wyświetl plik

@ -20,6 +20,418 @@
#include "hardware/dma.h"
#include "hmi.h"
#include "wfgout.pio.h"
#include "gen.h"
#include "lcd.h"
/** Some generic identifiers **/
// Mode strings
#define HMI_SQR 0
#define HMI_TRI 1
#define HMI_SAW 2
#define HMI_SIN 3
#define HMI_PUL 4
#define HMI_NMODE 5
uint8_t *hmi_chmode[HMI_NMODE] = { LCD_SQR24X12, LCD_TRI24X12, LCD_SAW24X12, LCD_SIN24X12, LCD_PUL24X12};
// Unit strings
#define HMI_USEC 0
#define HMI_MSEC 1
#define HMI_NUNIT 2
uint8_t *hmi_chdur[HMI_NUNIT] = {"usec", "msec"};
// Pulse parameters
#define HMI_DUTY 0
#define HMI_RISE 1
#define HMI_FALL 2
#define HMI_NPUL 3 // Additional Pulse parameters
uint8_t *hmi_chpul[HMI_NPUL] = {"Duty", "Rise", "Fall"};
/** Channel A/B wave definition structures **/
extern uint8_t sine[GEN_MAXBUFLEN]; // Full sine waveform
typedef struct
{
int mode; // Waveform type
float time; // Duration, in seconds
int duty; // Duty cycle, percentage of duration
int rise; // Rise time, percentage of duration
int fall; // Fall time, percentage of duration
} ch_t;
ch_t hmi_chdef[2];
uint8_t hmi_wave[GEN_MAXBUFLEN]; // Scratch buffer for waveform samples
// Generate waveform samples in scratch buffer
// Division factor should end-up above 4 to get a <0.1% deviation
// so: fsample < fsys/4, implying a bufferlength of maximum time*fsys/4.
// This is about 50 samples per usec, but this length should be minimized too
// which means that for times smaller than a few usec the frequency will be less accurate.
// Simple waveforms can be calculated, for Sine wave there is a lookup-table
void hmi_genwave(int ch)
{
int i;
uint32_t d, r, f;
float step;
wfg_t wf;
// Calculate optimum nr of samples
wf.len = (uint32_t)((_fsys/1) * hmi_chdef[ch].time); // Calculate required nr of samples
wf.len &= ~3; // Multiple of 4 bytes
if (wf.len<20) wf.len = 20; // Minimum size
if (wf.len>GEN_MAXBUFLEN) wf.len = GEN_MAXBUFLEN; // Maximum size
// Fill array
switch (hmi_chdef[ch].mode)
{
case HMI_SQR:
memset(&hmi_wave[0],0xff, wf.len/2); // High half samples
memset(&hmi_wave[wf.len/2],0x00, wf.len/2); // Low half samples
break;
case HMI_TRI:
step = 255.0/(wf.len/2); // Calculate slope (step per sample)
for (i=0; i<wf.len/2; i++)
{
hmi_wave[i] = (uint8_t)(i*step); // Samples way up
hmi_wave[i+wf.len/2] = 255 - hmi_wave[i]; // Samples way down
}
break;
case HMI_SAW:
step = 255.0/(wf.len); // Calculate slope (step per sample)
for (i=0; i<wf.len; i++)
hmi_wave[i] = (uint8_t)(i*step); // Samples rising side
break;
case HMI_SIN:
step = (float)GEN_MAXBUFLEN/wf.len; // Step to next sine index
for (i=0; i<wf.len; i++)
hmi_wave[i] = sine[(int)(i*step)]; // Truncate i*step to get index
break;
case HMI_PUL:
d = hmi_chdef[ch].duty * wf.len / 100; // Fraction of duty cycle samples
r = hmi_chdef[ch].rise * wf.len / 100; // Fraction of rising flank samples
f = hmi_chdef[ch].fall * wf.len / 100; // Fraction of falling flank samples
step = 255.0/r; // Calculate rise slope (step per sample)
for (i=0; i<r; i++)
hmi_wave[i] = (uint8_t)(i*step); // Samples way up
for (i=r; i<d; i++)
hmi_wave[i] = 0xff; // High samples
step = 255.0/f; // Calculate fall slope (step per sample)
for (i=0; i<f; i++)
hmi_wave[i+d] = 0xff-i*step; // Samples way down
for (i=d+f; i<wf.len; i++)
hmi_wave[i] = 0x00; // Low samples
break;
}
// Play waveform
wf.buf = hmi_wave;
wf.dur = hmi_chdef[ch].time;
gen_play(ch, &wf);
}
/** Channel setting menu **/
#define HMI_NPAR 8 // Normal channel parameters
#define HMI_NPPAR 6 // Additional Pulse parameters
// Channel parameter scratch and write function
typedef struct
{
uint8_t val; // Parameter value
uint8_t max; // Parameter range (0,max-1)
uint8_t **str; // String representation (NULL if false)
uint8_t x; // Field upperleft x
uint8_t y; // Field upperleft y
} par_t;
par_t hmi_chpar[HMI_NPAR+HMI_NPPAR] =
{
{0, HMI_NMODE-1, hmi_chmode, 8, 52}, // Mode string/graph
{0, 9, NULL, 46, 54}, // Time digits (000.000 - 999.999)
{0, 9, NULL, 54, 54},
{0, 9, NULL, 60, 54},
{0, 9, NULL, 72, 54},
{0, 9, NULL, 78, 54},
{0, 9, NULL, 84, 54},
{0, HMI_NUNIT-1, hmi_chdur, 92, 54}, // Time unit string
{0, 9, NULL, 24, 76}, // Duty cycle % digits (00-99)
{0, 9, NULL, 30, 76},
{0, 9, NULL, 66, 76}, // Rise time % digits (00-99)
{0, 9, NULL, 72, 76},
{0, 9, NULL, 108, 76}, // Fall time % digits (00-99)
{0, 9, NULL, 114, 76}
};
// Write a certain parameter according to par_t struct
void hmi_writepar(int i, bool invert)
{
if (i>=(HMI_NPAR+HMI_NPUL*2)) return; // Range check
if (hmi_chpar[i].str == NULL) // Character?
lcd_putc(hmi_chpar[i].x,hmi_chpar[i].y,'0'+hmi_chpar[i].val,LCD_6X8,invert);
else if (i==0) // or Graphic?
lcd_putg(hmi_chpar[i].x,hmi_chpar[i].y,hmi_chpar[i].str[hmi_chpar[i].val],invert);
else // else String
lcd_puts(hmi_chpar[i].x,hmi_chpar[i].y,hmi_chpar[i].str[hmi_chpar[i].val],LCD_6X8,invert);
}
// Write channel setting at display bottom
void hmi_writech(int ch)
{
int y = (ch?112:98);
lcd_clrscr(0,y,128,12);
lcd_puts( 4, y , (ch?"B:":"A:"), LCD_8X12, false);
lcd_putg( 20, y , hmi_chmode[hmi_chpar[0].val], false);
lcd_putc( 54, y+2, '0'+hmi_chpar[1].val, LCD_6X8, false);
lcd_putc( 60, y+2, '0'+hmi_chpar[2].val, LCD_6X8, false);
lcd_putc( 66, y+2, '0'+hmi_chpar[3].val, LCD_6X8, false);
lcd_putc( 72, y+2, '.', LCD_6X8, false);
lcd_putc( 78, y+2, '0'+hmi_chpar[4].val, LCD_6X8, false);
lcd_putc( 84, y+2, '0'+hmi_chpar[5].val, LCD_6X8, false);
lcd_putc( 90, y+2, '0'+hmi_chpar[6].val, LCD_6X8, false);
lcd_puts( 98, y+2, hmi_chdur[hmi_chpar[7].val], LCD_6X8, false);
}
// Init scratch from channel def
void hmi_initch(int ch, int par)
{
ch_t *chdef;
uint32_t dur;
char s[16];
int i;
chdef = &hmi_chdef[ch&1];
if (chdef->time < 1.0e-3) // Range is either 999.999 msec or usec
{
hmi_chpar[7].val = HMI_USEC;
dur = (uint32_t)(chdef->time*1e9);
}
else if (chdef->time < 1.0)
{
hmi_chpar[7].val = HMI_MSEC;
dur = (uint32_t)(chdef->time*1e6);
}
hmi_chpar[6].val = dur%10; dur = dur/10; // so only 6 digits are relevant
hmi_chpar[5].val = dur%10; dur = dur/10;
hmi_chpar[4].val = dur%10; dur = dur/10;
hmi_chpar[3].val = dur%10; dur = dur/10;
hmi_chpar[2].val = dur%10; dur = dur/10;
hmi_chpar[1].val = dur%10;
hmi_chpar[0].val = chdef->mode;
hmi_chpar[13].val = chdef->fall%10; // For some waveforms this is meaningless
hmi_chpar[12].val = chdef->fall/10;
hmi_chpar[11].val = chdef->rise%10;
hmi_chpar[10].val = chdef->rise/10;
hmi_chpar[ 9].val = chdef->duty%10;
hmi_chpar[ 8].val = chdef->duty/10;
if ((par<0)||(par>=HMI_NPAR+HMI_NPPAR)) return;
// Initialize display layout of scratch area
lcd_clrscr(32,0,96,32); // Clean title area
lcd_puts(44,8,ch?"Chan.B":"Chan.A",LCD_12X16,false); // Write title
lcd_hruler(44,24,72);
lcd_clrscr(0,32,128,62); // Clean scratch area
lcd_puts( 4, 36, "Mode",LCD_8X12,false); // Channel parameter captions
lcd_puts( 48, 36, "Time",LCD_8X12,false);
lcd_putc( 66, 54, '.', LCD_6X8, false);
lcd_puts( 2, 76, "Dut", LCD_6X8, false);
lcd_puts( 44, 76, "Ris", LCD_6X8, false);
lcd_puts( 86, 76, "Fal", LCD_6X8, false);
for (i=0;i<HMI_NPAR+HMI_NPPAR;i++)
hmi_writepar( i, (i==par)); // Channel parameter values
return;
}
// Save scratch to channel definition, after range check
void hmi_exitch(int ch)
{
ch_t *chdef;
uint32_t val;
char s[16];
chdef = &hmi_chdef[ch&1];
chdef->mode = hmi_chpar[0].val;
val = hmi_chpar[1].val;
val = val*10 + hmi_chpar[2].val;
val = val*10 + hmi_chpar[3].val;
val = val*10 + hmi_chpar[4].val;
val = val*10 + hmi_chpar[5].val;
val = val*10 + hmi_chpar[6].val;
if (hmi_chpar[7].val == HMI_USEC)
chdef->time = (val+1) * 1.0e-9;
else if (hmi_chpar[7].val == HMI_MSEC)
chdef->time = (val+1) * 1.0e-6;
// TBD Check range
// Store shape scratch values
switch (hmi_chdef[ch].mode)
{
case HMI_SQR:
hmi_chdef[ch].duty = 50; hmi_chdef[ch].rise = 1; hmi_chdef[ch].fall = 1;
break;
case HMI_TRI:
hmi_chdef[ch].duty = 50; hmi_chdef[ch].rise = 50; hmi_chdef[ch].fall = 50;
break;
case HMI_SAW:
hmi_chdef[ch].duty = 99; hmi_chdef[ch].rise = 99; hmi_chdef[ch].fall = 1;
break;
case HMI_SIN:
hmi_chdef[ch].duty = 50; hmi_chdef[ch].rise = 0; hmi_chdef[ch].fall = 0;
break;
case HMI_PUL:
hmi_chdef[ch].duty = 10*hmi_chpar[ 8].val + hmi_chpar[ 9].val;
hmi_chdef[ch].rise = 10*hmi_chpar[10].val + hmi_chpar[11].val;
if (hmi_chdef[ch].rise>hmi_chdef[ch].duty) hmi_chdef[ch].rise=hmi_chdef[ch].duty;
hmi_chdef[ch].fall = 10*hmi_chpar[12].val + hmi_chpar[13].val;
if (hmi_chdef[ch].fall>100-hmi_chdef[ch].duty) hmi_chdef[ch].rise=100-hmi_chdef[ch].duty;
break;
}
return;
}
/** Menu state machine **/
// The three menu selection keys (TOP, MID, BOT) choose the menu to enter.
// Pressing the menu selection key again re-enters the menu, rejecting all changes.
// Changes have to be committed by the CENTER key.
// The active menu context determines how the navigation keys are interpreted.
// Key definitions
#define HMI_NOKEY 0x00
#define HMI_BOT 0x01
#define HMI_MID 0x02
#define HMI_TOP 0x04
#define HMI_CENTER 0x08
#define HMI_DOWN 0x10
#define HMI_RIGHT 0x20
#define HMI_UP 0x40
#define HMI_LEFT 0x80
uint8_t keystat;
// Menu parameters
#define HMI_M_LCR 0x01
#define HMI_M_CHA 0x02
#define HMI_M_CHB 0x03
int hmi_menu = HMI_M_LCR; // Current active menu
int par; // Current active menu parameter
int channel; // Current active channel
void hmi_lcrmenu(int key)
{
}
void hmi_chmenu(int key)
{
switch(key)
{
case HMI_UP:
hmi_chpar[par].val = (hmi_chpar[par].val<hmi_chpar[par].max)?hmi_chpar[par].val+1:0;
hmi_writepar( par, true);
break;
case HMI_DOWN:
hmi_chpar[par].val = (hmi_chpar[par].val>0)?hmi_chpar[par].val-1:hmi_chpar[par].max;
hmi_writepar( par, true);
break;
case HMI_LEFT:
hmi_writepar( par, false);
par = (par>0)?par-1:par;
hmi_writepar( par, true);
break;
case HMI_RIGHT:
hmi_writepar( par, false);
if (hmi_chpar[0].val == HMI_PUL) // Only change shape when Pulse WF
par = (par<HMI_NPAR+HMI_NPPAR-1)?par+1:par;
else
par = (par<HMI_NPAR-1)?par+1:par;
hmi_writepar( par, true);
break;
case HMI_CENTER:
hmi_exitch(channel); // Save scratch, after range check
hmi_initch(channel, par); // Re-initialize scratch
hmi_genwave(channel); // Generate and start waveform
hmi_writech(channel); // Write current channel setting
break; // Initialize scratch area
}
}
/** Keypad interface **/
#define I2C_PCF8574 0x27
void hmi_handler(int key)
{
switch(key) // Process menu selections
{
case HMI_TOP: // Top button => LCR measurement
hmi_menu = HMI_M_LCR;
par = 0; // Initialize parameter nr
lcd_clrscr(32,0,96,32); // Clean title area
lcd_putg(55,8,LCD_CIR16X16,false); // Write title, 3 circles
lcd_putg(75,8,LCD_CIR16X16,false);
lcd_putg(95,8,LCD_CIR16X16,false);
lcd_clrscr(0,32,128,62); // Clean scratch area
break;
case HMI_MID: // Middle button => channel A
hmi_menu = HMI_M_CHA;
par = 0; // Initialize parameter nr
channel = 0;
hmi_initch(channel, par); // Init scratch
break;
case HMI_BOT: // Bottom button => channel B
hmi_menu = HMI_M_CHB;
par = 0; // Initialize parameter nr
channel = 1;
hmi_initch(channel, par); // Init scratch
break;
}
if (hmi_menu == HMI_M_LCR)
hmi_lcrmenu(key);
else
hmi_chmenu(key);
}
/** Called from main loop at regular intervals **/
void hmi_evaluate()
{
uint8_t rxdata[4];
uint8_t key;
static bool firsttime = true;
// Get key status
i2c_read_blocking(i2c0, I2C_PCF8574, rxdata, 1, false); // Poll PCF8574
key = rxdata[0] ^ 0xff; // Take XOR for selection
if (key == keystat) return; // No change: so bail out
if (firsttime) // Initialize screen
{
lcd_clrscr(0,0,128,128);
lcd_putg(0, 0, LCD_UDJAT32, false);
lcd_hruler(0, 94, 128);
hmi_initch(0, -1); hmi_writech(0);
hmi_initch(1, -1); hmi_writech(1);
lcd_hruler(0, 126, 128);
firsttime = false;
}
hmi_handler(key);
keystat = key; // Remember this key event
}
void hmi_init()
{
uint8_t rxdata[4];
// Get key status
i2c_read_blocking(i2c0, I2C_PCF8574, rxdata, 1, false); // Get PCF8574 byte
keystat = rxdata[0] ^ 0xff; // Initialize keystat
hmi_chdef[0].mode = HMI_SQR; // Waveform type
hmi_chdef[0].time = 1.001e-6f; // Duration, in seconds
hmi_chdef[0].duty = 50; // Duty cycle, percentage of duration
hmi_chdef[0].rise = 1; // Rise time, percentage of duration
hmi_chdef[0].fall = 1; // Fall time, percentage of duration
hmi_chdef[1].mode = HMI_TRI; // Waveform type
hmi_chdef[1].time = 1.001e-6f; // Duration, in seconds
hmi_chdef[1].duty = 50; // Duty cycle, percentage of duration
hmi_chdef[1].rise = 50; // Rise time, percentage of duration
hmi_chdef[1].fall = 50; // Fall time, percentage of duration
}

5
hmi.h
Wyświetl plik

@ -10,4 +10,7 @@
*/
#endif
#endif
void hmi_init(void);
void hmi_evaluate(void);

234
lcd-SH1106.c 100644
Wyświetl plik

@ -0,0 +1,234 @@
/*
* lcd.c
*
* Created: Jan 2022
* Author: Arjan te Marvelde
*
*
* Driver for SSD1306 or SSD1309 or SH1106 OLED LCD module
* It is an output-only MMI, so only write is supported
* For now only 5x7 ASCII font, character mode, i.e. grid of 6x8 sized fields
* Screen is 128x64 pixels, so that makes 21x8 character positions
* Character position is the display location (X,Y) coordinate
*
* Functions are:
* lcd_write() Write ASCII to current position. (truncate at right border or next line?)
* lcd_ctrl() LCD settings, e.g. current cursor position, clear screen, ...
* lcd_init() Initializes interface, sets cursor to (0,0), etc
* lcd_evaluate() Can be called regularly to update LCD with canvas changes
*
* buffer contents:
* control byte + following bytes
* control byte = 0bxy000000,
* x: single (1) or multiple (0)
* y: data transfer(1) or control(0)
* 0x00: burst command
* 0x80: single command
* 0x40: burst data
* 0xc0: single data
*
* Display is 128 columns wide by 8 pages (=64 pixels) high. Each (column, page) is a segment byte. LSB is top and MSB is bottom.
* So one 6x8 character box has 6 bytes:
* +--+--+--+--+--+--+
* |b0|b0|b0|b0|b0|b0|
* +--+--+--+--+--+--+
* |b1|b1|b1|b1|b1|b1|
* +--+--+--+--+--+--+
* |b2|b2|b2|b2|b2|b2|
* +--+--+--+--+--+--+
* |b3|b3|b3|b3|b3|b3|
* +--+--+--+--+--+--+
* |b4|b4|b4|b4|b4|b4|
* +--+--+--+--+--+--+
* |b5|b5|b5|b5|b5|b5|
* +--+--+--+--+--+--+
* |b6|b6|b6|b6|b6|b6|
* +--+--+--+--+--+--+
* |b7|b7|b7|b7|b7|b7|
* +--+--+--+--+--+--+
*/
#include <string.h>
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "lcd.h"
// Command definition DataBits RST Function
// SH1106/SSD1306 76543210 val
// ----------------------------------------------------------------------------------------------------------------------------
#define LCD_SETCOL_LO 0x00 // 0000XXXX 0x00 Sets 4 lower bits of column address of display RAM in register.
#define LCD_SETCOL_HI 0x10 // 0001XXXX 0x10 Sets 4 upper bits of column address of display RAM in register.
#define LCD_SETADDMODE 0x20 // 001000XX 0x22 Page address mode (SSD1306), 0x02 is page address mode
#define LCD_SETVREF 0x30 // 001100XX 0x32 Control the DC-DC voltage output value.
#define LCD_SETOFFSET 0x40 // 01XXXXXX 0x40 Specifies RAM display line for COM0.
#define LCD_SETCONTRAST 0x81 // 10000001 Sets Contrast of the display.
// XXXXXXXX 0x80 The chip has 256 contrast steps from 00 to FF.
#define LCD_SETADC 0xa0 // 1010000X 0xa0 The right (0) or left (1) rotation.
#define LCD_ALLWHITE 0xa4 // 1010010X 0x00 Selects normal display (0) or Entire Display ON (1).
#define LCD_SETREVERSE 0xa6 // 1010011X 0x00 Selects Normal (0) or Reverse (1) display.
#define LCD_SETMPXRATIO 0xa8 // 10101000 Sets multiplex mode to any multiplex ratio from 16 to 64.
// **XXXXXX 0x3f Ratio value (=X+1) (X<15 is invalid)
#define LCD_SETDCDC 0xad // 10101101 Controls the DC-DC voltage (only use when display off)
#define LCD_SETDCDC_V 0x8a // 1000101X 0x8b DC-DC converter turned on when display ON (1) or DC-DC OFF (0).
#define LCD_CHARGEPUMP 0x8d // 10001101 Control charge pump (Enable before display on, Disable after Display off)
// 00010X00 0x10 Enable (1) or Disable (0)
#define LCD_SETDISPLAY 0xae // 1010111X 0xae Set Display ON (1) or OFF (0).
#define LCD_SETPAGE 0xb0 // 1011XXXX 0xb0 Specifies current RAM page address (0-15).
#define LCD_SETSCANDIR 0xc0 // 1100X*** 0xc0 Scan direction COM[0 .. N-1] (0) or COM [N-1 .. 0] (1).
#define LCD_SETSTART 0xd3 // 11010011 Sets display start line to one of COM0-63.
// **XXXXXX 0x00 [0 .. 63]
#define LCD_SETFREQ 0xd5 // 11010101 Sets the frequency of the internal display clocks.
// XXXXYYYY 0x50 Frequency shift (=(X-5)*5%) | Divide ratio (=Y+1)
#define LCD_SETCHARGE 0xd9 // 11011001 Sets the duration of the dis-charge and pre-charge period.
// XXXXYYYY 0x22 Discharge period [1..15] | Precharge period [1..15]
#define LCD_SETLAYOUT 0xda // 11011010 Select the common signals pad layout (=display i/f)
#define LCD_SETALTSEQ 0x02 // 000X0010 0x12 Sequential (0) or Alternating (1)
#define LCD_SETVLEVEL 0xdd // 11011011 Sets the common pad output voltage level at deselect stage.
// XXXXXXXX 0x35 Vcom = 0.430 + X * 0.006415 * Vref
#define LCD_SETRMW_STA 0xe0 // 11100000 Read-Modify-Write start (see datasheet)
#define LCD_SETRMW_END 0xee // 11101110 Read-Modify-Write end (see datasheet)
#define LCD_NOP 0xe3 // 11100011 No-operation command
#define LCD_GETSTATE 0x00 // XY***000 Busy | On/Off
#define I2C_SH1106 0x3C // I2C address (0x3C)
#define LCD_DELAY 1000 // Screen refresh time
#define LCD_WIDTH 0x80 // Pixels or Columns
#define LCD_HEIGHT 0x40 // Pixels
#define LCD_PAGES (LCD_HEIGHT/0x08) // Character rows
#define LCD_ROWLEN (LCD_WIDTH/0x06) // Character cols
#define LCD_CTRLCMD 0x00 // Control byte for burst commands
#define LCD_CTRLDATA 0x40 // Control byte for burst data
#define LCD_CTRLSINGLE 0x80 // OR in case of single command or data
uint8_t lcd_canvas[LCD_HEIGHT/8][LCD_WIDTH+1];
uint8_t lcd_xch, lcd_ych; // Location of display (0-20, 0-7)
// Write to canvas starting at (lcd_xch, lcd_ych)
void lcd_puts(char *buf, int font)
{
uint8_t *sptr;
char *bptr;
int i,j;
bptr = buf;
while ((lcd_xch < LCD_ROWLEN) && (*bptr != 0))
{
switch(font)
{
case LCD_FONTSMALL: // Arial, 6x8
if ((*bptr<0x00) || (*bptr>0x7f)) break;
sptr = &lcd_canvas[lcd_ych][1+lcd_xch*6];
for (i=0; i<6; i++)
*sptr++ = ASCII6x8[6*(*bptr)+i];
bptr++; // next char
lcd_xch++; // move cursor
break;
case LCD_FONTLARGE: // Comic sans, 24x32
if ((*bptr<0x20) || (*bptr>0x3f)) break;
for (j=0; j<4; j++)
{
sptr = &lcd_canvas[lcd_ych+j][1+lcd_xch*6];
for (i=0; i<24; i++)
*sptr++ = ASCII24x32[96*((*bptr) - 0x20) + i + 24*j];
}
bptr++; // next char
lcd_xch+=4; // move cursor
break;
case LCD_FONTUDJAT: // Udjat logo, 32x32
for (j=0; j<4; j++)
{
sptr = &lcd_canvas[lcd_ych+j][1+lcd_xch*6];
for (i=0; i<32; i++)
*sptr++ = UDJAT32x32[i + 32*j];
}
bptr++; // next char
lcd_xch+=4; // move cursor
break;
}
}
}
// Control functions
void lcd_ctrl(uint8_t cmd, uint8_t x, uint8_t y)
{
int page;
uint8_t txdata[16], *iptr;
switch (cmd)
{
case LCD_GOTO: // Set cursor position for next write
lcd_xch = MIN(x, LCD_ROWLEN-1); // Cursor X location (0..20)
lcd_ych = MIN(y, LCD_PAGES-1); // Cursor Y location (0..7)
break;
case LCD_UPDATE: // Flush scratch RAM to display RAM
txdata[0] = LCD_CTRLCMD; // 0x00, control byte
txdata[1] = LCD_SETCOL_LO | 0x00; // Column 0
txdata[2] = LCD_SETCOL_HI | 0x00; //
for (page=0; page<LCD_PAGES; page++)
{
txdata[3] = LCD_SETPAGE | page; // Set row
i2c_write_blocking(i2c0, I2C_SH1106, txdata, 4, false); // Set position to (0,page) and write line
i2c_write_blocking(i2c0, I2C_SH1106, &lcd_canvas[page][0], (uint16_t)(LCD_WIDTH+1), false);
}
break;
case LCD_CLRSCR:
for (page=0; page<LCD_PAGES; page++)
memset(&lcd_canvas[page][1], 0x00, LCD_WIDTH);
lcd_xch = x;
lcd_ych = y;
break;
}
}
/** Initialize LCD and clear canvas **/
void lcd_init()
{
int page;
uint8_t txdata[16], *iptr;
iptr = txdata;
txdata[0] = LCD_CTRLCMD; // 0x00, control byte
txdata[1] = LCD_SETDISPLAY | 0x00; // DISPLAY OFF
txdata[2] = LCD_SETADC | 0x01; // Left rotation
txdata[3] = LCD_SETSCANDIR | 0x08; // COM 63-0 scan direction
txdata[4] = LCD_SETCONTRAST; // Set contrast
txdata[5] = 0xf0; //
txdata[6] = LCD_SETREVERSE | 0x00; // Normal video
txdata[7] = LCD_CHARGEPUMP; // Charge pump control
txdata[8] = 0x14; // Enable
txdata[9] = LCD_SETDISPLAY | 0x01; // DISPLAY ON
i2c_write_blocking(i2c0, I2C_SH1106, txdata, 10, false);
for (page=0; page<LCD_PAGES; page++)
lcd_canvas[page][0] = LCD_CTRLDATA; // First byte indicates data xfer
lcd_ctrl(LCD_CLRSCR, 0, 0); // Clear screen, go to (0,0)
// Udjat logo in left upper corner
txdata[0] = 0x20; // Construct one char dummy string
txdata[1] = 0x00;
lcd_ctrl(LCD_GOTO, 0, 0);
lcd_puts(txdata, 2);
lcd_ctrl(LCD_UPDATE, 0, 0);
}
/** Transfer canvas to LCD **/
void lcd_evaluate()
{
lcd_ctrl(LCD_UPDATE, 0, 0);
}

275
lcd.c 100644
Wyświetl plik

@ -0,0 +1,275 @@
/*
* lcd.c
*
* Created: Jan 2022
* Author: Arjan te Marvelde
*
* Driver for SSD1327 based 128x128 display.
*
* It is an output-only MMI, so only write is supported
*
* The interface functions are:
* lcd_putc(int x, int y, uint8_t c, uint8_t *font);
* lcd_puts(int x, int y, char *buf, uint8_t *font);
* lcd_putg(int x, int y, uint8_t *bitmap);
* lcd_clrscr(void);
* lcd_init(void);
* The parameters (x, y) determine upper left corner of object item, should be even numbers.
* The font or bitmap object itself contains other parameters like (w, h) and the item data content.
* (w, h) also should be even numbers.
*
* A font or bitmap object definition starts with:
* - uint8_t item field width
* - uint8_t item field height
* - uint8_t first item number (e.g. character code)
* - uint8_t last item number
* followed by the item data, a nibble per pixel.
*
* Use of graphics functions requires a bitmap, where the same format as for fonts is used but there is only one character.
* A predefined bitmap is for example the Udjat logo, but user defined bitmap can also be dumped on display.
* For filling the bitmap dynamically, several graphics functions are provided:
*
* I2C write sequence:
* control byte + following bytes
* control byte = 0bxy000000, where
* x: single (1) or multiple (0)
* y: data(1) or control(0)
*
* Display is 128 columns wide by 128 rows high. Each (col, row) is a grey value nibble [1-15].
* The nibbles are organized in display RAM as follows:
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
* |L 0 H 0|L 1 H 1|L 2 H 2| |L 63 H 63|
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
* |L 64 H 64|L 65 H 65|L 66 H 66| |L 127 H 127|
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
* / / / / / / / / / /
* \ \ \ \ \ \ \ \ \ \
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
* |L8128 H8128|L8129 H8129|L8130 H8130| |L8191 H8191|
* +-----+-----+-----+-----+-----+-----+ ~ +-----+-----+
*
*
* For writing Display RAM, first the conditions and start location are set (commands) then followed by a data burst.
*
*/
#include <string.h>
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/i2c.h"
#include "lcd.h"
// Command definition D Function (reset values)
// SSD1327 #
// ----------------------------------------------------------------------------------------------------------------------------
#define LCD_WINCOLADDR 0x15 // 2 Sets first and last column for window (0x00, 0x3F).
#define LCD_WINROWADDR 0x75 // 2 Sets first and last row for window (0x00, 0x7F).
#define LCD_SCR_RIGHT 0x26 // 7 Setup right-scrolling part of display (see below)
#define LCD_SCR_LEFT 0x27 // 7 Setup left-scrolling part of display
#define LCD_SCR_STOP 0x2e // 0 Stop scrolling window
#define LCD_SCR_START 0x2f // 0 Start scrolling window
#define LCD_CONTRAST 0x81 // 1 Sets Contrast of the display (0x7F).
#define LCD_REMAP 0xa0 // 1 Enables/Disables address remapping (0x00).
#define LCD_DSTARTLINE 0xa1 // 1 Sets display start line (0x00).
#define LCD_DOFFSET 0xa2 // 1 Sets display vertical offset (0x00).
#define LCD_MODENORM 0xa4 // 0 Display in normal mode
#define LCD_MODEWHITE 0xa5 // 0 Display all pixels white, greyscale=15
#define LCD_MODEBLACK 0xa6 // 0 Display all pixels black, greyscale=0
#define LCD_MODEINVERS 0xa7 // 0 Display all pixels inverted, greyscale=15-val
#define LCD_MUXRATIO 0xa8 // 1 Set ratio to X+1, X>14, (0x7f)
#define LCD_FASELECT 0xab // 0 Select internal Vdd regulator when 1, external when 0 (0x01)
#define LCD_INACTIVE 0xae // 0 Switches display to sleep mode
#define LCD_ACTIVE 0xaf // 0 Switches display on, normal mode
#define LCD_PHASELEN 0xb1 // 1 High nibble phase2, low nibbel phase1 (0x74)
#define LCD_NOP1 0xb2 // 0 No operation
#define LCD_OSC_D_F 0xb3 // 1 Set oscillator divider (0x00)
#define LCD_GPIO 0xb5 // 1 (0x02)
#define LCD_PCPER2 0xb6 // 1 (0x04)
#define LCD_GS_TABLE 0xb8 // 15 Pulse width for GS levels 1..15, all unequal and value rising
#define LCD_GS_LINEAR 0xb9 // 0 Sets linear GS table (default)
#define LCD_NOP2 0xbb // 0 No operation
#define LCD_PCLEVEL 0xbc // 1 (0x05)
#define LCD_CDLEVEL 0xbe // 1 (0x05)
#define LCD_FBSELECT 0xd5 // 1 (0x00)
#define LCD_CMDLOCK 0xfd // 1 Lock OLED command interface (0x16) or unlock (0x12)
#define I2C_SSD1327 0x3c // I2C address (0x3C)
#define LCD_CTRLCMD 0x00 // Control byte for burst commands
#define LCD_CTRLDATA 0x40 // Control byte for burst data
#define LCD_CTRLSINGLE 0x80 // OR in case of single command/data
#define LCD_CTRLMULTI 0x00 // OR in case of multibyte command/data
#define LCD_DELAY 1000 // Screen refresh time
#define LCD_WIDTH 0x80 // Pixels
#define LCD_HEIGHT 0x80 // Pixels
uint8_t txdata[1+LCD_WIDTH]; // Maximum transfer size, one line of data
/*
* Set display active area (cursor) to (x,y) left upper corner and (width x height) size
*/
void lcd_cursor(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
txdata[0] = LCD_CTRLCMD | LCD_CTRLMULTI; // Multiple command byte
txdata[1] = LCD_WINCOLADDR; // Set window columns
txdata[2] = (x/2)&0x3f; // left
txdata[3] = (((x+w)/2)-1)&0x3f; // right
txdata[4] = LCD_WINROWADDR; // Set window rows
txdata[5] = (y)&0x7f; // top
txdata[6] = (y+h-1)&0x7f; // bottom
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 7, false); // Send commands
}
/*
* Clear the display
*/
void lcd_clrscr(uint8_t x, uint8_t y, uint8_t w, uint8_t h)
{
int i;
lcd_cursor(x,y,w,h); // Set window
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI;
memset(&txdata[1], 0x00, w/2); // Black line
for (i=0; i<h; i++)
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+w/2, false); // Clear one line
}
/*
* Output one character to location (x,y)
* Inverse graphics if invert is true
*/
void lcd_putc(uint8_t x, uint8_t y, uint8_t c, uint8_t *font, bool invert)
{
int i, w, h;
uint8_t xorbyte; // XOR value for memcpy
uint8_t *srce, *dest; // Pointers for memcpy
if (!((c>=font[2]) && (c<=font[3]))) // Range check character code
c=0; // Not good: default on first
else
c-=font[2]; // Good: shift down value
w=font[0]; h=font[1]; // Retrieve character width and height
if (((x+w)>LCD_WIDTH)||((y+h)>LCD_HEIGHT)) return; // Out of range!
lcd_cursor(x,y,w,h); // Define window
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI ; // Multiple data byte
xorbyte = (invert?0xff:0x00); // Optionally set inversion
srce = &font[4 + (int)c*w*h/2]; // Initialize pointers
dest = &txdata[1];
for (i=0; i<w*h/2; i++) // Copy data
*dest++ = *srce++ ^ xorbyte;
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+w*h/2, false); // Send character
}
/*
* Output multiple characters, starting on (x,y)
*/
void lcd_puts(uint8_t x, uint8_t y, char *buf, uint8_t *font, bool invert)
{
int len, i;
len = strlen(buf);
for (i=0; i<len; i++)
lcd_putc(x+font[0]*i, y, (char)(buf[i]), font, invert);
}
/*
* Dump bitmap graphic on screen
* This is essentially lcd_putc(x, y, 0, bitmap): bitmap being a single character font...
*/
void lcd_putg(uint8_t x, uint8_t y, uint8_t *bitmap, bool invert)
{
int w, h, i, j;
uint8_t xorbyte = 0x00; // XOR value for memcpy
uint8_t *srce, *dest; // Pointers for memcpy
w=bitmap[0]; h=bitmap[1]; // Retrieve bitmap width and height
if (((x+w)>LCD_WIDTH)||((y+h)>LCD_HEIGHT)) return; // Out of range!
lcd_cursor(x,y,w,h); // Define window for writing
if (invert) xorbyte = 0xff; // Optionally set inversion
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI ; // Multiple data byte
for (i=0; i<h; i++) // Split up graphic into lines
{
srce = &bitmap[4 + i*w/2]; // Initialize pointers
dest = &txdata[1];
for (j=0; j<w/2; j++) // Copy data
*dest++ = *srce++ ^ xorbyte;
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+w/2, false); // Send graphic line
}
}
/*
* Write a horizontal line on row y
*/
void lcd_hruler(uint8_t x, uint8_t y, uint8_t w)
{
lcd_cursor(x,y,w,2); // Just the line
// range x, y and truncate w
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI;
memset(&txdata[1], 0x88, w); // White line
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+w, false); // Dump two lines
}
/*
* Write a vertical line on col x and x+1
*/
void lcd_vruler(uint8_t x, uint8_t y, uint8_t h)
{
lcd_cursor(x,y,2,h); // Just the line
// range x, y and truncate w
txdata[0] = LCD_CTRLDATA | LCD_CTRLMULTI;
memset(&txdata[1], 0x88, h); // White line
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 1+h, false); // Dump two lines
}
/*
* Initialize and clear display
*/
void lcd_init()
{
int page;
sleep_ms(1);
txdata[0] = LCD_CTRLCMD | LCD_CTRLMULTI ; // Multiple command byte
txdata[1] = LCD_CMDLOCK; // Unlock command interface
txdata[2] = 0x12;
txdata[3] = LCD_CTRLCMD | LCD_CTRLMULTI ; // Multiple command byte
txdata[4] = LCD_REMAP; // Display upside-down
txdata[5] = 0x53; // So change GDRAM mapping
txdata[6] = LCD_CTRLCMD | LCD_CTRLSINGLE ; // Multiple command byte
txdata[7] = LCD_ACTIVE; // DISPLAY ON
txdata[8] = LCD_CTRLCMD | LCD_CTRLSINGLE ; // Multiple command byte
txdata[9] = LCD_MODENORM; // NORMAL MODE
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 10, false);
// Output init flush screen
txdata[0] = LCD_CTRLCMD | LCD_CTRLSINGLE;
txdata[1] = LCD_INACTIVE;
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 2, false); // Send commands
lcd_clrscr(0,0,128,128);
lcd_putg(0, 0, LCD_UDJAT128, true);
txdata[0] = LCD_CTRLCMD | LCD_CTRLSINGLE;
txdata[1] = LCD_ACTIVE;
i2c_write_blocking(i2c0, I2C_SSD1327, txdata, 2, false); // Send commands
}

48
lcd.h 100644
Wyświetl plik

@ -0,0 +1,48 @@
#ifndef _LCD_H_
#define _LCD_H_
/*
* lcd.h
*
* Created: Jan 2022
* Author: Arjan te Marvelde
*
*/
/*
* Font and Bitmap definitions
* see lcdfont.c and lcdlogo.c
*/
#define LCD_6X8 ASCII6x8
#define LCD_8X12 ASCII8x12
#define LCD_12X16 ASCII12x16
#define LCD_SQR24X12 SQR24x12
#define LCD_SAW24X12 SAW24x12
#define LCD_TRI24X12 TRI24x12
#define LCD_SIN24X12 SIN24x12
#define LCD_PUL24X12 PUL24x12
#define LCD_UDJAT32 UDJAT32x32
#define LCD_UDJAT128 UDJAT128x128
#define LCD_CIR16X16 CIRCLE16x16
extern uint8_t ASCII6x8[];
extern uint8_t ASCII8x12[];
extern uint8_t ASCII12x16[];
extern uint8_t UDJAT32x32[];
extern uint8_t SQR24x12[];
extern uint8_t SAW24x12[];
extern uint8_t TRI24x12[];
extern uint8_t SIN24x12[];
extern uint8_t PUL24x12[];
extern uint8_t UDJAT128x128[];
extern uint8_t CIRCLE16x16[];
/* API */
void lcd_putc(uint8_t x, uint8_t y, uint8_t c, uint8_t *font, bool invert);
void lcd_puts(uint8_t x, uint8_t y, char *buf, uint8_t *font, bool invert);
void lcd_putg(uint8_t x, uint8_t y, uint8_t *bitmap, bool invert);
void lcd_hruler(uint8_t x, uint8_t y, uint8_t w);
void lcd_vruler(uint8_t x, uint8_t y, uint8_t h);
void lcd_clrscr(uint8_t x, uint8_t y, uint8_t w, uint8_t h);
void lcd_init(void);
#endif

4452
lcdfont.c 100644

Plik diff jest za duży Load Diff

306
lcdlogo.c 100644
Wyświetl plik

@ -0,0 +1,306 @@
/*
* lcdlogo.c
*
* Created: Jan 2022
* Author: Arjan te Marvelde
*
* Graphics definitions for SD1327
*/
#include "pico/stdlib.h"
#include "lcd.h"
// Sawtooth symbol 24x12
//
uint8_t SAW24x12[] =
{
0x18,0x0c,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0x00,
0x00,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xf0,0x00,0x00,
0x00,0xf0,0x00,0x00,0x00,0x00,0x00,0xff,0x00,0xf0,0x00,0x00,
0x00,0xf0,0x00,0x00,0x00,0x00,0xff,0x00,0x00,0xf0,0x00,0x00,
0x00,0xf0,0x00,0x00,0x00,0xff,0x00,0x00,0x00,0xf0,0x00,0x00,
0x00,0xf0,0x00,0x00,0xff,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,
0x00,0xf0,0x00,0xff,0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,
0x00,0xf0,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xff,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
// Square symbol 24x12
//
uint8_t SQR24x12[] =
{
0x18,0x0c,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x00,0x00,0x00,0xff,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,
0x00,0xff,0x0f,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
// Triangle symbol 24x12
//
uint8_t TRI24x12[] =
{
0x18,0x0c,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xf0,0x00,0xf0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0xf0,0x00,0x00,0xf0,0x00,
0x00,0x0f,0x00,0x0f,0x00,0x00,0x00,0x00,0x0f,0x00,0x0f,0x00,
0x00,0xf0,0xf0,0x00,0x00,0x00,0x00,0x00,0xf0,0xf0,0x00,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
// Sine symbol 24x12
//
uint8_t SIN24x12[] =
{
0x18,0x0c,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xff,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xff,0x00,0xf0,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0xf0,0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0x00,0xf0,0x00,
0x00,0x0f,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0f,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0x0f,0x00,0xff,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,0xff,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
// Puse symbol 24x12
//
uint8_t PUL24x12[] =
{
0x18,0x0c,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xf0,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xf0,0x00,0x00,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xf0,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,
0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0xf0,0x00,0x00,0x00,0x00,
0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
// Circle picture
// first: 0x00, last: 0x00
uint8_t CIRCLE16x16[] =
{
0x10,0x10,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0xf0,0xff,0xff,0x0f,0x00,0x00,
0x00,0xf0,0xff,0xff,0xff,0xff,0x00,0x00,
0x00,0xf0,0xff,0x00,0x00,0xff,0xff,0x00,
0x00,0xff,0x0f,0x00,0x00,0xf0,0xff,0x00,
0xf0,0xff,0x00,0x00,0x00,0x00,0xff,0x0f,
0xf0,0xff,0x00,0x00,0x00,0x00,0xf0,0x0f,
0xf0,0x0f,0x00,0x00,0x00,0x00,0xf0,0x0f,
0xf0,0x0f,0x00,0x00,0x00,0x00,0xf0,0x0f,
0xf0,0x0f,0x00,0x00,0x00,0x00,0xf0,0x0f,
0xf0,0xff,0x00,0x00,0x00,0x00,0xff,0x0f,
0x00,0xff,0x0f,0x00,0x00,0xf0,0xff,0x00,
0x00,0xff,0xff,0x0f,0x00,0xff,0x0f,0x00,
0x00,0x00,0xff,0xff,0xff,0xff,0x0f,0x00,
0x00,0x00,0xf0,0xff,0xff,0x0f,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
// UDJAT logo 32x32
// first: 0x00, last: 0x00
uint8_t UDJAT32x32[] =
{
0x20,0x20,0x00,0x00,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xbb,0xbb,0xbb,0xbb,0xbb,0x3b,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0xb3,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0xbb,0x3b,0x33,0x33,
0x33,0x33,0x33,0x33,0xbb,0xbb,0xbb,0x33,0x33,0x33,0x33,0x33,0xbb,0xbb,0x3b,0x33,
0x33,0x33,0x33,0xbb,0xbb,0x3b,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xb3,0xbb,0x33,
0x33,0x33,0xbb,0xbb,0x33,0x33,0x33,0x33,0x33,0xbb,0xbb,0x3b,0x33,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xbb,0xbb,0xbb,0xbb,0xbb,0x3b,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0xbb,0xbb,0xbb,0x33,0x33,0xbb,0xbb,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0xbb,0xbb,0x3b,0x33,0x33,0x33,0x33,0xbb,0x3b,0x33,0x33,
0x33,0x33,0x33,0xb3,0xbb,0xbb,0xb3,0x33,0x33,0x33,0x33,0x33,0xb3,0xbb,0x33,0x33,
0x33,0x33,0xbb,0xbb,0x3b,0x33,0xb3,0x3b,0x33,0x33,0x33,0x33,0xb3,0xb3,0x3b,0x33,
0x33,0xbb,0xbb,0xbb,0x33,0x33,0xb3,0x3b,0x33,0x33,0x33,0x33,0xbb,0x33,0xbb,0x33,
0x33,0x33,0x33,0xbb,0xbb,0x3b,0x33,0xbb,0x33,0x33,0x33,0xb3,0x3b,0xb3,0xbb,0x3b,
0x33,0x33,0x33,0x33,0xb3,0xbb,0xbb,0xbb,0xbb,0x33,0x33,0xbb,0xbb,0xbb,0x3b,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0xb3,0xbb,0xbb,0xbb,0xbb,0xbb,0x3b,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xb3,0xbb,0xbb,0x33,0x33,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xbb,0xbb,0xb3,0x3b,0x33,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xbb,0xbb,0xb3,0xbb,0x33,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0xb3,0xbb,0xbb,0x33,0xbb,0x33,0x33,0x33,0x33,
0x33,0xb3,0xbb,0x33,0x33,0x33,0x33,0xb3,0x3b,0xbb,0xb3,0x3b,0x33,0x33,0x33,0x33,
0x33,0xbb,0xbb,0x3b,0x33,0x33,0x33,0xbb,0x33,0xbb,0xb3,0x3b,0x33,0x33,0x33,0x33,
0xb3,0x3b,0x33,0xbb,0x33,0x33,0xb3,0xbb,0x33,0xb3,0xb3,0x33,0x33,0x33,0x33,0x33,
0xb3,0x3b,0x3b,0xbb,0x33,0x33,0xbb,0x3b,0x33,0xb3,0xbb,0x33,0x33,0x33,0x33,0x33,
0xb3,0x3b,0xbb,0xbb,0x33,0xb3,0xbb,0x33,0x33,0xb3,0xbb,0x33,0x33,0x33,0x33,0x33,
0xb3,0x3b,0xbb,0x3b,0x33,0xbb,0x3b,0x33,0x33,0xb3,0xbb,0x33,0x33,0x33,0x33,0x33,
0x33,0xbb,0x33,0x33,0xbb,0xbb,0x33,0x33,0x33,0xb3,0xbb,0x33,0x33,0x33,0x33,0x33,
0x33,0xbb,0xbb,0xbb,0xbb,0x33,0x33,0x33,0x33,0xb3,0xbb,0x33,0x33,0x33,0x33,0x33,
0x33,0x33,0xbb,0x3b,0x33,0x33,0x33,0x33,0x33,0xb3,0xb3,0x33,0x33,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,
0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33,0x33
};
// UDJAT logo 128x128
// first: 0x00, last: 0x00
uint8_t UDJAT128x128[] =
{
0x80,0x80,0x00,0x00,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x43,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x31,0x66,0x66,0xea,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6a,0x66,0x15,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x87,0xb8,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x8d,0x38,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x82,0xba,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xcf,0x1a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x31,0xcb,0xfc,0xff,0xff,0xff,0xff,0xff,0xff,0x8d,0x44,0x44,0x46,0x44,0x55,0xda,0xff,0xff,0xff,0xff,0xcf,0xbc,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0x92,0xfe,0xff,0xff,0xff,0xef,0xde,0x6c,0x22,0x22,0x13,0x11,0x11,0x11,0x11,0x11,0x22,0x22,0xa2,0xed,0xff,0xff,0xff,0xef,0x24,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x44,0xd7,0xff,0xff,0xff,0xff,0xcc,0x39,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0xb6,0xec,0xff,0xff,0xdf,0x34,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x64,0xfa,0xff,0xff,0xff,0xbe,0x8a,0x11,0x22,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x52,0xca,0xff,0xff,0xbf,0x46,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x21,0x21,0x98,0xfc,0xff,0xff,0xef,0x8b,0x26,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0x32,0xa7,0xfe,0xff,0xcf,0x48,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x72,0xba,0xff,0xff,0xff,0xef,0x58,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x32,0x76,0xff,0xff,0xcf,0x17,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x93,0xed,0xff,0xff,0xff,0x4e,0x34,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0x84,0xfe,0xff,0xde,0x15,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0xe7,0xff,0xff,0xff,0xde,0x26,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0x62,0xec,0xff,0xef,0x44,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x55,0xfa,0xff,0xff,0xcf,0x7b,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x32,0xda,0xff,0xdf,0x35,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0x42,0x86,0xff,0xff,0xff,0x9c,0x38,0x12,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0x41,0xa9,0xfe,0x7e,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x94,0xc9,0xff,0xff,0xce,0x78,0x25,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x86,0x37,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xb4,0xfc,0xff,0xff,0x6a,0x45,0x22,0x22,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xfb,0xff,0x6d,0x33,0x23,0x22,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x22,0x12,0x22,0x12,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0x22,0x22,0x55,0x56,0x56,0x55,0x45,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x75,0x78,0xa8,0xfe,0xff,0xff,0xff,0xdf,0x79,0x67,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x82,0xa9,0xfd,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0x89,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x21,0xb8,0xec,0xff,0xff,0xff,0xff,0x6f,0x55,0x66,0x55,0x86,0xfd,0xff,0xff,0xbc,0x13,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x51,0xed,0xff,0xff,0xff,0x9f,0x34,0x33,0x13,0x11,0x11,0x11,0x21,0x33,0x43,0xfe,0xff,0xce,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xa3,0xff,0xff,0xde,0xac,0x14,0x21,0x11,0x11,0x13,0x12,0x22,0x11,0x11,0x11,0x21,0x31,0xea,0xff,0xaf,0x23,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x62,0xb5,0xff,0xef,0x9c,0x26,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0x22,0x52,0xda,0xff,0x8c,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0x12,0x62,0xf9,0xff,0xef,0x79,0x24,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0x62,0xfb,0xff,0x68,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x22,0x83,0xea,0xff,0xdf,0x68,0x12,0x22,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x93,0xfe,0xef,0x16,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x92,0xed,0xff,0xff,0x4b,0x22,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xc4,0xff,0x6e,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xa2,0xfe,0xff,0xef,0xff,0x2a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x32,0xfe,0xff,0x16,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x94,0xff,0xff,0x9d,0x13,0xff,0x2a,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xfd,0xff,0x6f,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x31,0x86,0xff,0xef,0x8b,0x11,0x11,0xff,0x1a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0xf9,0xce,0xff,0x17,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x41,0xa8,0xff,0xcf,0x69,0x12,0x11,0x11,0xff,0x2a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xf7,0x5b,0xfc,0x7f,0x13,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x51,0xca,0xff,0xdf,0x46,0x11,0x11,0x11,0x21,0xfe,0x1a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xfd,0x2b,0xd4,0xff,0x3a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xeb,0xff,0x7f,0x44,0x11,0x11,0x11,0x11,0x11,0xfe,0x2a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xfd,0x1a,0x41,0xfd,0x7f,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x72,0xee,0xef,0x7d,0x22,0x11,0x11,0x11,0x11,0x11,0x21,0xf8,0x4c,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0xfe,0x28,0x11,0xe3,0xff,0x5d,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x43,0xf4,0xff,0xdf,0x1b,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xf4,0x7f,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x62,0xde,0x11,0x11,0x31,0xeb,0xbf,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x31,0x76,0xfb,0xff,0xad,0x38,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xf3,0x9f,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xb1,0xdf,0x11,0x11,0x11,0x61,0xfd,0x5e,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x31,0x87,0xb8,0xff,0xff,0x8a,0x25,0x21,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe3,0xdf,0x15,0x11,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xc3,0xaf,0x12,0x11,0x11,0x21,0xd6,0xdf,0x16,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x98,0xaa,0xaa,0xca,0xfe,0xff,0xff,0x8f,0x22,0x21,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x82,0xff,0x16,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0xf8,0x3f,0x12,0x11,0x11,0x11,0x61,0xfe,0x7d,0x13,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x21,0xfe,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0x24,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x41,0xff,0x3d,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x42,0xfc,0x1e,0x12,0x11,0x11,0x11,0x11,0xa4,0xef,0x5c,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x21,0xdb,0xad,0x77,0x94,0x95,0xed,0xee,0xff,0xff,0x9f,0x33,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xf7,0x5f,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x82,0xff,0x23,0x11,0x11,0x11,0x11,0x11,0x23,0xe6,0xff,0x28,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0x12,0x11,0xb6,0xcc,0xff,0xff,0x47,0x13,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xd5,0xff,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xf7,0x8f,0x22,0x11,0x11,0x11,0x11,0x11,0x22,0x42,0xea,0xdf,0x13,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x31,0xa8,0xfd,0xff,0x7b,0x25,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x72,0xfe,0x1e,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x32,0xff,0x5c,0x21,0x11,0x11,0x11,0x11,0x22,0x22,0x22,0xb2,0xff,0x4c,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x21,0x85,0xfe,0xff,0xad,0x38,0x11,0x11,0x11,0x11,0x11,0x11,0x32,0xfc,0x8f,0x23,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xa2,0xdf,0x25,0x12,0x21,0x12,0x22,0x88,0x99,0x99,0x99,0xc9,0xff,0x8f,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0x66,0xf8,0xff,0xcf,0x8a,0x13,0x11,0x11,0x11,0x11,0x11,0xe4,0xff,0x4a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xf9,0x9f,0x23,0x22,0x42,0xb9,0xcb,0xff,0xff,0xff,0xff,0xef,0x6e,0x36,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x43,0xf9,0xff,0xff,0xad,0x12,0x11,0x11,0x11,0x11,0x42,0xff,0xcf,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x92,0xff,0x2c,0x22,0x32,0xdd,0xff,0xff,0x9f,0x44,0x44,0x43,0x33,0x13,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xb2,0xed,0xff,0xff,0x4a,0x23,0x11,0x11,0x11,0xe3,0xff,0x5f,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x22,0x32,0xfa,0xdf,0x33,0xd7,0xff,0xee,0x4d,0x21,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x51,0xcb,0xfe,0xff,0x6d,0x25,0x12,0x11,0x42,0xec,0xff,0x49,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x31,0x92,0xff,0xdf,0xfd,0xcf,0xab,0x16,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x12,0x97,0xda,0xff,0xaf,0x78,0x26,0x22,0x83,0xfc,0xdf,0x37,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x72,0xf9,0xff,0xff,0xad,0x49,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x52,0x77,0xfd,0xff,0xae,0x9a,0x68,0xf9,0xff,0xcf,0x89,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x61,0xf9,0xff,0xff,0x8b,0x25,0x21,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x54,0x95,0xfe,0xff,0xef,0xfe,0xff,0xff,0xff,0xbb,0x38,0x24,0x21,0x21,0x89,0xbb,0xeb,0xff,0xef,0x58,0x23,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x32,0x33,0xa4,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xee,0xdd,0xdd,0xff,0xff,0xff,0xef,0x37,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x23,0x12,0x12,0x99,0xdd,0xdd,0xee,0xfe,0xfe,0xff,0xfe,0xee,0xdd,0x8c,0x22,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0x12,0x21,0x21,0x21,0x45,0x94,0xa7,0x98,0x87,0x53,0x21,0x21,0x21,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0x22,0x23,0x12,0x22,0x11,0x11,0x21,0x77,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0x62,0x7b,0x72,0x39,0x11,0x11,0x21,0xff,0x28,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xd1,0xbf,0xa1,0x3f,0x11,0x11,0x21,0xfd,0x28,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xfa,0x9f,0xa2,0x3f,0x11,0x11,0x11,0xf6,0x5e,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x71,0xfe,0x1b,0xa1,0xef,0x11,0x11,0x11,0xe4,0xdf,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xb3,0xff,0x11,0xa1,0xff,0x11,0x11,0x11,0x51,0xfd,0x3b,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0xf7,0x9f,0x11,0xa1,0xff,0x11,0x11,0x11,0x11,0xd5,0xbf,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x42,0xfc,0x3d,0x11,0x91,0xff,0x11,0x11,0x11,0x11,0x51,0xfd,0x6b,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xb3,0xff,0x17,0x11,0x31,0xed,0x11,0x11,0x11,0x11,0x11,0xa4,0xef,0x26,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xf4,0xbf,0x13,0x11,0x11,0xec,0x11,0x11,0x11,0x11,0x11,0x31,0xf6,0xce,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0xff,0x5e,0x11,0x11,0x11,0xfc,0x11,0x11,0x11,0x11,0x11,0x51,0xff,0x5d,0x31,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x62,0xef,0x16,0x11,0x11,0x11,0xfc,0x14,0x11,0x11,0x11,0x11,0xe1,0xdf,0x16,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0xd2,0xbf,0x11,0x11,0x11,0x11,0xfc,0x2a,0x11,0x11,0x11,0x11,0xf7,0x5d,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x42,0xf9,0x5e,0x11,0x11,0x11,0x11,0xfc,0x2b,0x12,0x11,0x11,0x61,0xfe,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x82,0xff,0x18,0x11,0x11,0x11,0x11,0xfc,0x2c,0x11,0x11,0x11,0xa1,0xef,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xe8,0xef,0x11,0x11,0x11,0x11,0x11,0xfc,0x2c,0x12,0x11,0x11,0xf8,0x3f,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x32,0xfc,0x4d,0x11,0x11,0x11,0x11,0x11,0xfb,0x1c,0x11,0x11,0x21,0xf9,0x1c,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x61,0xa6,0x6a,0x16,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x93,0xff,0x16,0x11,0x11,0x11,0x11,0x11,0xfb,0x2c,0x12,0x11,0x51,0xfd,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x61,0xf8,0xef,0xfe,0x9f,0x69,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xf8,0xaf,0x13,0x11,0x11,0x11,0x11,0x11,0xfb,0x2c,0x12,0x11,0x71,0xef,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x41,0xeb,0x6d,0x66,0x55,0x76,0xdf,0x4b,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0xff,0x3b,0x11,0x11,0x11,0x11,0x11,0x11,0xe5,0x2d,0x11,0x11,0xb3,0xbf,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0xe4,0x7e,0x13,0x11,0x11,0x11,0x84,0xdf,0x17,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xb1,0xdf,0x15,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0x1c,0x11,0x11,0xf4,0x7f,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x31,0xcf,0x25,0x11,0x11,0x11,0x11,0x12,0xf8,0x4f,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xf9,0xaf,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0x1c,0x11,0x11,0xf5,0x7f,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0xe4,0x1b,0x11,0x11,0x11,0x11,0x11,0x21,0xb2,0xdf,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x92,0xff,0x3a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0x1c,0x11,0x11,0xf5,0x5f,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0xec,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x71,0xee,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xd5,0xcf,0x22,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0x1c,0x11,0x11,0xf4,0x5d,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x62,0xae,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xfd,0x19,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x32,0xfb,0x6c,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0x1c,0x11,0x21,0xfa,0x2a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0xa1,0x4f,0x12,0x11,0x11,0x11,0x12,0x11,0x11,0x11,0xfb,0x1a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xc2,0xef,0x26,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xe2,0x1c,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0xa1,0x4f,0x11,0x11,0x11,0x21,0x12,0x11,0x11,0x11,0xf7,0x2b,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xfc,0x8f,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xe2,0x1c,0x11,0x21,0xff,0x29,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0xa2,0x2f,0x11,0x11,0x11,0x72,0x18,0x11,0x11,0x21,0xf3,0x1b,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xc2,0xff,0x29,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe2,0x1c,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0xa1,0x2f,0x11,0x11,0x11,0xf5,0x3a,0x11,0x11,0x11,0xfd,0x27,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x31,0xfb,0x9e,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xd2,0x1c,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0xa1,0x3f,0x11,0x11,0x21,0xf5,0x5e,0x11,0x11,0x51,0xde,0x21,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xb4,0xff,0x19,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe2,0x1c,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0xa2,0x2f,0x12,0x11,0x21,0xe4,0xbf,0x13,0x22,0xd6,0xdf,0x21,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x41,0xfb,0x9f,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xe1,0x5e,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0xa1,0x9f,0x12,0x11,0x21,0x82,0xff,0xbc,0xbb,0xfe,0x5f,0x22,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xc2,0xff,0x28,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xe1,0x9f,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0xa1,0xdf,0x11,0x11,0x11,0x21,0xfa,0xff,0xff,0xef,0x15,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0xfd,0x9f,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe2,0x7e,0x11,0x21,0xff,0x17,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0xfc,0x13,0x11,0x11,0x11,0x51,0xd9,0xdd,0x16,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xe6,0xef,0x19,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0xaf,0x11,0x21,0xff,0x28,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0xfc,0x15,0x11,0x11,0x11,0x11,0x21,0x11,0x22,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xa3,0xff,0x7e,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe2,0xaf,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0xf7,0x6e,0x12,0x11,0x11,0x11,0x21,0x21,0x22,0x11,0x11,0x11,0x11,0x11,0x11,0x32,0xfa,0xaf,0x17,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xe1,0xaf,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0xd1,0xdf,0x25,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x12,0xba,0xff,0x3a,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0xaf,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x51,0xff,0x5d,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0xc8,0xff,0x5c,0x13,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0xaf,0x12,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0xf5,0xef,0x34,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0xd5,0xff,0xcf,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xd3,0xaf,0x12,0x21,0xff,0x28,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x82,0xfe,0x5f,0x14,0x22,0x11,0x11,0x11,0x11,0x21,0x54,0xf4,0xff,0xce,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0xaf,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe6,0xff,0x6d,0x46,0x21,0x11,0x11,0x65,0x97,0xff,0xff,0xad,0x15,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0xbf,0x12,0x21,0xff,0x28,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x61,0xfc,0xff,0xcf,0x98,0x88,0x88,0xfd,0xff,0xff,0x8c,0x15,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0xaf,0x11,0x21,0xff,0x28,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x74,0xff,0xff,0xff,0xff,0xff,0xff,0xdf,0x68,0x14,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xe1,0xaf,0x11,0x21,0xff,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x64,0xfb,0xff,0xff,0xff,0x9e,0x44,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xd1,0xaf,0x12,0x21,0xf7,0x18,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x22,0x32,0x23,0x33,0x32,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x21,0x22,0x11,0x11,0x21,0x12,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11
};

124
monitor.c
Wyświetl plik

@ -13,19 +13,20 @@
#include <stdlib.h>
#include <string.h>
#include "pico/stdlib.h"
#include "hardware/pll.h"
#include "uWFG.h"
#include "gen.h"
#include "monitor.h"
#define CR 13
#define BS 8
#define LF 10
#define CR 13
#define SP 32
#define CMD_LEN 80
#define CMD_ARGS 16
char mon_cmd[CMD_LEN+1]; // Command string buffer
char *argv[CMD_ARGS]; // Argument pointers, first is command
int nargs; // Nr of arguments, including command
@ -45,7 +46,6 @@ typedef struct
void mon_init()
{
stdio_init_all(); // Initialize Standard IO
mon_cmd[CMD_LEN] = '\0'; // Termination to be sure
printf("\n");
printf("=============\n");
printf(" uWFG-Pico \n");
@ -55,80 +55,33 @@ void mon_init()
printf("Pico> "); // prompt
}
wfg_t mon_wave;
/*** ------------------------------------------------------------- ***/
/*** Below the definitions of the shell commands, add where needed ***/
/*** ------------------------------------------------------------- ***/
/*
* Dumps a defined range of Si5351 registers
wfg_t mon_wave;
/*
* Print Fsys
*/
void mon_si(void)
void mon_fsys(void)
{
if (nargs>1) mon_wave.freq = atof(argv[2]);
mon_wave.buf = (uint32_t *)sine16;
mon_wave.len = 16/4;
if (((*argv[1]) == 'A') || ((*argv[1]) == 'a'))
wfg_play(OUTA, &mon_wave);
else
wfg_play(OUTB, &mon_wave);
float f = 1.2e7; // Assume 12MHz XOSC
f *= pll_sys_hw->fbdiv_int&0xfff; // Feedback divider
f /= (pll_sys_hw->prim&0x00070000)>>16; // Primary divider 1
f /= (pll_sys_hw->prim&0x00007000)>>12; // Primary divider 2
printf("System clock: %9.0f Hz\n", f);
}
/*
* Dumps the entire built-in and programmed characterset on the LCD
*/
void mon_sq(void)
{
if (nargs>1) mon_wave.freq = atof(argv[2]);
mon_wave.buf = (uint32_t *)block16;
mon_wave.len = 16/4;
if (((*argv[1]) == 'A') || ((*argv[1]) == 'a'))
wfg_play(OUTA, &mon_wave);
else
wfg_play(OUTB, &mon_wave);
}
/*
* Checks for inter-core fifo overruns
*/
void mon_sa(void)
{
if (nargs>1) mon_wave.freq = atof(argv[2]);
mon_wave.buf = (uint32_t *)saw256;
mon_wave.len = 256/4;
if (((*argv[1]) == 'A') || ((*argv[1]) == 'a'))
wfg_play(OUTA, &mon_wave);
else
wfg_play(OUTB, &mon_wave);
}
/*
* Sets sample clock
*/
void mon_cl(void)
{
if (nargs>1) mon_wave.freq = atof(argv[2]);
if (((*argv[1]) == 'A') || ((*argv[1]) == 'a'))
wfg_play(OUTA, &mon_wave);
else
wfg_play(OUTB, &mon_wave);
}
/*
* Command shell table, organize the command functions above
*/
#define NCMD 4
#define NCMD 1
shell_t shell[NCMD]=
{
{"si", 2, &mon_si, "si {A|B} <clk>", "sine wave at sample rate clk"},
{"sq", 2, &mon_sq, "sq {A|B} <clk>", "square wave at sample rate clk"},
{"sa", 2, &mon_sa, "sa {A|B} <clk>", "sawtooth at sample rate clk"},
{"cl", 2, &mon_cl, "cl {A|B} <clk>", "set sample rate clk"}
{"fsys", 4, &mon_fsys, "fsys", "Print system clock frequency"}
};
@ -139,17 +92,20 @@ shell_t shell[NCMD]=
/*
* Command line parser
* Fills an array of argument substrings (char *argv[])
* Total number of arguments is stored (int nargs)
*/
void mon_parse(char* s)
{
char *p;
int i;
printf("%s\n", s); // Echo string for debugging purposes
p = s; // Set to start of string
nargs = 0;
while (*p!='\0') // Assume stringlength >0
{
while (*p==' ') p++; // Skip whitespace
while (*p==' ') *p++='\0'; // Replace & skip whitespace
if (*p=='\0') break; // String might end in spaces
argv[nargs++] = p; // Store first valid char loc after whitespace
while ((*p!=' ')&&(*p!='\0')) p++; // Skip non-whitespace
@ -171,29 +127,39 @@ void mon_parse(char* s)
* This function collects characters from stdin until CR
* Then the command is send to a parser and executed.
*/
char mon_cmd[CMD_LEN+1]; // Command string buffer
int mon_pos = 0; // Current position in command string
void mon_evaluate(void)
{
static int i = 0;
int c = getchar_timeout_us(10L); // NOTE: this is the only SDK way to read from stdin
if (c==PICO_ERROR_TIMEOUT) return; // Early bail out
int c;
c = getchar_timeout_us(10L); // NOTE: this is the only SDK way to read from stdin
if (c==PICO_ERROR_TIMEOUT) return; // Early bail out
switch (c)
{
case CR: // CR : need to parse command string
putchar('\n'); // Echo character, assume terminal appends CR
mon_cmd[i] = '\0'; // Terminate command string
if (i>0) // something to parse?
mon_parse(mon_cmd); // --> process command
i=0; // reset index
printf("Pico> "); // prompt
case BS:
if (mon_pos>0)
{
putchar(BS); // Echo backspace
mon_cmd[mon_pos--] = '\0'; // Reset character
}
break;
case LF:
break; // Ignore, assume CR as terminator
break; // Ignore LF, assume CR as terminator
case CR: // CR : need to parse command string
putchar('\n'); // Echo character, assume terminal appends CR
mon_cmd[mon_pos] = '\0'; // Terminate command string
if (mon_pos>0) // something to parse?
mon_parse(mon_cmd); // --> process command
mon_pos=0; // reset index
printf("Pico> "); // prompt
break;
default:
if ((c<32)||(c>=128)) break; // Only allow alfanumeric
putchar((char)c); // Echo character
mon_cmd[i] = (char)c; // store in command string
if (i<CMD_LEN) i++; // check range and increment
if ((c<32)||(c>=128)) break; // Only allow alfanumeric
putchar((char)c); // Echo character
mon_cmd[mon_pos] = (char)c; // store in command string
if (mon_pos<CMD_LEN) mon_pos++; // check range and increment
break;
}
}

83
uWFG.c
Wyświetl plik

@ -17,65 +17,25 @@
#include "hardware/timer.h"
#include "hardware/clocks.h"
#include "hardware/irq.h"
#include "hardware/i2c.h"
#include "uWFG.h"
#include "gen.h"
#include "monitor.h"
#include "hmi.h"
#include "lcd.h"
/** Some predefined waveforms **/
// Pico is little endian, so with proper alignment word and byte addressing overlap nicely
uint8_t sine16[16] __attribute__((aligned(4))) = \
{ \
128, 176, 218, 245, 255, 245, 218, 176, 128, 79, 37, 10, 0, 10, 37, 79 \
};
uint8_t sine64[64] __attribute__((aligned(4))) = \
{ \
128, 140, 152, 165, 176, 188, 198, 208, 218, 226, 234, 240, 245, 250, 253, 254, \
255, 254, 253, 250, 245, 240, 234, 226, 218, 208, 198, 188, 176, 165, 152, 140, \
128, 115, 103, 90, 79, 67, 57, 47, 37, 29, 21, 15, 10, 5, 2, 1, \
0, 1, 2, 5, 10, 15, 21, 29, 37, 47, 57, 67, 79, 90, 103, 115 \
};
uint8_t sine256[256] __attribute__((aligned(4))) = \
{ \
128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162, 165, 167, 170, 173, \
176, 179, 182, 185, 188, 190, 193, 196, 198, 201, 203, 206, 208, 211, 213, 215, \
218, 220, 222, 224, 226, 228, 230, 232, 234, 235, 237, 238, 240, 241, 243, 244, \
245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254, 254, 255, 255, 255, \
255, 255, 255, 255, 254, 254, 254, 253, 253, 252, 251, 250, 250, 249, 248, 246, \
245, 244, 243, 241, 240, 238, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220, \
218, 215, 213, 211, 208, 206, 203, 201, 198, 196, 193, 190, 188, 185, 182, 179, \
176, 173, 170, 167, 165, 162, 158, 155, 152, 149, 146, 143, 140, 137, 134, 131, \
128, 124, 121, 118, 115, 112, 109, 106, 103, 100, 97, 93, 90, 88, 85, 82, \
79, 76, 73, 70, 67, 65, 62, 59, 57, 54, 52, 49, 47, 44, 42, 40, \
37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 15, 14, 12, 11, \
10, 9, 7, 6, 5, 5, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, \
0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, \
10, 11, 12, 14, 15, 17, 18, 20, 21, 23, 25, 27, 29, 31, 33, 35, \
37, 40, 42, 44, 47, 49, 52, 54, 57, 59, 62, 65, 67, 70, 73, 76, \
79, 82, 85, 88, 90, 93, 97, 100, 103, 106, 109, 112, 115, 118, 121, 124 \
};
uint8_t saw256[256] __attribute__((aligned(4)));
uint8_t block16[16] __attribute__((aligned(4))) = \
{ \
0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255 \
};
#define I2C0_SDA 16
#define I2C0_SCL 17
/*
* LED TIMER definition and callback routine
*/
#define LED_MS 1000
struct repeating_timer led_timer;
volatile bool led_state;
bool led_callback(struct repeating_timer *t)
{
static bool led_state;
gpio_put(PICO_DEFAULT_LED_PIN, led_state);
led_state = (led_state?false:true);
return true;
@ -90,37 +50,48 @@ semaphore_t loop_sem;
struct repeating_timer loop_timer;
bool loop_callback(struct repeating_timer *t)
{
sem_release(&loop_sem);
sem_reset(&loop_sem, 1);
return(true);
}
int main()
{
/* Overclock by factor 2 */
set_sys_clock_khz(125000, false);
sleep_ms(2);
/* Initialize Pico power supply */
gpio_init(23);
gpio_set_dir(23, GPIO_OUT);
gpio_put(23, true); // Set PWM mode for less ripple
/* Initialize LED pin output */
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
gpio_put(PICO_DEFAULT_LED_PIN, true); // Set LED on
add_repeating_timer_ms(-LED_MS, led_callback, NULL, &led_timer);
/* Initialize Pico power supply */
gpio_init(23);
gpio_set_dir(23, GPIO_OUT);
gpio_put(23, true); // Set PWM mode for less ripple
/* i2c0 initialisation at 400Khz. */
i2c_init(i2c0, 400*1000);
gpio_set_function(I2C0_SDA, GPIO_FUNC_I2C);
gpio_set_function(I2C0_SCL, GPIO_FUNC_I2C);
gpio_pull_up(I2C0_SDA);
gpio_pull_up(I2C0_SCL);
for (int i=0; i<256; i++)
saw256[i] = (uint8_t)i;
wfg_init();
gen_init();
lcd_init();
hmi_init();
mon_init(); // Monitor shell on stdio
/* A simple round-robin scheduler */
sem_init(&loop_sem, 1, 1) ;
add_repeating_timer_ms(-LOOP_MS, loop_callback, NULL, &loop_timer);
add_repeating_timer_ms(-LOOP_MS, loop_callback, NULL, &loop_timer); // Run every LOOP_MS
while (1)
{
sem_acquire_blocking(&loop_sem);
mon_evaluate(); // Check monitor input
hmi_evaluate();
}
return 0;

2
uWFG.h
Wyświetl plik

@ -6,7 +6,7 @@
* Created: Dec 2021
* Author: Arjan te Marvelde
*
* See uWFG.c for more information
* See uWFG.c and waveform.c for more information
*/
extern uint8_t sine16[16];

53
waveform.c 100644
Wyświetl plik

@ -0,0 +1,53 @@
/** Some predefined waveforms **/
// Pico is little endian, so with proper alignment word and byte addressing overlap nicely
#include "pico/stdlib.h"
#include "gen.h"
uint8_t sine[GEN_MAXBUFLEN] __attribute__((aligned(4))) =
{
0x80, 0x80, 0x81, 0x81, 0x82, 0x82, 0x82, 0x83, 0x83, 0x84, 0x84, 0x84, 0x85, 0x85, 0x86, 0x86, 0x86, 0x87, 0x87, 0x88, 0x88, 0x88, 0x89, 0x89, 0x8a, 0x8a, 0x8a, 0x8b, 0x8b, 0x8c, 0x8c, 0x8c, 0x8d, 0x8d, 0x8e, 0x8e, 0x8e, 0x8f, 0x8f, 0x90, 0x90, 0x90, 0x91, 0x91, 0x91, 0x92, 0x92, 0x93, 0x93, 0x93,
0x94, 0x94, 0x95, 0x95, 0x95, 0x96, 0x96, 0x97, 0x97, 0x97, 0x98, 0x98, 0x99, 0x99, 0x99, 0x9a, 0x9a, 0x9b, 0x9b, 0x9b, 0x9c, 0x9c, 0x9c, 0x9d, 0x9d, 0x9e, 0x9e, 0x9e, 0x9f, 0x9f, 0xa0, 0xa0, 0xa0, 0xa1, 0xa1, 0xa2, 0xa2, 0xa2, 0xa3, 0xa3, 0xa3, 0xa4, 0xa4, 0xa5, 0xa5, 0xa5, 0xa6, 0xa6, 0xa6, 0xa7,
0xa7, 0xa8, 0xa8, 0xa8, 0xa9, 0xa9, 0xaa, 0xaa, 0xaa, 0xab, 0xab, 0xab, 0xac, 0xac, 0xad, 0xad, 0xad, 0xae, 0xae, 0xae, 0xaf, 0xaf, 0xaf, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb2, 0xb2, 0xb2, 0xb3, 0xb3, 0xb4, 0xb4, 0xb4, 0xb5, 0xb5, 0xb5, 0xb6, 0xb6, 0xb6, 0xb7, 0xb7, 0xb8, 0xb8, 0xb8, 0xb9, 0xb9, 0xb9,
0xba, 0xba, 0xba, 0xbb, 0xbb, 0xbb, 0xbc, 0xbc, 0xbc, 0xbd, 0xbd, 0xbe, 0xbe, 0xbe, 0xbf, 0xbf, 0xbf, 0xc0, 0xc0, 0xc0, 0xc1, 0xc1, 0xc1, 0xc2, 0xc2, 0xc2, 0xc3, 0xc3, 0xc3, 0xc4, 0xc4, 0xc4, 0xc5, 0xc5, 0xc5, 0xc6, 0xc6, 0xc6, 0xc7, 0xc7, 0xc7, 0xc8, 0xc8, 0xc8, 0xc9, 0xc9, 0xc9, 0xca, 0xca, 0xca,
0xcb, 0xcb, 0xcb, 0xcc, 0xcc, 0xcc, 0xcd, 0xcd, 0xcd, 0xce, 0xce, 0xce, 0xce, 0xcf, 0xcf, 0xcf, 0xd0, 0xd0, 0xd0, 0xd1, 0xd1, 0xd1, 0xd2, 0xd2, 0xd2, 0xd2, 0xd3, 0xd3, 0xd3, 0xd4, 0xd4, 0xd4, 0xd5, 0xd5, 0xd5, 0xd5, 0xd6, 0xd6, 0xd6, 0xd7, 0xd7, 0xd7, 0xd8, 0xd8, 0xd8, 0xd8, 0xd9, 0xd9, 0xd9, 0xda,
0xda, 0xda, 0xda, 0xdb, 0xdb, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdd, 0xdd, 0xdd, 0xdd, 0xde, 0xde, 0xde, 0xde, 0xdf, 0xdf, 0xdf, 0xe0, 0xe0, 0xe0, 0xe0, 0xe1, 0xe1, 0xe1, 0xe1, 0xe2, 0xe2, 0xe2, 0xe2, 0xe3, 0xe3, 0xe3, 0xe3, 0xe4, 0xe4, 0xe4, 0xe4, 0xe5, 0xe5, 0xe5, 0xe5, 0xe6, 0xe6, 0xe6, 0xe6, 0xe7,
0xe7, 0xe7, 0xe7, 0xe7, 0xe8, 0xe8, 0xe8, 0xe8, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xea, 0xea, 0xea, 0xea, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xec, 0xec, 0xec, 0xec, 0xec, 0xed, 0xed, 0xed, 0xed, 0xee, 0xee, 0xee, 0xee, 0xee, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf1, 0xf1, 0xf1,
0xf1, 0xf1, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf9, 0xf9,
0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd,
0xfd, 0xfd, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfd,
0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfd, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xfa, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,
0xf9, 0xf9, 0xf9, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf7, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf6, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf5, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf4, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf1,
0xf1, 0xf1, 0xf1, 0xf1, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xee, 0xee, 0xee, 0xee, 0xee, 0xed, 0xed, 0xed, 0xed, 0xec, 0xec, 0xec, 0xec, 0xec, 0xeb, 0xeb, 0xeb, 0xeb, 0xeb, 0xea, 0xea, 0xea, 0xea, 0xe9, 0xe9, 0xe9, 0xe9, 0xe9, 0xe8, 0xe8, 0xe8, 0xe8, 0xe7, 0xe7, 0xe7,
0xe7, 0xe7, 0xe6, 0xe6, 0xe6, 0xe6, 0xe5, 0xe5, 0xe5, 0xe5, 0xe4, 0xe4, 0xe4, 0xe4, 0xe3, 0xe3, 0xe3, 0xe3, 0xe2, 0xe2, 0xe2, 0xe2, 0xe1, 0xe1, 0xe1, 0xe1, 0xe0, 0xe0, 0xe0, 0xe0, 0xdf, 0xdf, 0xdf, 0xde, 0xde, 0xde, 0xde, 0xdd, 0xdd, 0xdd, 0xdd, 0xdc, 0xdc, 0xdc, 0xdb, 0xdb, 0xdb, 0xdb, 0xda, 0xda,
0xda, 0xda, 0xd9, 0xd9, 0xd9, 0xd8, 0xd8, 0xd8, 0xd8, 0xd7, 0xd7, 0xd7, 0xd6, 0xd6, 0xd6, 0xd5, 0xd5, 0xd5, 0xd5, 0xd4, 0xd4, 0xd4, 0xd3, 0xd3, 0xd3, 0xd2, 0xd2, 0xd2, 0xd2, 0xd1, 0xd1, 0xd1, 0xd0, 0xd0, 0xd0, 0xcf, 0xcf, 0xcf, 0xce, 0xce, 0xce, 0xce, 0xcd, 0xcd, 0xcd, 0xcc, 0xcc, 0xcc, 0xcb, 0xcb,
0xcb, 0xca, 0xca, 0xca, 0xc9, 0xc9, 0xc9, 0xc8, 0xc8, 0xc8, 0xc7, 0xc7, 0xc7, 0xc6, 0xc6, 0xc6, 0xc5, 0xc5, 0xc5, 0xc4, 0xc4, 0xc4, 0xc3, 0xc3, 0xc3, 0xc2, 0xc2, 0xc2, 0xc1, 0xc1, 0xc1, 0xc0, 0xc0, 0xc0, 0xbf, 0xbf, 0xbf, 0xbe, 0xbe, 0xbe, 0xbd, 0xbd, 0xbc, 0xbc, 0xbc, 0xbb, 0xbb, 0xbb, 0xba, 0xba,
0xba, 0xb9, 0xb9, 0xb9, 0xb8, 0xb8, 0xb8, 0xb7, 0xb7, 0xb6, 0xb6, 0xb6, 0xb5, 0xb5, 0xb5, 0xb4, 0xb4, 0xb4, 0xb3, 0xb3, 0xb2, 0xb2, 0xb2, 0xb1, 0xb1, 0xb1, 0xb0, 0xb0, 0xaf, 0xaf, 0xaf, 0xae, 0xae, 0xae, 0xad, 0xad, 0xad, 0xac, 0xac, 0xab, 0xab, 0xab, 0xaa, 0xaa, 0xaa, 0xa9, 0xa9, 0xa8, 0xa8, 0xa8,
0xa7, 0xa7, 0xa6, 0xa6, 0xa6, 0xa5, 0xa5, 0xa5, 0xa4, 0xa4, 0xa3, 0xa3, 0xa3, 0xa2, 0xa2, 0xa2, 0xa1, 0xa1, 0xa0, 0xa0, 0xa0, 0x9f, 0x9f, 0x9e, 0x9e, 0x9e, 0x9d, 0x9d, 0x9c, 0x9c, 0x9c, 0x9b, 0x9b, 0x9b, 0x9a, 0x9a, 0x99, 0x99, 0x99, 0x98, 0x98, 0x97, 0x97, 0x97, 0x96, 0x96, 0x95, 0x95, 0x95, 0x94,
0x94, 0x93, 0x93, 0x93, 0x92, 0x92, 0x91, 0x91, 0x91, 0x90, 0x90, 0x90, 0x8f, 0x8f, 0x8e, 0x8e, 0x8e, 0x8d, 0x8d, 0x8c, 0x8c, 0x8c, 0x8b, 0x8b, 0x8a, 0x8a, 0x8a, 0x89, 0x89, 0x88, 0x88, 0x88, 0x87, 0x87, 0x86, 0x86, 0x86, 0x85, 0x85, 0x84, 0x84, 0x84, 0x83, 0x83, 0x82, 0x82, 0x82, 0x81, 0x81, 0x80,
0x80, 0x80, 0x7f, 0x7f, 0x7e, 0x7e, 0x7e, 0x7d, 0x7d, 0x7c, 0x7c, 0x7c, 0x7b, 0x7b, 0x7a, 0x7a, 0x7a, 0x79, 0x79, 0x78, 0x78, 0x78, 0x77, 0x77, 0x76, 0x76, 0x76, 0x75, 0x75, 0x74, 0x74, 0x74, 0x73, 0x73, 0x72, 0x72, 0x72, 0x71, 0x71, 0x70, 0x70, 0x70, 0x6f, 0x6f, 0x6f, 0x6e, 0x6e, 0x6d, 0x6d, 0x6d,
0x6c, 0x6c, 0x6b, 0x6b, 0x6b, 0x6a, 0x6a, 0x69, 0x69, 0x69, 0x68, 0x68, 0x67, 0x67, 0x67, 0x66, 0x66, 0x65, 0x65, 0x65, 0x64, 0x64, 0x64, 0x63, 0x63, 0x62, 0x62, 0x62, 0x61, 0x61, 0x60, 0x60, 0x60, 0x5f, 0x5f, 0x5e, 0x5e, 0x5e, 0x5d, 0x5d, 0x5d, 0x5c, 0x5c, 0x5b, 0x5b, 0x5b, 0x5a, 0x5a, 0x5a, 0x59,
0x59, 0x58, 0x58, 0x58, 0x57, 0x57, 0x56, 0x56, 0x56, 0x55, 0x55, 0x55, 0x54, 0x54, 0x53, 0x53, 0x53, 0x52, 0x52, 0x52, 0x51, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, 0x4f, 0x4e, 0x4e, 0x4e, 0x4d, 0x4d, 0x4c, 0x4c, 0x4c, 0x4b, 0x4b, 0x4b, 0x4a, 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x48, 0x47, 0x47, 0x47,
0x46, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, 0x44, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x36, 0x36, 0x36,
0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2e, 0x2e, 0x2e, 0x2e, 0x2d, 0x2d, 0x2d, 0x2c, 0x2c, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2a, 0x2a, 0x29, 0x29, 0x29, 0x28, 0x28, 0x28, 0x28, 0x27, 0x27, 0x27, 0x26,
0x26, 0x26, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x23, 0x23, 0x23, 0x23, 0x22, 0x22, 0x22, 0x22, 0x21, 0x21, 0x21, 0x20, 0x20, 0x20, 0x20, 0x1f, 0x1f, 0x1f, 0x1f, 0x1e, 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, 0x1d, 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1b, 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, 0x1a, 0x19,
0x19, 0x19, 0x19, 0x19, 0x18, 0x18, 0x18, 0x18, 0x17, 0x17, 0x17, 0x17, 0x17, 0x16, 0x16, 0x16, 0x16, 0x15, 0x15, 0x15, 0x15, 0x15, 0x14, 0x14, 0x14, 0x14, 0x14, 0x13, 0x13, 0x13, 0x13, 0x12, 0x12, 0x12, 0x12, 0x12, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0f, 0x0f, 0x0f,
0x0f, 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x07, 0x07,
0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
0x03, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07,
0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f,
0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19,
0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x20, 0x21, 0x21, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x26,
0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2a, 0x2a, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2c, 0x2c, 0x2d, 0x2d, 0x2d, 0x2e, 0x2e, 0x2e, 0x2e, 0x2f, 0x2f, 0x2f, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, 0x32, 0x32, 0x32, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x35, 0x35,
0x35, 0x36, 0x36, 0x36, 0x37, 0x37, 0x37, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x3a, 0x3a, 0x3a, 0x3b, 0x3b, 0x3b, 0x3c, 0x3c, 0x3c, 0x3d, 0x3d, 0x3d, 0x3e, 0x3e, 0x3e, 0x3f, 0x3f, 0x3f, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x43, 0x43, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x46, 0x46,
0x46, 0x47, 0x47, 0x47, 0x48, 0x48, 0x48, 0x49, 0x49, 0x4a, 0x4a, 0x4a, 0x4b, 0x4b, 0x4b, 0x4c, 0x4c, 0x4c, 0x4d, 0x4d, 0x4e, 0x4e, 0x4e, 0x4f, 0x4f, 0x4f, 0x50, 0x50, 0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x54, 0x54, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x57, 0x57, 0x58, 0x58, 0x58,
0x59, 0x59, 0x5a, 0x5a, 0x5a, 0x5b, 0x5b, 0x5b, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f, 0x60, 0x60, 0x60, 0x61, 0x61, 0x62, 0x62, 0x62, 0x63, 0x63, 0x64, 0x64, 0x64, 0x65, 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68, 0x68, 0x69, 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, 0x6b, 0x6c,
0x6c, 0x6d, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x6f, 0x70, 0x70, 0x70, 0x71, 0x71, 0x72, 0x72, 0x72, 0x73, 0x73, 0x74, 0x74, 0x74, 0x75, 0x75, 0x76, 0x76, 0x76, 0x77, 0x77, 0x78, 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7c, 0x7d, 0x7d, 0x7e, 0x7e, 0x7e, 0x7f, 0x7f, 0x80,
};