diff --git a/Marlin/src/HAL/HAL_DUE/HAL_Due.h b/Marlin/src/HAL/HAL_DUE/HAL_Due.h index ab12a11e9..f3c50927f 100644 --- a/Marlin/src/HAL/HAL_DUE/HAL_Due.h +++ b/Marlin/src/HAL/HAL_DUE/HAL_Due.h @@ -41,15 +41,7 @@ // Defines // #define NUM_SERIAL 1 - -//#undef SERIAL_PORT -//#define SERIAL_PORT -1 - -#if SERIAL_PORT == -1 - #define MYSERIAL0 SerialUSB -#else - #define MYSERIAL0 customizedSerial -#endif +#define MYSERIAL0 customizedSerial // We need the previous define before the include, or compilation bombs... #include "MarlinSerial_Due.h" diff --git a/Marlin/src/HAL/HAL_DUE/HAL_spi_Due.cpp b/Marlin/src/HAL/HAL_DUE/HAL_spi_Due.cpp index 0f31ac7f5..0f1aa47fc 100644 --- a/Marlin/src/HAL/HAL_DUE/HAL_spi_Due.cpp +++ b/Marlin/src/HAL/HAL_DUE/HAL_spi_Due.cpp @@ -53,6 +53,7 @@ // -------------------------------------------------------------------------- #if ENABLED(SOFTWARE_SPI) + // -------------------------------------------------------------------------- // software SPI // -------------------------------------------------------------------------- @@ -493,44 +494,77 @@ static pfnSpiTxBlock spiTxBlock = spiTxBlockX; static pfnSpiRxBlock spiRxBlock = spiRxBlockX; - void spiBegin() { - SET_OUTPUT(SS_PIN); - WRITE(SS_PIN, HIGH); - SET_OUTPUT(SCK_PIN); - SET_INPUT(MISO_PIN); - SET_OUTPUT(MOSI_PIN); - } + #if MB(ALLIGATOR) // control SDSS pin + void spiBegin() { + SET_OUTPUT(SS_PIN); + WRITE(SS_PIN, HIGH); + SET_OUTPUT(SCK_PIN); + SET_INPUT(MISO_PIN); + SET_OUTPUT(MOSI_PIN); + } - uint8_t spiRec() { - WRITE(SS_PIN, LOW); - WRITE(MOSI_PIN, 1); /* Output 1s 1*/ - uint8_t b = spiTransferRx(0xFF); - WRITE(SS_PIN, HIGH); - return b; - } + uint8_t spiRec() { + WRITE(SS_PIN, LOW); + WRITE(MOSI_PIN, 1); /* Output 1s 1*/ + uint8_t b = spiTransferRx(0xFF); + WRITE(SS_PIN, HIGH); + return b; + } - void spiRead(uint8_t* buf, uint16_t nbyte) { - uint32_t todo = nbyte; - if (todo == 0) return; + void spiRead(uint8_t* buf, uint16_t nbyte) { + uint32_t todo = nbyte; + if (todo == 0) return; - WRITE(SS_PIN, LOW); - WRITE(MOSI_PIN, 1); /* Output 1s 1*/ - spiRxBlock(buf,nbyte); - WRITE(SS_PIN, HIGH); - } + WRITE(SS_PIN, LOW); + WRITE(MOSI_PIN, 1); /* Output 1s 1*/ + spiRxBlock(buf,nbyte); + WRITE(SS_PIN, HIGH); + } - void spiSend(uint8_t b) { - WRITE(SS_PIN, LOW); - (void) spiTransferTx(b); - WRITE(SS_PIN, HIGH); - } + void spiSend(uint8_t b) { + WRITE(SS_PIN, LOW); + (void) spiTransferTx(b); + WRITE(SS_PIN, HIGH); + } - void spiSendBlock(uint8_t token, const uint8_t* buf) { + void spiSendBlock(uint8_t token, const uint8_t* buf) { + WRITE(SS_PIN, LOW); + (void) spiTransferTx(token); + spiTxBlock(buf,512); + WRITE(SS_PIN, HIGH); + + #else // let calling routine control SDSS + void spiBegin() { + SET_OUTPUT(SS_PIN); + SET_OUTPUT(SCK_PIN); + SET_INPUT(MISO_PIN); + SET_OUTPUT(MOSI_PIN); + } - WRITE(SS_PIN, LOW); - (void) spiTransferTx(token); - spiTxBlock(buf,512); - WRITE(SS_PIN, HIGH); + uint8_t spiRec() { + WRITE(MOSI_PIN, 1); /* Output 1s 1*/ + uint8_t b = spiTransferRx(0xFF); + return b; + } + + void spiRead(uint8_t* buf, uint16_t nbyte) { + uint32_t todo = nbyte; + if (todo == 0) return; + + WRITE(MOSI_PIN, 1); /* Output 1s 1*/ + spiRxBlock(buf,nbyte); + } + + void spiSend(uint8_t b) { + (void) spiTransferTx(b); + } + + void spiSendBlock(uint8_t token, const uint8_t* buf) { + (void) spiTransferTx(token); + spiTxBlock(buf,512); + + #endif + } /** @@ -566,7 +600,9 @@ break; } - WRITE(SS_PIN, HIGH); + #if MB(ALLIGATOR) + WRITE(SS_PIN, HIGH); + #endif WRITE(MOSI_PIN, HIGH); WRITE(SCK_PIN, LOW); } @@ -574,211 +610,296 @@ /** Begin SPI transaction, set clock, bit order, data mode */ void spiBeginTransaction(uint32_t spiClock, uint8_t bitOrder, uint8_t dataMode) { // TODO: to be implemented - } #pragma GCC reset_options #else - // -------------------------------------------------------------------------- - // hardware SPI - // -------------------------------------------------------------------------- - // 8.4 MHz, 4 MHz, 2 MHz, 1 MHz, 0.5 MHz, 0.329 MHz, 0.329 MHz - int spiDueDividors[] = { 10, 21, 42, 84, 168, 255, 255 }; - bool spiInitMaded = false; - - void spiBegin() { - if(spiInitMaded == false) { - // Configure SPI pins - PIO_Configure( - g_APinDescription[SCK_PIN].pPort, - g_APinDescription[SCK_PIN].ulPinType, - g_APinDescription[SCK_PIN].ulPin, - g_APinDescription[SCK_PIN].ulPinConfiguration); - PIO_Configure( - g_APinDescription[MOSI_PIN].pPort, - g_APinDescription[MOSI_PIN].ulPinType, - g_APinDescription[MOSI_PIN].ulPin, - g_APinDescription[MOSI_PIN].ulPinConfiguration); - PIO_Configure( - g_APinDescription[MISO_PIN].pPort, - g_APinDescription[MISO_PIN].ulPinType, - g_APinDescription[MISO_PIN].ulPin, - g_APinDescription[MISO_PIN].ulPinConfiguration); - - // set master mode, peripheral select, fault detection - SPI_Configure(SPI0, ID_SPI0, SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PS); - SPI_Enable(SPI0); - - #if MB(ALLIGATOR) - SET_OUTPUT(DAC0_SYNC); - #if EXTRUDERS > 1 - SET_OUTPUT(DAC1_SYNC); - WRITE(DAC1_SYNC, HIGH); - #endif - SET_OUTPUT(SPI_EEPROM1_CS); - SET_OUTPUT(SPI_EEPROM2_CS); - SET_OUTPUT(SPI_FLASH_CS); - WRITE(DAC0_SYNC, HIGH); - WRITE(SPI_EEPROM1_CS, HIGH ); - WRITE(SPI_EEPROM2_CS, HIGH ); - WRITE(SPI_FLASH_CS, HIGH ); - WRITE(SS_PIN, HIGH ); - #endif // MB(ALLIGATOR) - - PIO_Configure( - g_APinDescription[SPI_PIN].pPort, - g_APinDescription[SPI_PIN].ulPinType, - g_APinDescription[SPI_PIN].ulPin, - g_APinDescription[SPI_PIN].ulPinConfiguration); - spiInit(1); - spiInitMaded = true; + + #if MB(ALLIGATOR) + + // slave selects controlled by SPI controller + // doesn't support changing SPI speeds for SD card + + // -------------------------------------------------------------------------- + // hardware SPI + // -------------------------------------------------------------------------- + // 8.4 MHz, 4 MHz, 2 MHz, 1 MHz, 0.5 MHz, 0.329 MHz, 0.329 MHz + int spiDueDividors[] = { 10, 21, 42, 84, 168, 255, 255 }; + bool spiInitMaded = false; + + void spiBegin() { + if(spiInitMaded == false) { + // Configure SPI pins + PIO_Configure( + g_APinDescription[SCK_PIN].pPort, + g_APinDescription[SCK_PIN].ulPinType, + g_APinDescription[SCK_PIN].ulPin, + g_APinDescription[SCK_PIN].ulPinConfiguration); + PIO_Configure( + g_APinDescription[MOSI_PIN].pPort, + g_APinDescription[MOSI_PIN].ulPinType, + g_APinDescription[MOSI_PIN].ulPin, + g_APinDescription[MOSI_PIN].ulPinConfiguration); + PIO_Configure( + g_APinDescription[MISO_PIN].pPort, + g_APinDescription[MISO_PIN].ulPinType, + g_APinDescription[MISO_PIN].ulPin, + g_APinDescription[MISO_PIN].ulPinConfiguration); + + // set master mode, peripheral select, fault detection + SPI_Configure(SPI0, ID_SPI0, SPI_MR_MSTR | SPI_MR_MODFDIS | SPI_MR_PS); + SPI_Enable(SPI0); + + #if MB(ALLIGATOR) + SET_OUTPUT(DAC0_SYNC); + #if EXTRUDERS > 1 + SET_OUTPUT(DAC1_SYNC); + WRITE(DAC1_SYNC, HIGH); + #endif + SET_OUTPUT(SPI_EEPROM1_CS); + SET_OUTPUT(SPI_EEPROM2_CS); + SET_OUTPUT(SPI_FLASH_CS); + WRITE(DAC0_SYNC, HIGH); + WRITE(SPI_EEPROM1_CS, HIGH ); + WRITE(SPI_EEPROM2_CS, HIGH ); + WRITE(SPI_FLASH_CS, HIGH ); + WRITE(SS_PIN, HIGH ); + #endif // MB(ALLIGATOR) + + OUT_WRITE(SDSS,0); + + PIO_Configure( + g_APinDescription[SPI_PIN].pPort, + g_APinDescription[SPI_PIN].ulPinType, + g_APinDescription[SPI_PIN].ulPin, + g_APinDescription[SPI_PIN].ulPinConfiguration); + + spiInit(1); + spiInitMaded = true; + } } - } - void spiInit(uint8_t spiRate) { - if(spiInitMaded == false) { - if(spiRate > 6) spiRate = 1; + void spiInit(uint8_t spiRate) { + if(spiInitMaded == false) { + if(spiRate > 6) spiRate = 1; + + #if MB(ALLIGATOR) + // Set SPI mode 1, clock, select not active after transfer, with delay between transfers + SPI_ConfigureNPCS(SPI0, SPI_CHAN_DAC, + SPI_CSR_CSAAT | SPI_CSR_SCBR(spiDueDividors[spiRate]) | + SPI_CSR_DLYBCT(1)); + // Set SPI mode 0, clock, select not active after transfer, with delay between transfers + SPI_ConfigureNPCS(SPI0, SPI_CHAN_EEPROM1, SPI_CSR_NCPHA | + SPI_CSR_CSAAT | SPI_CSR_SCBR(spiDueDividors[spiRate]) | + SPI_CSR_DLYBCT(1)); + #endif//MB(ALLIGATOR) - #if MB(ALLIGATOR) - // Set SPI mode 1, clock, select not active after transfer, with delay between transfers - SPI_ConfigureNPCS(SPI0, SPI_CHAN_DAC, - SPI_CSR_CSAAT | SPI_CSR_SCBR(spiDueDividors[spiRate]) | - SPI_CSR_DLYBCT(1)); // Set SPI mode 0, clock, select not active after transfer, with delay between transfers - SPI_ConfigureNPCS(SPI0, SPI_CHAN_EEPROM1, SPI_CSR_NCPHA | + SPI_ConfigureNPCS(SPI0, SPI_CHAN, SPI_CSR_NCPHA | SPI_CSR_CSAAT | SPI_CSR_SCBR(spiDueDividors[spiRate]) | SPI_CSR_DLYBCT(1)); - #endif//MB(ALLIGATOR) - - // Set SPI mode 0, clock, select not active after transfer, with delay between transfers - SPI_ConfigureNPCS(SPI0, SPI_CHAN, SPI_CSR_NCPHA | - SPI_CSR_CSAAT | SPI_CSR_SCBR(spiDueDividors[spiRate]) | - SPI_CSR_DLYBCT(1)); - SPI_Enable(SPI0); - spiInitMaded = true; + SPI_Enable(SPI0); + spiInitMaded = true; + } } - } - // Write single byte to SPI - void spiSend(byte b) { - // write byte with address and end transmission flag - SPI0->SPI_TDR = (uint32_t)b | SPI_PCS(SPI_CHAN) | SPI_TDR_LASTXFER; - // wait for transmit register empty - while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); - // wait for receive register - while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); - // clear status - SPI0->SPI_RDR; - //delayMicroseconds(1U); - } - - void spiSend(const uint8_t* buf, size_t n) { - if (n == 0) return; - for (size_t i = 0; i < n - 1; i++) { - SPI0->SPI_TDR = (uint32_t)buf[i] | SPI_PCS(SPI_CHAN); + // Write single byte to SPI + void spiSend(byte b) { + // write byte with address and end transmission flag + SPI0->SPI_TDR = (uint32_t)b | SPI_PCS(SPI_CHAN) | SPI_TDR_LASTXFER; + // wait for transmit register empty while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); + // wait for receive register while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); + // clear status SPI0->SPI_RDR; //delayMicroseconds(1U); } - spiSend(buf[n - 1]); - } - void spiSend(uint32_t chan, byte b) { - uint8_t dummy_read = 0; - // wait for transmit register empty - while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); - // write byte with address and end transmission flag - SPI0->SPI_TDR = (uint32_t)b | SPI_PCS(chan) | SPI_TDR_LASTXFER; - // wait for receive register - while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); - // clear status - while ((SPI0->SPI_SR & SPI_SR_RDRF) == 1) - dummy_read = SPI0->SPI_RDR; - UNUSED(dummy_read); - } + void spiSend(const uint8_t* buf, size_t n) { + if (n == 0) return; + for (size_t i = 0; i < n - 1; i++) { + SPI0->SPI_TDR = (uint32_t)buf[i] | SPI_PCS(SPI_CHAN); + while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); + while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); + SPI0->SPI_RDR; + //delayMicroseconds(1U); + } + spiSend(buf[n - 1]); + } - void spiSend(uint32_t chan, const uint8_t* buf, size_t n) { - uint8_t dummy_read = 0; - if (n == 0) return; - for (int i = 0; i < (int)n - 1; i++) { + void spiSend(uint32_t chan, byte b) { + uint8_t dummy_read = 0; + // wait for transmit register empty while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); - SPI0->SPI_TDR = (uint32_t)buf[i] | SPI_PCS(chan); + // write byte with address and end transmission flag + SPI0->SPI_TDR = (uint32_t)b | SPI_PCS(chan) | SPI_TDR_LASTXFER; + // wait for receive register while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); + // clear status while ((SPI0->SPI_SR & SPI_SR_RDRF) == 1) dummy_read = SPI0->SPI_RDR; UNUSED(dummy_read); } - spiSend(chan, buf[n - 1]); - } - - // Read single byte from SPI - uint8_t spiRec() { - // write dummy byte with address and end transmission flag - SPI0->SPI_TDR = 0x000000FF | SPI_PCS(SPI_CHAN) | SPI_TDR_LASTXFER; - // wait for transmit register empty - while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); - - // wait for receive register - while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); - // get byte from receive register - //delayMicroseconds(1U); - return SPI0->SPI_RDR; - } - uint8_t spiRec(uint32_t chan) { - uint8_t spirec_tmp; - // wait for transmit register empty - while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); - while ((SPI0->SPI_SR & SPI_SR_RDRF) == 1) - spirec_tmp = SPI0->SPI_RDR; - UNUSED(spirec_tmp); - - // write dummy byte with address and end transmission flag - SPI0->SPI_TDR = 0x000000FF | SPI_PCS(chan) | SPI_TDR_LASTXFER; - - // wait for receive register - while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); - // get byte from receive register - return SPI0->SPI_RDR; - } + void spiSend(uint32_t chan, const uint8_t* buf, size_t n) { + uint8_t dummy_read = 0; + if (n == 0) return; + for (int i = 0; i < (int)n - 1; i++) { + while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); + SPI0->SPI_TDR = (uint32_t)buf[i] | SPI_PCS(chan); + while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); + while ((SPI0->SPI_SR & SPI_SR_RDRF) == 1) + dummy_read = SPI0->SPI_RDR; + UNUSED(dummy_read); + } + spiSend(chan, buf[n - 1]); + } - // Read from SPI into buffer - void spiRead(uint8_t*buf, uint16_t nbyte) { - if (nbyte-- == 0) return; + // Read single byte from SPI + uint8_t spiRec() { + // write dummy byte with address and end transmission flag + SPI0->SPI_TDR = 0x000000FF | SPI_PCS(SPI_CHAN) | SPI_TDR_LASTXFER; + // wait for transmit register empty + while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); - for (int i = 0; i < nbyte; i++) { - //while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); - SPI0->SPI_TDR = 0x000000FF | SPI_PCS(SPI_CHAN); + // wait for receive register while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); - buf[i] = SPI0->SPI_RDR; + // get byte from receive register //delayMicroseconds(1U); + return SPI0->SPI_RDR; } - buf[nbyte] = spiRec(); - } - // Write from buffer to SPI - void spiSendBlock(uint8_t token, const uint8_t* buf) { - SPI0->SPI_TDR = (uint32_t)token | SPI_PCS(SPI_CHAN); - while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); - //while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); - //SPI0->SPI_RDR; - for (int i = 0; i < 511; i++) { - SPI0->SPI_TDR = (uint32_t)buf[i] | SPI_PCS(SPI_CHAN); + uint8_t spiRec(uint32_t chan) { + uint8_t spirec_tmp; + // wait for transmit register empty while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); + while ((SPI0->SPI_SR & SPI_SR_RDRF) == 1) + spirec_tmp = SPI0->SPI_RDR; + UNUSED(spirec_tmp); + + // write dummy byte with address and end transmission flag + SPI0->SPI_TDR = 0x000000FF | SPI_PCS(chan) | SPI_TDR_LASTXFER; + + // wait for receive register while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); - SPI0->SPI_RDR; - //delayMicroseconds(1U); + // get byte from receive register + return SPI0->SPI_RDR; } - spiSend(buf[511]); - } - /** Begin SPI transaction, set clock, bit order, data mode */ - void spiBeginTransaction(uint32_t spiClock, uint8_t bitOrder, uint8_t dataMode) { - // TODO: to be implemented - } + // Read from SPI into buffer + void spiRead(uint8_t*buf, uint16_t nbyte) { + if (nbyte-- == 0) return; + + for (int i = 0; i < nbyte; i++) { + //while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); + SPI0->SPI_TDR = 0x000000FF | SPI_PCS(SPI_CHAN); + while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); + buf[i] = SPI0->SPI_RDR; + //delayMicroseconds(1U); + } + buf[nbyte] = spiRec(); + } + + // Write from buffer to SPI + void spiSendBlock(uint8_t token, const uint8_t* buf) { + SPI0->SPI_TDR = (uint32_t)token | SPI_PCS(SPI_CHAN); + while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); + //while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); + //SPI0->SPI_RDR; + for (int i = 0; i < 511; i++) { + SPI0->SPI_TDR = (uint32_t)buf[i] | SPI_PCS(SPI_CHAN); + while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); + while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); + SPI0->SPI_RDR; + //delayMicroseconds(1U); + } + spiSend(buf[511]); + } + + /** Begin SPI transaction, set clock, bit order, data mode */ + void spiBeginTransaction(uint32_t spiClock, uint8_t bitOrder, uint8_t dataMode) { + // TODO: to be implemented + } + + #else // U8G compatible hardware SPI + + void spiInit(uint8_t spiRate = 6 ) { // default to slowest rate if not specified) + // 8.4 MHz, 4 MHz, 2 MHz, 1 MHz, 0.5 MHz, 0.329 MHz, 0.329 MHz + int spiDueDividors[] = { 10, 21, 42, 84, 168, 255, 255 }; + if(spiRate > 6) spiRate = 1; + + /* enable PIOA and SPI0 */ + REG_PMC_PCER0 = (1UL << ID_PIOA) | (1UL << ID_SPI0); + + /* disable PIO on A26 and A27 */ + REG_PIOA_PDR = 0x0c000000; + OUT_WRITE(SDSS, 1); + + /* reset SPI0 (from sam lib) */ + SPI0->SPI_CR = SPI_CR_SPIDIS; + SPI0->SPI_CR = SPI_CR_SWRST; + SPI0->SPI_CR = SPI_CR_SWRST; + SPI0->SPI_CR = SPI_CR_SPIEN; + + + /* master mode, no fault detection, chip select 0 */ + SPI0->SPI_MR = SPI_MR_MSTR | SPI_MR_PCSDEC | SPI_MR_MODFDIS; + + /* SPI mode 0, 8 Bit data transfer, baud rate */ + SPI0->SPI_CSR[0] = SPI_CSR_SCBR(spiDueDividors[spiRate]) | 1; + } + + static uint8_t spiTransfer(uint8_t data) { + + /* wait until tx register is empty */ + while( (SPI0->SPI_SR & SPI_SR_TDRE) == 0 ); + /* send data */ + SPI0->SPI_TDR = (uint32_t)data; // | SPI_PCS(0xF); + + // wait for transmit register empty + while ((SPI0->SPI_SR & SPI_SR_TDRE) == 0); + + // wait for receive register + while ((SPI0->SPI_SR & SPI_SR_RDRF) == 0); + // get byte from receive register + return SPI0->SPI_RDR; + } + + void spiBegin() { + spiInit(); + } + + uint8_t spiRec() { + uint8_t data = spiTransfer(0xff); + return data; + } + + void spiRead(uint8_t*buf, uint16_t nbyte) { + if (nbyte == 0) return; + for (int i = 0; i < nbyte; i++) { + buf[i] = spiTransfer(0xff); + } + } + + void spiSend(uint8_t data) { + spiTransfer(data); + } + + void spiSend(const uint8_t* buf, size_t n) { + if (n == 0) return; + for (uint16_t i = 0; i < n; i++) + spiTransfer(buf[i]); + } + void spiSendBlock(uint8_t token, const uint8_t* buf) { + spiTransfer(token); + for (uint16_t i = 0; i < 512; i++) + spiTransfer(buf[i]); + } + + #endif //MB(ALLIGATOR) #endif // ENABLED(SOFTWARE_SPI) #endif // ARDUINO_ARCH_SAM diff --git a/Marlin/src/HAL/HAL_DUE/u8g_com_HAL_DUE_shared_hw_spi.cpp b/Marlin/src/HAL/HAL_DUE/u8g_com_HAL_DUE_shared_hw_spi.cpp new file mode 100644 index 000000000..98c065fba --- /dev/null +++ b/Marlin/src/HAL/HAL_DUE/u8g_com_HAL_DUE_shared_hw_spi.cpp @@ -0,0 +1,163 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016, 2017, 2018 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +/* + + based on u8g_com_msp430_hw_spi.c + + Universal 8bit Graphics Library + + Copyright (c) 2012, olikraus@gmail.com + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + * 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR + CONTRIBUTORS 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. + +*/ + + +#ifdef __SAM3X8E__ + +// #include + +// #include "src/core/macros.h" +// #include "Configuration.h" +#include "../../Marlin.h" +#include "../../inc/MarlinConfig.h" + + #include + + #define SPI_FULL_SPEED 0 + #define SPI_HALF_SPEED 1 + #define SPI_QUARTER_SPEED 2 + #define SPI_EIGHTH_SPEED 3 + #define SPI_SIXTEENTH_SPEED 4 + #define SPI_SPEED_5 5 + #define SPI_SPEED_6 6 + + void spiBegin(); + void spiInit(uint8_t spiRate); + void spiSend(uint8_t b); + void spiSend(const uint8_t* buf, size_t n); + + #include + #include "../../core/macros.h" + #include "fastio_Due.h" + + + void u8g_SetPIOutput_DUE_hw_spi(u8g_t *u8g, uint8_t pin_index) { + PIO_Configure(g_APinDescription[u8g->pin_list[pin_index]].pPort, PIO_OUTPUT_1, + g_APinDescription[u8g->pin_list[pin_index]].ulPin, g_APinDescription[u8g->pin_list[pin_index]].ulPinConfiguration); // OUTPUT + } + + void u8g_SetPILevel_DUE_hw_spi(u8g_t *u8g, uint8_t pin_index, uint8_t level) { + volatile Pio* port = g_APinDescription[u8g->pin_list[pin_index]].pPort; + uint32_t mask = g_APinDescription[u8g->pin_list[pin_index]].ulPin; + if (level) port->PIO_SODR = mask; + else port->PIO_CODR = mask; + } + + uint8_t u8g_com_HAL_DUE_shared_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) + { + switch(msg) + { + case U8G_COM_MSG_STOP: + break; + + case U8G_COM_MSG_INIT: + u8g_SetPILevel_DUE_hw_spi(u8g, U8G_PI_CS, 1); + u8g_SetPILevel_DUE_hw_spi(u8g, U8G_PI_A0, 1); + + u8g_SetPIOutput_DUE_hw_spi(u8g, U8G_PI_CS); + u8g_SetPIOutput_DUE_hw_spi(u8g, U8G_PI_A0); + + u8g_Delay(5); + + spiBegin(); + + #ifndef SPI_SPEED + #define SPI_SPEED SPI_FULL_SPEED // use same SPI speed as SD card + #endif + spiInit(2); + + break; + + case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ + u8g_SetPILevel_DUE_hw_spi(u8g, U8G_PI_A0, arg_val); + break; + + case U8G_COM_MSG_CHIP_SELECT: + u8g_SetPILevel_DUE_hw_spi(u8g, U8G_PI_CS, (arg_val ? 0 : 1)); + break; + + case U8G_COM_MSG_RESET: + break; + + case U8G_COM_MSG_WRITE_BYTE: + + spiSend((uint8_t)arg_val); + break; + + case U8G_COM_MSG_WRITE_SEQ: { + uint8_t *ptr = (uint8_t*) arg_ptr; + while (arg_val > 0) { + spiSend(*ptr++); + arg_val--; + } + } + break; + + case U8G_COM_MSG_WRITE_SEQ_P: { + uint8_t *ptr = (uint8_t*) arg_ptr; + while (arg_val > 0) { + spiSend(*ptr++); + arg_val--; + } + } + break; + } + return 1; + } + +#endif //__SAM3X8E__ diff --git a/Marlin/src/config/examples/MakerParts/Configuration.h b/Marlin/src/config/examples/MakerParts/Configuration.h index 7a9554f6d..700485a77 100644 --- a/Marlin/src/config/examples/MakerParts/Configuration.h +++ b/Marlin/src/config/examples/MakerParts/Configuration.h @@ -125,7 +125,7 @@ * * :[-1, 0, 1, 2, 3, 4, 5, 6, 7] */ -#define SERIAL_PORT 0 +#define SERIAL_PORT -1 /** * Select a secondary serial port on the board to use for communication with the host. @@ -134,7 +134,7 @@ * * :[-1, 0, 1, 2, 3, 4, 5, 6, 7] */ -#define SERIAL_PORT_2 -1 +#define SERIAL_PORT_2 0 /** * This setting determines the communication speed of the printer. diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index 67471cb80..94b27abce 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -517,7 +517,7 @@ void advance_command_queue() { card.closefile(); SERIAL_PROTOCOLLNPGM(MSG_FILE_SAVED); - #ifndef USBCON + #if !defined(__AVR__) || !defined(USBCON) #if ENABLED(SERIAL_STATS_DROPPED_RX) SERIAL_ECHOLNPAIR("Dropped bytes: ", customizedSerial.dropped()); #endif @@ -525,7 +525,7 @@ void advance_command_queue() { #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) SERIAL_ECHOLNPAIR("Max RX Queue Size: ", customizedSerial.rxMaxEnqueued()); #endif - #endif // !USBCON + #endif // !defined(__AVR__) || !defined(USBCON) ok_to_send(); } diff --git a/Marlin/src/inc/Conditionals_adv.h b/Marlin/src/inc/Conditionals_adv.h index bfefb8991..289f17437 100644 --- a/Marlin/src/inc/Conditionals_adv.h +++ b/Marlin/src/inc/Conditionals_adv.h @@ -28,7 +28,7 @@ #ifndef CONDITIONALS_ADV_H #define CONDITIONALS_ADV_H - #ifndef USBCON + #if !defined(__AVR__) || !defined(USBCON) // Define constants and variables for buffering serial data. // Use only 0 or powers of 2 greater than 1 // : [0, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, ...] diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 93fee1b4d..fe7ca7553 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -275,7 +275,7 @@ /** * Serial */ -#ifndef USBCON +#if !defined(__AVR__) || !defined(USBCON) #if ENABLED(SERIAL_XON_XOFF) && RX_BUFFER_SIZE < 1024 #error "SERIAL_XON_XOFF requires RX_BUFFER_SIZE >= 1024 for reliable transfers without drops." #elif RX_BUFFER_SIZE && (RX_BUFFER_SIZE < 2 || !IS_POWER_OF_2(RX_BUFFER_SIZE)) @@ -1308,7 +1308,7 @@ static_assert(1 >= 0 /** * emergency-command parser */ -#if ENABLED(EMERGENCY_PARSER) && defined(USBCON) +#if ENABLED(EMERGENCY_PARSER) && defined(__AVR__) && defined(USBCON) #error "EMERGENCY_PARSER does not work on boards with AT90USB processors (USBCON)." #endif diff --git a/Marlin/src/lcd/dogm/u8g_dev_uc1701_mini12864_HAL.cpp b/Marlin/src/lcd/dogm/u8g_dev_uc1701_mini12864_HAL.cpp new file mode 100644 index 000000000..f7ff3aca0 --- /dev/null +++ b/Marlin/src/lcd/dogm/u8g_dev_uc1701_mini12864_HAL.cpp @@ -0,0 +1,189 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016, 2017, 2018 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +/* + + based on u8g_dev_uc1701_mini12864_HAL.c (dealextreme) + + Universal 8bit Graphics Library + + Copyright (c) 2011, olikraus@gmail.com + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + * 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR + CONTRIBUTORS 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 "../../inc/MarlinConfig.h" + +#if ENABLED(DOGLCD) + +#include + +#include "HAL_LCD_com_defines.h" + +#define WIDTH 128 +#define HEIGHT 64 +#define PAGE_HEIGHT 8 + +static const uint8_t u8g_dev_uc1701_mini12864_HAL_init_seq[] PROGMEM = { + U8G_ESC_CS(0), /* disable chip */ + U8G_ESC_ADR(0), /* instruction mode */ + U8G_ESC_RST(1), /* do reset low pulse with (1*16)+2 milliseconds */ + U8G_ESC_CS(1), /* enable chip */ + + 0x0e2, /* soft reset */ + 0x040, /* set display start line to 0 */ + 0x0a0, /* ADC set to reverse */ + 0x0c8, /* common output mode */ + 0x0a6, /* display normal, bit val 0: LCD pixel off. */ + 0x0a2, /* LCD bias 1/9 */ + 0x02f, /* all power control circuits on */ + 0x0f8, /* set booster ratio to */ + 0x000, /* 4x */ + 0x023, /* set V0 voltage resistor ratio to large */ + 0x081, /* set contrast */ + 0x027, /* contrast value */ + 0x0ac, /* indicator */ + 0x000, /* disable */ + 0x0af, /* display on */ + + U8G_ESC_DLY(100), /* delay 100 ms */ + 0x0a5, /* display all points, ST7565 */ + U8G_ESC_DLY(100), /* delay 100 ms */ + U8G_ESC_DLY(100), /* delay 100 ms */ + 0x0a4, /* normal display */ + U8G_ESC_CS(0), /* disable chip */ + U8G_ESC_END /* end of sequence */ +}; + +static const uint8_t u8g_dev_uc1701_mini12864_HAL_data_start[] PROGMEM = { + U8G_ESC_ADR(0), /* instruction mode */ + U8G_ESC_CS(1), /* enable chip */ + 0x010, /* set upper 4 bit of the col adr to 0 */ + 0x000, /* set lower 4 bit of the col adr to 4 */ + U8G_ESC_END /* end of sequence */ +}; + +uint8_t u8g_dev_uc1701_mini12864_HAL_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) +{ + switch(msg) + { + case U8G_DEV_MSG_INIT: + u8g_InitCom(u8g, dev, U8G_SPI_CLK_CYCLE_300NS); + u8g_WriteEscSeqP(u8g, dev, u8g_dev_uc1701_mini12864_HAL_init_seq); + break; + case U8G_DEV_MSG_STOP: + break; + case U8G_DEV_MSG_PAGE_NEXT: + { + u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem); + u8g_WriteEscSeqP(u8g, dev, u8g_dev_uc1701_mini12864_HAL_data_start); + u8g_WriteByte(u8g, dev, 0x0b0 | pb->p.page); /* select current page */ + u8g_SetAddress(u8g, dev, 1); /* data mode */ + if ( u8g_pb_WriteBuffer(pb, u8g, dev) == 0 ) + return 0; + u8g_SetChipSelect(u8g, dev, 0); + } + break; + case U8G_DEV_MSG_CONTRAST: + u8g_SetChipSelect(u8g, dev, 1); + u8g_SetAddress(u8g, dev, 0); /* instruction mode */ + u8g_WriteByte(u8g, dev, 0x081); + u8g_WriteByte(u8g, dev, (*(uint8_t *)arg) >> 2); + u8g_SetChipSelect(u8g, dev, 0); + return 1; + } + return u8g_dev_pb8v1_base_fn(u8g, dev, msg, arg); +} + +uint8_t u8g_dev_uc1701_mini12864_HAL_2x_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, void *arg) +{ + switch(msg) + { + case U8G_DEV_MSG_INIT: + u8g_InitCom(u8g, dev, U8G_SPI_CLK_CYCLE_300NS); + u8g_WriteEscSeqP(u8g, dev, u8g_dev_uc1701_mini12864_HAL_init_seq); + break; + case U8G_DEV_MSG_STOP: + break; + case U8G_DEV_MSG_PAGE_NEXT: + { + u8g_pb_t *pb = (u8g_pb_t *)(dev->dev_mem); + + u8g_WriteEscSeqP(u8g, dev, u8g_dev_uc1701_mini12864_HAL_data_start); + u8g_WriteByte(u8g, dev, 0x0b0 | (2*pb->p.page)); /* select current page */ + u8g_SetAddress(u8g, dev, 1); /* data mode */ + u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *)pb->buf); + u8g_SetChipSelect(u8g, dev, 0); + + u8g_WriteEscSeqP(u8g, dev, u8g_dev_uc1701_mini12864_HAL_data_start); + u8g_WriteByte(u8g, dev, 0x0b0 | (2*pb->p.page+1)); /* select current page */ + u8g_SetAddress(u8g, dev, 1); /* data mode */ + u8g_WriteSequence(u8g, dev, pb->width, (uint8_t *)(pb->buf)+pb->width); + u8g_SetChipSelect(u8g, dev, 0); + } + break; + case U8G_DEV_MSG_CONTRAST: + u8g_SetChipSelect(u8g, dev, 1); + u8g_SetAddress(u8g, dev, 0); /* instruction mode */ + u8g_WriteByte(u8g, dev, 0x081); + u8g_WriteByte(u8g, dev, (*(uint8_t *)arg) >> 2); + u8g_SetChipSelect(u8g, dev, 0); + return 1; + } + return u8g_dev_pb16v1_base_fn(u8g, dev, msg, arg); +} + +U8G_PB_DEV(u8g_dev_uc1701_mini12864_HAL_sw_spi, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_uc1701_mini12864_HAL_fn, U8G_COM_HAL_SW_SPI_FN); +U8G_PB_DEV(u8g_dev_uc1701_mini12864_HAL_hw_spi, WIDTH, HEIGHT, PAGE_HEIGHT, u8g_dev_uc1701_mini12864_HAL_fn, U8G_COM_HAL_HW_SPI_FN); + +uint8_t u8g_dev_uc1701_mini12864_HAL_2x_buf[WIDTH*2] U8G_NOCOMMON ; +u8g_pb_t u8g_dev_uc1701_mini12864_HAL_2x_pb = { {16, HEIGHT, 0, 0, 0}, WIDTH, u8g_dev_uc1701_mini12864_HAL_2x_buf}; +u8g_dev_t u8g_dev_uc1701_mini12864_HAL_2x_sw_spi = { u8g_dev_uc1701_mini12864_HAL_2x_fn, &u8g_dev_uc1701_mini12864_HAL_2x_pb, U8G_COM_HAL_SW_SPI_FN }; +u8g_dev_t u8g_dev_uc1701_mini12864_HAL_2x_hw_spi = { u8g_dev_uc1701_mini12864_HAL_2x_fn, &u8g_dev_uc1701_mini12864_HAL_2x_pb, U8G_COM_HAL_HW_SPI_FN }; + +#endif // DOGLCD \ No newline at end of file diff --git a/Marlin/src/pins/pins_DUE3DOM_MINI.h b/Marlin/src/pins/pins_DUE3DOM_MINI.h index 7ea23835a..9695e3ae4 100644 --- a/Marlin/src/pins/pins_DUE3DOM_MINI.h +++ b/Marlin/src/pins/pins_DUE3DOM_MINI.h @@ -156,5 +156,16 @@ #define BTN_ENC 37 #define BEEPER_PIN -1 + + #elif ENABLED(MINIPANEL) + #define BTN_EN1 52 + #define BTN_EN2 50 + #define BTN_ENC 48 + #define LCD_SDSS 4 + #define SD_DETECT_PIN 14 + #define BEEPER_PIN 41 + #define DOGLCD_A0 46 + #define DOGLCD_CS 45 + #endif // SPARK_FULL_GRAPHICS #endif // ULTRA_LCD diff --git a/Marlin/src/pins/pins_RAMPS_FD_V1.h b/Marlin/src/pins/pins_RAMPS_FD_V1.h index 0a632cfb3..98003889a 100644 --- a/Marlin/src/pins/pins_RAMPS_FD_V1.h +++ b/Marlin/src/pins/pins_RAMPS_FD_V1.h @@ -140,23 +140,58 @@ // LCD / Controller // #if ENABLED(ULTRA_LCD) + // ramps-fd lcd adaptor + + #if ENABLED(DOGLCD) + #define BEEPER_PIN 37 + #define BTN_EN1 33 + #define BTN_EN2 31 + #define BTN_ENC 35 + #define SD_DETECT_PIN 49 + #endif + #if ENABLED(NEWPANEL) - // ramps-fd lcd adaptor #define LCD_PINS_RS 16 #define LCD_PINS_ENABLE 17 #define LCD_PINS_D4 23 #define LCD_PINS_D5 25 #define LCD_PINS_D6 27 #define LCD_PINS_D7 29 + #endif + + #if ENABLED(MINIPANEL) + #define DOGLCD_CS 25 + #define DOGLCD_A0 27 + #endif +#endif // ULTRA_LCD - #if ENABLED(REPRAP_DISCOUNT_SMART_CONTROLLER) - #define BEEPER_PIN 37 - - #define BTN_EN1 33 - #define BTN_EN2 31 - #define BTN_ENC 35 +#if ENABLED(HAVE_TMC2208) + /** + * TMC2208 stepper drivers + * + * Hardware serial communication ports. + * If undefined software serial is used according to the pins below + */ + //#define X_HARDWARE_SERIAL Serial1 + //#define X2_HARDWARE_SERIAL Serial1 + //#define Y_HARDWARE_SERIAL Serial1 + //#define Y2_HARDWARE_SERIAL Serial1 + //#define Z_HARDWARE_SERIAL Serial1 + //#define Z2_HARDWARE_SERIAL Serial1 + //#define E0_HARDWARE_SERIAL Serial1 + //#define E1_HARDWARE_SERIAL Serial1 + //#define E2_HARDWARE_SERIAL Serial1 + //#define E3_HARDWARE_SERIAL Serial1 + //#define E4_HARDWARE_SERIAL Serial1 +#endif - #define SD_DETECT_PIN 49 - #endif +// +// M3/M4/M5 - Spindle/Laser Control +// +#if ENABLED(SPINDLE_LASER_ENABLE) && !PIN_EXISTS(SPINDLE_LASER_ENABLE) + #if HOTENDS < 3 + #define SPINDLE_LASER_ENABLE_PIN 45 // Use E2 ENA + #define SPINDLE_LASER_PWM_PIN 12 // MUST BE HARDWARE PWM + #define SPINDLE_DIR_PIN 47 // Use E2 DIR #endif -#endif // ULTRA_LCD +#endif diff --git a/Marlin/src/pins/pins_RAMPS_FD_V2.h b/Marlin/src/pins/pins_RAMPS_FD_V2.h index 2d2375f06..f33546e63 100644 --- a/Marlin/src/pins/pins_RAMPS_FD_V2.h +++ b/Marlin/src/pins/pins_RAMPS_FD_V2.h @@ -36,3 +36,11 @@ #undef INVERTED_FAN_PINS #define I2C_EEPROM + +#ifndef PS_ON_PIN + #define PS_ON_PIN 12 +#endif + +#ifndef FILWIDTH_PIN + #define FILWIDTH_PIN 5 // Analog Input on AUX2 +#endif