| 
				
			 | 
			
			 | 
			@@ -27,6 +27,8 @@ uint8_t SPIClass::interruptSave = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#ifdef SPI_TRANSACTION_MISMATCH_LED | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			uint8_t SPIClass::inTransactionFlag = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			uint8_t SPIClass::_transferWriteFill = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void SPIClass::begin() | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			{ | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -138,16 +140,63 @@ void SPIClass::usingInterrupt(uint8_t interruptNumber) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SREG = stmp; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void SPIClass::transfer(const void * buf, void * retbuf, uint32_t count) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (count == 0) return; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				const uint8_t *p = (const uint8_t *)buf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t *pret = (uint8_t *)retbuf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t in; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t out = p ? *p++ : _transferWriteFill; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SPDR = out; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				while (--count > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (p) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						out = *p++; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					while (!(SPSR & _BV(SPIF))) ; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					in = SPDR; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					SPDR = out; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (pret)*pret++ = in; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				while (!(SPSR & _BV(SPIF))) ; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				in = SPDR; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (pret)*pret = in; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			/**********************************************************/ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			/*     32 bit Teensy 3.x				  */ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			/**********************************************************/ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#elif defined(__arm__) && defined(TEENSYDUINO) && defined(KINETISK) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#if defined(KINETISK) && defined( SPI_HAS_TRANSFER_ASYNC) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#ifndef TRANSFER_COUNT_FIXED | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			inline void DMAChanneltransferCount(DMAChannel * dmac, unsigned int len) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// note does no validation of length... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				DMABaseClass::TCD_t *tcd = dmac->TCD; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (!(tcd->BITER & DMA_TCD_BITER_ELINK)) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					tcd->BITER = len & 0x7fff; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					tcd->BITER = (tcd->BITER & 0xFE00) | (len & 0x1ff); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				tcd->CITER = tcd->BITER;  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#else  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			inline void DMAChanneltransferCount(DMAChannel * dmac, unsigned int len) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				dmac->transferCount(len); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#if defined(__MK20DX128__) || defined(__MK20DX256__) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR0(void) {/*SPI.dma_rxisr();*/} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#ifdef SPI_HAS_TRANSFER_ASYNC | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR0(void) {SPI.dma_rxisr();} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR0(void) {;} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SIM_SCGC6, SIM_SCGC6_SPI0, 4, IRQ_SPI0, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				32767, DMAMUX_SOURCE_SPI0_TX, DMAMUX_SOURCE_SPI0_RX, | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -165,9 +214,15 @@ const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			SPIClass SPI((uintptr_t)&KINETISK_SPI0, (uintptr_t)&SPIClass::spi0_hardware); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#elif defined(__MK64FX512__) || defined(__MK66FX1M0__) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR0(void) {/*SPI.dma_rxisr();*/} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR1(void) {/*SPI1.dma_rxisr();*/} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR2(void) {/*SPI2.dma_rxisr();*/} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#ifdef SPI_HAS_TRANSFER_ASYNC | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR0(void) {SPI.dma_rxisr();} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR1(void) {SPI1.dma_rxisr();} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR2(void) {SPI2.dma_rxisr();} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR0(void) {;} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR1(void) {;} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR2(void) {;} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SIM_SCGC6, SIM_SCGC6_SPI0, 4, IRQ_SPI0, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				32767, DMAMUX_SOURCE_SPI0_TX, DMAMUX_SOURCE_SPI0_RX, | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -473,73 +528,367 @@ void SPIClass::setSCK(uint8_t pin) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void SPIClass::transfer(void *buf, size_t count) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void SPIClass::transfer(const void * buf, void * retbuf, size_t count) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			{ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (count == 0) return; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t *p_write = (uint8_t *)buf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t *p_read = p_write; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				size_t count_read = count; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				bool lsbfirst = (port().CTAR0 & SPI_CTAR_LSBFE) ? true : false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint32_t sr, full_mask; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Lets clear the reader queue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Now lets loop while we still have data to output | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (count & 1) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (count > 1) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						port().PUSHR = *p_write++ | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						port().PUSHR = *p_write++ | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					count--; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				full_mask = (hardware().queue_size-1) << 12; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				while (count > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Push out the next byte | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					uint16_t w = (*p_write++) << 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					w |= *p_write++; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (lsbfirst) w = __builtin_bswap16(w); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (count == 2) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						port().PUSHR = w | SPI_PUSHR_CTAS(1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					else	 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						port().PUSHR = w | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					count -= 2; // how many bytes to output. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Make sure queue is not full before pushing next byte out | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					do { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (!(port().CTAR0 & SPI_CTAR_LSBFE)) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// We are doing the standard MSB order | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					const uint8_t *p_write = (const uint8_t *)buf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					uint8_t *p_read = (uint8_t *)retbuf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					size_t count_read = count; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Lets clear the reader queue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					uint32_t sr; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Now lets loop while we still have data to output | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (count & 1) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    if (p_write) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (count > 1) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								port().PUSHR = *p_write++ | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								port().PUSHR = *p_write++ | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (count > 1) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								port().PUSHR = _transferWriteFill | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								port().PUSHR = _transferWriteFill | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						count--; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				    uint16_t w =  (uint16_t)(_transferWriteFill << 8) | _transferWriteFill; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					while (count > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Push out the next byte;  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    if (p_write) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    	w = (*p_write++) << 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							w |= *p_write++; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    uint16_t queue_full_status_mask = (hardware().queue_size-1) << 12; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (count == 2) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							port().PUSHR = w | SPI_PUSHR_CTAS(1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						else	 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							port().PUSHR = w | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						count -= 2; // how many bytes to output. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Make sure queue is not full before pushing next byte out | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						do { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							sr = port().SR; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (sr & 0xF0)  { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								uint16_t w = port().POPR;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								if (count_read & 1) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									if (p_read) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
										*p_read++ = w;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									}  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									count_read--; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									if (p_read) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
										*p_read++ = w >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
										*p_read++ = (w & 0xff); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									count_read -= 2; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} while ((sr & (15 << 12)) > queue_full_status_mask); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// now lets wait for all of the read bytes to be returned... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					while (count_read) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						sr = port().SR; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (sr & 0xF0)  { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							uint16_t w = port().POPR;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (count_read & 1) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								*p_read++ = w;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								if (p_read) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									*p_read++ = w;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								count_read--; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								if (lsbfirst) w = __builtin_bswap16(w); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								*p_read++ = w >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								*p_read++ = (w & 0xff); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								if (p_read) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									*p_read++ = w >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									*p_read++ = (w & 0xff); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								count_read -= 2; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} while ((sr & (15 << 12)) > full_mask); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// We are doing the less ofen LSB mode | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					const uint8_t *p_write = (const uint8_t *)buf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					uint8_t *p_read = (uint8_t *)retbuf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					size_t count_read = count; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Lets clear the reader queue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					uint32_t sr; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Now lets loop while we still have data to output | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (count & 1) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    if (p_write) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (count > 1) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								port().PUSHR = *p_write++ | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								port().PUSHR = *p_write++ | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (count > 1) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								port().PUSHR = _transferWriteFill | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								port().PUSHR = _transferWriteFill | SPI_PUSHR_CTAS(0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						count--; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				    uint16_t w = _transferWriteFill; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					while (count > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Push out the next byte;  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    if (p_write) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							w = *p_write++; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    	w |= ((*p_write++) << 8); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					    uint16_t queue_full_status_mask = (hardware().queue_size-1) << 12; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (count == 2) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							port().PUSHR = w | SPI_PUSHR_CTAS(1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						else	 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							port().PUSHR = w | SPI_PUSHR_CONT | SPI_PUSHR_CTAS(1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						count -= 2; // how many bytes to output. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Make sure queue is not full before pushing next byte out | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						do { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							sr = port().SR; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (sr & 0xF0)  { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								uint16_t w = port().POPR;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								if (count_read & 1) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									if (p_read) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
										*p_read++ = w;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									}  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									count_read--; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									if (p_read) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
										*p_read++ = (w & 0xff); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
										*p_read++ = w >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									count_read -= 2; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} while ((sr & (15 << 12)) > queue_full_status_mask); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// now lets wait for all of the read bytes to be returned... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					while (count_read) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						sr = port().SR; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (sr & 0xF0)  { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							uint16_t w = port().POPR;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (count_read & 1) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								if (p_read) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									*p_read++ = w;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								count_read--; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								if (p_read) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									*p_read++ = (w & 0xff); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
									*p_read++ = w >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								count_read -= 2; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//============================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// ASYNCH Support | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//============================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// Try Transfer using DMA. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#ifdef SPI_HAS_TRANSFER_ASYNC | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static uint8_t bit_bucket; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#define dontInterruptAtCompletion(dmac) (dmac)->TCD->CSR &= ~DMA_TCD_CSR_INTMAJOR | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// Init the DMA channels | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool SPIClass::initDMAChannels() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Allocate our channels.  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX = new DMAChannel(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (_dmaTX == nullptr) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// now lets wait for all of the read bytes to be returned... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				while (count_read) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					sr = port().SR; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (sr & 0xF0)  { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						uint16_t w = port().POPR;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (count_read & 1) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							*p_read++ = w;  // Read any pending RX bytes in | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							count_read--; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (lsbfirst) w = __builtin_bswap16(w); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							*p_read++ = w >> 8; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							*p_read++ = (w & 0xff); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							count_read -= 2; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX = new DMAChannel(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (_dmaRX == nullptr) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					delete _dmaTX; // release it | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX = nullptr;  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Let's setup the RX chain | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->disable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->source((volatile uint8_t&)port().POPR); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->disableOnCompletion(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->triggerAtHardwareEvent(hardware().rx_dma_channel); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->attachInterrupt(hardware().dma_rxisr); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->interruptAtCompletion(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// We may be using settings chain here so lets set it up.  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Now lets setup TX chain.  Note if trigger TX is not set | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// we need to have the RX do it for us. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->disable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->destination((volatile uint8_t&)port().PUSHR); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->disableOnCompletion(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (hardware().tx_dma_channel) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX->triggerAtHardwareEvent(hardware().tx_dma_channel); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//		Serial.printf("SPI InitDMA tx triger by RX: %x\n", (uint32_t)_dmaRX); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				    _dmaTX->triggerAtTransfersOf(*_dmaRX); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dma_state = DMAState::idle;  // Should be first thing set! | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				return true; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// Main Aync Transfer function | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool SPIClass::transfer(const void *buf, void *retbuf, size_t count, EventResponderRef event_responder) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t dma_first_byte; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (_dma_state == DMAState::notAllocated) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (!initDMAChannels()) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						return false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (_dma_state == DMAState::active) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return false; // already active | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				event_responder.clearEvent();	// Make sure it is not set yet | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (count < 2) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Use non-async version to simplify cases... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					transfer(buf, retbuf, count); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					event_responder.triggerEvent(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return true; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Now handle the cases where the count > then how many we can output in one DMA request | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (count > hardware().max_dma_count) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dma_count_remaining = count - hardware().max_dma_count; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					count = hardware().max_dma_count; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dma_count_remaining = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Now See if caller passed in a source buffer.  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->TCD->ATTR_DST = 0;		// Make sure set for 8 bit mode | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t *write_data = (uint8_t*) buf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (buf) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					dma_first_byte = *write_data; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX->sourceBuffer((uint8_t*)write_data+1, count-1);   | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX->TCD->SLAST = 0;	// Finish with it pointing to next location | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					dma_first_byte = _transferWriteFill; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX->source((uint8_t&)_transferWriteFill);   // maybe have setable value | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					DMAChanneltransferCount(_dmaTX, count-1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}	 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (retbuf) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// On T3.5 must handle SPI1/2 differently as only one DMA channel | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->TCD->ATTR_SRC = 0;		//Make sure set for 8 bit mode... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->destinationBuffer((uint8_t*)retbuf, count); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->TCD->DLASTSGA = 0;		// At end point after our bufffer | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Write  only mode | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->TCD->ATTR_SRC = 0;		//Make sure set for 8 bit mode... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->destination((uint8_t&)bit_bucket); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					DMAChanneltransferCount(_dmaRX, count); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dma_event_responder = &event_responder; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Now try to start it? | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Setup DMA main object | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				yield(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_CLR_TXF | SPI_MCR_PCSIS(0x1F); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				port().SR = 0xFF0F0000; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Lets try to output the first byte to make sure that we are in 8 bit mode... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				port().PUSHR = dma_first_byte | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;	 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (hardware().tx_dma_channel) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					port().RSER =  SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS | SPI_RSER_TFFF_RE | SPI_RSER_TFFF_DIRS; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->enable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Get the initial settings.  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX->enable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					//T3.5 SP1 and SPI2 - TX is not triggered by SPI but by RX... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					port().RSER =  SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS ; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				    _dmaTX->triggerAtTransfersOf(*_dmaRX); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX->enable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->enable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dma_state = DMAState::active; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				return true; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//------------------------------------------------------------------------- | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// DMA RX ISR | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//------------------------------------------------------------------------- | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void SPIClass::dma_rxisr(void) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->clearInterrupt(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->clearComplete(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->clearComplete(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t should_reenable_tx = true;	// should we re-enable TX maybe not if count will be 0... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (_dma_count_remaining) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// What do I need to do to start it back up again... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// We will use the BITR/CITR from RX as TX may have prefed some stuff | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (_dma_count_remaining > hardware().max_dma_count) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						_dma_count_remaining -= hardware().max_dma_count; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						DMAChanneltransferCount(_dmaTX, _dma_count_remaining-1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						DMAChanneltransferCount(_dmaRX, _dma_count_remaining); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (_dma_count_remaining == 1) should_reenable_tx = false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						_dma_count_remaining = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// In some cases we need to again start the TX manually to get it to work... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (_dmaTX->TCD->SADDR == &_transferWriteFill) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (port().CTAR0  & SPI_CTAR_FMSZ(8)) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							port().PUSHR = (_transferWriteFill | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} else  { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							port().PUSHR = (_transferWriteFill | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (port().CTAR0  & SPI_CTAR_FMSZ(8)) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							// 16 bit mode | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							uint16_t w = *((uint16_t*)_dmaTX->TCD->SADDR); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							_dmaTX->TCD->SADDR = (volatile uint8_t*)(_dmaTX->TCD->SADDR) + 2; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							port().PUSHR = (w | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} else  { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							uint8_t w = *((uint8_t*)_dmaTX->TCD->SADDR); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							_dmaTX->TCD->SADDR = (volatile uint8_t*)(_dmaTX->TCD->SADDR) + 1; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							port().PUSHR = (w | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->enable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (should_reenable_tx) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						_dmaTX->enable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					port().RSER = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					//port().MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F);  // clear out the queue | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					port().SR = 0xFF0F0000; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					port().CTAR0  &= ~(SPI_CTAR_FMSZ(8)); 	// Hack restore back to 8 bits | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dma_state = DMAState::completed;   // set back to 1 in case our call wants to start up dma again | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dma_event_responder->triggerEvent(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif // SPI_HAS_TRANSFER_ASYNC | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			/**********************************************************/ | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -548,7 +897,14 @@ void SPIClass::transfer(void *buf, size_t count) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#elif defined(__arm__) && defined(TEENSYDUINO) && defined(KINETISL) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR0(void) {/*SPI.dma_rxisr();*/} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#ifdef SPI_HAS_TRANSFER_ASYNC | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR0(void) {SPI.dma_isr();} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR1(void) {SPI1.dma_isr();} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#else | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR0(void) {;} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR1(void) {;} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SIM_SCGC4, SIM_SCGC4_SPI0, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				0, // BR index 0 | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -565,7 +921,6 @@ const SPIClass::SPI_Hardware_t SPIClass::spi0_hardware = { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			SPIClass SPI((uintptr_t)&KINETISL_SPI0, (uintptr_t)&SPIClass::spi0_hardware); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void _spi_dma_rxISR1(void) {/*SPI1.dma_rxisr();*/} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			const SPIClass::SPI_Hardware_t SPIClass::spi1_hardware = { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SIM_SCGC4, SIM_SCGC4_SPI1, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				1, // BR index 1 in SPI Settings | 
		
		
	
	
		
			
			| 
				
			 | 
			
			 | 
			@@ -752,8 +1107,145 @@ uint8_t SPIClass::setCS(uint8_t pin) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				return 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void SPIClass::transfer(const void * buf, void * retbuf, size_t count) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (count == 0) return; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				const uint8_t *p = (const uint8_t *)buf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t *pret = (uint8_t *)retbuf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t in; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				while (!(port().S & SPI_S_SPTEF)) ; // wait | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t out = p ? *p++ : _transferWriteFill; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				port().DL = out; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				while (--count > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (p) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						out = *p++; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					while (!(port().S & SPI_S_SPTEF)) ; // wait | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					__disable_irq(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					port().DL = out; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					while (!(port().S & SPI_S_SPRF)) ; // wait | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					in = port().DL; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					__enable_irq(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (pret)*pret++ = in; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				while (!(port().S & SPI_S_SPRF)) ; // wait | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				in = port().DL; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (pret)*pret = in; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//============================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// ASYNCH Support | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//============================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// Try Transfer using DMA. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#ifdef SPI_HAS_TRANSFER_ASYNC | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static uint8_t      _dma_dummy_rx; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void SPIClass::dma_isr(void) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				//  Serial.println("_spi_dma_rxISR"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->clearInterrupt(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				port().C2 = 0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t tmp __attribute__((unused)) = port().S; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->clearComplete(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->clearComplete(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dma_state = DMAState::completed;   // set back to 1 in case our call wants to start up dma again | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dma_event_responder->triggerEvent(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool SPIClass::initDMAChannels() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				//Serial.println("First dma call"); Serial.flush(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX = new DMAChannel(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (_dmaTX == nullptr) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->disable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->destination((volatile uint8_t&)port().DL); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->disableOnCompletion(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->triggerAtHardwareEvent(hardware().tx_dma_channel); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX = new DMAChannel(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (_dmaRX == NULL) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					delete _dmaTX; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX = nullptr; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->disable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->source((volatile uint8_t&)port().DL); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->disableOnCompletion(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->triggerAtHardwareEvent(hardware().rx_dma_channel); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->attachInterrupt(hardware().dma_isr); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->interruptAtCompletion(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dma_state = DMAState::idle;  // Should be first thing set! | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				//Serial.println("end First dma call"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				return true; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// Main Aync Transfer function | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			//========================================================================= | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			bool SPIClass::transfer(const void *buf, void *retbuf, size_t count, EventResponderRef event_responder) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (_dma_state == DMAState::notAllocated) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (!initDMAChannels()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						return false; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			   | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (_dma_state == DMAState::active) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return false; // already active | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				event_responder.clearEvent();	// Make sure it is not set yet | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (count < 2) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Use non-async version to simplify cases... | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					transfer(buf, retbuf, count); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					event_responder.triggerEvent(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return true; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				//_dmaTX->destination((volatile uint8_t&)port().DL); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				//_dmaRX->source((volatile uint8_t&)port().DL); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaTX->CFG->DCR = (_dmaTX->CFG->DCR & ~DMA_DCR_DSIZE(3)) | DMA_DCR_DSIZE(1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dmaRX->CFG->DCR = (_dmaRX->CFG->DCR & ~DMA_DCR_SSIZE(3)) | DMA_DCR_SSIZE(1);  // 8 bit transfer | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Now see if the user passed in TX buffer to send. | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				uint8_t first_char; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (buf) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					uint8_t *data_out = (uint8_t*)buf; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					first_char = *data_out++; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX->sourceBuffer(data_out, count-1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					first_char = (_transferWriteFill & 0xff); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX->source((uint8_t&)_transferWriteFill);   // maybe have setable value | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaTX->transferCount(count-1); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (retbuf) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->destinationBuffer((uint8_t*)retbuf, count); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->destination(_dma_dummy_rx);    // NULL ? | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					_dmaRX->transferCount(count); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				_dma_event_responder = &event_responder; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				//Serial.println("Before DMA C2"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Try pushing the first character | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    while (!(port().S & SPI_S_SPTEF)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    port().DL = first_char; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				port().C2 |= SPI_C2_TXDMAE | SPI_C2_RXDMAE; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Now  make sure SPI is enabled.  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				port().C1 |= SPI_C1_SPE; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			       | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    _dmaRX->enable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    _dmaTX->enable(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    _dma_state = DMAState::active; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			    return true; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif //SPI_HAS_TRANSFER_ASYNC | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#endif |