diff --git a/inc/SAMDDAC.h b/inc/SAMDDAC.h index 67540bd..f873792 100644 --- a/inc/SAMDDAC.h +++ b/inc/SAMDDAC.h @@ -107,7 +107,7 @@ class SAMDDAC : public CodalComponent, public DmaComponent, public DataSink /** * Interrupt callback when playback of DMA buffer has completed */ - virtual void dmaTransferComplete(DmaCode c); + virtual void dmaTransferComplete(DmaInstance *dma, DmaCode c) override; }; #endif diff --git a/inc/SAMDDMAC.h b/inc/SAMDDMAC.h index afa7a89..c40cf5e 100644 --- a/inc/SAMDDMAC.h +++ b/inc/SAMDDMAC.h @@ -49,10 +49,12 @@ enum DmaBeatSize BeatWord }; +class DmaInstance; + class DmaComponent { public: - virtual void dmaTransferComplete(DmaCode c); + virtual void dmaTransferComplete(DmaInstance *dma, DmaCode c) = 0; }; static inline int sercom_trigger_src(int sercomIdx, bool tx) diff --git a/inc/SAMDPDM.h b/inc/SAMDPDM.h index 63da503..dd32a66 100644 --- a/inc/SAMDPDM.h +++ b/inc/SAMDPDM.h @@ -102,7 +102,7 @@ class SAMD21PDM : public CodalComponent, public DmaComponent, public DataSource /** * Interrupt callback when playback of DMA buffer has completed */ - virtual void dmaTransferComplete(DmaCode c); + virtual void dmaTransferComplete(DmaInstance *dma, DmaCode c) override; /** * Enable this component diff --git a/inc/ZSPI.h b/inc/ZSPI.h index 5fec027..8befa23 100644 --- a/inc/ZSPI.h +++ b/inc/ZSPI.h @@ -62,7 +62,7 @@ class ZSPI : public codal::SPI, public codal::DmaComponent void init(); public: - virtual void dmaTransferComplete(DmaCode); + virtual void dmaTransferComplete(DmaInstance *, DmaCode) override; /** * Initialize SPI instance with given pins. diff --git a/inc/ZSingleWireSerial.h b/inc/ZSingleWireSerial.h index d699fe8..9282e50 100644 --- a/inc/ZSingleWireSerial.h +++ b/inc/ZSingleWireSerial.h @@ -24,9 +24,12 @@ namespace codal struct ::_usart_async_device USART_INSTANCE; DmaInstance* usart_tx_dma; DmaInstance* usart_rx_dma; - uint32_t pinmux; + Pin *rx; + uint8_t pinmux; uint8_t instance_number; uint8_t pad; + uint8_t rx_pinmux; + uint8_t rx_pad; protected: virtual void configureRxInterrupt(int enable); @@ -38,20 +41,14 @@ namespace codal public: /** - * This constructor is really ugly, but there isn't currently a nice representation of a model of the device - * i.e. a resource manager? + * Create a new instance of single wire serial. * - * @param p the pin instance to use for output + * @param p the pin instance to use for output (and input if no rx given) * - * @param instance the sercom instance that is compatible with p. - * - * @param instance_number the index into the sercom array (SERCOM0 == index 0) - * - * @param pinmux the pinmux settings for p, i.e. PA08 has pinmux settings PINMUX_PB08D_SERCOM4_PAD0 for sercom four - * - * @param pad the pad that the pin is routed through i.e. PA08 uses PAD0 of SERCOM4 (see data sheet). + * @param rx the pin instance to use for input + * **/ - ZSingleWireSerial(Pin& p); + ZSingleWireSerial(Pin& p, Pin *rx = NULL); virtual int putc(char c); virtual int getc(); @@ -73,7 +70,7 @@ namespace codal virtual int sendBreak(); - void dmaTransferComplete(DmaCode c) override; + void dmaTransferComplete(DmaInstance *dma, DmaCode c) override; }; } diff --git a/src/DmaFactory.cpp b/src/DmaFactory.cpp index 82c161e..5bb04a7 100644 --- a/src/DmaFactory.cpp +++ b/src/DmaFactory.cpp @@ -114,11 +114,6 @@ extern "C" void DMAC_4_Handler(void) } #endif -/** - * Base implementation of a DMA callback - */ -void DmaComponent::dmaTransferComplete(DmaCode) {} - DmaControllerInstance::DmaControllerInstance() { uint32_t ptr = (uint32_t)descriptorsBuffer; diff --git a/src/DmaInstance.cpp b/src/DmaInstance.cpp index 5bdcd6d..578394b 100644 --- a/src/DmaInstance.cpp +++ b/src/DmaInstance.cpp @@ -55,7 +55,7 @@ void DmaInstance::trigger(DmaCode c) disable(); if (this->cb) - this->cb->dmaTransferComplete(c); + this->cb->dmaTransferComplete(this, c); } @@ -107,7 +107,7 @@ void DmaInstance::transfer(const void *src_addr, void *dst_addr, uint32_t len) descriptor.SRCADDR.reg = (uint32_t)src_addr + len; if (dst_addr) descriptor.DSTADDR.reg = (uint32_t)dst_addr + len; - + enable(); target_enable_irq(); @@ -124,6 +124,13 @@ int DmaInstance::getBytesTransferred() #else DMAC_ACTIVE_Type active; active.reg = DMAC->ACTIVE.reg; + // datasheet (22.8.13) says BTCNT is only valid if ABUSY + // However, the chip doesn't seem to flush BTCNT to write back descriptor when it + // clears ABUSY, so if there are no other active channels, the write back will contain + // an old value indefinetely, while ABUSY is cleared. + // However, it is also possible that BTCNT contains the count from the previous + // transfer, before ABUSY is ever set. + // So this number may be flat wrong. if (active.bit.ID == channel_number) btcnt = active.bit.BTCNT; else diff --git a/src/SAMDDAC.cpp b/src/SAMDDAC.cpp index 231739d..9b24fff 100644 --- a/src/SAMDDAC.cpp +++ b/src/SAMDDAC.cpp @@ -273,7 +273,7 @@ extern void debug_flip(); /** * Base implementation of a DMA callback */ -void SAMDDAC::dmaTransferComplete(DmaCode c) +void SAMDDAC::dmaTransferComplete(DmaInstance *, DmaCode) { if (dataReady == 0) { diff --git a/src/SAMDPDM.cpp b/src/SAMDPDM.cpp index 9b55263..64e9c1b 100644 --- a/src/SAMDPDM.cpp +++ b/src/SAMDPDM.cpp @@ -266,7 +266,7 @@ void SAMD21PDM::decimate(Event) pdmDataBuffer = NULL; } -void SAMD21PDM::dmaTransferComplete(DmaCode c) +void SAMD21PDM::dmaTransferComplete(DmaInstance *, DmaCode c) { if (c == DMA_ERROR) while(1) DMESG("POO!!!"); diff --git a/src/ZSPI.cpp b/src/ZSPI.cpp index 7fd07cd..5ef880f 100644 --- a/src/ZSPI.cpp +++ b/src/ZSPI.cpp @@ -48,7 +48,7 @@ namespace codal #define ZERO(f) memset(&f, 0, sizeof(f)) -void ZSPI::dmaTransferComplete(DmaCode) +void ZSPI::dmaTransferComplete(DmaInstance *, DmaCode) { LOG("SPI complete D=%p", doneHandler); diff --git a/src/ZSingleWireSerial.cpp b/src/ZSingleWireSerial.cpp index 256cec6..bce9940 100644 --- a/src/ZSingleWireSerial.cpp +++ b/src/ZSingleWireSerial.cpp @@ -28,7 +28,7 @@ static void error_callback(struct _usart_async_device *device) { // flag any error to the dma handler. if (sws_instance) - sws_instance->dmaTransferComplete(DMA_ERROR); + sws_instance->dmaTransferComplete(NULL, DMA_ERROR); } static void tx_callback(struct _usart_async_device *) @@ -40,34 +40,34 @@ static void rx_callback(struct _usart_async_device *, uint8_t) { } -void ZSingleWireSerial::dmaTransferComplete(DmaCode errCode) +void ZSingleWireSerial::dmaTransferComplete(DmaInstance *dma, DmaCode errCode) { uint16_t mode = 0; if (errCode == DMA_COMPLETE) { - if (status & TX_CONFIGURED) + if (dma == usart_tx_dma && (status & TX_CONFIGURED)) mode = SWS_EVT_DATA_SENT; - if (status & RX_CONFIGURED) + if (dma == usart_rx_dma && (status & RX_CONFIGURED)) mode = SWS_EVT_DATA_RECEIVED; } else mode = SWS_EVT_ERROR; - Event evt(this->id, mode, CREATE_ONLY); - // if we have a cb member function, we invoke // otherwise fire the event for any listeners. if (this->cb) this->cb(mode); + else + Event(this->id, mode); } void ZSingleWireSerial::configureRxInterrupt(int enable) { } -ZSingleWireSerial::ZSingleWireSerial(Pin& p) : DMASingleWireSerial(p) +ZSingleWireSerial::ZSingleWireSerial(Pin& p, Pin *rx) : DMASingleWireSerial(p), rx(rx) { const mcu_pin_obj_t* single_wire_pin = samd_peripherals_get_pin(p.name); @@ -100,6 +100,23 @@ ZSingleWireSerial::ZSingleWireSerial(Pin& p) : DMASingleWireSerial(p) else target_panic(DEVICE_HARDWARE_CONFIGURATION_ERROR); + if (rx) { + const mcu_pin_obj_t* rx_pin = samd_peripherals_get_pin(rx->name); + if (rx_pin->sercom[0].index == this->instance_number) + { + this->rx_pad = rx_pin->sercom[0].pad; + this->rx_pinmux = MUX_C; // c + } + + else if (rx_pin->sercom[1].index == this->instance_number) + { + this->rx_pad = rx_pin->sercom[1].pad; + this->rx_pinmux = MUX_D; // d + } + else + target_panic(DEVICE_HARDWARE_CONFIGURATION_ERROR); + } + Sercom* instance = sercom_insts[this->instance_number]; DMESG("SWS pad %d, idx %d, fn: %d", 0, this->instance_number, this->pinmux); @@ -129,10 +146,10 @@ ZSingleWireSerial::ZSingleWireSerial(Pin& p) : DMASingleWireSerial(p) usart_tx_dma->configure(sercom_trigger_src(this->instance_number, true), BeatByte, NULL, (volatile void*)&CURRENT_USART->USART.DATA.reg); usart_rx_dma->configure(sercom_trigger_src(this->instance_number, false), BeatByte, (volatile void*)&CURRENT_USART->USART.DATA.reg, NULL); - setBaud(115200); - status = 0; + setBaud(115200); + #ifdef SAMD21 NVIC_SetPriority(SERCOM0_IRQn,1); NVIC_SetPriority(SERCOM1_IRQn,1); @@ -143,16 +160,30 @@ ZSingleWireSerial::ZSingleWireSerial(Pin& p) : DMASingleWireSerial(p) #else // SAMD51 has many more IRQs for SERCOMs #endif + } int ZSingleWireSerial::setBaud(uint32_t baud) { + if (rx) + { + configureRx(0); + configureTx(0); + } + #ifdef SERCOM_100MHZ_CLOCK CURRENT_USART->USART.BAUD.reg = 65536 - ((uint64_t)65536 * 16 * baud) / 100000000; #else CURRENT_USART->USART.BAUD.reg = 65536 - ((uint64_t)65536 * 16 * baud) / CONF_GCLK_SERCOM0_CORE_FREQUENCY;; #endif this->baud = baud; + + if (rx) + { + configureRx(1); + configureTx(1); + } + return DEVICE_OK; } @@ -227,7 +258,10 @@ int ZSingleWireSerial::configureRx(int enable) { if (enable && !(status & RX_CONFIGURED)) { - gpio_set_pin_function(p.name, this->pinmux); + if (rx) + gpio_set_pin_function(rx->name, this->rx_pinmux); + else + gpio_set_pin_function(p.name, this->pinmux); CURRENT_USART->USART.CTRLA.bit.ENABLE = 0; while(CURRENT_USART->USART.SYNCBUSY.bit.ENABLE); @@ -236,7 +270,7 @@ int ZSingleWireSerial::configureRx(int enable) CURRENT_USART->USART.CTRLA.bit.DORD = 1; #endif CURRENT_USART->USART.CTRLA.bit.SAMPR = 0; - CURRENT_USART->USART.CTRLA.bit.RXPO = this->pad; + CURRENT_USART->USART.CTRLA.bit.RXPO = rx ? this->rx_pad : this->pad; CURRENT_USART->USART.CTRLB.bit.CHSIZE = 0; // 8 BIT CURRENT_USART->USART.CTRLA.bit.ENABLE = 1; @@ -345,7 +379,8 @@ int ZSingleWireSerial::abortDMA() if (!(status & (RX_CONFIGURED | TX_CONFIGURED))) return DEVICE_INVALID_PARAMETER; - usart_tx_dma->abort(); + if (!rx) + usart_tx_dma->abort(); usart_rx_dma->abort(); // abort dma transfer