///////////////////////////////////////////////////////////////////////////// //! Outputs the Write Buffer Memory opcode/constant (8 bits) and data to //! write (8 bits) over the SPI. //! //! EWRPT is incremented after the write. //! \param[in] value Byte to write into the ENC28J60 buffer memory //! \return < 0 on errors ///////////////////////////////////////////////////////////////////////////// s32 MIOS32_ENC28J60_MACPut(u8 value) { s32 status = 0; CSN_0; status |= MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, WBM); status |= MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, value); CSN_1; return status; }
///////////////////////////////////////////////////////////////////////////// //! Sends the 8 bit BFS opcode/Address byte over the SPI and then sends //! the data in the next 8 SPI clocks //! //! This routine is almost identical to the MIOS32_ENC28J60_WriteReg() and //! MIOS32_ENC28J60_BFCReg() functions. It is separate to maximize speed. //! //! MIOS32_ENC28J60_BFSReg() must only be used on ETH registers. //! \param[in] address 5 bit address of the register to modify. The top 3 bits must be 0 //! \param[in] data Byte to be used with the Bit Field Set operation //! \return < 0 on errors ///////////////////////////////////////////////////////////////////////////// s32 MIOS32_ENC28J60_BFSReg(u8 address, u8 data) { s32 status = 0; CSN_0; status |= MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, BFS | address); status |= MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, data); CSN_1; return status; }
///////////////////////////////////////////////////////////////////////////// //! Returns the byte pointed to by ERDPT and increments ERDPT so MIOS32_ENC28J60_MACGet() //! can be called again. The increment will follow the receive buffer wrapping boundary. //! //! EWRPT is incremented after the read. //! \return < 0 on errors //! \return >= 0: Byte read from the ENC28J60's RAM ///////////////////////////////////////////////////////////////////////////// s32 MIOS32_ENC28J60_MACGet(void) { s32 status; CSN_0; status = MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, RBM); if( status >= 0 ) status = MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, 0xff); CSN_1; return status; }
///////////////////////////////////////////////////////////////////////////// //! Sends the System Reset SPI command to the Ethernet controller. //! It resets all register contents (except for ECOCON) and returns the //! device to the power on default state. //! //! \return < 0 on errors ///////////////////////////////////////////////////////////////////////////// s32 MIOS32_ENC28J60_SendSystemReset(void) { s32 status = 0; #if 0 // sequence taken from SendSystemReset() of Microchip driver // Note: The power save feature may prevent the reset from executing, so // we must make sure that the device is not in power save before issuing // a reset. if( (status=MIOS32_ENC28J60_BFCReg(ECON2, ECON2_PWRSV)) < 0 ) return status; // Give some opportunity for the regulator to reach normal regulation and // have all clocks running MIOS32_DELAY_Wait_uS(1000); // 1 mS #endif // Execute the System Reset command status = 0; CSN_0; status |= MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, SR); CSN_1; if( status < 0 ) return status; // Wait for the oscillator start up timer and PHY to become ready MIOS32_DELAY_Wait_uS(1000); // 1 mS return 0; // no error }
static void TASK_SPI_Handler(void *pvParameters) { // fill Tx Buffer with housenumbers { int i; for(i=0; i<TRANSFER_BUFFER_SIZE; ++i) tx_buffer[i] = i; } while( 1 ) { int i; for(i=0; i<TRANSFER_BUFFER_SIZE; ++i) { // toggle Status LED to as a sign of live MIOS32_BOARD_LED_Set(1, ~MIOS32_BOARD_LED_Get()); // receive byte rx_buffer[i] = MIOS32_SPI_TransferByte(SLAVE_SPI, tx_buffer[i]); } SPI_Callback(); // print received bytes MIOS32_MIDI_SendDebugHexDump((u8 *)rx_buffer, TRANSFER_BUFFER_SIZE); } }
///////////////////////////////////////////////////////////////////////////// //! Sends the 8 bit RCR opcode/Address byte as well as a dummy byte over the //! SPI and then retrives the register contents in the last 8 SPI clocks. //! //! This routine cannot be used to access ETH or PHY registers. //! Use MIOS32_ENC28J60_ReadETHReg() or MIOS32_ENC28J60_ReadPHYReg() for that //! purpose. //! \param[in] address 5 bit address of the MAC or MII register to read from. //! \return < 0 on errors //! \return >= 0: Byte read from the Ethernet controller's MAC/MII register. ///////////////////////////////////////////////////////////////////////////// s32 MIOS32_ENC28J60_ReadMACReg(u8 address) { s32 status = 0; // send opcode CSN_0; status |= MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, RCR | address); status |= MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, 0xff); // dummy byte if( status >= 0 ) { // read register status = MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, 0xff); } CSN_1; return status; }
///////////////////////////////////////////////////////////////////////////// //! Sends the 8 bit RCR opcode/Address byte over the SPI and then retrives //! the register contents in the next 8 SPI clocks. //! //! This routine cannot be used to access MAC/MII or PHY registers. //! Use MIOS32_ENC28J60_ReadMACReg() or MIOS32_ENC28J60_ReadPHYReg() for that //! purpose. //! \param[in] address 5 bit address of the ETH control register to read from. //! \return < 0 on errors //! \return >= 0: Byte read from the Ethernet controller's ETH register. ///////////////////////////////////////////////////////////////////////////// s32 MIOS32_ENC28J60_ReadETHReg(u8 address) { s32 status = 0; // send opcode CSN_0; status |= MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, RCR | address); // skip if something already failed if( status >= 0 ) { // read register status = MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, 0xff); } CSN_1; return status; }
///////////////////////////////////////////////////////////////////////////// //! writes several sequential bytes to the ENC28J60 RAM. It performs faster //! than multiple MIOS32_ENC28J60_MACPut() calls. //! //! EWRPT is incremented by len. //! \param[in] buffer source buffer //! \param[in] len number of bytes which should be written //! \return < 0 on errors ///////////////////////////////////////////////////////////////////////////// s32 MIOS32_ENC28J60_MACPutArray(u8 *buffer, u16 len) { s32 status = 0; CSN_0; status |= MIOS32_SPI_TransferByte(MIOS32_ENC28J60_SPI, WBM); status |= MIOS32_SPI_TransferBlock(MIOS32_ENC28J60_SPI, buffer, NULL, len, NULL); CSN_1; return status; }
///////////////////////////////////////////////////////////////////////////// //! (Re-)initializes SPI peripheral transfer mode //! By default, all SPI peripherals are configured with //! MIOS32_SPI_MODE_CLK1_PHASE1 and MIOS32_SPI_PRESCALER_128 //! //! \param[in] spi SPI number (0, 1 or 2) //! \param[in] spi_mode configures clock and capture phase: //! <UL> //! <LI>MIOS32_SPI_MODE_CLK0_PHASE0: Idle level of clock is 0, data captured at rising edge //! <LI>MIOS32_SPI_MODE_CLK0_PHASE1: Idle level of clock is 0, data captured at falling edge //! <LI>MIOS32_SPI_MODE_CLK1_PHASE0: Idle level of clock is 1, data captured at falling edge //! <LI>MIOS32_SPI_MODE_CLK1_PHASE1: Idle level of clock is 1, data captured at rising edge //! </UL> //! \param[in] spi_prescaler configures the SPI speed: //! <UL> //! <LI>MIOS32_SPI_PRESCALER_2: sets clock rate 27.7~ nS @ 72 MHz (36 MBit/s) (only supported for spi==0, spi1 uses 4 instead) //! <LI>MIOS32_SPI_PRESCALER_4: sets clock rate 55.5~ nS @ 72 MHz (18 MBit/s) //! <LI>MIOS32_SPI_PRESCALER_8: sets clock rate 111.1~ nS @ 72 MHz (9 MBit/s) //! <LI>MIOS32_SPI_PRESCALER_16: sets clock rate 222.2~ nS @ 72 MHz (4.5 MBit/s) //! <LI>MIOS32_SPI_PRESCALER_32: sets clock rate 444.4~ nS @ 72 MHz (2.25 MBit/s) //! <LI>MIOS32_SPI_PRESCALER_64: sets clock rate 888.8~ nS @ 72 MHz (1.125 MBit/s) //! <LI>MIOS32_SPI_PRESCALER_128: sets clock rate 1.7~ uS @ 72 MHz (0.562 MBit/s) //! <LI>MIOS32_SPI_PRESCALER_256: sets clock rate 3.5~ uS @ 72 MHz (0.281 MBit/s) //! </UL> //! \return 0 if no error //! \return -1 if disabled SPI port selected //! \return -2 if unsupported SPI port selected //! \return -3 if invalid spi_prescaler selected //! \return -4 if invalid spi_mode selected ///////////////////////////////////////////////////////////////////////////// s32 MIOS32_SPI_TransferModeInit(u8 spi, mios32_spi_mode_t spi_mode, mios32_spi_prescaler_t spi_prescaler) { // SPI configuration SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; switch( spi_mode ) { case MIOS32_SPI_MODE_SLAVE_CLK0_PHASE0: SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; case MIOS32_SPI_MODE_CLK0_PHASE0: SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; break; case MIOS32_SPI_MODE_SLAVE_CLK0_PHASE1: SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; case MIOS32_SPI_MODE_CLK0_PHASE1: SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; break; case MIOS32_SPI_MODE_SLAVE_CLK1_PHASE0: SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; case MIOS32_SPI_MODE_CLK1_PHASE0: SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; break; case MIOS32_SPI_MODE_SLAVE_CLK1_PHASE1: SPI_InitStructure.SPI_Mode = SPI_Mode_Slave; SPI_InitStructure.SPI_NSS = SPI_NSS_Hard; case MIOS32_SPI_MODE_CLK1_PHASE1: SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; break; default: return -4; // invalid SPI clock/phase mode } if( spi_prescaler >= 8 ) return -3; // invalid prescaler selected switch( spi ) { case 0: { #ifdef MIOS32_DONT_USE_SPI0 return -1; // disabled SPI port #else u16 prev_cr1 = MIOS32_SPI0_PTR->CR1; // SPI1 perpipheral is located in APB2 domain and clocked at full speed // therefore we have to add +1 to the prescaler SPI_InitStructure.SPI_BaudRatePrescaler = ((u16)spi_prescaler&7) << 3; SPI_Init(MIOS32_SPI0_PTR, &SPI_InitStructure); if( SPI_InitStructure.SPI_Mode == SPI_Mode_Master ) { if( (prev_cr1 ^ MIOS32_SPI0_PTR->CR1) & 3 ) { // CPOL and CPHA located at bit #1 and #0 // clock configuration has been changed - we should send a dummy byte // before the application activates chip select. // this solves a dependency between SDCard and ENC28J60 driver MIOS32_SPI_TransferByte(spi, 0xff); } } #endif } break; case 1: { #ifdef MIOS32_DONT_USE_SPI1 return -1; // disabled SPI port #else u16 prev_cr1 = MIOS32_SPI1_PTR->CR1; // SPI2 perpipheral is located in APB1 domain and clocked at half speed if( spi_prescaler == 0 ) SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; else SPI_InitStructure.SPI_BaudRatePrescaler = (((u16)spi_prescaler&7)-1) << 3; SPI_Init(MIOS32_SPI1_PTR, &SPI_InitStructure); if( SPI_InitStructure.SPI_Mode == SPI_Mode_Master ) { if( (prev_cr1 ^ MIOS32_SPI1_PTR->CR1) & 3 ) { // CPOL and CPHA located at bit #1 and #0 // clock configuration has been changed - we should send a dummy byte // before the application activates chip select. // this solves a dependency between SDCard and ENC28J60 driver MIOS32_SPI_TransferByte(spi, 0xff); } } #endif } break; case 2: #ifdef MIOS32_DONT_USE_SPI2 return -1; // disabled SPI port #else if( SPI_InitStructure.SPI_Mode == SPI_Mode_Slave ) { return -3; // slave mode not supported for this SPI } // no clock prescaler for SW emulated SPI // remember mode settings sw_spi2_mode = spi_mode; // set clock idle level switch( sw_spi2_mode ) { case MIOS32_SPI_MODE_CLK0_PHASE0: case MIOS32_SPI_MODE_CLK0_PHASE1: MIOS32_SPI2_SET_SCLK_0; break; case MIOS32_SPI_MODE_CLK1_PHASE0: case MIOS32_SPI_MODE_CLK1_PHASE1: MIOS32_SPI2_SET_SCLK_1; break; default: return -4; // invalid SPI clock/phase mode } break; #endif default: return -2; // unsupported SPI port } return 0; // no error }
///////////////////////////////////////////////////////////////////////////// //! Transfers a block of bytes via DMA. //! \param[in] spi SPI number (0, 1 or 2) //! \param[in] send_buffer pointer to buffer which should be sent.<BR> //! If NULL, 0xff (all-one) will be sent. //! \param[in] receive_buffer pointer to buffer which should get the received values.<BR> //! If NULL, received bytes will be discarded. //! \param[in] len number of bytes which should be transfered //! \param[in] callback pointer to callback function which will be executed //! from DMA channel interrupt once the transfer is finished. //! If NULL, no callback function will be used, and MIOS32_SPI_TransferBlock() will //! block until the transfer is finished. //! \return >= 0 if no error during transfer //! \return -1 if disabled SPI port selected //! \return -2 if unsupported SPI port selected //! \return -3 if function has been called during an ongoing DMA transfer ///////////////////////////////////////////////////////////////////////////// s32 MIOS32_SPI_TransferBlock(u8 spi, u8 *send_buffer, u8 *receive_buffer, u16 len, void *callback) { SPI_TypeDef *spi_ptr; DMA_Channel_TypeDef *dma_tx_ptr, *dma_rx_ptr; switch( spi ) { case 0: #ifdef MIOS32_DONT_USE_SPI0 return -1; // disabled SPI port #else spi_ptr = MIOS32_SPI0_PTR; dma_tx_ptr = MIOS32_SPI0_DMA_TX_PTR; dma_rx_ptr = MIOS32_SPI0_DMA_RX_PTR; break; #endif case 1: #ifdef MIOS32_DONT_USE_SPI1 return -1; // disabled SPI port #else spi_ptr = MIOS32_SPI1_PTR; dma_tx_ptr = MIOS32_SPI1_DMA_TX_PTR; dma_rx_ptr = MIOS32_SPI1_DMA_RX_PTR; break; #endif case 2: #ifdef MIOS32_DONT_USE_SPI2 return -1; // disabled SPI port #else // Software Emulation { u32 pos; // we have 4 cases: if( receive_buffer != NULL ) { if( send_buffer != NULL ) { for(pos=0; pos<len; ++pos) *receive_buffer++ = MIOS32_SPI_TransferByte(spi, *send_buffer++); } else { for(pos=0; pos<len; ++pos) *receive_buffer++ = MIOS32_SPI_TransferByte(spi, 0xff); } } else { // TODO: we could use an optimized "send only" function to speed up the SW emulation! if( send_buffer != NULL ) { for(pos=0; pos<len; ++pos) MIOS32_SPI_TransferByte(spi, *send_buffer++); } else { // nothing to do... } } // set callback function spi_callback[spi] = callback; if( spi_callback[spi] != NULL ) spi_callback[spi](); return 0; // END of SW emulation - EXIT here! } break; #endif default: return -2; // unsupported SPI port } // exit if ongoing transfer if( dma_rx_ptr->CNDTR ) return -3; // set callback function spi_callback[spi] = callback; // ensure that previously received value doesn't cause DMA access if( spi_ptr->DR ); // configure Rx channel // TK: optimization method: read rx_CCR once, write back only when required // the channel must be disabled to configure new values u32 rx_CCR = dma_rx_ptr->CCR & ~CCR_ENABLE; dma_rx_ptr->CCR = rx_CCR; if( receive_buffer != NULL ) { // enable memory addr. increment - bytes written into receive buffer dma_rx_ptr->CMAR = (u32)receive_buffer; rx_CCR |= DMA_MemoryInc_Enable; } else { // disable memory addr. increment - bytes written into dummy buffer rx_dummy_byte = 0xff; dma_rx_ptr->CMAR = (u32)&rx_dummy_byte; rx_CCR &= ~DMA_MemoryInc_Enable; } dma_rx_ptr->CNDTR = len; rx_CCR |= CCR_ENABLE; // configure Tx channel // TK: optimization method: read tx_CCR once, write back only when required // the channel must be disabled to configure new values u32 tx_CCR = dma_tx_ptr->CCR & ~CCR_ENABLE; dma_tx_ptr->CCR = tx_CCR; if( send_buffer != NULL ) { // enable memory addr. increment - bytes read from send buffer dma_tx_ptr->CMAR = (u32)send_buffer; tx_CCR |= DMA_MemoryInc_Enable; } else { // disable memory addr. increment - bytes read from dummy buffer tx_dummy_byte = 0xff; dma_tx_ptr->CMAR = (u32)&tx_dummy_byte; tx_CCR &= ~DMA_MemoryInc_Enable; } dma_tx_ptr->CNDTR = len; // enable DMA interrupt if callback function active if( callback != NULL ) { rx_CCR |= DMA_IT_TC; dma_rx_ptr->CCR = rx_CCR; // start DMA transfer dma_tx_ptr->CCR = tx_CCR | CCR_ENABLE; } else { rx_CCR &= ~DMA_IT_TC; dma_rx_ptr->CCR = rx_CCR; // start DMA transfer dma_tx_ptr->CCR = tx_CCR | CCR_ENABLE; // if no callback: wait until all bytes have been transmitted/received while( dma_rx_ptr->CNDTR ); } return 0; // no error; }