/* ======================================== * * Copyright YOUR COMPANY, THE YEAR * All Rights Reserved * UNPUBLISHED, LICENSED SOFTWARE. * * CONFIDENTIAL AND PROPRIETARY INFORMATION * WHICH IS THE PROPERTY OF your company. * * ======================================== */ #include // #include "pico/stdlib.h" #include "pico/sem.h" // #include "my_debug.h" // #include "spi.h" void spi_irq_handler(spi_t *this) { // Clear the interrupt request. dma_hw->ints0 = 1u << this->rx_dma; myASSERT(!dma_channel_is_busy(this->rx_dma)); sem_release(&this->sem); } // SPI Transfer: Read & Write (simultaneously) on SPI bus // If the data that will be received is not important, pass NULL as rx. // If the data that will be transmitted is not important, // pass NULL as tx and then the SPI_FILL_CHAR is sent out as each data // element. bool spi_transfer(spi_t *this, const uint8_t *tx, uint8_t *rx, size_t length) { myASSERT(512 == length); myASSERT(tx || rx); // myASSERT(!(tx && rx)); // tx write increment is already false if (tx) { channel_config_set_read_increment(&this->tx_dma_cfg, true); } else { static const uint8_t dummy = SPI_FILL_CHAR; tx = &dummy; channel_config_set_read_increment(&this->tx_dma_cfg, false); } // rx read increment is already false if (rx) { channel_config_set_write_increment(&this->rx_dma_cfg, true); } else { static uint8_t dummy = 0xA5; rx = &dummy; channel_config_set_write_increment(&this->rx_dma_cfg, false); } // Clear the interrupt request. dma_hw->ints0 = 1u << this->rx_dma; dma_channel_configure(this->tx_dma, &this->tx_dma_cfg, &spi_get_hw(this->hw_inst)->dr, // write address tx, // read address XFER_BLOCK_SIZE, // element count (each element is of // size transfer_data_size) false); // start dma_channel_configure(this->rx_dma, &this->rx_dma_cfg, rx, // write address &spi_get_hw(this->hw_inst)->dr, // read address XFER_BLOCK_SIZE, // element count (each element is of // size transfer_data_size) false); // start // start them exactly simultaneously to avoid races (in extreme cases // the FIFO could overflow) dma_start_channel_mask((1u << this->tx_dma) | (1u << this->rx_dma)); /* Timeout 1 sec */ uint32_t timeOut = 1000; /* Wait until master completes transfer or time out has occured. */ bool rc = sem_acquire_timeout_ms(&this->sem, timeOut); // Wait for notification from ISR if (!rc) { // If the timeout is reached the function will return false DBG_PRINTF("Notification wait timed out in %s\n", __FUNCTION__); return false; } dma_channel_wait_for_finish_blocking(this->tx_dma); dma_channel_wait_for_finish_blocking(this->rx_dma); myASSERT(!dma_channel_is_busy(this->tx_dma)); myASSERT(!dma_channel_is_busy(this->rx_dma)); return true; } bool my_spi_init(spi_t *this) { // bool __atomic_test_and_set (void *ptr, int memorder) // This built-in function performs an atomic test-and-set operation on the // byte at *ptr. The byte is set to some implementation defined nonzero // “set” value and the return value is true if and only if the previous // contents were “set”. if (__atomic_test_and_set(&(this->initialized), __ATOMIC_SEQ_CST)) return true; // The SPI may be shared (using multiple SSs); protect it sem_init(&this->sem, 0, 1); /* Configure component */ // Enable SPI at 100 kHz and connect to GPIOs spi_init(this->hw_inst, 100 * 1000); spi_set_format(this->hw_inst, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST); gpio_set_function(this->miso_gpio, GPIO_FUNC_SPI); gpio_set_function(this->mosi_gpio, GPIO_FUNC_SPI); gpio_set_function(this->sck_gpio, GPIO_FUNC_SPI); // ss_gpio is initialized in sd_spi_init() // SD cards' DO MUST be pulled up. gpio_pull_up(this->miso_gpio); // Grab some unused dma channels this->tx_dma = dma_claim_unused_channel(true); this->rx_dma = dma_claim_unused_channel(true); this->tx_dma_cfg = dma_channel_get_default_config(this->tx_dma); this->rx_dma_cfg = dma_channel_get_default_config(this->rx_dma); channel_config_set_transfer_data_size(&this->tx_dma_cfg, DMA_SIZE_8); channel_config_set_transfer_data_size(&this->rx_dma_cfg, DMA_SIZE_8); // We set the outbound DMA to transfer from a memory buffer to the SPI // transmit FIFO paced by the SPI TX FIFO DREQ The default is for the // read address to increment every element (in this case 1 byte - // DMA_SIZE_8) and for the write address to remain unchanged. channel_config_set_dreq(&this->tx_dma_cfg, spi_get_index(this->hw_inst) ? DREQ_SPI1_TX : DREQ_SPI0_TX); channel_config_set_write_increment(&this->tx_dma_cfg, false); // We set the inbound DMA to transfer from the SPI receive FIFO to a // memory buffer paced by the SPI RX FIFO DREQ We coinfigure the read // address to remain unchanged for each element, but the write address // to increment (so data is written throughout the buffer) channel_config_set_dreq(&this->rx_dma_cfg, spi_get_index(this->hw_inst) ? DREQ_SPI1_RX : DREQ_SPI0_RX); channel_config_set_read_increment(&this->rx_dma_cfg, false); /* Theory: we only need an interrupt on rx complete, since if rx is complete, tx must also be complete. */ // Configure the processor to run dma_handler() when DMA IRQ 0 is // asserted irq_set_exclusive_handler(DMA_IRQ_1, this->dma_isr); // JMH // Tell the DMA to raise IRQ line 0 when the channel finishes a block dma_channel_set_irq1_enabled(this->rx_dma, true); // JMH irq_set_enabled(DMA_IRQ_1, true); // JMH LED_INIT(); return true; } /* [] END OF FILE */