test publish

Dependencies:   BLE_API nRF51822 mbed

Fork of KS7 by masaaki makabe

Branch:
KS3
Revision:
30:f67850cc3cfe
Child:
31:b5e19d153db4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/io.cpp	Mon May 23 05:15:58 2016 +0000
@@ -0,0 +1,633 @@
+#include "io.h"
+#include "mbed.h"
+
+// COL[2:0]: P0_27 P0_26 P0_21
+// ROW[7:0]: P0_19 P0_16 P0_15 P0_13 P0_12 P0_11 P0_09 P0_08
+// PowA    : P0_00
+// Vin     : P0_01
+// SWx/y   : P0_02 P0_03
+// PWM     : P0_05
+//
+//  0a 0b 0c  1f 1g 1h  3c 3d 3e  4h 5a 5b
+//  0d    0e  2a    2b  3f    3g  5c    5d  6e 6f 6g
+//  0f 0g 0h  2c 2d 2e  3h 4a 4b  5e 5f 5g  6h
+//  1a    1b  2f    2g  4c    4d  5h    6a  7a    7b
+//  1c 1d 1e  2h 3a 3b  4e 4f 4g  6b 6c 6d  7c 7d 7e
+// [hgfe:dcba]
+//
+//  1  2  3   14 15 16  27 28 29  40 41 42
+//  4     5   17    18  30    31  43    44  53 54 55
+//  6  7  8   19 20 21  32 33 34  45 46 47  56
+//  9    10   22    23  35    36  48    49  57    58
+// 11 12 13   24 25 26  37 38 39  50 51 52  59 60 61
+//  seg[3]     seg[2]    seg[1]    seg[0]
+//
+// COL 0   1    2     3     4     5     6     7
+//     1-8 9-16 17-24 25-31 32-40 41-48 49-56 57-(64)
+//
+//     seg0
+// seg5     seg1
+//     seg6
+// seg4     seg2
+//     seg3
+//
+// Joystick connection
+//
+//  o o o
+//o
+//o   +->x
+//o   |
+//    y
+
+
+Ticker _tk;
+
+// multiple condition in #ifdef
+// http://qiita.com/D-3/items/b98b63c2629496856654
+#if defined(PCB_VER1) || defined(PCB_VER2)
+I2C i2c(P0_13, P0_15);
+#define I2C_FREQ    100000
+#endif
+
+#ifdef PCB_VER2
+#define ADC_ADS1115 // use 16bit ADC
+
+#ifdef ADC_ADS1115
+#define I2C_ADDR 0x90 // for ADS1115
+#else
+#define I2C_ADDR 0x68 // for MAX11613
+#endif
+#endif
+
+#ifdef PCB_VER4
+io::io(PinName CLK, PinName DAT) : _hx711(P0_14, DAT, CLK)
+#else
+io::io()
+#endif
+{
+    _col = 0;
+    for (uint8_t i = 0; i < 4; i++) _seg[i] = 0x00;
+// COL[2:0]: P0_27 P0_26 P0_21
+// ROW[7:0]: P0_19 P0_16 P0_15 P0_13 P0_12 P0_11 P0_09 P0_08
+#if defined(UART_DEBUG)
+ #if defined(PCB_VER1) || defined(PCB_VER2) // UART: P0_8 & P0_9 (sg0&sg1)
+    _sg2 = new DigitalOut(P0_11, 0);
+ #endif
+ #if defined(PCB_VER3) // UART: P0_8 & P0_11(sg0&sg2)
+    _sg1 = new DigitalOut(P0_9, 0);
+ #endif
+ #if defined(PCB_VER4) // UART: P0_9 & P0_11(sg1&sg2)
+    _sg0 = new DigitalOut(P0_8, 0);
+ #endif
+#else
+    _sg0 = new DigitalOut(P0_8, 0);
+    _sg1 = new DigitalOut(P0_9, 0);
+    _sg2 = new DigitalOut(P0_11, 0);
+#endif
+    _sg3 = new DigitalOut(P0_12, 0);
+
+#ifdef PCB_VER1
+    _sg4 = new DigitalOut(P0_13, 0);
+    _sg5 = new DigitalOut(P0_15, 0);
+#else
+    _sg4 = new DigitalOut(P0_2, 0);
+    _sg5 = new DigitalOut(P0_3, 0);
+#endif
+    _sg6 = new DigitalOut(P0_16, 0);
+    _sg7 = new DigitalOut(P0_19, 0);
+    _sa0 = new DigitalOut(P0_21, 0);
+#ifdef PCB_VER1
+    _sa1 = new DigitalOut(P0_26, 0);
+    _sa2 = new DigitalOut(P0_27, 0);
+    _jx = new AnalogIn(P0_2);
+    _jy = new AnalogIn(P0_3);
+    _weight = new AnalogIn(P0_1);
+#endif
+#if defined(PCB_VER2) || defined(PCB_VER3) || defined(PCB_VER4)
+    _sa1 = new DigitalOut(P0_24, 0);
+    _sa2 = new DigitalOut(P0_25, 0);
+#endif
+    _pow = new DigitalOut(P0_0, 1); // initial: analog power off
+    _pwm = new PwmOut(P0_5);
+//    _pwm = new PwmOut(P0_15); // for BLEnano debug
+    _adc0 = 0;
+    _pwm->period_us(100);
+    _pwm->write(0.0);
+#if defined(PCB_VER1) || defined(PCB_VER2)
+    i2c.frequency(I2C_FREQ);
+#endif
+
+#ifdef PCB_VER3
+    _sw = new DigitalIn(P0_4, PullDown);
+    _adc_ck = new DigitalOut(P0_15, 0);
+    _adc_di = new DigitalIn(P0_13);
+    _adc_ck->write(0);
+    for (uint8_t i = 0; i < 24; i++) {
+        _adc_ck->write(1);
+        wait_us(10);
+        _adc_ck->write(0);
+        wait_us(10);
+    }
+#endif
+#ifdef PCB_VER4
+    _hx711.format(8, 1); // 8bit mode1
+    _hx711.frequency(500000); // 500kHz
+    _sw = new DigitalIn(P0_4, PullDown);
+//    _sw = new DigitalIn(P0_13, PullDown); // for BLEnano debug
+//    _adc_ck = new DigitalOut(P0_15, 0); // for software SPI
+//    _adc_di = new DigitalIn(P0_13); // for software SPI
+    _PselSCK  = NRF_SPI1->PSELSCK; // for hardware SPI
+    _PselMISO = NRF_SPI1->PSELMISO; // for hardware SPI
+    _reg_ps = new DigitalOut(P0_1, 1); // 1=normal, 0=power_save
+//    _adc_rate = new DigitalOut(P0_6, 0); // 0=10Hz, 1=80Hz (HX711's RATE pin)
+    _adc_rate = new DigitalOut(P0_6, 1); // 0=10Hz, 1=80Hz (HX711's RATE pin)
+#endif
+    _fDisplaying = 0;
+}
+
+io::~io()
+{
+}
+
+void io::_set_col()
+{
+    if ((_col & 0x01) == 0) _sa0->write(0);
+    else _sa0->write(1);
+    if ((_col & 0x02) == 0) _sa1->write(0);
+    else _sa1->write(1);
+    if ((_col & 0x04) == 0) _sa2->write(0);
+    else _sa2->write(1);
+}
+
+void io::_set_row(uint8_t d)
+{
+#if defined(UART_DEBUG)
+ #if defined(PCB_VER1) || defined(PCB_VER2) // UART: P0_8 & P0_9 (sg0&sg1)
+    if ((d & 0x04) == 0) _sg2->write(0); else _sg2->write(1);
+ #endif
+ #if defined(PCB_VER3) // UART: P0_8 & P0_11 (sg0&sg2)
+    if ((d & 0x02) == 0) _sg1->write(0); else _sg1->write(1);
+ #endif
+ #if defined(PCB_VER4) // UART: P0_9 & P0_11(sg1&sg2)
+    if ((d & 0x01) == 0) _sg0->write(0); else _sg0->write(1);
+ #endif
+#else
+    if ((d & 0x01) == 0) _sg0->write(0); else _sg0->write(1);
+    if ((d & 0x02) == 0) _sg1->write(0); else _sg1->write(1);
+    if ((d & 0x04) == 0) _sg2->write(0); else _sg2->write(1);
+#endif
+    if ((d & 0x08) == 0) _sg3->write(0);
+    else _sg3->write(1);
+    if ((d & 0x10) == 0) _sg4->write(0);
+    else _sg4->write(1);
+    if ((d & 0x20) == 0) _sg5->write(0);
+    else _sg5->write(1);
+    if ((d & 0x40) == 0) _sg6->write(0);
+    else _sg6->write(1);
+    if ((d & 0x80) == 0) _sg7->write(0);
+    else _sg7->write(1);
+}
+
+uint8_t io::_set_segment(uint8_t d)
+{
+//     seg0
+// seg5     seg1
+//     seg6
+// seg4     seg2
+//     seg3
+    uint8_t v;
+    switch(d) {
+        case 0:
+            v = 0x3f;
+            break;
+        case 1:
+            v = 0x06;
+            break;
+        case 2:
+            v = 0x5b;
+            break;
+        case 3:
+            v = 0x4f;
+            break;
+        case 4:
+            v = 0x66;
+            break;
+        case 5:
+            v = 0x6d;
+            break;
+        case 6:
+            v = 0x7d;
+            break;
+        case 7:
+            v = 0x27;
+            break;
+        case 8:
+            v = 0x7f;
+            break;
+        case 9:
+            v = 0x6f;
+            break;
+        default :
+            v = 0x00;
+            break;
+    }
+    return(v);
+}
+
+void io::_timer_ticker()
+{
+    _set_row(0);
+    _set_col();
+    _set_row(_row_out[_col]);
+    _col++;
+    if (_col == 8) {
+        _col = 0;
+        if (display_value < 0) _display_value = 0;
+        else if (display_value > 9999) _display_value = 9999;
+        else _display_value = display_value;
+
+#ifdef DISPLAY_ZERO_SUPPRESS
+        if (_display_value < 1000) _seg[3] = 0;
+        else
+#endif
+            _seg[3] = _set_segment(_display_value / 1000);
+#ifdef DISPLAY_ZERO_SUPPRESS
+        if (_display_value < 100) _seg[2] = 0;
+        else
+#endif
+            _seg[2] = _set_segment((_display_value / 100) % 10);
+#ifdef DISPLAY_ZERO_SUPPRESS
+        if (_display_value < 10) _seg[1] = 0;
+        else
+#endif
+            _seg[1] = _set_segment((_display_value / 10) % 10);
+        _seg[0] = _set_segment(_display_value % 10);
+        for (uint8_t i = 0; i < 8; i++) _row_out[i] = 0;
+
+        if ((_seg[3] & 0x40) != 0) _row_out[0] |= 0xe0; // seg6/0f-0g-0h
+        if ((_seg[3] & 0x20) != 0) _row_out[0] |= 0x29; // seg5/0a-0d-0f
+        if ((_seg[3] & 0x10) != 0) {
+            _row_out[0] |= 0x20;    // seg4/0f-1a-1c
+            _row_out[1] |= 0x05;
+        }
+        if ((_seg[3] & 0x08) != 0) _row_out[1] |= 0x1c; // seg3/1c-1d-1e
+        if ((_seg[3] & 0x04) != 0) {
+            _row_out[0] |= 0x80;    // seg2/0h-1b-1e
+            _row_out[1] |= 0x12;
+        }
+        if ((_seg[3] & 0x02) != 0) _row_out[0] |= 0x94; // seg1/0c-0e-0h
+        if ((_seg[3] & 0x01) != 0) _row_out[0] |= 0x07; // seg0/0a-0b-0c
+
+        if ((_seg[2] & 0x40) != 0) _row_out[2] |= 0x1c; // seg6/2c-2d-2e
+        if ((_seg[2] & 0x20) != 0) {
+            _row_out[2] |= 0x05;    // seg5/1f-2a-2c
+            _row_out[1] |= 0x20;
+        }
+        if ((_seg[2] & 0x10) != 0) _row_out[2] |= 0xa4; // seg4/2c-2f-2h
+        if ((_seg[2] & 0x08) != 0) {
+            _row_out[3] |= 0x03;    // seg3/2h-3a-3b
+            _row_out[2] |= 0x80;
+        }
+        if ((_seg[2] & 0x04) != 0) {
+            _row_out[3] |= 0x02;    // seg2/2e-2g-3b
+            _row_out[2] |= 0x50;
+        }
+        if ((_seg[2] & 0x02) != 0) {
+            _row_out[2] |= 0x12;    // seg1/1h-2b-2e
+            _row_out[1] |= 0x80;
+        }
+        if ((_seg[2] & 0x01) != 0) _row_out[1] |= 0xe0; // seg0/1f-1g-1h
+
+        if ((_seg[1] & 0x40) != 0) {
+            _row_out[4] |= 0x03;    // seg6/3h-4a-4b
+            _row_out[3] |= 0x80;
+        }
+        if ((_seg[1] & 0x20) != 0) _row_out[3] |= 0xa4; // seg5/3c-3f-3h
+        if ((_seg[1] & 0x10) != 0) {
+            _row_out[4] |= 0x14;    // seg4/3h-4c-4e
+            _row_out[3] |= 0x80;
+        }
+        if ((_seg[1] & 0x08) != 0) _row_out[4] |= 0x70; // seg3/4e-4f-4g
+        if ((_seg[1] & 0x04) != 0) _row_out[4] |= 0x4a; // seg2/4b-4d-4g
+        if ((_seg[1] & 0x02) != 0) {
+            _row_out[4] |= 0x02;    // seg1/3e-3g-4b
+            _row_out[3] |= 0x50;
+        }
+        if ((_seg[1] & 0x01) != 0) _row_out[3] |= 0x1c; // seg0/3c-3d-3e
+
+        if ((_seg[0] & 0x40) != 0) _row_out[5] |= 0x70; // seg6/5e-5f-5g
+        if ((_seg[0] & 0x20) != 0) {
+            _row_out[5] |= 0x14;    // seg5/4h-5c-5e
+            _row_out[4] |= 0x80;
+        }
+        if ((_seg[0] & 0x10) != 0) {
+            _row_out[6] |= 0x02;    // seg4/5e-5h-6b
+            _row_out[5] |= 0x90;
+        }
+        if ((_seg[0] & 0x08) != 0) _row_out[6] |= 0x0e; // seg3/6b-6c-6d
+        if ((_seg[0] & 0x04) != 0) {
+            _row_out[6] |= 0x09;    // seg2/5g-6a-6d
+            _row_out[5] |= 0x40;
+        }
+        if ((_seg[0] & 0x02) != 0) _row_out[5] |= 0x4a; // seg1/5b-5d-5g
+        if ((_seg[0] & 0x01) != 0) {
+            _row_out[5] |= 0x03;    // seg0/4h-5a-5b
+            _row_out[4] |= 0x80;
+        }
+//        _row_out[6] |= 0xf0; _row_out[7] |= 0x1f; // 6e-6f-6g-6h 7a-7b-7c-7d-7e
+
+    // display unit ('G')
+#ifdef PCB_VER1
+        _row_out[6] |= 0xf0;
+        _row_out[7] |= 0x1f; // 6e-6f-6g-6h 7a-7b-7c-7d-7e
+#else
+        _row_out[6] |= 0xf0;
+        _row_out[7] |= 0xf4; // 6e-6f-6g-6h 7c-7e-7f-7g-7h
+#endif
+    }
+}
+
+void io::display(float f)
+{
+    _pwm->write(f);
+    if (f > 0.0 && _fDisplaying == 0) {
+        // ref: https://developer.mbed.org/cookbook/Compiler-Error-304
+        _tk.attach(this, &io::_timer_ticker, 0.002);
+        _fDisplaying = 1;
+    } else if (f == 0.0 && _fDisplaying == 1) {
+        _tk.detach();
+        _fDisplaying = 0;
+    }
+}
+
+float io::get_weight()
+{
+    float w;
+    w = (float)get_weight_raw() / WEIGHT_COEFFICIENT; // ToDo: coefficient calibration
+    return(w);
+}
+
+#ifdef PCB_VER4
+uint32_t io::get_weight_raw()
+#else
+uint16_t io::get_weight_raw()
+#endif
+{
+#ifdef PCB_VER4
+    long w;
+#else
+    int w;
+#endif
+    w = _get_adc_raw(0) - _adc0;
+    if (w < 0) w = 0;
+    return(w);
+}
+
+#ifdef PCB_VER1
+float io::_get_x()
+{
+    float v;
+    v = _get_adc(1);
+    return(v);
+}
+#endif
+
+#ifdef PCB_VER1
+float io::_get_y()
+{
+    float v;
+    v = _get_adc(2);
+    return(v);
+}
+#endif
+
+uint8_t io::get_switch()
+{
+    uint8_t f;
+#if defined(PCB_VER1) || defined(PCB_VER2)
+#ifdef USE_JOYSTICK
+    if (_get_x() > 0.8 || _get_y() > 0.8) f = 1;
+    else f = 0;
+#else
+    if(_get_adc(1) > 0.5) f = 1;
+    else f = 0;
+#endif
+#else
+    if (_sw->read() == 1) f = 1;
+    else f = 0;
+#endif
+    return(f);
+}
+
+void io::analog_pow(uint8_t f)
+{
+    if (f == 1){
+        _pow->write(0);
+#ifdef PCB_VER4
+        _set_adc_ck(0);
+#endif
+    }
+    else{
+         _pow->write(1);
+#ifdef PCB_VER4
+        _enableSPI(0);
+        _set_adc_ck(1);
+        wait(0.01); // keep HX711's SCK=1 to enter HX711 into sleep mode
+#endif
+    }
+}
+
+void io::_set_adc_ck(uint8_t f)
+{
+ //   if (f == 0) NRF_GPIO->OUTCLR |= (1 << _PselSCK);
+ //   else NRF_GPIO->OUTSET |= (1 << _PselSCK);
+   if (f == 0) NRF_GPIO->OUTCLR = (1 << _PselSCK);
+   else NRF_GPIO->OUTSET = (1 << _PselSCK);
+}
+
+void io::_enableSPI(uint8_t f)
+{
+    if (f == 0){
+        NRF_SPI1->ENABLE = 0;
+        NRF_SPI1->PSELSCK  = 0xffffffff;
+        NRF_SPI1->PSELMISO = 0xffffffff;
+    }
+    else{
+        NRF_SPI1->ENABLE = 0;
+        NRF_SPI1->PSELSCK  = _PselSCK;
+        NRF_SPI1->PSELMISO = _PselMISO;
+        NRF_SPI1->ENABLE = 1;
+    }
+}
+
+uint8_t io::_spi_transfer()
+{
+    return(_hx711.write(0x00));
+}
+
+void io::calibrate_weight()
+{
+    // (2016/3/28: take average in weight calibration)
+    int Navg = 10;
+    uint32_t _adc0_s = 0;
+    for (int i = 0; i < Navg; i++){
+        _adc0_s += _get_adc_raw(0);
+    }
+    _adc0 = (uint32_t)((float)_adc0_s / (float)Navg);
+}
+
+float io::_get_adc(uint8_t ch)
+{
+#ifdef PCB_VER4
+    return(_get_adc_raw(ch) / (float)0xffffff);
+#else
+    return(_get_adc_raw(ch) / (float)0xffff);
+#endif
+}
+
+#ifdef PCB_VER4
+uint32_t io::_get_adc_raw(uint8_t ch)
+#else
+uint16_t io::_get_adc_raw(uint8_t ch)
+#endif
+{
+#ifdef PCB_VER1
+    if (ch == 0) return(_weight->read());
+    else if (ch == 1) return(_jx->read());
+    else return(_jy->read());
+#endif
+
+#ifdef PCB_VER2
+    uint16_t adc = 0;
+    uint8_t dh, dl;
+#ifdef ADC_ADS1115
+    // for ADS1115
+    // Input Voltage Range
+    // VIN3 = VDD/2 = 1.65
+    // VIN0 = 1.8Vpp --> 0.75V - 2.55V
+    // VIN0 - VIN3 = -0.9 - +0.9V (+-1.024V)
+    _adc_dat[0] = 0x01; // config reg.
+    if (ch == 0) _adc_dat[1] = 0x17; // single conv. & FS=+-1.0.24V, AIN0/1/2-AIN3
+    else if (ch == 1) _adc_dat[1] = 0x27;
+    else _adc_dat[1] = 0x37;
+    _adc_dat[1] |= 0x80;
+
+    _adc_dat[2] = 0x83; // 128SPS & disable comp.
+
+    i2c.write(I2C_ADDR, _adc_dat, 3);
+    uint8_t f = 0;
+    uint16_t count = 10;
+    while (f == 0) {
+        _adc_dat[0] = 0x01;
+        i2c.write(I2C_ADDR, _adc_dat, 1);
+        i2c.read(I2C_ADDR, _adc_dat, 2);
+        if ((_adc_dat[0] & 0x80) != 0) f = 1;
+
+        if(--count == 0) {
+            break;
+        }
+    }
+    _adc_dat[0] = 0x00;
+    i2c.write(I2C_ADDR, _adc_dat, 1);
+    i2c.read(I2C_ADDR, _adc_dat, 2);
+    dh = _adc_dat[0];
+    dl = _adc_dat[1];
+    adc = dh << 8 | dl;
+#else
+    // for MAX11613
+    _adc_dat[0] = 0xda; // setup / int.reference, ext.clock unipolar
+    _adc_dat[1] = 0x61 | (ch << 1); // config / single-ended
+    i2cwrite(I2C_ADDR, _adc_dat, 2);
+
+    i2cread(I2C_ADDR, _adc_dat, 2);
+    dh = _adc_dat[0] & 0x0f;
+    dl = _adc_dat[1];
+    adc = (dh << 8 | dl) << 4; // align to 16 bit
+#endif
+    // convert 2's complementary -> straight binary
+    // 0x8000 -> 0x0000
+    // 0x0000 -> 0x8000
+    // 0x7fff -> 0xffff
+    adc = (adc + 0x8000) & 0xffff;
+    return(adc);
+#endif
+
+#ifdef PCB_VER3
+    uint16_t adc = 0;
+    // MAX11205, parameter ch is ignored
+    _adc_ck->write(0);
+    while(_adc_di->read() == 1); // wait until conversion is finished
+    // ToDo: data ready timeout
+    for (uint8_t i = 0; i < 16; i++) {
+        _adc_ck->write(1);
+        wait_us(10);
+        adc = adc << 1;
+        if (_adc_di->read() == 1) adc |= 0x01;
+        else adc &= ~0x01;
+        _adc_ck->write(0);
+        wait_us(10);
+    }
+    for (uint8_t i = 0; i < 10; i++) { // additional clock for self calibration
+        _adc_ck->write(1);
+        wait_us(10);
+        _adc_ck->write(0);
+        wait_us(10);
+    }
+    // 0x8000 -> 0x0000
+    // 0xffff -> 0x7fff
+    // 0x0000 -> 0x8000
+    // 0x7fff -> 0xffff
+    adc = (adc + 0x8000) & 0xffff;
+    return(adc);
+#endif
+
+#ifdef PCB_VER4
+    // using HX711
+    uint32_t adc = 0;
+    // hardware SPI
+    uint8_t d2, d1, d0;
+    _set_adc_ck(0);
+    // note: comment out the while() below for debugging without ADC, HX711
+    while((NRF_GPIO->IN & (1 << _PselMISO)));
+    _enableSPI(1);
+    d2 = _spi_transfer();
+    d1 = _spi_transfer();
+    d0 = _spi_transfer();
+    _enableSPI(0);
+    _set_adc_ck(1); wait_us(1);
+    _set_adc_ck(0);
+    adc = (d2 << 16) | (d1 << 8) | d0;
+/*
+    // software SPI
+    while(_adc_di->read() == 1); // wait until conversion is finished
+//    _tk.detach(); // disable timer to keep _adc_ck's H timeb
+    for (uint8_t i = 0; i < 24; i++) {
+        _adc_ck->write(1);
+        wait_us(10);
+        _adc_ck->write(0);
+        wait_us(10);
+        adc = adc << 1;
+        if (_adc_di->read() == 1) adc |= 0x01;
+        else adc &= ~0x01;
+    }
+    for (uint8_t i = 0; i < 1; i++) {
+        _adc_ck->write(1);
+        wait_us(10);
+        _adc_ck->write(0);
+        wait_us(10);
+    }
+*/
+//    _tk.attach(this, &io::_timer_ticker, 0.002);
+    adc = (adc + 0x800000) & 0xffffff;
+    return(adc);
+#endif
+}
+
+#ifdef PCB_VER4
+// f=1 for power-save mode during sleep
+void io::power_save_mode(uint8_t f)
+{
+    if (f == 1) _reg_ps->write(0); // power save mode for sleep
+    else _reg_ps->write(1); // non-power save (high power) mode for normal operation
+}
+#endif