/***********************************************************************************************************************
PicoMite MMBasic

External.c

<COPYRIGHT HOLDERS>  Geoff Graham, Peter Mather
Copyright (c) 2021, <COPYRIGHT HOLDERS> All rights reserved. 
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 
1.	Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2.	Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
    in the documentation and/or other materials provided with the distribution.
3.	The name MMBasic be used when referring to the interpreter in any documentation and promotional material and the original copyright message be displayed 
    on the console at startup (additional copyright messages may be added).
4.	All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed 
    by the <copyright holder>.
5.	Neither the name of the <copyright holder> nor the names of its contributors may be used to endorse or promote products derived from this software 
    without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDERS> AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDERS> BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 

************************************************************************************************************************/#include "MMBasic_Includes.h"
/**
* @file External.c
* @author Geoff Graham, Peter Mather
* @brief Source for I/O MMBasic commands and functions
*/
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */
#include "Hardware_Includes.h"
#include "hardware/watchdog.h"
#include "pico/stdlib.h"
#include "hardware/clocks.h"
#include "hardware/pwm.h"
//#include "hardware/gpio.h"
#include "hardware/adc.h"
#include "hardware/structs/systick.h"
#include "hardware/structs/pwm.h"
#include "hardware/structs/pads_bank0.h"
#include "hardware/structs/adc.h"
#include "hardware/dma.h"
#include <hardware/structs/ioqspi.h>
#include <hardware/sync.h>
#define ANA_AVERAGE     10
#define ANA_DISCARD     2

extern MMFLOAT FDiv(MMFLOAT a, MMFLOAT b);
extern MMFLOAT FMul(MMFLOAT a, MMFLOAT b);
extern MMFLOAT FSub(MMFLOAT a, MMFLOAT b);
const char *PinFunction[] = {	
        "OFF",
		"AIN",
		"DIN",
		"FIN",
		"PER",
		"CIN",
		"INTH",
		"INTL",
		"DOUT",
        "HEARTBEAT",
		"INTB",
		"UART0TX",
		"UART0RX",
		"UART1TX",
		"UART1RX",
		"I2C0SDA",
		"I2C0SCL",
		"I2C1SDA",
		"I2C1SCL",
		"SPI0RX",
		"SPI0TX",
		"SPI0SCK",
		"SPI1RX",
		"SPI1TX",
		"SPI1SCK",
        "IR",
        "INT1",
        "INT2",
        "INT3",
        "INT4",
        "PWM0A",
        "PWM0B",
        "PWM1A",
        "PWM1B",
        "PWM2A",
        "PWM2B",
        "PWM3A",
        "PWM3B",
        "PWM4A",
        "PWM4B",
        "PWM5A",
        "PWM5B",
        "PWM6A",
        "PWM6B",
        "PWM7A",
        "PWM7B",
        "ADCRAW",
#ifdef rp2350
        "PWM8A",
        "PWM8B",
        "PWM9A",
        "PWM9B",
        "PWM10A",
        "PWM10B",
        "PWM11A",
        "PWM11B",
#endif
        "PIO0",
#ifdef rp2350
        "PIO1",
        "PIO2",
        "FFIN"
#else
        "PIO1"
#endif
};
;

volatile int ExtCurrentConfig[NBRPINS + 1];
volatile int INT1Value, INT1InitTimer, INT1Timer;
volatile int INT2Value, INT2InitTimer, INT2Timer;
volatile int INT3Value, INT3InitTimer, INT3Timer;
volatile int INT4Value, INT4InitTimer, INT4Timer;
volatile int64_t INT1Count,INT2Count,INT3Count, INT4Count;
uint64_t uSecoffset=0;
uint32_t pinmask=0;
volatile uint64_t IRoffset=0;
void *IrDev, *IrCmd;
volatile char IrVarType;
volatile char IrState, IrGotMsg;
int IrBits, IrCount;
unsigned char *IrInterrupt;
int last_adc=99;
volatile int CallBackEnabled=0;
uint8_t IRpin=99;
uint8_t PWM0Apin=99;
uint8_t PWM1Apin=99;
uint8_t PWM2Apin=99;
uint8_t PWM3Apin=99;
uint8_t PWM4Apin=99;
uint8_t PWM5Apin=99;
uint8_t PWM6Apin=99;
uint8_t PWM7Apin=99;
#ifdef rp2350
uint8_t PWM8Apin=99;
uint8_t PWM9Apin=99;
uint8_t PWM10Apin=99;
uint8_t PWM11Apin=99;
#endif
uint8_t PWM0Bpin=99;
uint8_t PWM1Bpin=99;
uint8_t PWM2Bpin=99;
uint8_t PWM3Bpin=99;
uint8_t PWM4Bpin=99;
uint8_t PWM5Bpin=99;
uint8_t PWM6Bpin=99;
uint8_t PWM7Bpin=99;
#ifdef rp2350
uint8_t PWM8Bpin=99;
uint8_t PWM9Bpin=99;
uint8_t PWM10Bpin=99;
uint8_t PWM11Bpin=99;
#endif
uint8_t UART1RXpin=99;
uint8_t UART1TXpin=99;
uint8_t UART0TXpin=99;
uint8_t UART0RXpin=99;
uint8_t SPI1TXpin=99;
uint8_t SPI1RXpin=99;
uint8_t SPI1SCKpin=99;
uint8_t SPI0TXpin=99;
uint8_t SPI0RXpin=99;
uint8_t SPI0SCKpin=99;
uint8_t I2C1SDApin=99;
uint8_t I2C1SCLpin=99;
uint8_t I2C0SDApin=99;
uint8_t I2C0SCLpin=99;
uint8_t slice0=0,slice1=0,slice2=0,slice3=0,slice4=0,slice5=0,slice6=0,slice7=0;
#ifdef rp2350
uint8_t slice8=0,slice9=0,slice10=0,slice11=0;
bool fast_timer_active=false;
volatile uint64_t INT5Count, INT5Value, INT5InitTimer, INT5Timer;
#endif
bool dmarunning=false;
bool ADCDualBuffering=false;
uint32_t ADCmax=0;
int ADCopen=0;
char *ADCInterrupt;
volatile MMFLOAT * volatile a1float=NULL, * volatile a2float=NULL, * volatile a3float=NULL, * volatile a4float=NULL;
volatile int ADCpos=0;
float frequency;
uint32_t ADC_dma_chan=ADC_DMA;
uint32_t ADC_dma_chan2=ADC_DMA2;
short *ADCbuffer=NULL;
void PWMoff(int slice);
volatile uint8_t *adcint=NULL; 
uint8_t *adcint1=NULL; 
uint8_t *adcint2=NULL; 
MMFLOAT ADCscale[4], ADCbottom[4];
extern void mouse0close(void);
//Vector to CFunction routine called every command (ie, from the BASIC interrupt checker)

uint64_t readusclock(void){
    return time_us_64()-uSecoffset;
}
void writeusclock(uint64_t timeset){
  uSecoffset=time_us_64()-(uint64_t)timeset;
}
uint64_t readIRclock(void){
    return time_us_64()-IRoffset;
}
void writeIRclock(uint64_t timeset){
  IRoffset=time_us_64()-(uint64_t)timeset;
}
#ifdef rp2350
const uint8_t PINMAP[48]={1,2,4,5,6,7,9,10,11,12,14,15,16,17,19,20,21,22,24,25,26,27,29,41,42,43,31,32,34,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62};
int codemap(int pin){
#ifdef PICOMITEWEB
    if(pin>29 || pin<0 || pin==23 || pin==24 || pin==25 || pin==29) error("Invalid GPIO");
#else
	if(pin>(rp2350a? 29:47) || pin<0) error("Invalid GPIO");
#endif
	return (int)PINMAP[pin];
}
#else
const uint8_t PINMAP[30]={1,2,4,5,6,7,9,10,11,12,14,15,16,17,19,20,21,22,24,25,26,27,29,41,42,43,31,32,34,44};
int codemap(int pin){
#ifdef PICOMITEWEB
    if(pin>29 || pin<0 || pin==23 || pin==24 || pin==25 || pin==29) error("Invalid GPIO");
#else
	if(pin>29 || pin<0) error("Invalid GPIO");
#endif
	return (int)PINMAP[pin];
}
#endif
int codecheck(unsigned char *line){
	if((line[0]=='G' || line[0]=='g') && (line[1]=='P' || line[1]=='p')){
		line+=2;
		if(isnamestart(*line) || *line=='.') return 1;

		if(isdigit(*line) && !isnamechar(line[1])) {
			return 0;
		}
		line++;
		
		if(!(isdigit(*line))) return 2;
		line++;
		if(isnamechar(*line)) return 3;
	} else return 4;
	return 0;
}
#ifdef rp2350
void __not_in_flash_func(on_pwm_wrap_1)(void) {
    pwm_clear_irq(0);
    INT5Count++;
}

#endif

