Merge pull request #91 from XPila/MK3

Print class printf function (experimental, disabled by default)
This commit is contained in:
XPila 2017-11-20 20:10:40 +01:00 committed by GitHub
commit 521feba277
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 359 additions and 110 deletions

View File

@ -262,3 +262,48 @@ size_t Print::printFloat(double number, uint8_t digits)
return n;
}
//printf
#ifdef _Print_printf
#include <stdarg.h>
#define PRINTF_BUF 80 // define the tmp buffer size (change if desired)
void Print::printf(const char *format, ...)
{
char buf[PRINTF_BUF];
va_list ap;
va_start(ap, format);
vsnprintf(buf, sizeof(buf), format, ap);
for(char *p = &buf[0]; *p; p++) // emulate cooked mode for newlines
{
if(*p == '\n')
write('\r');
write(*p);
}
va_end(ap);
}
#ifdef F // check to see if F() macro is available
void Print::printf(const __FlashStringHelper *format, ...)
{
char buf[PRINTF_BUF];
va_list ap;
va_start(ap, format);
#ifdef __AVR__
vsnprintf_P(buf, sizeof(buf), (const char *)format, ap); // progmem for AVR
#else
vsnprintf(buf, sizeof(buf), (const char *)format, ap); // for the rest of the world
#endif
for(char *p = &buf[0]; *p; p++) // emulate cooked mode for newlines
{
if(*p == '\n')
write('\r');
write(*p);
}
va_end(ap);
}
#endif
#endif //_Print_printf

View File

@ -31,6 +31,8 @@
#define OCT 8
#define BIN 2
//#define _Print_printf
class Print
{
private:
@ -79,6 +81,14 @@ class Print
size_t println(double, int = 2);
size_t println(const Printable&);
size_t println(void);
//printf
#ifdef _Print_printf
void printf(const char *format, ...);
#ifdef F // check to see if F() macro is available
void printf(const __FlashStringHelper *format, ...);
#endif
#endif //_Print_printf
};
#endif

View File

@ -82,7 +82,7 @@ const bool Z_MIN_ENDSTOP_INVERTING = false; // set to true to invert the logic o
//DEBUG
//#define _NO_ASM
//#define DEBUG_DCODES //D codes
#define DEBUG_DCODES //D codes
#if 1
//#define DEBUG_FSENSOR_LOG //Reports fsensor status to serial
//#define DEBUG_CRASHDET_COUNTERS //Display crash-detection counters on LCD

View File