void SoftReset(void){
    _excep_code = SOFT_RESET;
#ifdef USBKEYBOARD
    USBenabled=false;
    uSec(50000); //wait for outstanding requests to complete
#endif
	watchdog_enable(1, 1);
	while(1);
}
#ifdef rp2350
void __not_in_flash_func(PinSetBit)(int pin, unsigned int offset) {
#else
#if defined(PICOMITEVGA) || defined(PICOMITEWEB)
void PinSetBit(int pin, unsigned int offset) {
#else
void __not_in_flash_func(PinSetBit)(int pin, unsigned int offset) {
#endif
#endif
	switch (offset){
	case LATCLR:
		gpio_set_pulls(PinDef[pin].GPno,false,false);
		gpio_pull_down(PinDef[pin].GPno);
		gpio_put(PinDef[pin].GPno,GPIO_PIN_RESET);
		return;
	case LATSET:
		gpio_set_pulls(PinDef[pin].GPno,false,false);
		gpio_pull_up(PinDef[pin].GPno);
		gpio_put(PinDef[pin].GPno,GPIO_PIN_SET);
		return;
	case LATINV:
        gpio_xor_mask64(1<<PinDef[pin].GPno);
		return;
	case TRISSET:
        gpio_set_dir(PinDef[pin].GPno, GPIO_IN);
	    gpio_set_input_enabled(PinDef[pin].GPno, true);
        uSec(2);
        return;
	case TRISCLR:
        gpio_set_dir(PinDef[pin].GPno, GPIO_OUT);
        gpio_set_input_enabled(PinDef[pin].GPno, false);
        gpio_set_drive_strength (PinDef[pin].GPno, GPIO_DRIVE_STRENGTH_8MA);
        uSec(2);
        return;
	case CNPUSET:
		gpio_set_pulls(PinDef[pin].GPno,true,false);
	    return;
	case CNPDSET:
		gpio_set_pulls(PinDef[pin].GPno,false,true);
	    return;
	case CNPUCLR:
	case CNPDCLR:
		gpio_set_pulls(PinDef[pin].GPno,false,false);
		return;
	case ODCCLR:
        gpio_set_dir(PinDef[pin].GPno, GPIO_OUT);
		gpio_put(PinDef[pin].GPno,GPIO_PIN_RESET);
        uSec(2);
		return;
	case ODCSET:
		gpio_set_pulls(PinDef[pin].GPno,true,false);
        gpio_set_dir(PinDef[pin].GPno, GPIO_IN);
        uSec(2);
		return;
    case ANSELCLR:
        gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_SIO);
        gpio_set_dir(PinDef[pin].GPno, GPIO_IN);
        return;
    case ANSELSET:
        gpio_set_dir(PinDef[pin].GPno, GPIO_IN);
        gpio_disable_pulls(PinDef[pin].GPno);
        gpio_set_input_enabled(PinDef[pin].GPno, false);
        adc_select_input(PinDef[pin].ADCpin);
        return;
	default: error("Unknown PinSetBit command");
	}
}

int IsInvalidPin(int pin) {
#ifdef rp2350
    #ifdef PICOMITEWEB
        if(pin < 1 || pin > NBRPINS) return true;
    #else
        if(pin < 1 || pin > (rp2350a ? 44: NBRPINS)) return true;
    #endif
#else
    if(pin < 1 || pin > NBRPINS) return true;
#endif
    if(PinDef[pin].mode & UNUSED) return true;
    return false;
}
#ifdef PICOMITEWEB
void ExtSet(int pin, int val){
#else
#ifndef rp2350
#ifdef PICOMITEVGA
void ExtSet(int pin, int val){
#else
void __not_in_flash_func(ExtSet)(int pin, int val){
#endif
#else
void __not_in_flash_func(ExtSet)(int pin, int val){
#endif
#endif

    if(ExtCurrentConfig[pin] == EXT_NOT_CONFIG || ExtCurrentConfig[pin] == EXT_DIG_OUT/* || ExtCurrentConfig[pin] == EXT_OC_OUT*/) {
        PinSetBit(pin, val ? LATSET : LATCLR);
        if(ExtCurrentConfig[pin] == EXT_NOT_CONFIG){
            pinmask|=(1<<PinDef[pin].GPno);
            if(val)pinmask|=(1<<PinDef[pin].GPno);
            else pinmask &= (~(1<<PinDef[pin].GPno));
            gpio_set_input_enabled(PinDef[pin].GPno,false);
            last_adc=99;
        }
//        INTEnableInterrupts();
    }
    else if(ExtCurrentConfig[pin] == EXT_CNT_IN){ //allow the user to zero the count
        if(pin == Option.INT1pin) INT1Count=val;
        if(pin == Option.INT2pin) INT2Count=val;
        if(pin == Option.INT3pin) INT3Count=val;
        if(pin == Option.INT4pin) INT4Count=val;
    }
    else
        error("Pin %/| is not an output",pin,pin);
}
/*  @endcond */
#if defined(PICOMITEVGA) && !defined(rp2350)
    void cmd_sync(void){
#else
void __not_in_flash_func(cmd_sync)(void){
#endif
	uint64_t i;
    static uint64_t synctime=0,endtime=0;
	getargs(&cmdline,3,(unsigned char *)",");
	if(synctime && argc==0){
        while(time_us_64()<endtime){
            if(synctime-time_us_64()> 2000)CheckAbort();
        }
        endtime+=synctime;
	} else {
		if(argc==0)error("sync not initialised");
		i=getint(argv[0],0,0x7FFFFFFFFFFFFFFF);
		if(i){
			if(argc==3){
				if(checkstring(argv[2],(unsigned char *)"U")){
					i *= 1;
				} else if(checkstring(argv[2],(unsigned char *)"M")){
					i *= 1000;
				} else if(checkstring(argv[2],(unsigned char *)"S")){
					i *= 1000000;
				}
            }
            synctime=i;
            endtime=time_us_64()+synctime;
		} else {
			synctime=endtime=0;
		}
	}
}


// this is invoked as a command (ie, pin(3) = 1)
// first get the argument then step over the closing bracket.  Search through the rest of the command line looking
// for the equals sign and step over it, evaluate the rest of the command and set the pin accordingly
void cmd_pin(void) {
	int pin, value;
	unsigned char code;
	if(!(code=codecheck(cmdline)))cmdline+=2;
	pin = getinteger(cmdline);
	if(!code)pin=codemap(pin);
	if(IsInvalidPin(pin)) error("Invalid pin");
	while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
	if(!*cmdline) error("Invalid syntax");
	++cmdline;
	if(!*cmdline) error("Invalid syntax");
	value = getinteger(cmdline);
	ExtSet(pin, value);
}
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */
void ClearPin(int pin){
    if(pin==IRpin)IRpin=99;
    if(pin==PWM0Apin)PWM0Apin=99;
    if(pin==PWM1Apin)PWM1Apin=99;
    if(pin==PWM2Apin)PWM2Apin=99;
    if(pin==PWM3Apin)PWM3Apin=99;
    if(pin==PWM4Apin)PWM4Apin=99;
    if(pin==PWM5Apin)PWM5Apin=99;
    if(pin==PWM6Apin)PWM6Apin=99;
    if(pin==PWM7Apin)PWM7Apin=99;
#ifdef rp2350
    if(pin==PWM8Apin)PWM8Apin=99;
    if(pin==PWM9Apin)PWM9Apin=99;
    if(pin==PWM10Apin)PWM10Apin=99;
    if(pin==PWM11Apin)PWM11Apin=99;
#endif
    if(pin==PWM0Bpin)PWM0Bpin=99;
    if(pin==PWM1Bpin)PWM1Bpin=99;
    if(pin==PWM2Bpin)PWM2Bpin=99;
    if(pin==PWM3Bpin)PWM3Bpin=99;
    if(pin==PWM4Bpin)PWM4Bpin=99;
    if(pin==PWM5Bpin)PWM5Bpin=99;
    if(pin==PWM6Bpin)PWM6Bpin=99;
    if(pin==PWM7Bpin)PWM7Bpin=99;
#ifdef rp2350
    if(pin==PWM8Bpin)PWM8Bpin=99;
    if(pin==PWM9Bpin)PWM9Bpin=99;
    if(pin==PWM10Bpin)PWM10Bpin=99;
    if(pin==PWM11Bpin)PWM11Bpin=99;
#endif
    if(pin==UART0TXpin)UART0TXpin=99;
    if(pin==UART0RXpin)UART0RXpin=99;
    if(pin==UART1RXpin)UART1RXpin=99;
    if(pin==UART1TXpin)UART1TXpin=99;
    if(pin==SPI1TXpin)SPI1TXpin=99;
    if(pin==SPI1RXpin)SPI1RXpin=99;
    if(pin==SPI1SCKpin)SPI1SCKpin=99;
    if(pin==SPI0TXpin)SPI0TXpin=99;
    if(pin==SPI0RXpin)SPI0RXpin=99;
    if(pin==SPI0SCKpin)SPI0SCKpin=99;
    if(pin==I2C1SDApin)I2C1SDApin=99;
    if(pin==I2C1SCLpin)I2C1SCLpin=99;
    if(pin==I2C0SDApin)I2C0SDApin=99;
    if(pin==I2C0SCLpin)I2C0SCLpin=99;
}
/****************************************************************************************************************************
Configure an I/O pin
*****************************************************************************************************************************/
void MIPS16 ExtCfg(int pin, int cfg, int option) {
  int i, tris=0, ana=0, edge;

    CheckPin(pin, CP_IGNORE_INUSE | CP_IGNORE_RESERVED);

    if(cfg >= EXT_DS18B20_RESERVED) {
        ExtCurrentConfig[pin] |= cfg;                                // don't do anything except set the config type
        return;
    }
    ClearPin(pin);  //disable the link to any special functions
    if(pin == Option.INT1pin) {
        if(CallBackEnabled==2) gpio_set_irq_enabled_with_callback(PinDef[pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
        else gpio_set_irq_enabled(PinDef[pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        CallBackEnabled &= (~2);
    }
    if(pin == Option.INT2pin) {
        if(CallBackEnabled==4) gpio_set_irq_enabled_with_callback(PinDef[pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
        else gpio_set_irq_enabled(PinDef[pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        CallBackEnabled &= (~4);
    }
    if(pin == Option.INT3pin) {
        if(CallBackEnabled==8) gpio_set_irq_enabled_with_callback(PinDef[pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
        else gpio_set_irq_enabled(PinDef[pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        CallBackEnabled &= (~8);
    }
    if(pin == Option.INT4pin) {
        if(CallBackEnabled==16) gpio_set_irq_enabled_with_callback(PinDef[pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
        else gpio_set_irq_enabled(PinDef[pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        CallBackEnabled &= (~16);
    }
    #ifdef rp2350
    if(pin==FAST_TIMER_PIN && ExtCurrentConfig[pin]==EXT_FAST_TIMER){
        slice0=0;
        pwm_set_irq1_enabled(0, false);
    }
    #endif
 

    // make sure any pullups/pulldowns are removed in case we are changing from a digital input
    gpio_disable_pulls(PinDef[pin].GPno);
    // disable ADC if we are changing from a analogue input
    if(ExtCurrentConfig[pin]==EXT_ANA_IN || ExtCurrentConfig[pin]==EXT_ADCRAW  )PinSetBit(pin, ANSELCLR);

    for(i = 0; i < NBRINTERRUPTS; i++)
        if(inttbl[i].pin == pin)
            inttbl[i].pin = 0;                                      // start off by disable a software interrupt (if set) on this pin
    gpio_set_input_enabled(PinDef[pin].GPno,false);
    gpio_deinit(PinDef[pin].GPno); 
    gpio_set_input_hysteresis_enabled(PinDef[pin].GPno,true);
    if(cfg!=EXT_NOT_CONFIG)gpio_init(PinDef[pin].GPno); 
    switch(cfg) {
        case EXT_NOT_CONFIG:    tris = 1; ana = 1;
//                                gpio_init(PinDef[pin].GPno); 
//		                        gpio_set_input_hysteresis_enabled(PinDef[pin].GPno,true);
                                gpio_set_input_enabled(PinDef[pin].GPno,false);
                                gpio_deinit(PinDef[pin].GPno);
                                switch(ExtCurrentConfig[pin]){      //Disable the pin numbers used by the special function code
                                     case EXT_IR:
				                        IRpin=99;
					                    break;
                                     case EXT_PWM0A:
				                        PWM0Apin=99;
					                    break;
                                     case EXT_PWM1A:
				                        PWM1Apin=99;
					                    break;
                                     case EXT_PWM2A:
				                        PWM2Apin=99;
					                    break;
                                     case EXT_PWM3A:
				                        PWM3Apin=99;
					                    break;
                                     case EXT_PWM4A:
				                        PWM4Apin=99;
					                    break;
                                     case EXT_PWM5A:
				                        PWM5Apin=99;
					                    break;
                                     case EXT_PWM6A:
				                        PWM6Apin=99;
					                    break;
                                     case EXT_PWM7A:
				                        PWM7Apin=99;
					                    break;
#ifdef rp2350
                                     case EXT_PWM8A:
				                        PWM8Apin=99;
					                    break;
                                     case EXT_PWM9A:
				                        PWM9Apin=99;
					                    break;
                                     case EXT_PWM10A:
				                        PWM10Apin=99;
					                    break;
                                     case EXT_PWM11A:
				                        PWM11Apin=99;
					                    break;
#endif
                                     case EXT_PWM0B:
				                        PWM0Bpin=99;
					                    break;
                                     case EXT_PWM1B:
				                        PWM1Bpin=99;
					                    break;
                                     case EXT_PWM2B:
				                        PWM2Bpin=99;
					                    break;
                                     case EXT_PWM3B:
				                        PWM3Bpin=99;
					                    break;
                                     case EXT_PWM4B:
				                        PWM4Bpin=99;
					                    break;
                                     case EXT_PWM5B:
				                        PWM5Bpin=99;
					                    break;
                                     case EXT_PWM6B:
				                        PWM6Bpin=99;
					                    break;
                                     case EXT_PWM7B:
				                        PWM7Bpin=99;
					                    break;
#ifdef rp2350
                                     case EXT_PWM8B:
				                        PWM8Bpin=99;
					                    break;
                                     case EXT_PWM9B:
				                        PWM9Bpin=99;
					                    break;
                                     case EXT_PWM10B:
				                        PWM10Bpin=99;
					                    break;
                                     case EXT_PWM11B:
				                        PWM11Bpin=99;
					                    break;
#endif
                                     case EXT_UART0TX:
				                        UART0TXpin=99;
					                    break;
                                    case EXT_UART0RX:
				                        UART0RXpin=99;
					                    break;
                                    case EXT_UART1TX:
				                        UART1TXpin=99;
					                    break;
                                    case EXT_UART1RX:
				                        UART1RXpin=99;
					                    break;
                                    case EXT_I2C0SDA:
				                        I2C0SDApin=99;
					                    break;
                                    case EXT_I2C0SCL:
				                        I2C0SCLpin=99;
					                    break;
                                    case EXT_I2C1SDA:
				                        I2C1SDApin=99;
					                    break;
                                    case EXT_I2C1SCL:
				                        I2C1SCLpin=99;
					                    break;
                                    case EXT_SPI0RX:
				                        SPI0RXpin=99;
					                    break;
                                    case EXT_SPI0TX:
				                        SPI0TXpin=99;
					                    break;
                                    case EXT_SPI0SCK:
				                        SPI0SCKpin=99;
					                    break;
                                    case EXT_SPI1RX:
				                        SPI1RXpin=99;
					                    break;
                                    case EXT_SPI1TX:
				                        SPI1TXpin=99;
					                    break;
                                    case EXT_SPI1SCK:
				                        SPI1SCKpin=99;
					                    break;
                                }
                                break;

#ifdef rp2350
        case EXT_ADCRAW:
        case EXT_ANA_IN:        if(!(PinDef[pin].mode & ANALOG_IN)) error("Invalid configuration");
                                if(pin<=44 && rp2350a==0) error("Invalid configuration");
                                if(pin> 44 && rp2350a) error("Invalid configuration");
                                tris = 1; ana = 0; 
                                break;
#else
        case EXT_ADCRAW:
        case EXT_ANA_IN:        if(!(PinDef[pin].mode & ANALOG_IN)) error("Invalid configuration");
                                tris = 1; ana = 0; 
                                break;
#endif
        case EXT_CNT_IN:        
        case EXT_FREQ_IN:   // same as counting, so fall through
        case EXT_PER_IN:        // same as counting, so fall through
                                    edge = GPIO_IRQ_EDGE_RISE;
                                    if(cfg==EXT_CNT_IN && option==2)edge = GPIO_IRQ_EDGE_FALL;
                                    if(cfg==EXT_CNT_IN && option>=3)edge = GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE;
                                    if(option==1 || option==4)gpio_pull_down (PinDef[pin].GPno);
                                    if(option==2 || option==5)gpio_pull_up (PinDef[pin].GPno);
                                    irq_set_priority(IO_IRQ_BANK0,0);
                                    PinSetBit(pin,TRISSET);
                                    if(pin == Option.INT1pin) {
                                    if(!CallBackEnabled){
                                        gpio_set_irq_enabled_with_callback(PinDef[pin].GPno, edge , true, &gpio_callback);
                                        CallBackEnabled=2;
                                    } else {
                                        gpio_set_irq_enabled(PinDef[pin].GPno, edge, true);
                                        CallBackEnabled|=2;
                                    }
                                    INT1Count = INT1Value = 0;
                                    INT1Timer = INT1InitTimer = option;  // only used for frequency and period measurement
                                    tris = 1; ana = 1;
		                            gpio_set_input_hysteresis_enabled(PinDef[pin].GPno,true);
                                    break;
                                }
                                if(pin == Option.INT2pin) {
                                    if(!CallBackEnabled){
                                        gpio_set_irq_enabled_with_callback(PinDef[pin].GPno, edge , true, &gpio_callback);
                                        CallBackEnabled=4;
                                    } else {
                                        gpio_set_irq_enabled(PinDef[pin].GPno, edge, true);
                                        CallBackEnabled|=4;
                                    }
                                    INT2Count = INT2Value = 0;
                                    INT2Timer = INT2InitTimer = option;  // only used for frequency and period measurement
                                    tris = 1; ana = 1; 
		                            gpio_set_input_hysteresis_enabled(PinDef[pin].GPno,true);
                                    break;
                                }
                                if(pin == Option.INT3pin) {
                                    if(!CallBackEnabled){
                                        gpio_set_irq_enabled_with_callback(PinDef[pin].GPno, edge , true, &gpio_callback);
                                        CallBackEnabled=8;
                                    } else {
                                        gpio_set_irq_enabled(PinDef[pin].GPno, edge, true);
                                        CallBackEnabled|=8;
                                    }
                                    INT3Count = INT3Value = 0;
                                    INT3Timer = INT3InitTimer = option;  // only used for frequency and period measurement
                                    tris = 1; ana = 1; 
		                            gpio_set_input_hysteresis_enabled(PinDef[pin].GPno,true);
                                    break;
                                }
                                if(pin == Option.INT4pin) {
                                    if(!CallBackEnabled){
                                        gpio_set_irq_enabled_with_callback(PinDef[pin].GPno, edge , true, &gpio_callback);
                                        CallBackEnabled=16;
                                    } else {
                                        gpio_set_irq_enabled(PinDef[pin].GPno, edge, true);
                                        CallBackEnabled|=16;
                                    }
                                    INT4Count = INT4Value = 0;
                                    INT4Timer = INT4InitTimer = option;  // only used for frequency and period measurement
                                    tris = 1; ana = 1; 
		                            gpio_set_input_hysteresis_enabled(PinDef[pin].GPno,true);
                                    break;
                                }
                                error("Invalid configuration");       // not an interrupt enabled pin
                                return;

        case EXT_INT_LO:                                              // same as digital input, so fall through
        case EXT_INT_HI:                                              // same as digital input, so fall through
        case EXT_INT_BOTH:                                            // same as digital input, so fall through
        case EXT_DIG_IN:        if(!(PinDef[pin].mode & DIGITAL_IN)) error("Invalid configuration");
                                if(option) PinSetBit(pin, option);
                                tris = 1; ana = 1; 
		                        gpio_set_input_hysteresis_enabled(PinDef[pin].GPno,true);
                                break;

        case EXT_PIO0_OUT:       
        case EXT_PIO1_OUT:       
#ifdef rp2350
        case EXT_PIO2_OUT:       
#endif
        case EXT_DIG_OUT:       if(!(PinDef[pin].mode & DIGITAL_OUT)) error("Invalid configuration");
                                tris = 0; ana = 1; 
                                gpio_set_drive_strength(PinDef[pin].GPno,GPIO_DRIVE_STRENGTH_8MA);
                                break;
#ifndef PICOMITEWEB
        case EXT_HEARTBEAT:     if(!(pin=HEARTBEATpin)) error("Invalid configuration");
                                tris = 0; ana = 1; 
                                break;
#endif
        case EXT_UART0TX:       if(!(PinDef[pin].mode & UART0TX)) error("Invalid configuration");
                                if((UART0TXpin!=99)) error("Already Set to pin %",UART0TXpin);
                                UART0TXpin=pin;
                                break;
        case EXT_UART0RX:       if(!(PinDef[pin].mode & UART0RX)) error("Invalid configuration");
                                if((UART0RXpin!=99)) error("Already Set to pin %",UART0RXpin);
                                UART0RXpin=pin;
                                break;
        case EXT_UART1TX:       if(!(PinDef[pin].mode & UART1TX)) error("Invalid configuration");
                                if((UART1TXpin!=99)) error("Already Set to pin %",UART1TXpin);
                                UART1TXpin=pin;
                                break;
        case EXT_UART1RX:       if(!(PinDef[pin].mode & UART1RX)) error("Invalid configuration");
                                if((UART1RXpin!=99)) error("Already Set to pin %",UART1RXpin);
                                UART1RXpin=pin;
                                break;
        case EXT_SPI0TX:        if(!(PinDef[pin].mode & SPI0TX)) error("Invalid configuration");
                                if(SPI0locked)error("SPI in use for SYSTEM SPI");
                                if((SPI0TXpin!=99 && SPI0TXpin!=pin)) error("Already Set to pin %",SPI0TXpin);
                                SPI0TXpin=pin;
                                break;
        case EXT_SPI0RX:        if(!(PinDef[pin].mode & SPI0RX)) error("Invalid configuration");
                                if(SPI0locked)error("SPI in use for SYSTEM SPI");
                                if((SPI0RXpin!=99 && SPI0RXpin!=pin)) error("Already Set to pin %",SPI0RXpin);
                                SPI0RXpin=pin;
                                break;
        case EXT_SPI0SCK:       if(!(PinDef[pin].mode & SPI0SCK)) error("Invalid configuration");
                                if(SPI0locked)error("SPI in use for SYSTEM SPI");
                                if((SPI0SCKpin!=99 && SPI0SCKpin!=pin)) error("Already Set to pin %",SPI0SCKpin);
                                SPI0SCKpin=pin;
                                break;
        case EXT_SPI1TX:        if(!(PinDef[pin].mode & SPI1TX)) error("Invalid configuration");
                                if(SPI1locked)error("SPI2 in use for SYSTEM SPI");
                                if((SPI1TXpin!=99 && SPI1TXpin!=pin)) error("Already Set to pin %",SPI1TXpin);
                                SPI1TXpin=pin;
                                break;
        case EXT_SPI1RX:        if(!(PinDef[pin].mode & SPI1RX)) error("Invalid configuration");
                                if(SPI1locked)error("SPI2 in use for SYSTEM SPI");
                                if((SPI1RXpin!=99 && SPI1RXpin!=pin)) error("Already Set to pin %",SPI1RXpin);
                                SPI1RXpin=pin;
                                break;
        case EXT_SPI1SCK:       if(!(PinDef[pin].mode & SPI1SCK)) error("Invalid configuration");
                                if(SPI1locked)error("SPI2 in use for SYSTEM SPI");
                                if((SPI1SCKpin!=99 && SPI1SCKpin!=pin)) error("Already Set to pin %",SPI1SCKpin);
                                SPI1SCKpin=pin;
                                break;
        case EXT_IR:            if((IRpin!=99)) error("Already Set to pin %",IRpin);
                                IRpin=pin;
                                break;
        case EXT_PWM0A:         if(!(PinDef[pin].mode & PWM0A)) error("Invalid configuration");
                                if((PWM0Apin!=99 && PWM0Apin!=pin)) error("Already Set to pin %",PWM0Apin);
                                PWM0Apin=pin;
                                break;
        case EXT_PWM1A:         if(!(PinDef[pin].mode & PWM1A)) error("Invalid configuration");
                                if((PWM1Apin!=99 && PWM1Apin!=pin)) error("Already Set to pin %",PWM1Apin);
                                PWM1Apin=pin;
                                break;
        case EXT_PWM2A:         if(!(PinDef[pin].mode & PWM2A)) error("Invalid configuration");
                                if((PWM2Apin!=99 && PWM2Apin!=pin)) error("Already Set to pin %",PWM2Apin);
                                PWM2Apin=pin;
                                gpio_set_drive_strength (PinDef[pin].GPno, GPIO_DRIVE_STRENGTH_8MA);
                                gpio_set_slew_rate(PinDef[pin].GPno,GPIO_SLEW_RATE_FAST);
                                break;
        case EXT_PWM3A:         if(!(PinDef[pin].mode & PWM3A)) error("Invalid configuration");
                                if((PWM3Apin!=99 && PWM3Apin!=pin)) error("Already Set to pin %",PWM3Apin);
                                PWM3Apin=pin;
                                break;
        case EXT_PWM4A:         if(!(PinDef[pin].mode & PWM4A)) error("Invalid configuration");
                                if((PWM4Apin!=99 && PWM4Apin!=pin)) error("Already Set to pin %",PWM4Apin);
                                PWM4Apin=pin;
                                break;
        case EXT_PWM5A:         if(!(PinDef[pin].mode & PWM5A)) error("Invalid configuration");
                                if((PWM5Apin!=99 && PWM5Apin!=pin)) error("Already Set to pin %",PWM5Apin);
                                PWM5Apin=pin;
                                break;
        case EXT_PWM6A:         if(!(PinDef[pin].mode & PWM6A)) error("Invalid configuration");
                                if((PWM6Apin!=99 && PWM6Apin!=pin)) error("Already Set to pin %",PWM6Apin);
                                PWM6Apin=pin;
                                break;
        case EXT_PWM7A:         if(!(PinDef[pin].mode & PWM7A)) error("Invalid configuration");
                                if((PWM7Apin!=99 && PWM7Apin!=pin)) error("Already Set to pin %",PWM7Apin);
                                PWM7Apin=pin;
                                break;
#ifdef rp2350
        case EXT_PWM8A:         if(!(PinDef[pin].mode & PWM8A) || rp2350a) error("Invalid configuration");
                                if((PWM8Apin!=99 && PWM8Apin!=pin)) error("Already Set to pin %",PWM8Apin);
                                PWM8Apin=pin;
                                break;
        case EXT_PWM9A:         if(!(PinDef[pin].mode & PWM9A) || rp2350a) error("Invalid configuration");
                                if((PWM9Apin!=99 && PWM9Apin!=pin)) error("Already Set to pin %",PWM9Apin);
                                PWM9Apin=pin;
                                break;
        case EXT_PWM10A:         if(!(PinDef[pin].mode & PWM10A) || rp2350a) error("Invalid configuration");
                                if((PWM10Apin!=99 && PWM10Apin!=pin)) error("Already Set to pin %",PWM10Apin);
                                PWM10Apin=pin;
                                break;
        case EXT_PWM11A:         if(!(PinDef[pin].mode & PWM11A) || rp2350a) error("Invalid configuration");
                                if((PWM11Apin!=99 && PWM11Apin!=pin)) error("Already Set to pin %",PWM11Apin);
                                PWM11Apin=pin;
                                break;
#endif
        case EXT_PWM0B:         if(!(PinDef[pin].mode & PWM0B)) error("Invalid configuration");
                                if((PWM0Bpin!=99 && PWM0Bpin!=pin)) error("Already Set to pin %",PWM0Bpin);
                                PWM0Bpin=pin;
                                break;
        case EXT_PWM1B:         if(!(PinDef[pin].mode & PWM1B)) error("Invalid configuration");
                                if((PWM1Bpin!=99 && PWM1Bpin!=pin)) error("Already Set to pin %",PWM1Bpin);
                                PWM1Bpin=pin;
                                break;
        case EXT_PWM2B:         if(!(PinDef[pin].mode & PWM2B)) error("Invalid configuration");
                                if((PWM2Bpin!=99 && PWM2Bpin!=pin)) error("Already Set to pin %",PWM2Bpin);
                                PWM2Bpin=pin;
                                break;
        case EXT_PWM3B:         if(!(PinDef[pin].mode & PWM3B)) error("Invalid configuration");
                                if((PWM3Bpin!=99 && PWM3Bpin!=pin)) error("Already Set to pin %",PWM3Bpin);
                                PWM3Bpin=pin;
                                break;
        case EXT_PWM4B:         if(!(PinDef[pin].mode & PWM4B)) error("Invalid configuration");
                                if((PWM4Bpin!=99 && PWM4Bpin!=pin)) error("Already Set to pin %",PWM4Bpin);
                                PWM4Bpin=pin;
                                break;
        case EXT_PWM5B:         if(!(PinDef[pin].mode & PWM5B)) error("Invalid configuration");
                                if((PWM5Bpin!=99 && PWM5Bpin!=pin)) error("Already Set to pin %",PWM5Bpin);
                                PWM5Bpin=pin;
                                break;
        case EXT_PWM6B:         if(!(PinDef[pin].mode & PWM6B)) error("Invalid configuration");
                                if((PWM6Bpin!=99 && PWM6Bpin!=pin)) error("Already Set to pin %",PWM6Bpin);
                                PWM6Bpin=pin;
                                break;
        case EXT_PWM7B:         if(!(PinDef[pin].mode & PWM7B)) error("Invalid configuration");
                                if((PWM7Bpin!=99 && PWM7Bpin!=pin)) error("Already Set to pin %",PWM7Bpin);
                                PWM7Bpin=pin;
                                break;
#ifdef rp2350
#ifndef PICOMITEWEB
        case EXT_PWM8B:         if(!(PinDef[pin].mode & PWM8B) || rp2350a) error("Invalid configuration");
                                if((PWM8Bpin!=99 && PWM8Bpin!=pin)) error("Blready Set to pin %",PWM8Bpin);
                                PWM8Bpin=pin;
                                break;
        case EXT_PWM9B:         if(!(PinDef[pin].mode & PWM9B) || rp2350a) error("Invalid configuration");
                                if((PWM9Bpin!=99 && PWM9Bpin!=pin)) error("Blready Set to pin %",PWM9Bpin);
                                PWM9Bpin=pin;
                                break;
        case EXT_PWM10B:         if(!(PinDef[pin].mode & PWM10B) || rp2350a) error("Invalid configuration");
                                if((PWM10Bpin!=99 && PWM10Bpin!=pin)) error("Blready Set to pin %",PWM10Bpin);
                                PWM10Bpin=pin;
                                break;
        case EXT_PWM11B:         if(!(PinDef[pin].mode & PWM11B) || rp2350a) error("Invalid configuration");
                                if((PWM11Bpin!=99 && PWM11Bpin!=pin)) error("Blready Set to pin %",PWM11Bpin);
                                PWM11Bpin=pin;
                                break;
#endif
        case EXT_FAST_TIMER:    if(!(PinDef[pin].mode & PWM0B)) error("Invalid configuration");
                                if((PWM0Bpin!=99 && PWM0Bpin!=pin)) error("Already Set to pin %",PWM0Bpin);
                                PWM0Bpin=pin;
                                INT5Count = INT5Value = 0;
                                INT5Timer = INT5InitTimer = option;  // only used for frequency and period measurement
                                tris = 1; ana = 1;
//                                PinSetBit(pin,TRISSET);
                                gpio_set_input_hysteresis_enabled(PinDef[pin].GPno,true);
                                gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_PWM);
                                pwm_config cfg = pwm_get_default_config();
                                pwm_config_set_clkdiv_mode(&cfg, PWM_DIV_B_RISING);
                                pwm_config_set_clkdiv(&cfg, 1);
                                pwm_init(0, &cfg, false);
                                pwm_set_wrap(0, 49999);
                                pwm_clear_irq(0);
                                irq_set_exclusive_handler(PWM_IRQ_WRAP_1, on_pwm_wrap_1);
                                irq_set_enabled(PWM_IRQ_WRAP_1, true);
                                irq_set_priority(PWM_IRQ_WRAP_1,0);
	                            pwm_set_irq1_enabled(0, true);
                                pwm_set_enabled(0, true); 
                                break;
#endif
        case EXT_I2C0SDA:       if(!(PinDef[pin].mode & I2C0SDA)) error("Invalid configuration");
                                if(I2C0locked)error("I2C in use for SYSTEM I2C");
                                if((I2C0SDApin!=99 && I2C0SDApin!=pin)) error("Already Set to pin %",I2C0SDApin);
                                I2C0SDApin=pin;
                                break;
        case EXT_I2C0SCL:       if(!(PinDef[pin].mode & I2C0SCL)) error("Invalid configuration");
                                if(I2C0locked)error("I2C in use for SYSTEM I2C");
                                 if((I2C0SCLpin!=99 && I2C0SCLpin!=pin)) error("Already Set to pin %",I2C0SCLpin);
                                I2C0SCLpin=pin;
                                break;
        case EXT_I2C1SDA:       if(!(PinDef[pin].mode & I2C1SDA)) error("Invalid configuration");
                                if(I2C1locked)error("I2C2 in use for SYSTEM I2C");
                                 if((I2C1SDApin!=99) && I2C1SDApin!=pin) error("Already Set to pin %",I2C1SDApin);
                                I2C1SDApin=pin;
                                break;
        case EXT_I2C1SCL:       if(!(PinDef[pin].mode & I2C1SCL)) error("Invalid configuration");
                                if(I2C1locked)error("I2C2 in use for SYSTEM I2C");
                                 if((I2C1SCLpin!=99 && I2C1SCLpin!=pin)) error("Already Set to pin %",I2C1SCLpin);
                                I2C1SCLpin=pin;
                                break;
        default:                error("Invalid configuration");
                              return;
  }
    ExtCurrentConfig[pin] = cfg;
    if(cfg<=EXT_INT_BOTH || cfg==EXT_ADCRAW){
        //    *GetPortAddr(pin, ana ? ANSELCLR : ANSELSET) = (1 << GetPinBit(pin));// if ana = 1 then it is a digital I/O
        PinSetBit(pin, tris ? TRISSET : TRISCLR);                         // if tris = 1 then it is an input
        if(!tris && (pinmask & (1<<PinDef[pin].GPno))){
            gpio_put(PinDef[pin].GPno,GPIO_PIN_SET);
        }
        pinmask &= (~(1<<PinDef[pin].GPno));
        if(cfg == EXT_NOT_CONFIG) ExtSet(pin, 0);                         // set the default output to low
        if(ana==0)PinSetBit(pin, ANSELSET);
    }
    else if(cfg>=EXT_UART0TX && cfg<=EXT_UART1RX){
        gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_UART);
        if(cfg==EXT_UART0RX || cfg==EXT_UART1RX)gpio_set_pulls(PinDef[pin].GPno,true,false);
    }
    else if(cfg>=EXT_I2C0SDA && cfg<=EXT_I2C1SCL)gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_I2C);
    else if(cfg>=EXT_SPI0RX && cfg<=EXT_SPI1SCK)gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_SPI);
#ifdef rp2350
    else if(cfg>=EXT_PWM0A && cfg<=(rp2350a ? EXT_PWM7B : EXT_PWM11B))gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_PWM);
#else
    else if(cfg>=EXT_PWM0A && cfg<=EXT_PWM7B)gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_PWM);
#endif
    else if(cfg==EXT_PIO0_OUT){
	    gpio_set_input_enabled(PinDef[pin].GPno, true);
        gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_PIO0);
    }
    else if(cfg==EXT_PIO1_OUT){
	    gpio_set_input_enabled(PinDef[pin].GPno, true);
        gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_PIO1);
    }
#ifdef rp2350
    else if(cfg==EXT_PIO2_OUT){
	    gpio_set_input_enabled(PinDef[pin].GPno, true);
        gpio_set_function(PinDef[pin].GPno, GPIO_FUNC_PIO2);
    }
#endif
    uSec(2);
}
extern int adc_clk_div;
#ifndef rp2350
#ifdef PICOMITEVGA
int64_t ExtInp(int pin){
#else
int64_t __not_in_flash_func(ExtInp)(int pin){
#endif
#else
int64_t __not_in_flash_func(ExtInp)(int pin){
#endif
    if(ExtCurrentConfig[pin]==EXT_ANA_IN || ExtCurrentConfig[pin]==EXT_ADCRAW){
        if(adc_clk_div!=adc_hw->div){
            SetADCFreq(500000.0);
        }

        if(last_adc!=pin){
            last_adc=pin;
            adc_select_input(PinDef[pin].ADCpin);
        }
        int a= adc_read();   
        if(adc_hw->cs & (ADC_CS_ERR_STICKY_BITS | ADC_CS_ERR_BITS)) {
            hw_set_bits(&adc_hw->cs, ADC_CS_ERR_STICKY_BITS);
            a=-1;
        }
        return a;      
    } else if(ExtCurrentConfig[pin] == EXT_FREQ_IN || ExtCurrentConfig[pin] == EXT_PER_IN) {
      // select input channel
        if(pin == Option.INT1pin) return INT1Value;
        if(pin == Option.INT2pin) return INT2Value;
        if(pin == Option.INT3pin) return INT3Value;
        if(pin == Option.INT4pin) return INT4Value;
    }  else if(ExtCurrentConfig[pin] == EXT_CNT_IN) {
        // select input channel
            if(pin == Option.INT1pin) return INT1Count;
            if(pin == Option.INT2pin) return INT2Count;
            if(pin == Option.INT3pin) return INT3Count;
            if(pin == Option.INT4pin) return INT4Count;
    }  else if(ExtCurrentConfig[pin] == EXT_DIG_OUT) {
        return gpio_get_out_level(PinDef[pin].GPno);
    }  else {
        return  gpio_get(PinDef[pin].GPno);
    }
    return 0;
}
/*  @endcond */
void MIPS16 cmd_setpin(void) {
	int i, pin, pin2=0, pin3=0, value=-1, value2=0, value3=0, option = 0;
	getargs(&cmdline, 7, (unsigned char *)",");
	if(argc%2 == 0 || argc < 3) error("Argument count");
	char code;
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	pin = getinteger(argv[0]);
	if(!code)pin=codemap(pin);

    if(checkstring(argv[2], (unsigned char *)"OFF") || checkstring(argv[2], (unsigned char *)"0"))
        value = EXT_NOT_CONFIG;
    else if(checkstring(argv[2], (unsigned char *)"AIN"))
        value = EXT_ANA_IN;
    else if(checkstring(argv[2], (unsigned char *)"ARAW"))
        value = EXT_ADCRAW;
    else if(checkstring(argv[2], (unsigned char *)"DIN"))
        value = EXT_DIG_IN;
    else if(checkstring(argv[2], (unsigned char *)"FIN"))
        value = EXT_FREQ_IN;
    else if(checkstring(argv[2], (unsigned char *)"PIN"))
        value = EXT_PER_IN;
    else if(checkstring(argv[2], (unsigned char *)"CIN"))
        value = EXT_CNT_IN;
    else if(checkstring(argv[2], (unsigned char *)"INTH"))
        value = EXT_INT_HI;
    else if(checkstring(argv[2], (unsigned char *)"INTL"))
        value = EXT_INT_LO;
    else if(checkstring(argv[2], (unsigned char *)"DOUT"))
        value = EXT_DIG_OUT;
    else if(checkstring(argv[2], (unsigned char *)"HEARTBEAT"))
        value = EXT_HEARTBEAT;
    else if(checkstring(argv[2], (unsigned char *)"INTB"))
        value = EXT_INT_BOTH;
    else if(checkstring(argv[2], (unsigned char *)"IR"))
        value = EXT_IR;
    else if(checkstring(argv[2], (unsigned char *)"PWM0A"))
        value = EXT_PWM0A;
    else if(checkstring(argv[2], (unsigned char *)"PWM1A"))
        value = EXT_PWM1A;
    else if(checkstring(argv[2], (unsigned char *)"PWM2A"))
        value = EXT_PWM2A;
    else if(checkstring(argv[2], (unsigned char *)"PWM3A"))
        value = EXT_PWM3A;
    else if(checkstring(argv[2], (unsigned char *)"PWM4A"))
        value = EXT_PWM4A;
    else if(checkstring(argv[2], (unsigned char *)"PWM5A"))
        value = EXT_PWM5A;
    else if(checkstring(argv[2], (unsigned char *)"PWM6A"))
        value = EXT_PWM6A;
    else if(checkstring(argv[2], (unsigned char *)"PWM7A"))
        value = EXT_PWM7A;
#ifdef rp2350
    else if(checkstring(argv[2], (unsigned char *)"FFIN"))
        value = EXT_FAST_TIMER;
#endif
#ifdef rp2350
    else if(checkstring(argv[2], (unsigned char *)"PWM8A"))
        value = EXT_PWM8A;
    else if(checkstring(argv[2], (unsigned char *)"PWM9A"))
        value = EXT_PWM9A;
    else if(checkstring(argv[2], (unsigned char *)"PWM10A"))
        value = EXT_PWM10A;
    else if(checkstring(argv[2], (unsigned char *)"PWM711"))
        value = EXT_PWM11A;
#endif
    else if(checkstring(argv[2], (unsigned char *)"PWM0B"))
        value = EXT_PWM0B;
    else if(checkstring(argv[2], (unsigned char *)"PWM1B"))
        value = EXT_PWM1B;
    else if(checkstring(argv[2], (unsigned char *)"PWM2B"))
        value = EXT_PWM2B;
    else if(checkstring(argv[2], (unsigned char *)"PWM3B"))
        value = EXT_PWM3B;
    else if(checkstring(argv[2], (unsigned char *)"PWM4B"))
        value = EXT_PWM4B;
    else if(checkstring(argv[2], (unsigned char *)"PWM5B"))
        value = EXT_PWM5B;
    else if(checkstring(argv[2], (unsigned char *)"PWM6B"))
        value = EXT_PWM6B;
    else if(checkstring(argv[2], (unsigned char *)"PWM7B"))
        value = EXT_PWM7B;
#ifdef rp2350
    else if(checkstring(argv[2], (unsigned char *)"PWM8B"))
        value = EXT_PWM8B;
    else if(checkstring(argv[2], (unsigned char *)"PWM9B"))
        value = EXT_PWM9B;
    else if(checkstring(argv[2], (unsigned char *)"PWM10B"))
        value = EXT_PWM10B;
    else if(checkstring(argv[2], (unsigned char *)"PWM11B"))
        value = EXT_PWM11B;
#endif
    else if(checkstring(argv[2], (unsigned char *)"PIO0"))
        value = EXT_PIO0_OUT;
    else if(checkstring(argv[2], (unsigned char *)"PIO1"))
        value = EXT_PIO1_OUT;
#ifdef rp2350
    else if(checkstring(argv[2], (unsigned char *)"PIO2"))
        value = EXT_PIO2_OUT;
#endif
    else if(checkstring(argv[2],(unsigned char *)"PWM")){
        if(PinDef[pin].mode & PWM0A)value = EXT_PWM0A;
        else if(PinDef[pin].mode & PWM0B)value = EXT_PWM0B;
        else if(PinDef[pin].mode & PWM1A)value = EXT_PWM1A;
        else if(PinDef[pin].mode & PWM1B)value = EXT_PWM1B;
        else if(PinDef[pin].mode & PWM2A)value = EXT_PWM2A;
        else if(PinDef[pin].mode & PWM2B)value = EXT_PWM2B;
        else if(PinDef[pin].mode & PWM3A)value = EXT_PWM3A;
        else if(PinDef[pin].mode & PWM3B)value = EXT_PWM3B;
        else if(PinDef[pin].mode & PWM4A)value = EXT_PWM4A;
        else if(PinDef[pin].mode & PWM4B)value = EXT_PWM4B;
        else if(PinDef[pin].mode & PWM5A)value = EXT_PWM5A;
        else if(PinDef[pin].mode & PWM5B)value = EXT_PWM5B;
        else if(PinDef[pin].mode & PWM6A)value = EXT_PWM6A;
        else if(PinDef[pin].mode & PWM6B)value = EXT_PWM6B;
        else if(PinDef[pin].mode & PWM7A)value = EXT_PWM7A;
        else if(PinDef[pin].mode & PWM7B)value = EXT_PWM7B;
#ifdef rp2350
        else if(PinDef[pin].mode & PWM8A)value = EXT_PWM8A;
        else if(PinDef[pin].mode & PWM8B)value = EXT_PWM8B;
        else if(PinDef[pin].mode & PWM9A)value = EXT_PWM9A;
        else if(PinDef[pin].mode & PWM9B)value = EXT_PWM9B;
        else if(PinDef[pin].mode & PWM10A)value = EXT_PWM10A;
        else if(PinDef[pin].mode & PWM10B)value = EXT_PWM10B;
        else if(PinDef[pin].mode & PWM11A)value = EXT_PWM11A;
        else if(PinDef[pin].mode & PWM11B)value = EXT_PWM11B;
#endif
        else error("Invalid configuration");
    }  else if(checkstring(argv[2],(unsigned char *)"INT")){
        if(pin==Option.INT1pin)value = EXT_INT1;
        else if(pin==Option.INT2pin)value = EXT_INT2;
        else if(pin==Option.INT3pin)value = EXT_INT3;
        else if(pin==Option.INT4pin)value = EXT_INT4;
        else error("Invalid configuration");
    }
    if(value!=-1)goto process;
    if(argc<5)error("Syntax"); 
    if(checkstring(argv[4],(unsigned char *)"COM1")){
        if(!(code=codecheck(argv[2])))argv[2]+=2;
        pin2 = getinteger(argv[2]);
        if(!code)pin2=codemap(pin2);
        if(PinDef[pin].mode & UART0TX)value = EXT_UART0TX;
        else if(PinDef[pin].mode & UART0RX)value = EXT_UART0RX;
        else error("Invalid configuration");
        if(PinDef[pin2].mode & UART0TX)value2 = EXT_UART0TX;
        else if(PinDef[pin2].mode & UART0RX)value2 = EXT_UART0RX;
        else error("Invalid configuration");
        if(value==value2)error("Invalid configuration");
    } else if(checkstring(argv[4],(unsigned char *)"COM2")){
        if(!(code=codecheck(argv[2])))argv[2]+=2;
        pin2 = getinteger(argv[2]);
        if(!code)pin2=codemap(pin2);
        if(PinDef[pin].mode & UART1TX)value = EXT_UART1TX;
        else if(PinDef[pin].mode & UART1RX)value = EXT_UART1RX;
        else error("Invalid configuration");
        if(PinDef[pin2].mode & UART1TX)value2 = EXT_UART1TX;
        else if(PinDef[pin2].mode & UART1RX)value2 = EXT_UART1RX;
        else error("Invalid configuration");
        if(value==value2)error("Invalid configuration");
    }  else if(checkstring(argv[4],(unsigned char *)"I2C")){
        if(!(code=codecheck(argv[2])))argv[2]+=2;
        pin2 = getinteger(argv[2]);
        if(!code)pin2=codemap(pin2);
        if(PinDef[pin].mode & I2C0SCL)value = EXT_I2C0SCL;
        else if(PinDef[pin].mode & I2C0SDA)value = EXT_I2C0SDA;
        else error("Invalid configuration");
        if(PinDef[pin2].mode & I2C0SCL)value2 = EXT_I2C0SCL;
        else if(PinDef[pin2].mode & I2C0SDA)value2 = EXT_I2C0SDA;
        else error("Invalid configuration");
        if(value==value2)error("Invalid configuration");
    }  else if(checkstring(argv[4],(unsigned char *)"I2C2")){
        if(!(code=codecheck(argv[2])))argv[2]+=2;
        pin2 = getinteger(argv[2]);
        if(!code)pin2=codemap(pin2);
        if(PinDef[pin].mode & I2C1SCL)value = EXT_I2C1SCL;
        else if(PinDef[pin].mode & I2C1SDA)value = EXT_I2C1SDA;
        else error("Invalid configuration");
        if(PinDef[pin2].mode & I2C1SCL)value2 = EXT_I2C1SCL;
        else if(PinDef[pin2].mode & I2C1SDA)value2 = EXT_I2C1SDA;
        else error("Invalid configuration");
        if(value==value2)error("Invalid configuration");
    }  
    if(value!=-1)goto process;
    if(argc<7)error("Syntax"); 
    if(checkstring(argv[6],(unsigned char *)"SPI")){
        if(!(code=codecheck(argv[2])))argv[2]+=2;
        pin2 = getinteger(argv[2]);
        if(!code)pin2=codemap(pin2);
        if(!(code=codecheck(argv[4])))argv[4]+=2;
        pin3 = getinteger(argv[4]);
        if(!code)pin3=codemap(pin3);
        if(PinDef[pin].mode & SPI0RX)value = EXT_SPI0RX;
        else if(PinDef[pin].mode & SPI0TX)value = EXT_SPI0TX;
        else if(PinDef[pin].mode & SPI0SCK)value = EXT_SPI0SCK;
        else error("Invalid configuration");
        if(PinDef[pin2].mode & SPI0RX)value2 = EXT_SPI0RX;
        else if(PinDef[pin2].mode & SPI0TX)value2 = EXT_SPI0TX;
        else if(PinDef[pin2].mode & SPI0SCK)value2 = EXT_SPI0SCK;
        else error("Invalid configuration");
        if(PinDef[pin3].mode & SPI0RX)value3 = EXT_SPI0RX;
        else if(PinDef[pin3].mode & SPI0TX)value3 = EXT_SPI0TX;
        else if(PinDef[pin3].mode & SPI0SCK)value3 = EXT_SPI0SCK;
        else error("Invalid configuration");
        if(value==value2 || value==value3 || value2==value3)error("Invalid configuration");
    }  else if(checkstring(argv[6],(unsigned char *)"SPI2")){
        if(!(code=codecheck(argv[2])))argv[2]+=2;
        pin2 = getinteger(argv[2]);
        if(!code)pin2=codemap(pin2);
        if(!(code=codecheck(argv[4])))argv[4]+=2;
        pin3 = getinteger(argv[4]);
        if(!code)pin3=codemap(pin3);
        if(PinDef[pin].mode & SPI1RX)value = EXT_SPI1RX;
        else if(PinDef[pin].mode & SPI1TX)value = EXT_SPI1TX;
        else if(PinDef[pin].mode & SPI1SCK)value = EXT_SPI1SCK;
        else error("Invalid configuration");
        if(PinDef[pin2].mode & SPI1RX)value2 = EXT_SPI1RX;
        else if(PinDef[pin2].mode & SPI1TX)value2 = EXT_SPI1TX;
        else if(PinDef[pin2].mode & SPI1SCK)value2 = EXT_SPI1SCK;
        else error("Invalid configuration");
        if(PinDef[pin3].mode & SPI1RX)value3 = EXT_SPI1RX;
        else if(PinDef[pin3].mode & SPI1TX)value3 = EXT_SPI1TX;
        else if(PinDef[pin3].mode & SPI1SCK)value3 = EXT_SPI1SCK;
        else error("Invalid configuration");
        if(value==value2 || value==value3 || value2==value3)error("Invalid configuration");
    } else error("Syntax");
//        value = getint(argv[2], 1, 9);
process:
    // check for any options
    switch(value) {
    	case EXT_ANA_IN:
        case EXT_ADCRAW: if(argc == 5) {
    						option = getint((argv[4]), 8, 12);
    						if(option & 1)error("Invalid bit count");
        					} else
        					option = 12;
        break;

        case EXT_DIG_IN:    if(argc == 5) {
                                if(checkstring(argv[4], (unsigned char *)"PULLUP")) option = CNPUSET;
                                else if(checkstring(argv[4], (unsigned char *)"PULLDOWN")) {
#ifdef rp2350
                                    PinSetBit(pin,TRISCLR);
                                    PinSetBit(pin,LATCLR);
                                    PinSetBit(pin,TRISSET);
#endif
                                    option = CNPDSET;
                                }
                                else error("Invalid configuration");
                            } else
                                option = 0;
                            break;
        case EXT_INT_HI:
        case EXT_INT_LO:
        case EXT_INT_BOTH:  if(argc == 7) {
                                if(checkstring(argv[6], (unsigned char *)"PULLUP")) option = CNPUSET;
                                else if(checkstring(argv[6], (unsigned char *)"PULLDOWN")) {
#ifdef rp2350
                                    PinSetBit(pin,TRISCLR);
                                    PinSetBit(pin,LATCLR);
                                    PinSetBit(pin,TRISSET);
#endif
                                    option = CNPDSET;
                                }
                                else error("Invalid configuration");
                            } else
                                option = 0;
                            break;
        case EXT_FREQ_IN:   if(argc == 5)
                                option = getint((argv[4]), 1, 100000);
                            else
                                option = 1000;
                            break;
        case EXT_PER_IN:   if(argc == 5)
                                option = getint((argv[4]), 1, 10000);
                            else
                                option = 1;
                            break;
        case EXT_CNT_IN:   if(argc == 5)
                                option = getint((argv[4]), 1, 10);
                            else
                                option = 1;
                            break;
#ifdef rp2350
        case EXT_FAST_TIMER:    if(pin!=FAST_TIMER_PIN)error("Use pin2/GP1 for fast counter");
                            if(BacklightSlice==0)error("Channel in use for backlight");
                            if(Option.AUDIO_SLICE==0)error("Channel in use for Audio");
                            if(CameraSlice==0)error("Channel in use for Camera");
                            if(argc == 5)
                                option = getint((argv[4]), 1, 100000);
                            else
                                option = 1000;
                            break;
#endif
        case EXT_DIG_OUT:
        case EXT_HEARTBEAT:   
                            option=0;
        default:            if(argc > 3 && !value2) error("Unexpected text");
    }
    // this allows the user to set a software interrupt on the touch IRQ pin if the GUI environment is not enabled
    if(pin == Option.TOUCH_IRQ)
    #ifdef GUICONTROLS
    if(Option.MaxCtrls==0) 
    #endif
    {
        if(value == EXT_INT_HI || value == EXT_INT_LO || value == EXT_INT_BOTH)
            ExtCurrentConfig[pin] = value;
        else if(value == EXT_NOT_CONFIG) {
            ExtCurrentConfig[pin] = EXT_BOOT_RESERVED;
            for(i = 0; i < NBRINTERRUPTS; i++)
                if(inttbl[i].pin == pin)
                    inttbl[i].pin = 0;                              // disable the software interrupt on this pin
        }
        else
            error("Pin %/| is reserved on startup", pin,pin);
    } 

    CheckPin(pin, CP_IGNORE_INUSE);
    ExtCfg(pin, value, option);

    if(value2)    {
        CheckPin(pin2, CP_IGNORE_INUSE);
        ExtCfg(pin2, value2, option);
    }
    if(value3)    {
        CheckPin(pin3, CP_IGNORE_INUSE);
        ExtCfg(pin3, value3, option);
    }


	if(value == EXT_INT_HI || value == EXT_INT_LO || value == EXT_INT_BOTH) {
		// we need to set up a software interrupt
		if(argc < 5) error("Argument count");
        for(i = 0; i < NBRINTERRUPTS; i++) if(inttbl[i].pin == 0) break;
        if(i >= NBRINTERRUPTS) error("Too many interrupts");
        inttbl[i].pin = pin;
		inttbl[i].intp = (char *)GetIntAddress(argv[4]);					// get the interrupt routine's location
		inttbl[i].last = ExtInp(pin);								// save the current pin value for the first test
        switch(value) {                                             // and set trigger polarity
            case EXT_INT_HI:    inttbl[i].lohi = T_LOHI; break;
            case EXT_INT_LO:    inttbl[i].lohi = T_HILO; break;
            case EXT_INT_BOTH:  inttbl[i].lohi = T_BOTH; break;
        }
		InterruptUsed = true;
	}
}
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */
bool __no_inline_not_in_flash_func(bb_get_bootsel_button)() {
    const uint CS_PIN_INDEX = 1;
    disable_interrupts_pico();
    hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
                    GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
                    IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
    for (volatile int i = 0; i < 100; ++i);
    bool button_state = !(sio_hw->gpio_hi_in & (1u << CS_PIN_INDEX));
    hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
                    GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
                    IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
    enable_interrupts_pico();

    return button_state;
}
/*  @endcond */

void fun_pin(void) {
	char code;
    int pin, i, j, b[ANA_AVERAGE];
    MMFLOAT t;
	if(checkstring(ep, (unsigned char *)"TEMP")){
        if(ADCDualBuffering || dmarunning)error("ADC in use");
        adc_init();
        adc_set_temp_sensor_enabled(true);
#ifdef rp2350
        adc_select_input((rp2350a ? 4 : 8));
#else
        adc_select_input(4);
#endif
        last_adc=4;
        t=(MMFLOAT)adc_read()/4095.0*VCC;
        fret=(27.0-(t-0.706)/0.001721);
        targ=T_NBR;
        return;
    }
    if(checkstring(ep, (unsigned char *)"BOOTSEL")){
        iret=bb_get_bootsel_button();
        targ=T_INT;
        return;
    }

	if(!(code=codecheck(ep)))ep+=2;
	pin = getinteger(ep);
	if(!code)pin=codemap(pin);
    if(IsInvalidPin(pin)) error("Invalid pin");
    switch(ExtCurrentConfig[pin]) {
        case EXT_DIG_IN:
        case EXT_CNT_IN:
        case EXT_INT_HI:
        case EXT_INT_LO:
        case EXT_INT_BOTH:
        case EXT_DIG_OUT:
        case EXT_PIO0_OUT:
        case EXT_PIO1_OUT:
#ifdef rp2350
        case EXT_PIO2_OUT:
#endif
                            iret = ExtInp(pin);
                            targ = T_INT;
                            return;
#ifdef rp2350
        case EXT_FAST_TIMER:
                            fret = (MMFLOAT)INT5Value  * (MMFLOAT)1000.0 / (MMFLOAT)INT5InitTimer;
                            targ = T_NBR;
                            return;
#endif
        case EXT_PER_IN:	// if period measurement get the count and average it over the number of cycles
                            if(pin == Option.INT1pin) fret = (MMFLOAT)ExtInp(pin) / (MMFLOAT)INT1InitTimer;
                            else if(pin == Option.INT2pin)  fret = (MMFLOAT)ExtInp(pin) / (MMFLOAT)INT2InitTimer;
                            else if(pin == Option.INT3pin)  fret = (MMFLOAT)ExtInp(pin) / (MMFLOAT)INT3InitTimer;
                            else if(pin == Option.INT4pin)  fret = (MMFLOAT)ExtInp(pin) / (MMFLOAT)INT4InitTimer;
                            targ = T_NBR;
                            return;
        case EXT_FREQ_IN:	// if frequency measurement get the count and scale the reading
                            if(pin == Option.INT1pin) fret = (MMFLOAT)(ExtInp(pin)) * (MMFLOAT)1000.0 / (MMFLOAT)INT1InitTimer;
                            else if(pin == Option.INT2pin)  fret = (MMFLOAT)(ExtInp(pin)) * (MMFLOAT)1000.0 / (MMFLOAT)INT2InitTimer;
                            else if(pin == Option.INT3pin)  fret = (MMFLOAT)(ExtInp(pin)) * (MMFLOAT)1000.0 / (MMFLOAT)INT3InitTimer;
                            else if(pin == Option.INT4pin)  fret = (MMFLOAT)(ExtInp(pin)) * (MMFLOAT)1000.0 / (MMFLOAT)INT4InitTimer;
                            targ = T_NBR;
                            return;
        case EXT_ADCRAW:
                            if(ADCDualBuffering || dmarunning)error("ADC in use");
                            iret=ExtInp(pin); 
                            targ=T_INT;
                            return;  
        case EXT_ANA_IN:    
                            if(ADCDualBuffering || dmarunning)error("ADC in use");
                            for(i = 0; i < ANA_AVERAGE; i++) {
                                b[i] = ExtInp(pin);                 // get the value
                                for(j = i; j > 0; j--) {            // and sort into position
                                    if(b[j - 1] < b[j]) {
                                        t = b[j - 1];
                                        b[j - 1] = b[j];
                                        b[j] = t;
                                    }
                                    else
                                        break;
                                }
                            }
                            // we then discard the top ANA_DISCARD samples and the bottom ANA_DISCARD samples and add up the remainder
                            for(j = 0, i = ANA_DISCARD; i < ANA_AVERAGE - ANA_DISCARD; i++) j += b[i];

                            // the total is averaged and scaled
                            fret = FMul((MMFLOAT)j , VCC) / (MMFLOAT)(4095 * (ANA_AVERAGE - ANA_DISCARD*2));
                            targ = T_NBR;
                            return;

        default:            error("Pin %/| is not an input",pin,pin);
    }
}
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */

int CheckPin(int pin, int action) {

#ifdef rp2350
    if(rp2350a && pin>44)error("Pin | is invalid", pin);
#endif

    if(pin < 1 || pin > NBRPINS || (PinDef[pin].mode & UNUSED)) {
        if(!(action & CP_NOABORT)) error("Pin %/| is invalid", pin,pin);
        return false;
    }

    if(!(action & CP_IGNORE_INUSE) && ExtCurrentConfig[pin] >= EXT_DS18B20_RESERVED && ExtCurrentConfig[pin] < EXT_COM_RESERVED) {
        if(!(action & CP_NOABORT)) error("Pin %/| is in use", pin,pin);
        return false;
    }

    if(!(action & CP_IGNORE_BOOTRES) && ExtCurrentConfig[pin] >= EXT_BOOT_RESERVED) {
        if(!(action & CP_NOABORT)) {
            error("Pin %/| is reserved on startup", pin,pin);
            uSec(1000000);
        }
        return false;
    }

    if(!(action & CP_IGNORE_RESERVED) && ExtCurrentConfig[pin] >= EXT_DS18B20_RESERVED) {
        if(!(action & CP_NOABORT)) error("Pin %/| is in use", pin,pin);
        return false;
    }

    return true;
}
/*  @endcond */
// this is invoked as a command (ie, port(3, 8) = Value)
// first get the arguments then step over the closing bracket.  Search through the rest of the command line looking
// for the equals sign and step over it, evaluate the rest of the command and set the pins accordingly
void cmd_port(void) {
	int pin, nbr, value, code, pincode;
    int i;
	getargs(&cmdline, NBRPINS * 4, (unsigned char *)",");

	if((argc & 0b11) != 0b11) error("Invalid syntax");
    if(!strchr((char *)cmdline,')'))error ("Syntax");
    // step over the equals sign and get the value for the assignment
	while(*cmdline && tokenfunction(*cmdline) != op_equal) cmdline++;
	if(!*cmdline) error("Invalid syntax");
	++cmdline;
	if(!*cmdline) error("Invalid syntax");
	value = getinteger(cmdline);
    uint64_t mask=0,setmask=0, readmask;

    for(i = 0; i < argc; i += 4) {
    	code=0;
    	if(!(code=codecheck(argv[i])))argv[i]+=2;
    	pincode = getinteger(argv[i]);
        nbr = getinteger(argv[i + 2]);
        if(nbr < 0 || (pincode == 0 && code!=0) || (pincode<0)) error("Invalid argument");

        while(nbr) {
        	if(!code)pin=codemap(pincode);
        	else pin=pincode;
            if(IsInvalidPin(pin) || !(ExtCurrentConfig[pin] == EXT_DIG_OUT )) error("Invalid output pin");
            mask |=(1<<PinDef[pin].GPno);
            if(value & 1)setmask |= (1<<PinDef[pin].GPno);
            value >>= 1;
            nbr--;
            pincode++;
        }
    } 
    readmask=gpio_get_out_level_all64();
    readmask &=mask;
    gpio_xor_mask(setmask ^ readmask);
}


void fun_distance(void) {
    int trig, echo,techo;

	getargs(&ep, 3, (unsigned char *)",");
	if((argc &1) != 1) error("Invalid syntax");
	char code;
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	trig = getinteger(argv[0]);
	if(!code)trig=codemap(trig);
    if(argc == 3){
    	if(!(code=codecheck(argv[2])))argv[2]+=2;
    	echo = getinteger(argv[2]);
    	if(!code)echo=codemap(echo);
    }
    else
        echo = trig;                                                // they are the same if it is a 3-pin device
    if(IsInvalidPin(trig) || IsInvalidPin(echo)) error("Invalid pin |",echo);
    if(ExtCurrentConfig[trig] >= EXT_COM_RESERVED || ExtCurrentConfig[echo] >= EXT_COM_RESERVED)  error("Pin %/| is in use",trig,trig);
    ExtCfg(echo, EXT_DIG_IN, CNPUSET);                              // setup the echo input
    PinSetBit(trig, LATCLR);                                        // trigger output must start low
    ExtCfg(trig, EXT_DIG_OUT, 0);                                   // setup the trigger output
    PinSetBit(trig, LATSET); uSec(20); PinSetBit(trig, LATCLR);     // pulse the trigger
    uSec(50);
    ExtCfg(echo, EXT_DIG_IN, CNPUSET);                              // this is in case the sensor is a 3-pin type
    uSec(50);
    PauseTimer = 0;                                                 // this is our timeout
    while(PinRead(echo)) if(PauseTimer > 50) { fret = -2; return; } // wait for the acknowledgement pulse start
    while(!PinRead(echo)) if(PauseTimer > 100) { fret = -2; return;}// then its end
    PauseTimer = 0;
    writeusclock(0);
    while(PinRead(echo)) {                                          // now wait for the echo pulse
        if(PauseTimer > 38) {                                       // timeout is 38mS
            fret = -1;
            return;
        }
    }
    techo=readusclock();
    // we have the echo, convert the time to centimeters
    fret = FDiv((MMFLOAT)techo,58.0);  //200 ticks per us, 58 us per cm
    targ = T_NBR;
}



// this is invoked as a function (ie, x = port(10,8) )
void fun_port(void) {
	int pin, nbr, i, value = 0, code, pincode;

	getargs(&ep, NBRPINS * 4, (unsigned char *)",");
	if((argc & 0b11) != 0b11) error("Invalid syntax");
    uint64_t pinstate=gpio_get_all64();
    uint64_t outpinstate=gpio_get_out_level_all64();
    for(i = argc - 3; i >= 0; i -= 4) {
    	code=0;
    	if(!(code=codecheck(argv[i])))argv[i]+=2;
        pincode = getinteger(argv[i]);
        nbr = getinteger(argv[i + 2]);
        if(nbr < 0 || (pincode == 0 && code!=0) || (pincode<0)) error("Invalid argument");
        pincode += nbr - 1;                                             // we start by reading the most significant bit

        while(nbr) {
        	if(!code)pin=codemap(pincode);
        	else pin=pincode;
            if(IsInvalidPin(pin) || !(ExtCurrentConfig[pin] == EXT_DIG_IN || ExtCurrentConfig[pin] == EXT_DIG_OUT || ExtCurrentConfig[pin] == EXT_INT_HI || ExtCurrentConfig[pin] == EXT_INT_LO || ExtCurrentConfig[pin] == EXT_INT_BOTH)) error("Invalid input pin");
            value <<= 1;
            if(ExtCurrentConfig[pin] == EXT_DIG_OUT)value |= (outpinstate & (1<<PinDef[pin].GPno)? 1:0);
            else value |= (pinstate & (1<<PinDef[pin].GPno)? 1:0);
            nbr--;
            pincode--;
        }
    }

    iret = value;
    targ = T_INT;
}


void cmd_pulse(void) {
    int pin, i, x, y;
    MMFLOAT f;

	getargs(&cmdline, 3, (unsigned char *)",");
	if(argc != 3) error("Invalid syntax");
	char code;
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	pin = getinteger(argv[0]);
	if(!code)pin=codemap(pin);
	if(!(ExtCurrentConfig[pin] == EXT_DIG_OUT)) error("Pin %/| is not an output", pin,pin);

    f = getnumber(argv[2]);                                         // get the pulse width
    if(f < 0) error("Number out of bounds");
    x = f;                                                          // get the integer portion (in mSec)
    y = (int)(FSub(f , (MMFLOAT)x) * 1000.0);                             // get the fractional portion (in uSec)

    for(i = 0; i < NBR_PULSE_SLOTS; i++)                            // search looking to see if the pin is in use
        if(PulseCnt[i] != 0 && PulsePin[i] == pin) {
            mT4IntEnable(0);       									// disable the timer interrupt to prevent any conflicts while updating
            PulseCnt[i] = x;                                        // and if the pin is in use, set its time to the new setting or reset if the user wants to terminate
            mT4IntEnable(1);
            if(x == 0) PinSetBit(PulsePin[i], LATINV);
            return;
        }

    if(x == 0 && y == 0) return;                                    // silently ignore a zero pulse width

    if(x < 3) {                                                     // if this is under 3 milliseconds just do it now
        PinSetBit(pin, LATINV);                    // starting edge of the pulse
        uSec(x * 1000 + y);
        PinSetBit(pin, LATINV);                    // finishing edge
        return;
    }

    for(i = 0; i < NBR_PULSE_SLOTS; i++)
        if(PulseCnt[i] == 0) break;                                 // find a spare slot

    if(i >= NBR_PULSE_SLOTS) error("Too many concurrent PULSE commands");

    PinSetBit(pin, LATINV);                        // starting edge of the pulse
    if(x == 1) uSec(500);                                           // prevent too narrow a pulse if there is just one count
    PulsePin[i] = pin;                                              // save the details
    PulseCnt[i] = x;
    PulseActive = true;
}


void fun_pulsin(void) { //allowas timeouts up to 10 seconds
    int pin, polarity;
    unsigned int t1, t2;

	getargs(&ep, 7, (unsigned char *)",");
	if((argc &1) != 1 || argc < 3) error("Invalid syntax");
	char code;
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	pin = getinteger(argv[0]);
	if(!code)pin=codemap(pin);
    if(IsInvalidPin(pin)) error("Invalid pin");
	if(ExtCurrentConfig[pin] != EXT_DIG_IN) error("Pin %/| is not an input",pin,pin);
    polarity = getinteger(argv[2]);

    t1 = t2 = 100000;                                               // default timeout is 100mS
    if(argc >= 5) t1 = t2 = getint(argv[4], 5, 10000000);
    if(argc == 7) t2 = getint(argv[6], 5, 10000000);
    iret = -1;                                                      // in anticipation of a timeout
    writeusclock(0);
    if(polarity) {
        while(PinRead(pin)) if(readusclock() > t1) return;
        while(!PinRead(pin)) if(readusclock() > t1) return;
        writeusclock(0);
        while(PinRead(pin)) if(readusclock() > t2) return;
    } else {
        while(!PinRead(pin)) if(readusclock() > t1) return;
        while(PinRead(pin)) if(readusclock() > t1) return;
        writeusclock(0);
        while(!PinRead(pin)) if(readusclock() > t2) return;
    }
    t1 = readusclock();
    iret = t1;
    targ = T_INT;
}
/****************************************************************************************************************************
IR routines
*****************************************************************************************************************************/

void cmd_ir(void) {
    unsigned char *p;
    int i, pin, dev, cmd;
    if(checkstring(cmdline, (unsigned char *)"CLOSE")) {
        if(IrState == IR_CLOSED) error("Not Open");
        if(CallBackEnabled==1) gpio_set_irq_enabled_with_callback(PinDef[IRpin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
        else gpio_set_irq_enabled(PinDef[IRpin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        IrInterrupt = NULL;
        CallBackEnabled &= (~1);
        ExtCfg(IRpin, EXT_NOT_CONFIG, 0);
    } else if((p = checkstring(cmdline, (unsigned char *)"SEND"))) {
        getargs(&p, 5, (unsigned char *)",");
        char code;
        if(!(code=codecheck(argv[0])))argv[0]+=2;
        pin = getinteger(argv[0]);
        if(!code)pin=codemap(pin);
        if(IsInvalidPin(pin)) error("Invalid pin");
        dev = getint(argv[2], 0, 0b11111);
        cmd = getint(argv[4], 0, 0b1111111);
        if(ExtCurrentConfig[pin] >= EXT_COM_RESERVED)  error("Pin %/| is in use",pin,pin);
        ExtCfg(pin, EXT_DIG_OUT, 0);
        cmd = (dev << 7) | cmd;
        IRSendSignal(pin, 186);
        for(i = 0; i < 12; i++) {
            uSec(600);
            if(cmd & 1)
                IRSendSignal(pin, 92);
            else
                IRSendSignal(pin, 46);
            cmd >>= 1;
        }
    } else {
        getargs(&cmdline, 5, (unsigned char *)",");
        if(IRpin==99)error("Pin not configured for IR");
        if(IrState != IR_CLOSED) error("Already open");
        if(argc%2 == 0 || argc == 0) error("Invalid syntax");
        IrVarType = 0;
        IrDev = findvar(argv[0], V_FIND);
        if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
        if(g_vartbl[g_VarIndex].type & T_STR)  error("Invalid variable");
        if(g_vartbl[g_VarIndex].type & T_NBR) IrVarType |= 0b01;
        IrCmd = findvar(argv[2], V_FIND);
        if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
        if(g_vartbl[g_VarIndex].type & T_STR)  error("Invalid variable");
        if(g_vartbl[g_VarIndex].type & T_NBR) IrVarType |= 0b10;
        InterruptUsed = true;
        IrInterrupt = GetIntAddress(argv[4]);							// get the interrupt location
        IrInit();
    }
}

/* 
 * @cond
 * The following section will be excluded from the documentation.
 */

void IrInit(void) {
    writeusclock(0);
    if(ExtCurrentConfig[IRpin] >= EXT_COM_RESERVED)  error("Pin %/% is in use",IRpin,IRpin);
    ExtCfg(IRpin, EXT_IR, 0);
    ExtCfg(IRpin, EXT_COM_RESERVED, 0);
    if(!CallBackEnabled){
        gpio_set_irq_enabled_with_callback(PinDef[IRpin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
        CallBackEnabled=1;
    } else {
        gpio_set_irq_enabled(PinDef[IRpin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true);
        CallBackEnabled|=1;
    }
    IrReset();
}


void IrReset(void) {
	IrState = IR_WAIT_START;
    IrCount = 0;
    writeIRclock(0);
}


// this modulates (at about 38KHz) the IR beam for transmit
// half_cycles is the number of half cycles to send.  ie, 186 is about 2.4mSec
void IRSendSignal(int pin, int half_cycles) {
    while(half_cycles--) {
        PinSetBit(pin, LATINV);
        uSec(13);
    }
}
void MIPS16 set_PWM(int slice, MMFLOAT duty1, MMFLOAT duty2, int high1, int high2, int delaystart){
    if(slice==0 && PWM0Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==0 && PWM0Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
#ifdef rp2350
    if(slice==0 && fast_timer_active)error("Channel 0 in use for fast timer");
#endif
    if(slice==1 && PWM1Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==1 && PWM1Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
    if(slice==2 && PWM2Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==2 && PWM2Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
    if(slice==3 && PWM3Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==3 && PWM3Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
    if(slice==4 && PWM4Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==4 && PWM4Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
    if(slice==5 && PWM5Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==5 && PWM5Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
    if(slice==6 && PWM6Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==6 && PWM6Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
    if(slice==7 && PWM7Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==7 && PWM7Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
#ifdef rp2350
    if(slice==8 && PWM8Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==8 && PWM8Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
    if(slice==9 && PWM9Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==9 && PWM9Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
    if(slice==10 && PWM10Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==10 && PWM10Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
    if(slice==11 && PWM11Apin==99 && duty1>=0.0)error("Pin not set for PWM");
    if(slice==11 && PWM11Bpin==99 && duty2>=0.0)error("Pin not set for PWM");
#endif
    if(slice==0 && PWM0Apin!=99 && duty1>=0.0){
        ExtCfg(PWM0Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==0 && PWM0Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM0Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==1 && PWM1Apin!=99 && duty1>=0.0){
        ExtCfg(PWM1Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==1 && PWM1Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM1Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==2 && PWM2Apin!=99 && duty1>=0.0){
        ExtCfg(PWM2Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==2 && PWM2Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM2Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==3 && PWM3Apin!=99 && duty1>=0.0){
        ExtCfg(PWM3Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==3 && PWM3Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM3Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==4 && PWM4Apin!=99 && duty1>=0.0){
        ExtCfg(PWM4Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==4 && PWM4Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM4Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==5 && PWM5Apin!=99 && duty1>=0.0){
        ExtCfg(PWM5Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==5 && PWM5Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM5Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==6 && PWM6Apin!=99 && duty1>=0.0){
        ExtCfg(PWM6Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==6 && PWM6Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM6Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==7 && PWM7Apin!=99 && duty1>=0.0){
        ExtCfg(PWM7Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==7 && PWM7Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM7Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
#ifdef rp2350
    if(slice==8 && PWM8Apin!=99 && duty1>=0.0){
        ExtCfg(PWM8Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==8 && PWM8Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM8Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==9 && PWM9Apin!=99 && duty1>=0.0){
        ExtCfg(PWM9Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==9 && PWM9Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM9Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==10 && PWM10Apin!=99 && duty1>=0.0){
        ExtCfg(PWM10Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==10 && PWM10Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM10Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
    if(slice==11 && PWM11Apin!=99 && duty1>=0.0){
        ExtCfg(PWM11Apin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_A, high1);
    }
    if(slice==11 && PWM11Bpin!=99 && duty2>=0.0){
        ExtCfg(PWM11Bpin,EXT_COM_RESERVED,0);
        pwm_set_chan_level(slice, PWM_CHAN_B, high2);
    }
#endif
        if(slice==0 && slice0==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice0=1;
    }
        if(slice==1 && slice1==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice1=1;
    }
        if(slice==2 && slice2==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice2=1;
    }
        if(slice==3 && slice3==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice3=1;
    }
        if(slice==4 && slice4==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice4=1;
    }
        if(slice==5 && slice5==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice5=1;
    }
        if(slice==6 && slice6==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice6=1;
    }
        if(slice==7 && slice7==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice7=1;
    }
#ifdef rp2350
        if(slice==8 && slice8==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice8=1;
    }
        if(slice==9 && slice9==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice9=1;
    }
        if(slice==10 && slice10==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice10=1;
    }
        if(slice==11 && slice11==0){
        if(!delaystart)pwm_set_enabled(slice, true);
        slice11=1;
    }
#endif
}

void PWMoff(int slice){
    if(slice==0 && PWM0Apin!=99 && ExtCurrentConfig[PWM0Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM0Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==0 && PWM0Bpin!=99 && ExtCurrentConfig[PWM0Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM0Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==1 && PWM1Apin!=99 && ExtCurrentConfig[PWM1Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM1Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==1 && PWM1Bpin!=99 && ExtCurrentConfig[PWM1Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM1Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==2 && PWM2Apin!=99 && ExtCurrentConfig[PWM2Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM2Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==2 && PWM2Bpin!=99 && ExtCurrentConfig[PWM2Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM2Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==3 && PWM3Apin!=99 && ExtCurrentConfig[PWM3Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM3Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==3 && PWM3Bpin!=99 && ExtCurrentConfig[PWM3Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM3Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==4 && PWM4Apin!=99 && ExtCurrentConfig[PWM4Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM4Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==4 && PWM4Bpin!=99 && ExtCurrentConfig[PWM4Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM4Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==5 && PWM5Apin!=99 && ExtCurrentConfig[PWM5Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM5Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==5 && PWM5Bpin!=99 && ExtCurrentConfig[PWM5Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM5Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==6 && PWM6Apin!=99 && ExtCurrentConfig[PWM6Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM6Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==6 && PWM6Bpin!=99 && ExtCurrentConfig[PWM6Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM6Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==7 && PWM7Apin!=99 && ExtCurrentConfig[PWM7Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM7Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==7 && PWM7Bpin!=99 && ExtCurrentConfig[PWM7Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM7Bpin,EXT_NOT_CONFIG,0);
    }
#ifdef rp2350
    if(slice==8 && PWM8Apin!=99 && ExtCurrentConfig[PWM8Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM8Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==8 && PWM8Bpin!=99 && ExtCurrentConfig[PWM8Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM8Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==9 && PWM9Apin!=99 && ExtCurrentConfig[PWM9Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM9Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==9 && PWM9Bpin!=99 && ExtCurrentConfig[PWM9Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM9Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==10 && PWM10Apin!=99 && ExtCurrentConfig[PWM10Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM10Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==10 && PWM10Bpin!=99 && ExtCurrentConfig[PWM10Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM10Bpin,EXT_NOT_CONFIG,0);
    }
    if(slice==11 && PWM11Apin!=99 && ExtCurrentConfig[PWM11Apin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM11Apin,EXT_NOT_CONFIG,0);
    }
    if(slice==11 && PWM11Bpin!=99 && ExtCurrentConfig[PWM11Bpin] < EXT_BOOT_RESERVED){
        ExtCfg(PWM11Bpin,EXT_NOT_CONFIG,0);
    }
#endif
    pwm_set_enabled(slice, false);
}
#ifndef PICOMITEVGA
#ifndef PICOCALC
void setBacklight(int level, int setfrequency){
    if(((Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel ) || (Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL)) && Option.DISPLAY_BL){
        MMFLOAT frequency=setfrequency ? (MMFLOAT)setfrequency : (Option.DISPLAY_TYPE==ILI9488W ? 1000.0 : 50000.0);
        int wrap=(Option.CPU_Speed*1000)/frequency;
        int high=(int)((MMFLOAT)Option.CPU_Speed/frequency*level*10.0);
        int div=1;
        while(wrap>65535){
            wrap>>=1;
            if(level>=0.0)high>>=1;
            div<<=1;
        }
        wrap--;
        if(div!=1)pwm_set_clkdiv(BacklightSlice,(float)div);
        pwm_set_wrap(BacklightSlice, wrap);
        pwm_set_chan_level(BacklightSlice, BacklightChannel, high);
    } else if(Option.DISPLAY_TYPE<=I2C_PANEL){
        level*=255;
        level/=100;
        I2C_Send_Command(0x81);//SETCONTRAST
        I2C_Send_Command((uint8_t)level);
    } else if(Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL){
        SetBacklightSSD1963(level);
    } else if(Option.DISPLAY_TYPE==SSD1306SPI){
        level*=255;
        level/=100;
        spi_write_command(0x81);//SETCONTRAST
        spi_write_command((uint8_t)level);
    } 
#else
void setBacklight(int level){//STM32: i2c reg is REG_ID_BKL(0x05)
    //level is 0-100%
    level*=255;
    level/=100;
    I2C_Send_RegData(0x1f,0x05,(uint8_t)level);
#endif
}
/*  @endcond */
void MIPS16 cmd_backlight(void){
    getargs(&cmdline,3,(unsigned char *)",");
    int level=getint(argv[0],0,100);
    int frequency=0;
#ifndef PICOCALC
    if(((Option.DISPLAY_TYPE>I2C_PANEL && Option.DISPLAY_TYPE<BufferedPanel ) || (Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL)) && Option.DISPLAY_BL){
    } else if(Option.DISPLAY_TYPE<=I2C_PANEL){
    } else if(Option.DISPLAY_TYPE>=SSDPANEL && Option.DISPLAY_TYPE<VIRTUAL){
    } else if(Option.DISPLAY_TYPE==SSD1306SPI){
    } else error("Backlight not set up");
#endif
    if(argc==3){
        if(checkstring(argv[2],(unsigned char *)"DEFAULT")){
            Option.BackLightLevel=level;
            SaveOptions();
        } else {
            frequency=getint(argv[2],100,100000);
        }
    }
#ifndef PICOCALC
    setBacklight(level, frequency);
#else
    setBacklight(level);
#endif
}
#endif
void MIPS16 cmd_Servo(void){
    unsigned char *tp;
    int div=1, high1=0, high2=0;
    MMFLOAT duty1=-1.0, duty2=-1.0;
    getargs(&cmdline,5,(unsigned char *)",");
    if(!(argc>=3))error("Syntax");
    int CPU_Speed=Option.CPU_Speed;
#ifdef rp2350
    int slice=getint(argv[0],0,rp2350a ? 7:11);
    if(slice==0 && ExtCurrentConfig[FAST_TIMER_PIN]==EXT_FAST_TIMER)error("Channel in use for fast frequency");
#else
    int slice=getint(argv[0],0,7);
#endif
    if(slice==BacklightSlice)error("Channel in use for backlight");
    if(slice==Option.AUDIO_SLICE)error("Channel in use for Audio");
    if(slice==CameraSlice)error("Channel in use for Camera");
    if((tp=checkstring(argv[2],(unsigned char *)"OFF"))){
        PWMoff(slice);
        if(slice==0)slice0=0;
        if(slice==1)slice1=0;
        if(slice==2)slice2=0;
        if(slice==3)slice3=0;
        if(slice==4)slice4=0;
        if(slice==5)slice5=0;
        if(slice==6)slice6=0;
        if(slice==7)slice7=0;
#ifdef rp2350
        if(slice==8)slice8=0;
        if(slice==9)slice9=0;
        if(slice==10)slice10=0;
        if(slice==11)slice11=0;
#endif
        return;
    }
    MMFLOAT frequency=50.0;
    if(*argv[2]){
        duty1=getnumber(argv[2]);
        if(duty1>120.0 || duty1<-20.0)error("Syntax");
        duty1=5.0+duty1*0.05;
    }
    if(argc>=5 && *argv[4]){
        duty2=getnumber(argv[4]);
        if(duty2>120.0 || duty2<-20.0)error("Syntax");
        duty2=5.0+duty2*0.05;
    }
    int wrap=(CPU_Speed*1000)/frequency;
    if(duty1>=0.0)high1=(int)((MMFLOAT)CPU_Speed/frequency*duty1*10.0);
    if(duty2>=0.0)high2=(int)((MMFLOAT)CPU_Speed/frequency*duty2*10.0);
    while(wrap>65535){
        wrap>>=1;
        if(duty1>=0.0)high1>>=1;
        if(duty2>=0.0)high2>>=1;
        div<<=1;
    }
    if(div>256)error("Invalid frequency");
    wrap--;
    if(high1)high1--;
    if(high2)high2--;
    pwm_set_clkdiv(slice,(float)div);
    pwm_set_wrap(slice, wrap);
    pwm_set_output_polarity(slice,false,false);
    pwm_set_phase_correct(slice,false);
    set_PWM(slice,duty1,duty2,high1,high2, 0);
}
void MIPS16 cmd_pwm(void){
    unsigned char *tp;
    if((tp=checkstring(cmdline, (unsigned char *)"SYNC"))) {
        MMFLOAT count0=-1.0,count1=-1.0,count2=-1.0,count3=-1.0,count4=-1.0,count5=-1.0,count6=-1.0,count7=-1.0;
#ifdef rp2350
        MMFLOAT count8=-1.0,count9=-1.0,count10=-1.0,count11=-1.0;
        getargs(&tp,23,(unsigned char *)",");
#else
        getargs(&tp,15,(unsigned char *)",");
#endif
        if(argc>=1){count0=getnumber(argv[0]);
        if((count0<0.0 || count0>100.0) && count0!=-1.0)error("Syntax");}
        if(argc>=3 && *argv[2]){count1=getnumber(argv[2]);
        if((count1<0.0 || count1>100.0) && count1!=-1.0)error("Syntax");}
        if(argc>=5 && *argv[4]){count2=getnumber(argv[4]);
        if((count2<0.0 || count2>100.0) && count2!=-1.0)error("Syntax");}
        if(argc>=7 && *argv[6]){count3=getnumber(argv[6]);
        if((count3<0.0 || count3>100.0) && count3!=-1.0)error("Syntax");}
        if(argc>=9 && *argv[8]){count4=getnumber(argv[8]);
        if((count4<0.0 || count4>100.0) && count4!=-1.0)error("Syntax");}
        if(argc>=11 && *argv[10]){count5=getnumber(argv[10]);
        if((count5<0.0 || count5>100.0) && count5!=-1.0)error("Syntax");}
        if(argc>=13 && *argv[12]){count6=getnumber(argv[12]);
        if((count6<0.0 || count6>100.0) && count6!=-1.0)error("Syntax");}
#ifdef rp2350
        if(argc>=15 && *argv[14]){count7=getnumber(argv[14]);
        if((count7<0.0 || count7>100.0) && count7!=-1.0)error("Syntax");}
        if(argc>=17 && *argv[16]){count8=getnumber(argv[16]);
        if((count8<0.0 || count8>100.0) && count8!=-1.0)error("Syntax");}
        if(argc>=19 && *argv[18]){count9=getnumber(argv[18]);
        if((count9<0.0 || count9>100.0) && count9!=-1.0)error("Syntax");}
        if(argc>=21 && *argv[20]){count10=getnumber(argv[20]);
        if((count10<0.0 || count10>100.0) && count10!=-1.0)error("Syntax");}
        if(argc==23 && *argv[22]){count11=getnumber(argv[22]);
        if((count11<0.0 || count11>100.0) && count11!=-1.0)error("Syntax");}
#else
        if(argc==15 && *argv[14]){count7=getnumber(argv[14]);
        if((count7<0.0 || count7>100.0) && count7!=-1.0)error("Syntax");}
#endif
        
        int enabled=0;
        if(slice0 || Option.AUDIO_SLICE ==0 || BacklightSlice==0 || CameraSlice==0){
            enabled |=1;
            if(!(Option.AUDIO_SLICE ==0 || BacklightSlice==0 || CameraSlice==0 || count0<0.0)){
                pwm_set_enabled(0,false);
                count0=(MMFLOAT)pwm_hw->slice[0].top * (100.0-count0) / 100.0;
                pwm_set_counter(0,(int)count0);
            }
        }
        if(slice1 || Option.AUDIO_SLICE ==1 || BacklightSlice==1 || CameraSlice==1){
            enabled |=2;
            if(!(Option.AUDIO_SLICE ==1 || BacklightSlice==1 || CameraSlice==1 || count1<0.0)){
                pwm_set_enabled(1,false);
                count1=(MMFLOAT)pwm_hw->slice[1].top * (100.0-count1) / 100.0;
                pwm_set_counter(1,count1);
            }
        }
        if(slice2 || Option.AUDIO_SLICE ==2 || BacklightSlice==2 || CameraSlice==2){
            enabled |=4;
            if(!(Option.AUDIO_SLICE ==2 || BacklightSlice==2 || CameraSlice==2 || count2<0.0)){
                pwm_set_enabled(2,false);
                count2=(MMFLOAT)pwm_hw->slice[2].top * (100.0-count2) / 100.0;
                pwm_set_counter(2,count2);
            }
        }
        if(slice3 || Option.AUDIO_SLICE ==3 || BacklightSlice==3 || CameraSlice==3){
            enabled |=8;
            if(!(Option.AUDIO_SLICE ==3 || BacklightSlice==3 || CameraSlice==3 || count3<0.0)){
                pwm_set_enabled(3,false);
                count3=(MMFLOAT)pwm_hw->slice[3].top * (100.0-count3) / 100.0;
                pwm_set_counter(3,count3);
            }
        }
        if(slice4 || Option.AUDIO_SLICE ==4 || BacklightSlice==4 || CameraSlice==4){
            enabled |=16;
            if(!(Option.AUDIO_SLICE ==4 || BacklightSlice==4 || CameraSlice==4 || count4<0.0)){
                pwm_set_enabled(4,false);
                count4=(MMFLOAT)pwm_hw->slice[4].top * (100.0-count4) / 100.0;
                pwm_set_counter(4,count4);
            }
        }
        if(slice5 || Option.AUDIO_SLICE ==5 || BacklightSlice==5 || CameraSlice==5){
            enabled |=32;
            if(!(Option.AUDIO_SLICE ==5 || BacklightSlice==5 || CameraSlice==5 || count5<0.0)){
                pwm_set_enabled(5,false);
                count5=(MMFLOAT)pwm_hw->slice[5].top * (100.0-count5) / 100.0;
                pwm_set_counter(5,count5);
            }
        }
        if(slice6 || Option.AUDIO_SLICE ==6 || BacklightSlice==6 || CameraSlice==6){
            enabled |=64;
            if(!(Option.AUDIO_SLICE ==6 || BacklightSlice==6 || CameraSlice==6 || count6<0.0)){
                pwm_set_enabled(6,false);
                count6=(MMFLOAT)pwm_hw->slice[6].top * (100.0-count6) / 100.0;
                pwm_set_counter(6,count6);
            }
        }
        if(slice7 || Option.AUDIO_SLICE ==7 || BacklightSlice==7 || CameraSlice==7){
            enabled |=128;
            if(!(Option.AUDIO_SLICE ==7 || BacklightSlice==7 || CameraSlice==7 || count7<0.0)){
                pwm_set_enabled(7,false);
                count7=(MMFLOAT)pwm_hw->slice[7].top * (100.0-count7) / 100.0;
                pwm_set_counter(7,count7);
            }
        }
#ifdef rp2350
        if(slice8 || Option.AUDIO_SLICE ==8 || BacklightSlice==8 || CameraSlice==8){
            enabled |=256;
            if(!(Option.AUDIO_SLICE ==8 || BacklightSlice==8 || CameraSlice==8 || count8<0.0)){
                pwm_set_enabled(8,false);
                count8=(MMFLOAT)pwm_hw->slice[8].top * (100.0-count8) / 100.0;
                pwm_set_counter(8,count8);
            }
        }
        if(slice9 || Option.AUDIO_SLICE ==9 || BacklightSlice==9 || CameraSlice==9){
            enabled |=512;
            if(!(Option.AUDIO_SLICE ==9 || BacklightSlice==9 || CameraSlice==9 || count9<0.0)){
                pwm_set_enabled(9,false);
                count9=(MMFLOAT)pwm_hw->slice[9].top * (100.0-count9) / 100.0;
                pwm_set_counter(9,count9);
            }
        }
        if(slice10 || Option.AUDIO_SLICE ==10 || BacklightSlice==10 || CameraSlice==10){
            enabled |=1024;
            if(!(Option.AUDIO_SLICE ==10 || BacklightSlice==10 || CameraSlice==10 || count10<0.0)){
                pwm_set_enabled(10,false);
                count10=(MMFLOAT)pwm_hw->slice[10].top * (100.0-count10) / 100.0;
                pwm_set_counter(10,count10);
            }
        }
        if(slice11 || Option.AUDIO_SLICE ==11 || BacklightSlice==11 || CameraSlice==11){
            enabled |=2048;
            if(!(Option.AUDIO_SLICE ==11 || BacklightSlice==11 || CameraSlice==11 || count11<0.0)){
                pwm_set_enabled(11,false);
                count11=(MMFLOAT)pwm_hw->slice[11].top * (100.0-count11) / 100.0;
                pwm_set_counter(11,count11);
            }
        }
#endif
        pwm_hw->en=enabled;
        return;
    }
    int div=1, high1=0, high2=0;
    int phase1=0,phase2=0;
    MMFLOAT duty1=-1.0, duty2=-1.0;
    getargs(&cmdline,11,(unsigned char *)",");
    if(!(argc>=3))error("Syntax");
    int CPU_Speed=Option.CPU_Speed;
#ifdef rp2350
    int slice=getint(argv[0],0,rp2350a ? 7:11);
    if(slice==0 && ExtCurrentConfig[FAST_TIMER_PIN]==EXT_FAST_TIMER)error("Channel in use for fast frequency");
#else
    int slice=getint(argv[0],0,7);
#endif
    if(slice==BacklightSlice)error("Channel in use for backlight");
    if(slice==Option.AUDIO_SLICE)error("Channel in use for Audio");
    if(slice==CameraSlice)error("Channel in use for Camera");
    if((tp=checkstring(argv[2],(unsigned char *)"OFF"))){
        PWMoff(slice);
        if(slice==0)slice0=0;
        if(slice==1)slice1=0;
        if(slice==2)slice2=0;
        if(slice==3)slice3=0;
        if(slice==4)slice4=0;
        if(slice==5)slice5=0;
        if(slice==6)slice6=0;
        if(slice==7)slice7=0;
#ifdef rp2350
        if(slice==8)slice8=0;
        if(slice==9)slice9=0;
        if(slice==10)slice10=0;
        if(slice==11)slice11=0;
#endif
        return;
    }
    if(!(argc>=5))error("Syntax");
    int delaystart=0;
    int phase=1;
    if(argc>=9 && *argv[8])phase+=getint(argv[8],0,1);
    if(argc==11)delaystart=getint(argv[10],0,1);
    MMFLOAT frequency=getnumber(argv[2])*phase;
    if(frequency>(MMFLOAT)(CPU_Speed>>2)*1000.0)error("Invalid frequency");
    if(*argv[4]){
        duty1=getnumber(argv[4]);
        if(duty1>100.0 || duty1<-100.0)error("Syntax");
        if(duty1<0){
            duty1=-duty1;
            phase1=1;
        }
    }
    if(argc>=7 && *argv[6]){
        duty2=getnumber(argv[6]);
        if(duty2>100.0 || duty2<-100.0)error("Syntax");
        if(duty2<0){
            duty2=-duty2;
            phase2=1;
        }
    }
    int wrap=(CPU_Speed*1000)/frequency;
    if(duty1>=0.0)high1=(int)((MMFLOAT)CPU_Speed/frequency*duty1*10.0);
    if(duty2>=0.0)high2=(int)((MMFLOAT)CPU_Speed/frequency*duty2*10.0);
    while(wrap>65535){
        wrap>>=1;
        if(duty1>=0.0)high1>>=1;
        if(duty2>=0.0)high2>>=1;
        div<<=1;
    }
    if(div>256)error("Invalid frequency");
    wrap--;
    if(high1)high1--;
    if(high2)high2--;
    pwm_set_clkdiv(slice,(float)div);
    pwm_set_wrap(slice, wrap);
    pwm_set_output_polarity(slice,phase1,phase2);
    pwm_set_phase_correct(slice,(phase==2? true : false));
    set_PWM(slice,duty1,duty2,high1,high2, delaystart);
}


/****************************************************************************************************************************
 The KEYPAD command
*****************************************************************************************************************************/
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */
static char keypad_pins[8];
MMFLOAT *KeypadVar;
unsigned char *KeypadInterrupt = NULL;
void KeypadClose(void);
/*  @endcond */

void cmd_keypad(void) {
    int i, j, code;

    if(checkstring(cmdline, (unsigned char *)"CLOSE"))
        KeypadClose();
    else {
        getargs(&cmdline, 19, (unsigned char *)",");
        if(argc%2 == 0 || argc < 17) error("Invalid syntax");
        if(KeypadInterrupt != NULL) error("Already open");
        KeypadVar = findvar(argv[0], V_FIND);
        if(g_vartbl[g_VarIndex].type & T_CONST) error("Cannot change a constant");
        if(!(g_vartbl[g_VarIndex].type & T_NBR)) error("Floating point variable required");
        InterruptUsed = true;
        KeypadInterrupt = GetIntAddress(argv[2]);					// get the interrupt location
        for(i = 0; i < 8; i++) {
            if(i == 7 && argc < 19) {
                keypad_pins[i] = 0;
                break;
            }
        	code=0;
        	if(!(code=codecheck(argv[(i + 2) * 2])))argv[(i + 2) * 2]+=2;
        	j = getinteger(argv[(i + 2) * 2]);
        	if(!code)j=codemap(j);
            if(ExtCurrentConfig[j] >= EXT_COM_RESERVED)  error("Pin %/| is in use",j,j);
//            if(i < 4) {
            ExtCfg(j, EXT_DIG_IN, ODCSET);
            ExtCfg(j, EXT_COM_RESERVED, 0);
            keypad_pins[i] = j;
        }
    }
}

/* 
 * @cond
 * The following section will be excluded from the documentation.
 */

void KeypadClose(void) {
    int i;
    if(KeypadInterrupt == NULL) return;
    for(i = 0; i < 8; i++) {
        if(keypad_pins[i]) {
            ExtCfg(keypad_pins[i], EXT_NOT_CONFIG, 0);				// all set to unconfigured
        }
    }
    KeypadInterrupt = NULL;
}


int KeypadCheck(void) {
    static unsigned char count = 0, keydown = false;
    int i, j;
    const char PadLookup[16] = { 1, 2, 3, 20, 4, 5, 6, 21, 7, 8, 9, 22, 10, 0, 11, 23 };

    if(count++ % 64) return false;                                  // only check every 64 loops through the interrupt processor

    for(j = 4; j < 8; j++) {                                        // j controls the pull down pins
        if(keypad_pins[j]) {                                        // we might just have 3 pull down pins
            PinSetBit(keypad_pins[j], ODCCLR);                      // pull it low
            for(i = 0; i < 4; i++) {                                // i is the row sense inputs
                if(PinRead((unsigned char)keypad_pins[i]) == 0) {                  // if it is low we have found a keypress
                    if(keydown) goto exitcheck;                     // we have already reported this, so just exit
                    uSec(40 * 1000);                                // wait 40mS and check again
                    if(PinRead((unsigned char)keypad_pins[i]) != 0) goto exitcheck;// must be contact bounce if it is now high
                    *KeypadVar = PadLookup[(i << 2) | (j - 4)];     // lookup the key value and set the variable
                    PinSetBit(keypad_pins[j], ODCSET);
                    keydown = true;                                 // record that we know that the key is down
                    return true;                                    // and tell the interrupt processor that we are good to go
                }
            }
            PinSetBit(keypad_pins[j], ODCSET);                      // wasn't this pin, clear the pulldown
        }
    }
    keydown = false;                                                // no key down, record the fact
    return false;

exitcheck:
    PinSetBit(keypad_pins[j], ODCSET);
    return false;
}




/****************************************************************************************************************************
 The LCD command
*****************************************************************************************************************************/

void LCD_Nibble(int Data, int Flag, int Wait_uSec);
void LCD_Byte(int Data, int Flag, int Wait_uSec);
void LcdPinSet(int pin, int val);
static char lcd_pins[6];
/*  @endcond */

void cmd_lcd(void)
 {
    unsigned char *p;
    int i, j, code;

    if((p = checkstring(cmdline, (unsigned char *)"INIT"))) {
        getargs(&p, 11, (unsigned char *)",");
        if(argc != 11) error("Invalid syntax");
        if(*lcd_pins) error("Already open");
        for(i = 0; i < 6; i++) {
        	code=0;
        	if(!(code=codecheck(argv[i * 2])))argv[i * 2]+=2;
            lcd_pins[i] = getinteger(argv[i * 2]);
        	if(!code)lcd_pins[i]=codemap(lcd_pins[i]);
            if(ExtCurrentConfig[(int)lcd_pins[i]] >= EXT_COM_RESERVED)  error("Pin %/| is in use",lcd_pins[i],lcd_pins[i]);
            ExtCfg(lcd_pins[i], EXT_DIG_OUT, 0);
            ExtCfg(lcd_pins[i], EXT_COM_RESERVED, 0);
        }
        LCD_Nibble(0b0011, 0, 5000);                                // reset
        LCD_Nibble(0b0011, 0, 5000);                                // reset
        LCD_Nibble(0b0011, 0, 5000);                                // reset
        LCD_Nibble(0b0010, 0, 2000);                                // 4 bit mode
        LCD_Byte(0b00101100, 0, 600);                               // 4 bits, 2 lines
        LCD_Byte(0b00001100, 0, 600);                               // display on, no cursor
        LCD_Byte(0b00000110, 0, 600);                               // increment on write
        LCD_Byte(0b00000001, 0, 3000);                              // clear the display
        return;
    }

    if(!*lcd_pins) error("Not open");
    if(checkstring(cmdline, (unsigned char *)"CLOSE")) {
        for(i = 0; i < 6; i++) {
			ExtCfg(lcd_pins[i], EXT_NOT_CONFIG, 0);					// all set to unconfigured
			ExtSet(lcd_pins[i], 0);									// all outputs (when set) default to low
            *lcd_pins = 0;
        }
    } else if(checkstring(cmdline, (unsigned char *)"CLEAR")) {                // clear the display
        LCD_Byte(0b00000001, 0, 3000);
    } else if((p = checkstring(cmdline, (unsigned char *)"CMD")) || (p = checkstring(cmdline, (unsigned char *)"DATA"))) { // send a command or data
        getargs(&p, MAX_ARG_COUNT * 2, (unsigned char *)",");
        for(i = 0; i < argc; i += 2) {
            j = getint(argv[i], 0, 255);
            LCD_Byte(j, toupper(*cmdline) == 'D', 0);
        }
    } else {
        const char linestart[4] = {0, 64, 20, 84};
        int center, pos;

        getargs(&cmdline, 5, (unsigned char *)",");
        if(argc != 5) error("Invalid syntax");
        i = getint(argv[0], 1, 4);
        pos = 1;
        if(checkstring(argv[2], (unsigned char *)"C8"))
            center = 8;
        else if(checkstring(argv[2], (unsigned char *)"C16"))
            center = 16;
        else if(checkstring(argv[2], (unsigned char *)"C20"))
            center = 20;
        else if(checkstring(argv[2], (unsigned char *)"C40"))
            center = 40;
        else {
            center = 0;
            pos = getint(argv[2], 1, 256);
        }
        p = getstring(argv[4]);                                     // returns an MMBasic string
        i = 128 + linestart[i - 1] + (pos - 1);
        LCD_Byte(i, 0, 600);
        for(j = 0; j < (center - *p) / 2; j++) {
            LCD_Byte(' ', 1, 0);
        }
        for(i = 1; i <= *p; i++) {
            LCD_Byte(p[i], 1, 0);
            j++;
        }
        for(; j < center; j++) {
            LCD_Byte(' ', 1, 0);
        }
    }
}
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */

void LCD_Nibble(int Data, int Flag, int Wait_uSec) {
    int i;
    LcdPinSet(lcd_pins[4], Flag);
    for(i = 0; i < 4; i++)
        LcdPinSet(lcd_pins[i], (Data >> i) & 1);
    LcdPinSet(lcd_pins[5], 1); uSec(250); LcdPinSet(lcd_pins[5], 0);
    if(Wait_uSec)
        uSec(Wait_uSec);
    else
        uSec(250);
}


void LCD_Byte(int Data, int Flag, int Wait_uSec) {
    LCD_Nibble(Data/16, Flag, 0);
    LCD_Nibble(Data, Flag, Wait_uSec);
}


void LcdPinSet(int pin, int val) {
    PinSetBit(pin, val ? LATSET : LATCLR);
}
int64_t DHmem(int pin){
    int timeout = 400;
    long long int r;
    int i;
    writeusclock(0);
    PinSetBit(pin, CNPUSET);
    PinSetBit(pin, TRISSET);
    uSec(5);
    // wait for the DHT22 to pull the pin low and return it high then take it low again
    while(PinRead(pin)) if(readusclock() > timeout) goto error_exit;
    while(!PinRead(pin)) if(readusclock() > timeout) goto error_exit;
    while(PinRead(pin)) if(readusclock() > timeout) goto error_exit;
//    PInt(readusclock());PRet();

    // now we wait for the pin to go high and measure how long it stays high (> 50uS is a one bit, < 50uS is a zero bit)
    for(r = i = 0; i < 40; i++) {
    	timeout=400;
        while(!PinRead(pin)) if(readusclock() > timeout) goto error_exit;
        timeout=400;writeusclock(0);
        while(PinRead(pin)) if(readusclock() > timeout) goto error_exit;
        r <<= 1;
        r |= (readusclock() > 50);
    }
    return r;
    error_exit:
    return -1;

}
/*  @endcond */

void cmd_DHT22(void) {
     int pin;
    union colourmap {
    unsigned char dhtbytes[8];
    long long int r;
    } dht;
//    long long int r;
    int dht11=0;
    MMFLOAT *temp, *humid;

    getargs(&cmdline, 7, (unsigned char *)",");
    if(!(argc == 5 || argc == 7)) error("Incorrect number of arguments");

    // get the two variables
	temp = findvar(argv[2], V_FIND);
	if(!(g_vartbl[g_VarIndex].type & T_NBR)) error("Invalid variable");
	humid = findvar(argv[4], V_FIND);
	if(!(g_vartbl[g_VarIndex].type & T_NBR)) error("Invalid variable");

    // get the pin number and set it up
    // get the pin number and set it up
	char code;
	if(!(code=codecheck(argv[0])))argv[0]+=2;
	pin = getinteger(argv[0]);
	if(!code)pin=codemap(pin);
    if(IsInvalidPin(pin)) error("Invalid pin ");
    if(ExtCurrentConfig[pin] != EXT_NOT_CONFIG)  error("Pin %/| is in use",pin,pin);
    if(argc==7){
    	dht11=getint(argv[6],0,1);
    }
    ExtCfg(pin, EXT_DIG_OUT, 0);
    
    // pulse the pin low for 1.5mS
    uSec(1500+dht11*18000);
    // we have all 40 bits
    // first validate against the checksum
    if((dht.r=DHmem(pin))==-1) goto error_exit;
    if((uint8_t)(dht.dhtbytes[4]+dht.dhtbytes[3]+dht.dhtbytes[2]+dht.dhtbytes[1])!=dht.dhtbytes[0])goto error_exit; 
//    if( ( (uint8_t)( ((r >> 8) & 0xff) + ((r >> 16) & 0xff) + ((r >> 24) & 0xff) + ((r >> 32) & 0xff) ) & 0xff) != (r & 0xff)) goto error_exit;                                           // returning temperature
    if(dht11==0){
		*temp = (MMFLOAT)((dht.r >> 8) &0x7fff) / 10.0;                       // get the temperature
		if((dht.r >> 8) &0x8000) *temp = -*temp;                            // the top bit is the sign
		*humid = (MMFLOAT)(dht.r >> 24) / 10.0;                               // get the humidity
    } else {
		*temp = (MMFLOAT)(dht.dhtbytes[2])+(MMFLOAT)(dht.dhtbytes[1])/10.0;                       // get the temperature
		*humid =(MMFLOAT)(dht.dhtbytes[4])+(MMFLOAT)(dht.dhtbytes[3])/10.0;                               // get the humidity
    }
    goto normal_exit;

error_exit:
    *temp = *humid = 1000.0;                                        // an obviously incorrect reading

normal_exit:
    ExtCfg(pin, EXT_NOT_CONFIG, 0);
    PinSetBit(pin, LATCLR);
}
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */
void __not_in_flash_func(WS2812e)(int gppin, int T1H, int T1L, int T0H, int T0L, int nbr, char *p){
    for(int i=0;i<nbr;i++){
        for(int j=0;j<8;j++){
            if(*p & 1){
                gpio_put(gppin,true);
                shortpause(T1H);
                gpio_put(gppin,false);
                shortpause(T1L);
            } else {
                gpio_put(gppin,true);
                shortpause(T0H);
                gpio_put(gppin,false);
                shortpause(T0L);
            }
            *p>>=1;
        }
        p++;
    }
}
/*  @endcond */
void fun_dev(void){
    unsigned char *tp=NULL;
    tp = checkstring(ep, (unsigned char *)"WII");
    if(tp==NULL)tp = checkstring(ep, (unsigned char *)"CLASSIC");
    if(tp){
       //	int ax; //classic left x
        //	int ay; //classic left y
        //	int az; //classic centre
        //	int Z;  //classic right x
        //	int C;  //classic right y
        //	int L;  //classic left analog
        //	int R;  //classic right analog
        //	unsigned short x0; //classic buttons
        getargs(&tp,1,(unsigned char *)",");
		if(!classic1)error("Not open");
         if(checkstring(argv[0], (unsigned char *)"LX"))iret=nunstruct[0].ax;
        else if(checkstring(argv[0], (unsigned char *)"LY"))iret=nunstruct[0].ay;
        else if(checkstring(argv[0], (unsigned char *)"RX"))iret=nunstruct[0].Z;
        else if(checkstring(argv[0], (unsigned char *)"RY"))iret=nunstruct[0].C;
        else if(checkstring(argv[0], (unsigned char *)"L"))iret=nunstruct[0].L;
        else if(checkstring(argv[0], (unsigned char *)"R"))iret=nunstruct[0].R;
        else if(checkstring(argv[0], (unsigned char *)"B"))iret=nunstruct[0].x0;
        else if(checkstring(argv[0], (unsigned char *)"T"))iret=nunstruct[0].type;
        else iret=0;
        targ=T_INT;
    } else if((tp=checkstring(ep,(unsigned char *)"NUNCHUCK"))){
        unsigned char *p;
        getargs(&tp,1,(unsigned char *)",");
        p=argv[0];
        if(toupper(*p)=='A'){
            p++;
            if(p[1]==0){
                if(toupper(*p)=='X')iret=nunstruct[5].ax;
                else if(toupper(*p)=='Y')iret=nunstruct[5].ay;
                else if(toupper(*p)=='Z')iret=nunstruct[5].az;
                else error("Syntax");
            } else {
                if(p[1]=='0'){
                    if(toupper(*p)=='X')iret=nunstruct[5].x0;
                    else if(toupper(*p)=='Y')iret=nunstruct[5].y0;
                    else if(toupper(*p)=='Z')iret=nunstruct[5].z0;
                    else error("Syntax");
                } else if(p[1]=='1'){
                    if(toupper(*p)=='X')iret=nunstruct[5].x1;
                    else if(toupper(*p)=='Y')iret=nunstruct[5].y1;
                    else if(toupper(*p)=='Z')iret=nunstruct[5].z1;
                    else error("Syntax");
                } else error("Syntax");
            }
        } else if(toupper(*p)=='J'){
            p++;
            if(p[1]==0){
                if(toupper(*p)=='X')iret=nunstruct[5].x;
                else if(toupper(*p)=='Y')iret=nunstruct[5].y;
            } else {
                if(toupper(*p)=='X'){
                    p++;
                    if(toupper(*p)=='L'){
                        iret=nunstruct[5].calib[9];
                    } else if(toupper(*p)=='C'){
                        iret=nunstruct[5].calib[10];
                    } else if(toupper(*p)=='R'){
                        iret=nunstruct[5].calib[8];
                    } else error("Syntax");
                } else if(toupper(*p)=='Y'){
                    p++;
                    if(toupper(*p)=='T'){
                        iret=nunstruct[5].calib[11];
                    } else if(toupper(*p)=='C'){
                        iret=nunstruct[5].calib[13];
                    } else if(toupper(*p)=='B'){
                        iret=nunstruct[5].calib[12];
                    } else error("Syntax");
                } else error("Syntax");
            }
        } else {
            if(toupper(*p)=='Z')iret=nunstruct[5].Z;
            else if(toupper(*p)=='C')iret=nunstruct[5].C;
            else if(toupper(*p)=='T')iret=nunstruct[5].type;
            else error("Syntax");
        }
	    targ=T_INT;
    } else if((tp=checkstring(ep,(unsigned char *)"GAMEPAD"))){
      //	int ax; //classic left x
        //	int ay; //classic left y
        //	int az; //classic centre
        //	int Z;  //classic right x
        //	int C;  //classic right y
        //	int L;  //classic left analog
        //	int R;  //classic right analog
        //	unsigned short x0; //classic buttons
         getargs(&tp,3,(unsigned char *)",");
         int n=getint(argv[0],1,4);
         if(checkstring(argv[2], (unsigned char *)"LX"))iret=nunstruct[n].ax;
        else if(checkstring(argv[2], (unsigned char *)"LY"))iret=nunstruct[n].ay;
        else if(checkstring(argv[2], (unsigned char *)"RX"))iret=nunstruct[n].Z;
        else if(checkstring(argv[2], (unsigned char *)"RY"))iret=nunstruct[n].C;
        else if(checkstring(argv[2], (unsigned char *)"L"))iret=nunstruct[n].L;
        else if(checkstring(argv[2], (unsigned char *)"R"))iret=nunstruct[n].R;
        else if(checkstring(argv[2], (unsigned char *)"B"))iret=nunstruct[n].x0;
        else if(checkstring(argv[2], (unsigned char *)"GX"))iret=nunstruct[n].gyro[0];
        else if(checkstring(argv[2], (unsigned char *)"GY"))iret=nunstruct[n].gyro[1];
        else if(checkstring(argv[2], (unsigned char *)"GZ"))iret=nunstruct[n].gyro[2];
        else if(checkstring(argv[2], (unsigned char *)"AX"))iret=nunstruct[n].accs[0];
        else if(checkstring(argv[2], (unsigned char *)"AY"))iret=nunstruct[n].accs[1];
        else if(checkstring(argv[2], (unsigned char *)"AZ"))iret=nunstruct[n].accs[2];
        else if(checkstring(argv[2], (unsigned char *)"T"))iret=nunstruct[n].type;
#ifdef USBKEYBOARD
        else if(checkstring(argv[2], (unsigned char *)"RAW")){
            sret=GetTempMemory(STRINGSIZE);
            targ=T_STR;
            Mstrcpy(sret,(unsigned char *)HID[n-1].report);
            return;
        }
#endif
        else iret=0;
        targ=T_INT;
    } else if((tp=checkstring(ep,(unsigned char *)"MOUSE"))){
/*        Returns data from a PS2 mouse
        'funct' is a 1 letter code indicating the information to return as follows:
        X returns the value of the mouse X-position
        Y returns the value of the mouse Y-position
        L returns the value of the left mouse button (1 if pressed)
        R returns the value of the right mouse button (1 if pressed)
        W returns the value of the scroll wheel mouse button (1 if pressed)
        D This allows you to detect a double click of the left mouse button. The
        algorithm requires that the two clicks must occur between 100 and
        500 milliseconds apart. The report via MOUSE(D) is then valid for
        500mSec before it times out or until it is read.
        T This returns 3 if a PS2 mouse has a scroll wheel or 0 if not.*/

         getargs(&tp,3,(unsigned char *)",");
         int n=getint(argv[0],1,4);
         if(checkstring(argv[2], (unsigned char *)"X"))iret=nunstruct[n].ax;
        else if(checkstring(argv[2], (unsigned char *)"Y"))iret=nunstruct[n].ay;
        else if(checkstring(argv[2], (unsigned char *)"L"))iret=nunstruct[n].L;
        else if(checkstring(argv[2], (unsigned char *)"R"))iret=nunstruct[n].R;
        else if(checkstring(argv[2], (unsigned char *)"M"))iret=nunstruct[n].C;
        else if(checkstring(argv[2], (unsigned char *)"W"))iret=nunstruct[n].az;
        else if(checkstring(argv[2], (unsigned char *)"D")){iret=nunstruct[n].Z;nunstruct[n].Z=0;}
        else if(checkstring(argv[2], (unsigned char *)"T"))iret=nunstruct[n].classic[0];
        else error("Syntax");
        targ=T_INT;
    } else error("Syntax");

}

void cmd_WS2812(void){
        int64_t *dest=NULL;
        uint32_t pin, red , green, blue, white, colour;
        int T0H=0,T0L=0,T1H=0,T1L=0,TRST=0;
        unsigned char *p;
        int i, j, bit, nbr=0, colours=3;
        int ticks_per_millisecond=ticks_per_second/1000; 
    	getargs(&cmdline, 7, (unsigned char *)",");
        if(argc != 7)error("Argument count");
    	p=argv[0];
    	if(toupper(*p)=='O'){
    		T0H=16777215 + setuptime-((7*ticks_per_millisecond)/20000) ;
    		T1H=16777215 + setuptime-((14*ticks_per_millisecond)/20000) ;
    		T0L=16777215 + setuptime-((13*ticks_per_millisecond)/20000) ;
    		T1L=16777215 + setuptime-((9.5*ticks_per_millisecond)/20000) ;
            TRST=50;
    	} else if(toupper(*p)=='B'){
    		T0H=16777215 + setuptime-((8*ticks_per_millisecond)/20000) ;
    		T1H=16777215 + setuptime-((16*ticks_per_millisecond)/20000) ;
    		T0L=16777215 + setuptime-((14*ticks_per_millisecond)/20000) ;
    		T1L=16777215 + setuptime-((6.5*ticks_per_millisecond)/20000) ;
            TRST=280;
    	} else if(toupper(*p)=='S' || toupper(*p)=='W' ){
            if(toupper(*p)=='W')colours=4;
    		T0H=16777215 + setuptime-((6*ticks_per_millisecond)/20000) ;
    		T0L=16777215 + setuptime-((15*ticks_per_millisecond)/20000) ;
    		T1H=16777215 + setuptime-((12*ticks_per_millisecond)/20000) ;
    		T1L=16777215 + setuptime-((9*ticks_per_millisecond)/20000) ;
            TRST=80;
    	} else error("Syntax");
        nbr=getint(argv[4],1,256);
        if(nbr>1){
            parseintegerarray(argv[6], &dest, 4, 1, NULL, false);
        } else {
            colour=getinteger(argv[6]);
            dest = (long long int *)&colour;
        }
        unsigned char code;
        if(!(code=codecheck(argv[2])))argv[2]+=2;
        pin = getinteger(argv[2]);
        if(!code)pin=codemap(pin);
        if(IsInvalidPin(pin)) error("Invalid pin");
        int gppin=PinDef[pin].GPno;
        if(!(ExtCurrentConfig[pin] == EXT_DIG_OUT || ExtCurrentConfig[pin] == EXT_NOT_CONFIG)) error("Pin %/| is not off or an output",pin,pin);
        if(ExtCurrentConfig[pin] == EXT_NOT_CONFIG)ExtCfg(pin, EXT_DIG_OUT, 0);
		p=GetTempMemory((nbr+1)*colours);
		uint64_t endreset=time_us_64()+TRST;
    	for(i=0;i<nbr;i++){
    		green=(dest[i]>>8) & 0xFF;
    		red=(dest[i]>>16) & 0xFF;
    		blue=dest[i] & 0xFF;
            if(colours==4)white=(dest[i]>>24) & 0xFF;
			p[0]=0;p[1]=0;p[2]=0;
            if(colours==4){p[3]=0;}
    		for(j=0;j<8;j++){
    			bit=1<<j;
    			if( green &  (1<<(7-j)) )p[0] |= bit;
    			if(red   & (1<<(7-j)))p[1] |= bit;
    			if(blue  & (1<<(7-j)))p[2] |= bit;
                if(colours==4){
    			    if(white  & (1<<(7-j)))p[3] |= bit;
                }
    		}
    		p+=colours;
    	}
    	p-=(nbr*colours);
        while(time_us_64()<endreset){}
        disable_interrupts_pico();
        WS2812e(gppin, T1H, T1L, T0H, T0L, nbr*colours, (char *)p);
        enable_interrupts_pico();
}
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */
void __not_in_flash_func(bitstream)(int gppin, unsigned int *data, int num){
    for(int i=0;i<num;i++){
        gpio_xor_mask64(gppin);
        shortpause(data[i])
    }
}
void __not_in_flash_func(serialtx)(int gppin, unsigned char *string, int bittime){
    int mask;
    int count = 0;
    while(count++ < string[0]) {
        systick_hw->cvr=0;
        gpio_clr_mask64(gppin);                                    // send the start bit
        mask=1;
        while(systick_hw->cvr>bittime){}; 
        systick_hw->cvr=0;
        for (mask=1;mask<0x100; mask<<=1) {
            if(string[count] & mask) {                               // check the bit to send
                gpio_set_mask64(gppin);                                    // send the start bit
            } else {
                gpio_clr_mask64(gppin);                                    // send the start bit
            }
            while(systick_hw->cvr>bittime){}; 
            systick_hw->cvr=0;
        }
        gpio_set_mask64(gppin);                                    // send the start bit
        while(systick_hw->cvr>bittime){}; 
    }
}
unsigned short FloatToUint32(MMFLOAT x) {
    if(x<0 || x > 4294967295)
        error("Number range");
    return (x >= 0 ? (unsigned int)(x + 0.5) : (unsigned int)(x - 0.5)) ;
}
int __not_in_flash_func(serialrx)(int gppin, unsigned char *string, int timeout, int bittime, int half, int maxchars, char *termchars){
    int i,c,count=0;
    while(1){
        while(gpio_get_all64() & gppin) {                              // wait for the start bit
            if(readusclock() >= timeout) return -1;                   // return if there is a timeout
        }
        systick_hw->cvr=0;
        while(systick_hw->cvr>half){}; 
        systick_hw->cvr=0;
        if(gpio_get_all64() & gppin) continue;                         // go around again if not low
        c=0;
        for(i = 0; i < 8; i++) {
            while(systick_hw->cvr>bittime){}; 
            systick_hw->cvr=0;
            c |= (((gpio_get_all64() & gppin) ? 1 : 0) << i);                      // and add this bit in
        }
        while(systick_hw->cvr>bittime){}; 
        if(!(gpio_get_all64() & gppin)) continue;                      // a framing error if not high
        count++;
        string[count] = c;                                          // save the character
        string[0] = count;                                          // and update the numbers of characters in the string
        if(count >= maxchars) return 2;                // do we have our maximun number of characters?
        if(termchars) {
            for(i = 0; i < termchars[0]; i++) {                     // check each terminating character
                if(c == termchars[i + 1]) return 3;                 // and exit if this character is one of them
            }
        }
    }
}
/*  @endcond */
void cmd_device(void){
	unsigned char *tp;
	tp = checkstring(cmdline, (unsigned char *)"WS2812");
	if(tp) {
        cmdline=tp;
		cmd_WS2812();
		return;
	}
	tp = checkstring(cmdline, (unsigned char *)"KEYPAD");
	if(tp) {
        cmdline=tp;
		cmd_keypad();
		return;
	}
	tp = checkstring(cmdline, (unsigned char *)"LCD");
	if(tp) {
        cmdline=tp;
		cmd_lcd();
		return;
	}
#ifndef PICOMITEVGA
	tp = checkstring(cmdline, (unsigned char *)"CAMERA");
	if(tp) {
        cmdline=tp;
		cmd_camera();
		return;
	}
#endif
    tp = checkstring(cmdline, (unsigned char *)"WII CLASSIC");
    if(tp==NULL)tp = checkstring(cmdline, (unsigned char *)"WII");
	if(tp) {
        cmdline=tp;
		cmd_Classic();
		return;
	}
	tp = checkstring(cmdline, (unsigned char *)"NUNCHUCK");
	if(tp) {
        cmdline=tp;
		cmd_Nunchuck();
		return;
	}
#ifdef USBKEYBOARD
	tp = checkstring(cmdline, (unsigned char *)"MOUSE");
	if(tp) {
        cmdline=tp;
		cmd_mouse();
		return;
	}
	tp = checkstring(cmdline, (unsigned char *)"GAMEPAD");
	if(tp) {
        cmdline=tp;
		cmd_gamepad();
		return;
	}
#endif
	tp = checkstring(cmdline, (unsigned char *)"HUMID");
	if(tp) {
        cmdline=tp;
		cmd_DHT22();
		return;
	}
	tp = checkstring(cmdline, (unsigned char *)"SERIALRX");
	if(tp) {
        int maxchars=255;
        char *termchars=NULL;
		getargs(&tp, 13,(unsigned char *)",");
		if(argc < 9) error("Argument count");
        unsigned char code;
        if(!(code=codecheck(argv[0])))argv[0]+=2;
        int pin = getinteger(argv[0]);
        if(!code)pin=codemap(pin);
        if(IsInvalidPin(pin)) error("Invalid pin");
        if(!(ExtCurrentConfig[pin] == EXT_DIG_IN || ExtCurrentConfig[pin] == EXT_NOT_CONFIG)) error("Pin %/| is not off or an input",pin,pin);
        if(ExtCurrentConfig[pin] == EXT_NOT_CONFIG)ExtCfg(pin, EXT_DIG_IN, CNPUSET);
        int gppin=(1<<PinDef[pin].GPno);
        int baudrate=getint(argv[2],110,230400);
        unsigned char *string=NULL;
        string = findvar(argv[4], V_FIND);
	    if(!(g_vartbl[g_VarIndex].type & T_STR)) error("Invalid variable");
        int timeout=getint(argv[6],1,100000)*1000;
        void *status=findvar(argv[8], V_FIND);
        int type=g_vartbl[g_VarIndex].type;
	    if(!(type & (T_NBR | T_INT))) error("Invalid variable");
        if(argc>9 && *argv[10])maxchars=getint(argv[10],1,255);
        if(argc==13)termchars=(char *)getstring(argv[12]);
        writeusclock(0);
        int bittime=16777215 + 12  - (ticks_per_second/baudrate) ;
        int half = 16777215 + 12  - (ticks_per_second/(baudrate<<1)) ;
        if(!(gpio_get_all64() & gppin))error("Framing error");
        disable_interrupts_pico();
        int istat=serialrx(gppin, string, timeout, bittime, half, maxchars, termchars);
        enable_interrupts_pico();
        if(type & T_INT)*(int64_t *)status=(int64_t)istat;
        else *(MMFLOAT *)status=(MMFLOAT)istat;
        return;
    }
	tp = checkstring(cmdline, (unsigned char *)"SERIALTX");
	if(tp) {
//        int mask;
		getargs(&tp, 5,(unsigned char *)",");
		if(!(argc == 5)) error("Argument count");
        unsigned char code;
//        int count = 0;
        if(!(code=codecheck(argv[0])))argv[0]+=2;
        int pin = getinteger(argv[0]);
        if(!code)pin=codemap(pin);
        if(IsInvalidPin(pin)) error("Invalid pin");
        int gppin=(1<<PinDef[pin].GPno);
        int baudrate=getint(argv[2],110,230400);
        unsigned char *string=getstring(argv[4]);
        if(!(ExtCurrentConfig[pin] == EXT_DIG_OUT || ExtCurrentConfig[pin] == EXT_NOT_CONFIG)) error("Pin %/| is not off or an output",pin,pin);
        if(ExtCurrentConfig[pin] == EXT_NOT_CONFIG)ExtCfg(pin, EXT_DIG_OUT, 0);
        gpio_set_mask64(gppin);                                    // send the start bit
        int bittime=16777215 + 12  - (ticks_per_second/baudrate) ;
        disable_interrupts_pico();
        serialtx(gppin,string, bittime);
        enable_interrupts_pico();
		return;
	}
	tp = checkstring(cmdline, (unsigned char *)"BITSTREAM");
	if(tp) {
		int i,num,size;
		uint32_t pin;
        int ticks_per_millisecond=ticks_per_second/1000;
		MMFLOAT *a1float=NULL;
		int64_t *a1int=NULL;
		unsigned int *data;
		getargs(&tp, 5,(unsigned char *)",");
		if(!(argc == 5)) error("Argument count");
		num=getint(argv[2],1,10000);
        unsigned char code;
        if(!(code=codecheck(argv[0])))argv[0]+=2;
        pin = getinteger(argv[0]);
        if(!code)pin=codemap(pin);
        if(IsInvalidPin(pin)) error("Invalid pin");
        int gppin=(1<<PinDef[pin].GPno);
        if(!(ExtCurrentConfig[pin] == EXT_DIG_OUT || ExtCurrentConfig[pin] == EXT_NOT_CONFIG)) error("Pin %/| is not off or an output",pin,pin);
        if(ExtCurrentConfig[pin] == EXT_NOT_CONFIG)ExtCfg(pin, EXT_DIG_OUT, 0);
        size=parsenumberarray(argv[4],&a1float, &a1int, 3, 1, NULL, false);
        if(size < num)error("Array too small");
        data=GetTempMemory(num * sizeof(unsigned int));
        if(a1float!=NULL){
            for(i=0; i< num;i++)data[i]= FloatToUint32(*a1float++);
        } else {
            for(i=0; i< num;i++){
                if(*a1int <0 || *a1int>67108)error("Number range");
                data[i]= *a1int++ ;
            }
        }
        for(i=0; i< num;i++){
            data[i]=16777215 + setuptime-((data[i]*ticks_per_millisecond)/1000) ;
        }
//        data[0]+=((ticks_per_millisecond/2000)+(250000-Option.CPU_Speed)/1000);
        disable_interrupts_pico();
        bitstream(gppin,data,num);
        enable_interrupts_pico();
		return;
	}
    error("Syntax");
}
/* 
 * @cond
 * The following section will be excluded from the documentation.
 */
void __not_in_flash_func(ADCint)()
{
	// Clear the interrupt request for DMA control channel
	dma_hw->ints1 = (1u << ADC_dma_chan);
    if(adcint==adcint2)adcint=adcint1;
    else adcint=adcint2;
    ADCDualBuffering=true;
}
/*  @endcond */

void cmd_adc(void){
	unsigned char *tp;
	tp = checkstring(cmdline, (unsigned char *)"OPEN");
	if(tp) {
        getargs(&tp,5,(unsigned char *)",");
        if(ADCopen)error("Already open");
        if(!(argc==3 || argc==5))error("Syntax");
#ifndef PICOMITEWEB
        int nbr=getint(argv[2],1,4); //number of ADC channels
#else
        int nbr=getint(argv[2],1,3); //number of ADC channels
#endif
        frequency=(float)getnumber(argv[0])*nbr;
        if(frequency<ADC_CLK_SPEED/65536.0/96.0 || frequency> ADC_CLK_SPEED/96.0)error("Invalid frequency");
#ifdef rp2350
#ifdef PICOMITEWEB
        if(!(ExtCurrentConfig[31] == EXT_ANA_IN || ExtCurrentConfig[31] == EXT_NOT_CONFIG)) error("Pin GP26 is not off or an ADC input");
        if(ExtCurrentConfig[31] == EXT_NOT_CONFIG)ExtCfg(31, EXT_ANA_IN, 0);
        ExtCfg(31, EXT_COM_RESERVED, 0);
        if(nbr>=2){
            if(!(ExtCurrentConfig[32] == EXT_ANA_IN || ExtCurrentConfig[32] == EXT_NOT_CONFIG)) error("Pin GP27 is not off or an ADC input");
            if(ExtCurrentConfig[32] == EXT_NOT_CONFIG)ExtCfg(32, EXT_ANA_IN, 0);
            ExtCfg(32, EXT_COM_RESERVED, 0);
        }
        if(nbr>=3){
            if(!(ExtCurrentConfig[34] == EXT_ANA_IN || ExtCurrentConfig[34] == EXT_NOT_CONFIG)) error("Pin GP28 is not off or an ADC input");
            if(ExtCurrentConfig[34] == EXT_NOT_CONFIG)ExtCfg(34, EXT_ANA_IN, 0);
            ExtCfg(34, EXT_COM_RESERVED, 0);
        }
#else
if(rp2350a){
        if(!(ExtCurrentConfig[31] == EXT_ANA_IN || ExtCurrentConfig[31] == EXT_NOT_CONFIG)) error("Pin GP26 is not off or an ADC input");
        if(ExtCurrentConfig[31] == EXT_NOT_CONFIG)ExtCfg(31, EXT_ANA_IN, 0);
        ExtCfg(31, EXT_COM_RESERVED, 0);
        if(nbr>=2){
            if(!(ExtCurrentConfig[32] == EXT_ANA_IN || ExtCurrentConfig[32] == EXT_NOT_CONFIG)) error("Pin GP27 is not off or an ADC input");
            if(ExtCurrentConfig[32] == EXT_NOT_CONFIG)ExtCfg(32, EXT_ANA_IN, 0);
            ExtCfg(32, EXT_COM_RESERVED, 0);
        }
        if(nbr>=3){
            if(!(ExtCurrentConfig[34] == EXT_ANA_IN || ExtCurrentConfig[34] == EXT_NOT_CONFIG)) error("Pin GP28 is not off or an ADC input");
            if(ExtCurrentConfig[34] == EXT_NOT_CONFIG)ExtCfg(34, EXT_ANA_IN, 0);
            ExtCfg(34, EXT_COM_RESERVED, 0);
        }
        if(nbr>=4){
            if(!(ExtCurrentConfig[44] == EXT_ANA_IN || ExtCurrentConfig[44] == EXT_NOT_CONFIG)) error("Pin GP29 is not off or an ADC input");
            if(ExtCurrentConfig[44] == EXT_NOT_CONFIG)ExtCfg(44, EXT_ANA_IN, 0);
            ExtCfg(44, EXT_COM_RESERVED, 0);
        }
} else {
        if(!(ExtCurrentConfig[55] == EXT_ANA_IN || ExtCurrentConfig[55] == EXT_NOT_CONFIG)) error("Pin GP40 is not off or an ADC input");
        if(ExtCurrentConfig[55] == EXT_NOT_CONFIG)ExtCfg(55, EXT_ANA_IN, 0);
        ExtCfg(55, EXT_COM_RESERVED, 0);
        if(nbr>=2){
            if(!(ExtCurrentConfig[56] == EXT_ANA_IN || ExtCurrentConfig[56] == EXT_NOT_CONFIG)) error("Pin GP41 is not off or an ADC input");
            if(ExtCurrentConfig[56] == EXT_NOT_CONFIG)ExtCfg(56, EXT_ANA_IN, 0);
            ExtCfg(56, EXT_COM_RESERVED, 0);
        }
        if(nbr>=3){
            if(!(ExtCurrentConfig[57] == EXT_ANA_IN || ExtCurrentConfig[57] == EXT_NOT_CONFIG)) error("Pin GP42 is not off or an ADC input");
            if(ExtCurrentConfig[57] == EXT_NOT_CONFIG)ExtCfg(57, EXT_ANA_IN, 0);
            ExtCfg(57, EXT_COM_RESERVED, 0);
        }
        if(nbr>=4){
            if(!(ExtCurrentConfig[58] == EXT_ANA_IN || ExtCurrentConfig[58] == EXT_NOT_CONFIG)) error("Pin GP43 is not off or an ADC input");
            if(ExtCurrentConfig[58] == EXT_NOT_CONFIG)ExtCfg(58, EXT_ANA_IN, 0);
            ExtCfg(58, EXT_COM_RESERVED, 0);
        }
}
#endif
#else
        if(!(ExtCurrentConfig[31] == EXT_ANA_IN || ExtCurrentConfig[31] == EXT_NOT_CONFIG)) error("Pin GP26 is not off or an ADC input");
        if(ExtCurrentConfig[31] == EXT_NOT_CONFIG)ExtCfg(31, EXT_ANA_IN, 0);
        ExtCfg(31, EXT_COM_RESERVED, 0);
        if(nbr>=2){
            if(!(ExtCurrentConfig[32] == EXT_ANA_IN || ExtCurrentConfig[32] == EXT_NOT_CONFIG)) error("Pin GP27 is not off or an ADC input");
            if(ExtCurrentConfig[32] == EXT_NOT_CONFIG)ExtCfg(32, EXT_ANA_IN, 0);
            ExtCfg(32, EXT_COM_RESERVED, 0);
        }
        if(nbr>=3){
            if(!(ExtCurrentConfig[34] == EXT_ANA_IN || ExtCurrentConfig[34] == EXT_NOT_CONFIG)) error("Pin GP28 is not off or an ADC input");
            if(ExtCurrentConfig[34] == EXT_NOT_CONFIG)ExtCfg(34, EXT_ANA_IN, 0);
            ExtCfg(34, EXT_COM_RESERVED, 0);
        }
#ifndef PICOMITEWEB
        if(nbr>=4){
            if(!(ExtCurrentConfig[44] == EXT_ANA_IN || ExtCurrentConfig[44] == EXT_NOT_CONFIG)) error("Pin GP29 is not off or an ADC input");
            if(ExtCurrentConfig[44] == EXT_NOT_CONFIG)ExtCfg(44, EXT_ANA_IN, 0);
            ExtCfg(44, EXT_COM_RESERVED, 0);
        }
#endif
#endif
        if(argc==5){
        	InterruptUsed = true;
        	ADCInterrupt = (char *)GetIntAddress(argv[4]);							// get the interrupt location
    	} else ADCInterrupt = NULL;
        ADCopen=nbr;
		return;
	}
	tp = checkstring(cmdline, (unsigned char *)"RUN");
    if(tp){
        getargs(&tp, 3, (unsigned char *)",");
		if(!ADCopen)error("ADC not open");
        if(!(argc == 3))error("Argument count");
        ADCmax=0;
        ADCpos=0;
        adcint1=adcint2=NULL;
        int64_t *adcval=NULL;
#ifdef rp2350
        int dims[MAXDIM]={0};
#else
        short dims[MAXDIM]={0};
#endif
        int card1=parseintegerarray(argv[0], &adcval, 1, 1, dims, true);
        adcint1=(uint8_t *)adcval;
        adcval=NULL;
        ADCmax=parseintegerarray(argv[2], &adcval, 2, 1, dims, true);
        adcint2=(uint8_t *)adcval;
        if(card1!=ADCmax)error("Array size mismatch %,%",card1, ADCmax);
        ADCmax *=8;
        dma_channel_cleanup(ADC_dma_chan);
        dma_channel_cleanup(ADC_dma_chan2);
        adc_init();
        adc_set_round_robin(ADCopen==1 ? 1 : ADCopen==2 ? 3 : ADCopen==3 ? 7 : 15);
        adc_fifo_setup(
            true,    // Write each completed conversion to the sample FIFO
            true,    // Enable DMA data request (DREQ)
            1,       // DREQ (and IRQ) asserted when at least 1 sample present
            false,   // We won't see the ERR bit because of 8 bit reads; disable.
            true     // Shift each sample to 8 bits when pushing to FIFO
        );
        adcint=adcint1;
        SetADCFreq(frequency);
        // Set up the DMA to start transferring data as soon as it appears in FIFO
        dma_channel_config cfg = dma_channel_get_default_config(ADC_dma_chan);

        // Reading from constant address, writing to incrementing byte addresses
        channel_config_set_transfer_data_size(&cfg, DMA_SIZE_8);
        channel_config_set_read_increment(&cfg, false);
        channel_config_set_write_increment(&cfg, true);
        channel_config_set_irq_quiet(&cfg, false);
        channel_config_set_dreq(&cfg, DREQ_ADC);
        channel_config_set_chain_to(&cfg, ADC_dma_chan2);
        dma_channel_configure(ADC_dma_chan, &cfg,
            adcint,    // dst
            &adc_hw->fifo,  // src
            ADCmax,  // transfer count
            false            // start immediately
        );
        dma_channel_config c2 = dma_channel_get_default_config(ADC_dma_chan2); //Get configurations for control channel
        channel_config_set_transfer_data_size(&c2, DMA_SIZE_32); //Set control channel data transfer size to 32 bits
        channel_config_set_read_increment(&c2, false); //Set control channel read increment to false
        channel_config_set_write_increment(&c2, false); //Set control channel write increment to false
        channel_config_set_dreq(&c2, 0x3F);
//                                channel_config_set_chain_to(&c2, dma_tx_chan); 
        dma_channel_configure(ADC_dma_chan2,
                &c2,
                &dma_hw->ch[ADC_dma_chan].al2_write_addr_trig,
                &adcint,
                1,
                false); //Configure control channel  
	    dma_channel_set_irq1_enabled(ADC_dma_chan, true);

	// set DMA IRQ handler
        irq_set_exclusive_handler(DMA_IRQ_1, ADCint);
	// set highest IRQ priority
        irq_set_enabled(DMA_IRQ_1, true);
        dma_start_channel_mask(1u << ADC_dma_chan2);
        adc_run(true);
        adcint=adcint2;
        ADCDualBuffering=false;
		return;
	}

	tp = checkstring(cmdline, (unsigned char *)"FREQUENCY");
	if(tp) {
        getargs(&tp,1,(unsigned char *)",");
        if(!ADCopen)error("Not open");
        float localfrequency=(float)getnumber(argv[0])*ADCopen;
        if(localfrequency<ADC_CLK_SPEED/65536.0/96.0 || localfrequency> ADC_CLK_SPEED/96.0)error("Invalid frequency");
        frequency=localfrequency;
		return;
	}
	tp = checkstring(cmdline, (unsigned char *)"START");
	if(tp) {
        getargs(&tp, 23, (unsigned char *)",");
		if(!ADCopen)error("ADC not open");
        if(!(argc >= 1))error("Argument count");
        a1float=NULL; a2float=NULL; a3float=NULL; a4float=NULL;
        ADCmax=0;
        ADCpos=0;
        int card;
        MMFLOAT top;
        for(int i=0;i<4;i++){
            ADCscale[i]=VCC/4095.0;
            ADCbottom[i]=0;
        }
        ADCmax=parsefloatrarray(argv[0], (MMFLOAT **)&a1float, 1, 1, NULL, true);
        if(argc>=3 && *argv[2]){
            if(ADCopen<2)error("Second channel not open");
            card=parsefloatrarray(argv[2], (MMFLOAT **)&a2float, 2, 1, NULL, true);
            if(card!=ADCmax)error("Array size mismatch");
        }
        if(argc>=5 && *argv[4]){
           if(ADCopen<3)error("Third channel not open");
            card=parsefloatrarray(argv[4], (MMFLOAT **)&a3float, 3, 1, NULL, true);
            if(card!=ADCmax)error("Array size mismatch");
        }
        if(argc>=7 && *argv[6]){
           if(ADCopen<4)error("Fourth channel not open");
            card=parsefloatrarray(argv[6], (MMFLOAT **)&a4float, 4, 1, NULL, true);
            if(card!=ADCmax)error("Array size mismatch");
        }
        if(argc>=11){
            ADCbottom[0]=getnumber(argv[8]);
            top=getnumber(argv[10]);
            ADCscale[0]=(top-ADCbottom[0])/4095.0;
        }
        if(argc>=15){
            ADCbottom[1]=getnumber(argv[12]);
            top=getnumber(argv[14]);
            ADCscale[1]=(top-ADCbottom[1])/4095.0;
        }
        if(argc>=19){
            ADCbottom[2]=getnumber(argv[16]);
            top=getnumber(argv[18]);
            ADCscale[2]=(top-ADCbottom[2])/4095.0;
        }
        if(argc>=23){
            ADCbottom[3]=getnumber(argv[20]);
            top=getnumber(argv[22]);
            ADCscale[3]=(top-ADCbottom[3])/4095.0;
        }
        ADCmax++;
        ADCbuffer=GetMemory(ADCmax*ADCopen*2);
        adc_init();
        adc_set_round_robin(ADCopen==1 ? 1 : ADCopen==2 ? 3 : ADCopen==3 ? 7 : 15);
        adc_fifo_setup(
            true,    // Write each completed conversion to the sample FIFO
            true,    // Enable DMA data request (DREQ)
            1,       // DREQ (and IRQ) asserted when at least 1 sample present
            false,   // We won't see the ERR bit because of 8 bit reads; disable.
            false     // Shift each sample to 8 bits when pushing to FIFO
        );
        SetADCFreq(frequency);
        // Set up the DMA to start transferring data as soon as it appears in FIFO
        dma_channel_config cfg = dma_channel_get_default_config(ADC_dma_chan);

        // Reading from constant address, writing to incrementing byte addresses
        channel_config_set_transfer_data_size(&cfg, DMA_SIZE_16);
        channel_config_set_read_increment(&cfg, false);
        channel_config_set_write_increment(&cfg, true);
        channel_config_set_irq_quiet(&cfg, true);

        // Pace transfers based on availability of ADC samples
        channel_config_set_dreq(&cfg, DREQ_ADC);

        dma_channel_configure(ADC_dma_chan, &cfg,
            (uint8_t *)ADCbuffer,    // dst
            &adc_hw->fifo,  // src
            ADCmax*ADCopen,  // transfer count
            true            // start immediately
        );
        adc_run(true);

        // Once DMA finishes, stop any new conversions from starting, and clean up
        // the FIFO in case the ADC was still mid-conversion.
        if(!ADCInterrupt){
            while(dma_channel_is_busy(ADC_dma_chan))tight_loop_contents();
            __compiler_memory_barrier();
            adc_run(false);
            adc_fifo_drain();
            int k=0;
            for(int i=0;i<ADCmax;i++){
                for(int j=0;j<ADCopen;j++){
                    if(j==0)*a1float++ = (MMFLOAT)ADCbuffer[k++]*ADCscale[0]+ADCbottom[0];
                    if(j==1)*a2float++ = (MMFLOAT)ADCbuffer[k++]*ADCscale[1]+ADCbottom[1];
                    if(j==2)*a3float++ = (MMFLOAT)ADCbuffer[k++]*ADCscale[2]+ADCbottom[2];
                    if(j==3)*a4float++ = (MMFLOAT)ADCbuffer[k++]*ADCscale[3]+ADCbottom[3];
                }
            }
            FreeMemory((void *)ADCbuffer);
            adc_init();
            last_adc=99;
        } else dmarunning=true;
		return;
	}
	tp = checkstring(cmdline, (unsigned char *)"CLOSE");
	if(tp) {
        if(!ADCopen)error("Not open");
        irq_set_enabled(DMA_IRQ_1, false);
        dma_hw->abort = ((1u << ADC_dma_chan2) | (1u << ADC_dma_chan));
        dma_channel_abort(ADC_dma_chan);
        dma_channel_abort(ADC_dma_chan2);
        adc_set_round_robin(0);
        SetADCFreq(500000);
        #ifdef rp2350
if(rp2350a){
        ExtCfg(31, EXT_NOT_CONFIG, 0);
        if(ADCopen>=2)ExtCfg(32, EXT_NOT_CONFIG, 0);
        if(ADCopen>=3)ExtCfg(34, EXT_NOT_CONFIG, 0);
        if(ADCopen>=4)ExtCfg(44, EXT_NOT_CONFIG, 0);
} else {
        ExtCfg(55, EXT_NOT_CONFIG, 0);
        if(ADCopen>=2)ExtCfg(56, EXT_NOT_CONFIG, 0);
        if(ADCopen>=3)ExtCfg(57, EXT_NOT_CONFIG, 0);
        if(ADCopen>=4)ExtCfg(58, EXT_NOT_CONFIG, 0);
}
#else
        ExtCfg(31, EXT_NOT_CONFIG, 0);
        if(ADCopen>=2)ExtCfg(32, EXT_NOT_CONFIG, 0);
        if(ADCopen>=3)ExtCfg(34, EXT_NOT_CONFIG, 0);
        if(ADCopen>=4)ExtCfg(44, EXT_NOT_CONFIG, 0);
#endif
        ADCopen=0;
        adcint=adcint1=adcint2=NULL;
        ADCDualBuffering=false;
        dmarunning=false;
        last_adc=99;
        adc_init();
		return;
	}
    error("Syntax");
}
void SetADCFreq(float frequency){
    //Our ADC clock is running at ADC_CLK_SPEED (in Hz) so we need to stretch the time to produce the required frequency
    //The time delta for our frequency is 1/frequency*96 - 1/(ADC_CLK_SPEED) seconds
    //This must be added to clk_div as a number of CPU clock ticks i.e. delta/(1/ADC_CLK_SPEED)
    //Plus we need to add 95 to cater for the actual time taken for the conversion
    double delta = 1.0/(frequency*96.0) - 1.0/((double)ADC_CLK_SPEED);
    float div=delta/(1.0/((double)ADC_CLK_SPEED))*96.0 +95.0;
    if(div<=96.0)div=0;
    adc_set_clkdiv(div);
}

/* 
 * @cond
 * The following section will be excluded from the documentation.
 */
void MIPS16 ClearExternalIO(void) {
    int i;
  	CloseAudio(1);
	#ifndef PICOMITEVGA
        cameraclose();
    #endif
    InterruptUsed = false;
	InterruptReturn = NULL;
    irq_set_enabled(DMA_IRQ_1, false);
#ifdef rp2350
    irq_set_enabled(PWM_IRQ_WRAP_1, false);
#endif
    closeframebuffer('A');
    if(CallBackEnabled==1) gpio_set_irq_enabled_with_callback(PinDef[IRpin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
    else if(CallBackEnabled & 1){
        gpio_set_irq_enabled(PinDef[IRpin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        CallBackEnabled &= (~1);
    }
    if(CallBackEnabled==2) gpio_set_irq_enabled_with_callback(PinDef[Option.INT1pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
    else if(CallBackEnabled & 2){
        gpio_set_irq_enabled(PinDef[Option.INT1pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        CallBackEnabled &= (~2);
    }
    if(CallBackEnabled==4) gpio_set_irq_enabled_with_callback(PinDef[Option.INT2pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
    else if(CallBackEnabled & 4){
        gpio_set_irq_enabled(PinDef[Option.INT2pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        CallBackEnabled &= (~4);
    }
    if(CallBackEnabled==8) gpio_set_irq_enabled_with_callback(PinDef[Option.INT3pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
    else  if(CallBackEnabled & 8){
        gpio_set_irq_enabled(PinDef[Option.INT3pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        CallBackEnabled &= (~8);
    }
    if(CallBackEnabled==16) gpio_set_irq_enabled_with_callback(PinDef[Option.INT4pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
    else  if(CallBackEnabled & 16){
        gpio_set_irq_enabled(PinDef[Option.INT4pin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false);
        CallBackEnabled &= (~16);
    }
    CallBackEnabled &= (~32);
    for(i=0;i<MAXBLITBUF;i++){
    	spritebuff[i].spritebuffptr = NULL;
    	blitbuff[i].blitbuffptr = NULL;
    }
    CallBackEnabled=0;
    KeypadClose();
    if(*lcd_pins){
        for(i = 0; i < 6; i++) {
          ExtCfg(lcd_pins[i], EXT_NOT_CONFIG, 0);                   // all set to unconfigured
            *lcd_pins = 0;
        }
    }
    memset(lcd_pins,0,sizeof(lcd_pins));
    SerialClose(1); 
    SerialClose(2);                                 // same for serial ports
    if(!I2C0locked)i2c_disable();   
    if(!I2C1locked)i2c2_disable();   
    sprite_transparent=0;
    SPIClose();
    SPI2Close();
    if(IRpin!=99){
        gpio_set_irq_enabled_with_callback(PinDef[IRpin].GPno, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, false, &gpio_callback);
        IrInterrupt = NULL;
        ExtCfg(IRpin, EXT_NOT_CONFIG, 0);
    }
    ResetDisplay();
    IrState = IR_CLOSED;
    IrInterrupt = NULL;
    IrGotMsg = false;
    memset(&PIDchannels,0,sizeof(s_PIDchan)*(MAXPID+1));
#ifdef rp2350
#ifdef PICOMITEWEB
	for(i = 1; i < (NBRPINS) ; i++) {
		if(CheckPin(i, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED)) {    // don't reset invalid or boot reserved pins
          ExtCfg(i, EXT_NOT_CONFIG, 0);                                       // all set to unconfigured
		}
	}
#else
	for(i = 1; i < (rp2350a ? 44: NBRPINS) ; i++) {
		if(CheckPin(i, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED)) {    // don't reset invalid or boot reserved pins
          ExtCfg(i, EXT_NOT_CONFIG, 0);                                       // all set to unconfigured
		}
	}
#endif
#else
	for(i = 1; i < (NBRPINS) ; i++) {
		if(CheckPin(i, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED)) {    // don't reset invalid or boot reserved pins
          ExtCfg(i, EXT_NOT_CONFIG, 0);                                       // all set to unconfigured
		}
	}
#endif
	for(i = 0; i < NBRINTERRUPTS; i++) {
      inttbl[i].pin = 0;                                            // disable all interrupts
  	}
    #ifndef PICOMITEWEB
    if(!Option.AllPins){
        if(CheckPin(41, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED))ExtCfg(41,EXT_DIG_OUT,Option.PWM);
        if(CheckPin(42, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED))ExtCfg(42,EXT_DIG_IN,0);
        if(CheckPin(44, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED))ExtCfg(44,EXT_ANA_IN,0);
    }
    if(CheckPin(HEARTBEATpin, CP_NOABORT | CP_IGNORE_INUSE | CP_IGNORE_RESERVED) && !Option.NoHeartbeat){
        gpio_init(PinDef[HEARTBEATpin].GPno);
        gpio_set_dir(PinDef[HEARTBEATpin].GPno, GPIO_OUT);
        ExtCurrentConfig[PinDef[HEARTBEATpin].pin]=EXT_HEARTBEAT;
    }
    #endif
    FreeMemorySafe((void **)&ds18b20Timers);
    KeypadInterrupt = NULL;

    for(i = 0; i < NBRSETTICKS; i++) TickInt[i] = NULL;
    for(i = 0; i < NBRSETTICKS; i++) TickActive[i] = 0;
	for(i = 0; i < NBR_PULSE_SLOTS; i++) PulseCnt[i] = 0;             // disable any pending pulse commands
    PulseActive = false;
#ifdef rp2350
    slice0=0;slice1=0;slice2=0;slice3=0;slice4=0;slice5=0;slice6=0;slice7=0;slice8=0;slice9=0;slice10=0;slice11=0;
    for(i=0; i<=(rp2350a ? 7:11);i++)if(!(i==Option.AUDIO_SLICE || i==BacklightSlice))PWMoff(i);
#else
    slice0=0;slice1=0;slice2=0;slice3=0;slice4=0;slice5=0;slice6=0;slice7=0;
    for(i=0; i<=7;i++)if(!(i==Option.AUDIO_SLICE || i==BacklightSlice))PWMoff(i);
#endif
    IRpin=99;
    PWM0Apin=99;
    PWM1Apin=99;
    PWM2Apin=99;
    PWM3Apin=99;
    PWM4Apin=99;
    PWM5Apin=99;
    PWM6Apin=99;
    PWM7Apin=99;
#ifdef rp2350
    PWM8Apin=99;
    PWM9Apin=99;
    PWM10Apin=99;
    PWM11Apin=99;
#endif
    PWM0Bpin=99;
    PWM1Bpin=99;
    PWM2Bpin=99;
    PWM3Bpin=99;
    PWM4Bpin=99;
    PWM5Bpin=99;
    PWM6Bpin=99;
    PWM7Bpin=99;
#ifdef rp2350
    PWM8Bpin=99;
    PWM9Bpin=99;
    PWM10Bpin=99;
    PWM11Bpin=99;
#endif
    UART1RXpin=99;
    UART1TXpin=99;
    UART0TXpin=99;
    UART0RXpin=99;
    SPI0TXpin=99;
    SPI0RXpin=99;
    SPI0SCKpin=99;
    SPI1TXpin=99;
    SPI1RXpin=99;
    SPI1SCKpin=99;
    if(!I2C0locked){
        I2C0SDApin=99;
        I2C0SCLpin=99;
    }
    if(!I2C1locked){
        I2C1SDApin=99;
        I2C1SCLpin=99;	
    }
#ifdef rp2350
if(rp2350a){
    if(ADCopen) ExtCfg(31, EXT_NOT_CONFIG, 0);
    if(ADCopen>=2)ExtCfg(32, EXT_NOT_CONFIG, 0);
    if(ADCopen>=3)ExtCfg(34, EXT_NOT_CONFIG, 0);
    if(ADCopen>=4)ExtCfg(44, EXT_NOT_CONFIG, 0);
} else {
    if(ADCopen) ExtCfg(55, EXT_NOT_CONFIG, 0);
    if(ADCopen>=2)ExtCfg(56, EXT_NOT_CONFIG, 0);
    if(ADCopen>=3)ExtCfg(57, EXT_NOT_CONFIG, 0);
    if(ADCopen>=4)ExtCfg(58, EXT_NOT_CONFIG, 0);
}
#else
    if(ADCopen) ExtCfg(31, EXT_NOT_CONFIG, 0);
    if(ADCopen>=2)ExtCfg(32, EXT_NOT_CONFIG, 0);
    if(ADCopen>=3)ExtCfg(34, EXT_NOT_CONFIG, 0);
    if(ADCopen>=4)ExtCfg(44, EXT_NOT_CONFIG, 0);
#endif
    ADCopen=0;
    adc_set_round_robin(0);
    SetADCFreq(500000);
    KeyInterrupt=NULL;
    OnKeyGOSUB=NULL;
#ifndef USBKEYBOARD
    OnPS2GOSUB=NULL;
    PS2code=0;
    PS2int=false;
    if(!Option.MOUSE_CLOCK)mouse0close();
#endif 
    for (int i=0; i<6;i++)nunInterruptc[i]=NULL;
    if((classic1 || nunchuck1) && (classicread || nunchuckread))WiiReceive(6, (char *)nunbuff);
    classic1=0;
    nunchuck1=0;
    classicread=false;
    nunchuckread=false;
#ifdef PICOMITEWEB
    MQTTInterrupt=NULL;
    MQTTComplete=0;
    closeMQTT();
#endif
#ifndef PICOMITEWEB
#ifdef PICOMITEVGA
    CollisionFound = false;
    COLLISIONInterrupt=NULL;
#endif
#endif
#ifdef GUICONTROLS
    gui_int_down = false;
    gui_int_up = false;
    GuiIntDownVector=NULL;
    GuiIntUpVector=NULL;
#endif
    dmarunning=false;
    ADCDualBuffering=false;
    ADCInterrupt=NULL;
    CSubInterrupt=NULL;
    CSubComplete=0;
    keyselect=0;
    g_myrand=NULL;
    CMM1=0;
    dirOK=2;
    nextline[0]=0;nextline[1]=0;nextline[2]=0;nextline[3]=99;
    memset(pioTXlast,0,sizeof(pioTXlast));
    memset(pioRXinterrupts,0,sizeof(pioRXinterrupts));
    memset(pioTXinterrupts,0,sizeof(pioTXinterrupts));
    piointerrupt=0;
    DMAinterruptRX=NULL;
    DMAinterruptTX=NULL;
    WAVInterrupt=NULL;
    dma_hw->abort = ((1u << dma_rx_chan2) | (1u << dma_rx_chan));
    if(dma_channel_is_busy(dma_rx_chan))dma_channel_abort(dma_rx_chan);
    if(dma_channel_is_busy(dma_rx_chan2))dma_channel_abort(dma_rx_chan2);
//    dma_channel_cleanup(dma_rx_chan);
//    dma_channel_cleanup(dma_rx_chan2);
    dma_hw->abort = ((1u << dma_tx_chan2) | (1u << dma_tx_chan));
    if(dma_channel_is_busy(dma_tx_chan))dma_channel_abort(dma_tx_chan);
    if(dma_channel_is_busy(dma_tx_chan2))dma_channel_abort(dma_tx_chan2);
//    dma_channel_cleanup(dma_tx_chan);
//    dma_channel_cleanup(dma_tx_chan2);
    dma_hw->abort = ((1u << ADC_dma_chan2) | (1u << ADC_dma_chan));
    if(dma_channel_is_busy(ADC_dma_chan))dma_channel_abort(ADC_dma_chan);
    if(dma_channel_is_busy(ADC_dma_chan2))dma_channel_abort(ADC_dma_chan2);
//    dma_channel_cleanup(ADC_dma_chan);
//    dma_channel_cleanup(ADC_dma_chan2);
    adcint=adcint1=adcint2=NULL;
}



void __not_in_flash_func(TM_EXTI_Handler_1)(void) {
	if(ExtCurrentConfig[Option.INT1pin] == EXT_PER_IN) {
        if(--INT1Timer <= 0) {
            INT1Value = INT1Count;
            INT1Timer = INT1InitTimer;
            INT1Count = 0;
        }
	}
    else {
        if(CFuncInt1)
            CallCFuncInt1();                                        // Hardware interrupt 2 for a CFunction (see CFunction.c)
        else
            INT1Count++;
    }
}



// perform the counting functions for INT2
void __not_in_flash_func(TM_EXTI_Handler_2)(void) {
    if(ExtCurrentConfig[Option.INT2pin] == EXT_PER_IN) {
        if(--INT2Timer <= 0) {
            INT2Value = INT2Count;
            INT2Timer = INT2InitTimer;
            INT2Count = 0;
        }
    }
    else {
        if(CFuncInt2)
            CallCFuncInt2();                                        // Hardware interrupt 2 for a CFunction (see CFunction.c)
        else
            INT2Count++;
    }
}




// perform the counting functions for INT3
void __not_in_flash_func(TM_EXTI_Handler_3)(void) {
	if(ExtCurrentConfig[Option.INT3pin] == EXT_PER_IN) {
        if(--INT3Timer <= 0) {
            INT3Value = INT3Count;
            INT3Timer = INT3InitTimer;
            INT3Count = 0;
        }
	}
    else {
        if(CFuncInt3)
            CallCFuncInt3();                                        // Hardware interrupt 2 for a CFunction (see CFunction.c)
        else
            INT3Count++;
    }
}




// perform the counting functions for INT4
void __not_in_flash_func(TM_EXTI_Handler_4)(void) {
	if(ExtCurrentConfig[Option.INT4pin] == EXT_PER_IN) {
        if(--INT4Timer <= 0) {
            INT4Value = INT4Count;
            INT4Timer = INT4InitTimer;
            INT4Count = 0;
        }
	}
    else {
        if(CFuncInt4)
            CallCFuncInt4();                                        // Hardware interrupt 2 for a CFunction (see CFunction.c)
        else
            INT4Count++;
    }

}
void MIPS16 __not_in_flash_func(IRHandler)(void) {
    int ElapsedMicroSec;
    static unsigned int LastIrBits;
        ElapsedMicroSec = readIRclock();
        switch(IrState) {
            case IR_WAIT_START:
                writeIRclock(0);                                           // reset the timer
                IrState = IR_WAIT_START_END;                        // wait for the end of the start bit
                break;
            case IR_WAIT_START_END:
                if(ElapsedMicroSec > 2000 && ElapsedMicroSec < 2800)
                    IrState = SONY_WAIT_BIT_START;                  // probably a Sony remote, now wait for the first data bit
                else if(ElapsedMicroSec > 8000 && ElapsedMicroSec < 10000)
                    IrState = NEC_WAIT_FIRST_BIT_START;             // probably an NEC remote, now wait for the first data bit
                else {
                    IrReset();                                      // the start bit was not valid
                    break;
                }
                IrCount = 0;                                        // count the bits in the message
                IrBits = 0;                                         // reset the bit accumulator
                writeIRclock(0);                                           // reset the timer
                break;
            case SONY_WAIT_BIT_START:
                if(ElapsedMicroSec < 300 || ElapsedMicroSec > 900) { IrReset(); break; }
                writeIRclock(0);                                           // reset the timer
                IrState = SONY_WAIT_BIT_END;                         // wait for the end of this data bit
                break;
            case SONY_WAIT_BIT_END:
                if(ElapsedMicroSec < 300 || ElapsedMicroSec > 1500 || IrCount > 20) { IrReset(); break; }
                IrBits |= (ElapsedMicroSec > 900) << IrCount;       // get the data bit
                IrCount++;                                          // and increment our count
                writeIRclock(0);                                           // reset the timer
                IrState = SONY_WAIT_BIT_START;                       // go back and wait for the next data bit
                break;
            case NEC_WAIT_FIRST_BIT_START:
            	if(ElapsedMicroSec > 2000 && ElapsedMicroSec < 2500) {
                    IrBits = LastIrBits;                            // key is held down so just repeat the last code
                    IrCount = 32;                                   // and signal that we are finished
                    IrState = NEC_WAIT_BIT_END;
                    break;
                }
                else if(ElapsedMicroSec > 4000 && ElapsedMicroSec < 5000)
                    IrState = NEC_WAIT_BIT_END;                     // wait for the end of this data bit
                else {
                    IrReset();                                      // the start bit was not valid
                    break;
                }
                writeIRclock(0);                                           // reset the timer
                break;
            case NEC_WAIT_BIT_START:
                if(ElapsedMicroSec < 400 || ElapsedMicroSec > 1800) { IrReset(); break; }
                IrBits |= (ElapsedMicroSec > 840) << (31 - IrCount);// get the data bit
                LastIrBits = IrBits;
                IrCount++;                                          // and increment our count
                writeIRclock(0);                                           // reset the timer
                IrState = NEC_WAIT_BIT_END;                         // wait for the end of this data bit
                break;
            case NEC_WAIT_BIT_END:
                if(ElapsedMicroSec < 400 || ElapsedMicroSec > 700) { IrReset(); break; }
                if(IrCount == 32) break;
                writeIRclock(0);                                           // reset the timer
                IrState = NEC_WAIT_BIT_START;                       // go back and wait for the next data bit
                break;
        }
    }
void __not_in_flash_func(gpio_callback)(uint gpio, uint32_t events) {
#ifndef USBKEYBOARD
    static uint64_t data;
    data=gpio_get_all64();
    if(Option.KEYBOARD_CLOCK) if( !(Option.KeyboardConfig == NO_KEYBOARD || Option.KeyboardConfig == CONFIG_I2C ) && gpio==PinDef[Option.KEYBOARD_CLOCK].GPno) CNInterrupt(data);
    if(MOUSE_CLOCK && gpio==PinDef[MOUSE_CLOCK].GPno)MNInterrupt(data);
#endif
    if(gpio==PinDef[IRpin].GPno)IRHandler();
    if(gpio==PinDef[Option.INT1pin].GPno)TM_EXTI_Handler_1();
    if(gpio==PinDef[Option.INT2pin].GPno)TM_EXTI_Handler_2();
    if(gpio==PinDef[Option.INT3pin].GPno)TM_EXTI_Handler_3();
    if(gpio==PinDef[Option.INT4pin].GPno)TM_EXTI_Handler_4();
}
/*  @endcond */