@ -9,6 +9,8 @@
#include <avr/wdt.h>
#define FLASHSIZE 0x40000
#define RAMSIZE 0x2000
#define boot_src_addr (*((uint32_t*)(RAMSIZE - 16)))
#define boot_dst_addr (*((uint32_t*)(RAMSIZE - 12)))
@ -25,23 +27,53 @@ extern float current_temperature_pinda;
extern float axis_steps_per_unit[NUM_AXIS];
inline void serial_print_hex_nibble(uint8_t val)
inline void print_hex_nibble(uint8_t val)
{
MYSERIAL.write((val > 9)?(val - 10 + 'a'):(val + '0'));
putchar((val > 9)?(val - 10 + 'a'):(val + '0'));
}
void serial_print_hex_byte(uint8_t val)
void print_hex_byte(uint8_t val)
{
serial_print_hex_nibble(val >> 4);
serial_print_hex_nibble(val & 15);
print_hex_nibble(val >> 4);
print_hex_nibble(val & 15);
}
void serial_print_hex_word(uint16_t val)
void print_hex_word(uint16_t val)
{
serial_print_hex_byte(val >> 8);
serial_print_hex_byte(val & 255);
print_hex_byte(val >> 8);
print_hex_byte(val & 255);
}
void print_mem(uint32_t address, uint16_t count, uint8_t type, uint8_t countperline = 16)
{
while (count)
{
if (type == 2)
print_hex_nibble(address >> 16);
print_hex_word(address);
putchar(' ');
uint8_t count_line = countperline;
while (count && count_line)
{
uint8_t data = 0;
switch (type)
{
case 0: data = *((uint8_t*)address++); break;
case 1: data = eeprom_read_byte((uint8_t*)address++); break;
case 2: data = pgm_read_byte_far((uint8_t*)address++); break;
}
putchar(' ');
print_hex_byte(data);
count_line--;
count--;
}
putchar('\n');
}
}
//#define LOG(args...) printf(args)
#define LOG(args...)
int parse_hex(char* hex, uint8_t* data, int count)
{
int parsed = 0;
@ -68,7 +100,7 @@ int parse_hex(char* hex, uint8_t* data, int count)
void dcode_0()
{
if (*(strchr_pointer + 1) == 0) return;
MYSERIAL.println("D0 - Reset");
LOG("D0 - Reset\n");
if (code_seen('B')) //bootloader
{
cli();
@ -85,7 +117,7 @@ void dcode_0()
void dcode_1()
{
MYSERIAL.println("D1 - Clear EEPROM and RESET");
LOG("D1 - Clear EEPROM and RESET\n");
cli();
for (int i = 0; i < 8192; i++)
eeprom_write_byte((unsigned char*)i, (unsigned char)0xff);
@ -95,7 +127,7 @@ void dcode_1()
void dcode_2()
{
MYSERIAL.println("D2 - Read/Write RAM");
LOG("D2 - Read/Write RAM\n");
uint16_t address = 0x0000; //default 0x0000
uint16_t count = 0x2000; //default 0x2000 (entire ram)
if (code_seen('A')) // Address (0x0000-0x1fff)
@ -113,34 +145,32 @@ void dcode_2()
{
for (int i = 0; i < count; i++)
*((uint8_t*)(address + i)) = data[i];
MYSERIAL.print(count, DEC);
MYSERIAL.println(" bytes written to RAM at address ");
serial_print_hex_word(address);
MYSERIAL.write('\n');
LOG("%d bytes written to RAM at address %04x", count, address);
}
else
count = 0;
}
while (count)
print_mem(address, count, 0);
/* while (count)
{
serial_print_hex_word(address);
MYSERIAL.write(' ');
print_hex_word(address);
putchar(' ');
uint8_t countperline = 16;
while (count && countperline)
{
uint8_t data = *((uint8_t*)address++);
MYSERIAL.write(' ');
serial_print_hex_byte(data);
putchar(' ');
print_hex_byte(data);
countperline--;
count--;
}
MYSERIAL.write('\n');
}
putchar('\n');
}*/
}
void dcode_3()
{
MYSERIAL.println("D3 - Read/Write EEPROM");
LOG("D3 - Read/Write EEPROM\n");
uint16_t address = 0x0000; //default 0x0000
uint16_t count = 0x2000; //default 0x2000 (entire eeprom)
if (code_seen('A')) // Address (0x0000-0x1fff)
@ -158,34 +188,35 @@ void dcode_3()
{
for (int i = 0; i < count; i++)
eeprom_write_byte((uint8_t*)(address + i), data[i]);
MYSERIAL.print(count, DEC);
MYSERIAL.println(" bytes written to EEPROM at address ");
serial_print_hex_word(address);
MYSERIAL.write('\n');
LOG(count, DEC);
LOG(" bytes written to EEPROM at address ");
print_hex_word(address);
putchar('\n');
}
else
count = 0;
}
while (count)
print_mem(address, count, 1);
/* while (count)
{
serial_print_hex_word(address);
MYSERIAL.write(' ');
print_hex_word(address);
putchar(' ');
uint8_t countperline = 16;
while (count && countperline)
{
uint8_t data = eeprom_read_byte((uint8_t*)address++);
MYSERIAL.write(' ');
serial_print_hex_byte(data);
putchar(' ');
print_hex_byte(data);
countperline--;
count--;
}
MYSERIAL.write('\n');
}
putchar('\n');
}*/
}
void dcode_4()
{
MYSERIAL.println("D4 - Read/Write PIN");
LOG("D4 - Read/Write PIN\n");
if (code_seen('P')) // Pin (0-255)
{
int pin = (int)code_value();
@ -206,18 +237,15 @@ void dcode_4()
else
{
int val = (digitalRead(pin) != LOW)?1:0;
MYSERIAL.print("PIN");
MYSERIAL.print(pin);
MYSERIAL.print("=");
MYSERIAL.println(val);
printf("PIN%d=%d", pin, val);
}
}
}
}
/*
void dcode_5()
{
MYSERIAL.println("D5 - Read/Write FLASH");
LOG("D5 - Read/Write FLASH\n");
uint32_t address = 0x0000; //default 0x0000
uint16_t count = 0x0400; //default 0x0400 (1kb block)
if (code_seen('A')) // Address (0x00000-0x3ffff)
@ -241,17 +269,17 @@ void dcode_5()
{
if (bErase)
{
MYSERIAL.print(count, DEC);
MYSERIAL.println(" bytes of FLASH at address ");
serial_print_hex_word(address);
MYSERIAL.write(" will be erased\n");
LOG(count, DEC);
LOG(" bytes of FLASH at address ");
print_hex_word(address);
putchar(" will be erased\n");
}
if (bCopy)
{
MYSERIAL.print(count, DEC);
MYSERIAL.println(" bytes will be written to FLASH at address ");
serial_print_hex_word(address);
MYSERIAL.write('\n');
LOG(count, DEC);
LOG(" bytes will be written to FLASH at address ");
print_hex_word(address);
putchar('\n');
}
cli();
boot_app_magic = 0x55aa55aa;
@ -264,30 +292,31 @@ void dcode_5()
}
while (count)
{
serial_print_hex_nibble(address >> 16);
serial_print_hex_word(address);
MYSERIAL.write(' ');
print_hex_nibble(address >> 16);
print_hex_word(address);
putchar(' ');
uint8_t countperline = 16;
while (count && countperline)
{
uint8_t data = pgm_read_byte_far((uint8_t*)address++);
MYSERIAL.write(' ');
serial_print_hex_byte(data);
putchar(' ');
print_hex_byte(data);
countperline--;
count--;
}
MYSERIAL.write('\n');
putchar('\n');
}
}
*/
void dcode_6()
{
MYSERIAL.println("D6 - Read/Write external FLASH");
LOG("D6 - Read/Write external FLASH\n");
}
void dcode_7()
{
MYSERIAL.println("D7 - Read/Write Bootloader");
LOG("D7 - Read/Write Bootloader\n");
/*
cli();
boot_app_magic = 0x55aa55aa;
@ -302,23 +331,23 @@ void dcode_7()
void dcode_8()
{
MYSERIAL.println("D8 - Read/Write PINDA");
LOG("D8 - Read/Write PINDA\n");
uint8_t cal_status = calibration_status_pinda();
float temp_pinda = current_temperature_pinda;
float offset_z = temp_compensation_pinda_thermistor_offset(temp_pinda);
if ((strchr_pointer[1+1] == '?') || (strchr_pointer[1+1] == 0))
{
MYSERIAL.print("cal_status=");
MYSERIAL.println(cal_status?"1":"0");
LOG("cal_status=");
LOG(cal_status?"1\n":"0\n");
for (uint8_t i = 0; i < 6; i++)
{
MYSERIAL.print("temp_pinda=");
MYSERIAL.print(35 + i * 5, DEC);
MYSERIAL.print("C, temp_shift=");
LOG("temp_pinda=");
LOG(35 + i * 5, DEC);
LOG("C, temp_shift=");
uint16_t offs = 0;
if (i > 0) offs = eeprom_read_word(((uint16_t*)EEPROM_PROBE_TEMP_SHIFT) + (i - 1));
MYSERIAL.print(((float)offs) / axis_steps_per_unit[Z_AXIS], 3);
MYSERIAL.println("mm");
LOG(((float)offs) / axis_steps_per_unit[Z_AXIS], 3);
LOG("mm\n");
}
}
else if (strchr_pointer[1+1] == '!')
@ -341,21 +370,21 @@ void dcode_8()
offset_z = code_value();
}
}
MYSERIAL.print("temp_pinda=");
MYSERIAL.println(temp_pinda);
MYSERIAL.print("offset_z=");
MYSERIAL.println(offset_z, 3);
LOG("temp_pinda=");
LOG(temp_pinda);
LOG("offset_z=");
LOG(offset_z, 3);
}
void dcode_10()
{//Tell the printer that XYZ calibration went OK
MYSERIAL.println("D10 - XYZ calibration = OK");
LOG("D10 - XYZ calibration = OK\n");
calibration_status_store(CALIBRATION_STATUS_LIVE_ADJUST);
}
void dcode_12()
{//Reset Filament error, Power loss and crash counter ( Do it before every print and you can get stats for the print )
MYSERIAL.println("D12 - Reset failstat counters");
LOG("D12 - Reset failstat counters\n");
eeprom_update_byte((uint8_t*)EEPROM_CRASH_COUNT, 0x00);
eeprom_update_byte((uint8_t*)EEPROM_FERROR_COUNT, 0x00);
eeprom_update_byte((uint8_t*)EEPROM_POWER_COUNT, 0x00);
@ -368,59 +397,37 @@ void dcode_2130()
void dcode_9125()
{
MYSERIAL.println("D9125 - PAT9125");
LOG("D9125 - PAT9125\n");
if ((strchr_pointer[1+4] == '?') || (strchr_pointer[1+4] == 0))
{
MYSERIAL.print("res_x=");
MYSERIAL.print(pat9125_xres, DEC);
MYSERIAL.print(" res_y=");
MYSERIAL.print(pat9125_yres, DEC);
MYSERIAL.print(" x=");
MYSERIAL.print(pat9125_x, DEC);
MYSERIAL.print(" y=");
MYSERIAL.print(pat9125_y, DEC);
MYSERIAL.print(" b=");
MYSERIAL.print(pat9125_b, DEC);
MYSERIAL.print(" s=");
MYSERIAL.println(pat9125_s, DEC);
printf("res_x=%d res_y=%d x=%d y=%d b=%d s=%d\n", pat9125_xres, pat9125_yres, pat9125_x, pat9125_y, pat9125_b, pat9125_s);
return;
}
if (strchr_pointer[1+4] == '!')
{
pat9125_update();
MYSERIAL.print("x=");
MYSERIAL.print(pat9125_x, DEC);
MYSERIAL.print(" y=");
MYSERIAL.print(pat9125_y, DEC);
MYSERIAL.print(" b=");
MYSERIAL.print(pat9125_b, DEC);
MYSERIAL.print(" s=");
MYSERIAL.println(pat9125_s, DEC);
printf("x=%d y=%d b=%d s=%d\n", pat9125_x, pat9125_y, pat9125_b, pat9125_s);
return;
}
if (code_seen('R'))
{
unsigned char res = (int)code_value();
MYSERIAL.print("pat9125_init(xres=yres=");
MYSERIAL.print(res, DEC);
MYSERIAL.print(")=");
MYSERIAL.println(pat9125_init(res, res), DEC);
LOG("pat9125_init(xres=yres=%d)=%d\n", res, pat9125_init(res, res));
}
if (code_seen('X'))
{
pat9125_x = (int)code_value();
MYSERIAL.print("pat9125_x=");
MYSERIAL.print(pat9125_x, DEC);
LOG("pat9125_x=%d\n", pat9125_x);
}
if (code_seen('Y'))
{
pat9125_y = (int)code_value();
MYSERIAL.print("pat9125_y=");
MYSERIAL.print(pat9125_y, DEC);
LOG("pat9125_y=%d\n", pat9125_y);
}
if (code_seen('L'))
{
fsensor_log = (int)code_value();
LOG("fsensor_log=%d\n", fsensor_log);
}
}

View File

@ -156,6 +156,8 @@ void LiquidCrystal::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
command(LCD_ENTRYMODESET | _displaymode);
delayMicroseconds(60);
_escape[0] = 0;
}
@ -354,10 +356,149 @@ inline void LiquidCrystal::command(uint8_t value) {
}
inline size_t LiquidCrystal::write(uint8_t value) {
if (_escape[0] || (value == 0x1b))
return escape_write(value);
send(value, HIGH);
return 1; // assume sucess
}
//Supported VT100 escape codes:
//EraseScreen "\x1b[2J"
//CursorHome "\x1b[%d;%dH"
//CursorShow "\x1b[?25h"
//CursorHide "\x1b[?25l"
inline size_t LiquidCrystal::escape_write(uint8_t chr)
{
#define escape_cnt (_escape[0]) //escape character counter
#define is_num_msk (_escape[1]) //numeric character bit mask
#define chr_is_num (is_num_msk & 0x01) //current character is numeric
#define e_2_is_num (is_num_msk & 0x04) //escape char 2 is numeric
#define e_3_is_num (is_num_msk & 0x08) //...
#define e_4_is_num (is_num_msk & 0x10)
#define e_5_is_num (is_num_msk & 0x20)
#define e_6_is_num (is_num_msk & 0x40)
#define e_7_is_num (is_num_msk & 0x80)
#define e2_num (_escape[2] - '0') //number from character 2
#define e3_num (_escape[3] - '0') //number from character 3
#define e23_num (10*e2_num+e3_num) //number from characters 2 and 3
#define e4_num (_escape[4] - '0') //number from character 4
#define e5_num (_escape[5] - '0') //number from character 5
#define e45_num (10*e4_num+e5_num) //number from characters 4 and 5
#define e6_num (_escape[6] - '0') //number from character 6
#define e56_num (10*e5_num+e6_num) //number from characters 5 and 6
if (escape_cnt > 1) // escape length > 1 = "\x1b["
{
_escape[escape_cnt] = chr; // store current char
if ((chr >= '0') && (chr <= '9')) // char is numeric
is_num_msk |= (1 | (1 << escape_cnt)); //set mask
else
is_num_msk &= ~1; //clear mask
}
switch (escape_cnt++)
{
case 0:
if (chr == 0x1b) return 1; // escape = "\x1b"
break;
case 1:
is_num_msk = 0x00; // reset 'is number' bit mask
if (chr == '[') return 1; // escape = "\x1b["
break;
case 2:
switch (chr)
{
case '2': return 1; // escape = "\x1b[2"
case '?': return 1; // escape = "\x1b[?"
default:
if (chr_is_num) return 1; // escape = "\x1b[%1d"
}
break;
case 3:
switch (_escape[2])
{
case '?': // escape = "\x1b[?"
if (chr == '2') return 1; // escape = "\x1b[?2"
break;
case '2':
if (chr == 'J') // escape = "\x1b[2J"
{ clear(); break; } // EraseScreen
default:
if (e_2_is_num && // escape = "\x1b[%1d"
((chr == ';') || // escape = "\x1b[%1d;"
chr_is_num)) // escape = "\x1b[%2d"
return 1;
}
break;
case 4:
switch (_escape[2])
{
case '?': // "\x1b[?"
if ((_escape[3] == '2') && (chr == '5')) return 1; // escape = "\x1b[?25"
break;
default:
if (e_2_is_num) // escape = "\x1b[%1d"
{
if ((_escape[3] == ';') && chr_is_num) return 1; // escape = "\x1b[%1d;%1d"
else if (e_3_is_num && (chr == ';')) return 1; // escape = "\x1b[%2d;"
}
}
break;
case 5:
switch (_escape[2])
{
case '?':
if ((_escape[3] == '2') && (_escape[4] == '5')) // escape = "\x1b[?25"
switch (chr)
{
case 'h': // escape = "\x1b[?25h"
void cursor(); // CursorShow
break;
case 'l': // escape = "\x1b[?25l"
noCursor(); // CursorHide
break;
}
break;
default:
if (e_2_is_num) // escape = "\x1b[%1d"
{
if ((_escape[3] == ';') && e_4_is_num) // escape = "\x1b%1d;%1dH"
{
if (chr == 'H') // escape = "\x1b%1d;%1dH"
setCursor(e4_num, e2_num); // CursorHome
else if (chr_is_num)
return 1; // escape = "\x1b%1d;%2d"
}
else if (e_3_is_num && (_escape[4] == ';') && chr_is_num)
return 1; // escape = "\x1b%2d;%1d"
}
}
break;
case 6:
if (e_2_is_num) // escape = "\x1b[%1d"
{
if ((_escape[3] == ';') && e_4_is_num && e_5_is_num && (chr == 'H')) // escape = "\x1b%1d;%2dH"
setCursor(e45_num, e2_num); // CursorHome
else if (e_3_is_num && (_escape[4] == ';') && e_5_is_num) // escape = "\x1b%2d;%1d"
{
if (chr == 'H') // escape = "\x1b%2d;%1dH"
setCursor(e5_num, e23_num); // CursorHome
else if (chr_is_num) // "\x1b%2d;%2d"
return 1;
}
}
break;
case 7:
if (e_2_is_num && e_3_is_num && (_escape[4] == ';')) // "\x1b[%2d;"
if (e_5_is_num && e_6_is_num && (chr == 'H')) // "\x1b[%2d;%2dH"
setCursor(e56_num, e23_num); // CursorHome
break;
}
escape_cnt = 0; // reset escape
end:
return 1; // assume sucess
}
/************ low level data pushing commands **********/
// write either command or data, with automatic 4/8-bit selection

View File

@ -103,6 +103,15 @@ private:
uint8_t _initialized;
uint8_t _numlines,_currline;
uint8_t _escape[8];
size_t escape_write(uint8_t value);
};
#define ESC_2J "\x1b[2J"
#define ESC_25h "\x1b[?25h"
#define ESC_25l "\x1b[?25l"
#define ESC_H(c,r) "\x1b["#r";"#c"H"
#endif

View File

@ -62,6 +62,12 @@
#define MYSERIAL MSerial
#endif
extern FILE _lcdout;
#define lcdout (&_lcdout)
extern FILE _uartout;
#define uartout (&_uartout)
#define SERIAL_PROTOCOL(x) (MYSERIAL.print(x))
#define SERIAL_PROTOCOL_F(x,y) (MYSERIAL.print(x,y))
#define SERIAL_PROTOCOLPGM(x) (serialprintPGM(PSTR(x)))

View File

@ -720,7 +720,32 @@ void factory_reset(char level, bool quiet)
}
#include "LiquidCrystal.h"
extern LiquidCrystal lcd;
FILE _lcdout = {0};
int lcd_putchar(char c, FILE *stream)
{
lcd.write(c);
return 0;
}
FILE _uartout = {0};
int uart_putchar(char c, FILE *stream)
{
MYSERIAL.write(c);
return 0;
}
void lcd_splash()
{
// lcd_print_at_PGM(0, 1, PSTR(" Original Prusa "));
// lcd_print_at_PGM(0, 2, PSTR(" 3D Printers "));
// lcd.print_P(PSTR("\x1b[1;3HOriginal Prusa\x1b[2;4H3D Printers"));
fputs_P(PSTR(ESC_2J ESC_H(3,1) "Original Prusa" ESC_H(4,2) "3D Printers"), lcdout);
}
// "Setup" function is called by the Arduino framework on startup.
// Before startup, the Timers-functions (PWM)/Analog RW and HardwareSerial provided by the Arduino-code
@ -728,8 +753,8 @@ void factory_reset(char level, bool quiet)
void setup()
{
lcd_init();
lcd_print_at_PGM(0, 1, PSTR(" Original Prusa "));
lcd_print_at_PGM(0, 2, PSTR(" 3D Printers "));
fdev_setup_stream(lcdout, lcd_putchar, NULL, _FDEV_SETUP_WRITE); //setup lcdout stream
lcd_splash();
setup_killpin();
setup_powerhold();
farm_mode = eeprom_read_byte((uint8_t*)EEPROM_FARM_MODE);
@ -744,6 +769,8 @@ void setup()
else
selectedSerialPort = 0;
MYSERIAL.begin(BAUDRATE);
fdev_setup_stream(uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE); //setup uart out stream
stdout = uartout;
SERIAL_PROTOCOLLNPGM("start");
SERIAL_ECHO_START;
@ -794,7 +821,11 @@ void setup()
// loads data from EEPROM if available else uses defaults (and resets step acceleration rate)
Config_RetrieveSettings(EEPROM_OFFSET);
SdFatUtil::set_stack_guard(); //writes magic number at the end of static variables to protect against overwriting static memory by stack
tp_init(); // Initialize temperature loop
lcd_splash(); // we need to do this again, because tp_init() kills lcd
plan_init(); // Initialize planner;
watchdog_init();
@ -837,8 +868,7 @@ void setup()
st_init(); // Initialize stepper, this enables interrupts!
setup_photpin();
lcd_print_at_PGM(0, 1, PSTR(" Original Prusa ")); // we need to do this again for some reason, no time to research
lcd_print_at_PGM(0, 2, PSTR(" 3D Printers "));
servo_init();
// Reset the machine correction matrix.
// It does not make sense to load the correction matrix until the machine is homed.
@ -5853,7 +5883,8 @@ case 404: //M404 Enter the nominal filament width (3mm, 1.75mm ) N<3.0> or disp
case 4: // D4 - Read/Write PIN
dcode_4(); break;
case 5: // D5 - Read/Write FLASH
dcode_5(); break;
// dcode_5(); break;
break;
case 6: // D6 - Read/Write external FLASH
dcode_6(); break;
case 7: // D7 - Read/Write Bootloader