/**************************************************************************//** * @brief * Called on USB transfer completion callback for memory-mapped transfers. * Will initiate a new transfer if there is more data available. * If all data has been sent it will either send a CSW or stall the bulk-in * endpoint if needed. * * @param[in] status * The transfer status. * * @param[in] xferred * Number of bytes actually transferred. * * @param[in] remaining * Number of bytes not transferred. * * @return * USB_STATUS_OK. *****************************************************************************/ static int XferBotDataCallback(USB_Status_TypeDef status, uint32_t xferred, uint32_t remaining) { (void) status; (void) remaining; pCmdStatus->xferLen -= xferred; pCsw->dCSWDataResidue -= xferred; if (pCmdStatus->xferLen) { pCmdStatus->pData += xferred; UsbXferBotData(pCmdStatus->pData, EFM32_MIN(pCmdStatus->xferLen, pCmdStatus->maxBurst), XferBotDataCallback); } else { if (msdState == MSDD_SEND_CSW) { SendCsw(); EnableNextCbw(); msdState = MSDD_WAITFOR_CBW; } else if (msdState == MSDD_STALL_IN) { USBD_StallEp(BULK_IN); msdState = MSDD_WAIT_FOR_INUNSTALLED; } } return USB_STATUS_OK; }
/**************************************************************************//** * @brief * Serve the MSD state machine. * This function should be called on a regular basis from your main loop. * It cannot be called from within an interrupt handler. * @return * Returns true if there is no pending tasks to perform. This means that * energymodes (sleep) functionality can be used. *****************************************************************************/ bool MSDD_Handler(void) { static uint32_t len; /* Note: len is static ! */ switch (msdState) { case MSDD_ACCESS_INDIRECT: if (pCmdStatus->xferLen) { len = EFM32_MIN(pCmdStatus->xferLen, pCmdStatus->maxBurst); msdState = MSDD_IDLE; if (pCmdStatus->direction) { MSDDMEDIA_Read(pCmdStatus, mediaBuffer, len / 512); } UsbXferBotData(mediaBuffer, len, XferBotDataIndirectCallback); } else { /* We are done ! */ msdState = savedState; if (msdState == MSDD_SEND_CSW) { SendCsw(); EnableNextCbw(); msdState = MSDD_WAITFOR_CBW; } else if (msdState == MSDD_STALL_IN) { USBD_StallEp(BULK_IN); msdState = MSDD_WAIT_FOR_INUNSTALLED; } } break; case MSDD_WRITE_INDIRECT: MSDDMEDIA_Write(pCmdStatus, mediaBuffer, len / 512); pCmdStatus->lba += len / 512; msdState = MSDD_ACCESS_INDIRECT; break; case MSDD_DO_CMD_TASK: if (pCbw->CBWCB[ 0 ] == SCSI_STARTSTOP_UNIT) { MSDDMEDIA_Flush(); } /* else if ( .... ) Add more when needed. */ SendCsw(); EnableNextCbw(); msdState = MSDD_WAITFOR_CBW; break; default: break; } return (msdState == MSDD_WAITFOR_CBW) || (msdState == MSDD_IDLE); }
/**************************************************************************//** * @brief Read touchscreen X or Y position (12bit resolution) * @param[in] ch X (ADC_X) or Y (ADC_Y) touchscreen readout * @return Touchscreen X or Y position *****************************************************************************/ static uint32_t getTouchChSample12bit( ADC_SingleInput_TypeDef ch ) { int i; uint32_t value, min, max, acc; if ( ch == ADC_X ) { GPIO_PinModeSet(LCD_TOUCH_Y1, gpioModePushPull, 0); GPIO_PinModeSet(LCD_TOUCH_Y2, gpioModePushPull, 1); GPIO_PinModeSet(LCD_TOUCH_X1, gpioModeInput, 0); GPIO_PinModeSet(LCD_TOUCH_X2, gpioModeInput, 0); } else { GPIO_PinModeSet(LCD_TOUCH_X1, gpioModePushPull, 1); GPIO_PinModeSet(LCD_TOUCH_X2, gpioModePushPull, 0); GPIO_PinModeSet(LCD_TOUCH_Y1, gpioModeInput, 0); GPIO_PinModeSet(LCD_TOUCH_Y2, gpioModeInput, 0); } sInit.input = ch; ADC_InitSingle(ADC0, &sInit); delayUs( 10 ); acc = 0; max = 0; min = 4096; for ( i=0; i<5; i++ ) { value = readAdc(); acc += value; min = EFM32_MIN( min, value ); max = EFM32_MAX( max, value ); } /* Throw away largest and smallest sample */ acc = acc - min - max; /* Average */ acc = acc / 3; if ( ch == ADC_X ) { GPIO_PinModeSet(LCD_TOUCH_Y1, gpioModeInput, 0); GPIO_PinModeSet(LCD_TOUCH_Y2, gpioModeInput, 0); } else { GPIO_PinModeSet(LCD_TOUCH_X1, gpioModeInput, 0); GPIO_PinModeSet(LCD_TOUCH_X2, gpioModeInput, 0); } return acc; }
/**************************************************************************//** * @brief * Start a USB bulk-in or bulk-out transfer to transfer a data payload * to/from host according to the transfer mode of the transfer. * * @param[in] len * Number of bytes to transfer. *****************************************************************************/ static void XferBotData(uint32_t length) { pCmdStatus->xferLen = length; pCsw->dCSWDataResidue = pCbw->dCBWDataTransferLength; if (pCmdStatus->xferType == XFER_INDIRECT) { /* Access media in "background" polling loop, i.e. in MSDD_Handler() */ savedState = msdState; msdState = MSDD_ACCESS_INDIRECT; } else { UsbXferBotData(pCmdStatus->pData, EFM32_MIN(length, pCmdStatus->maxBurst), XferBotDataCallback); } }
static void rescheduleRtc( uint32_t rtcCnt ) { int i; uint64_t min = UINT64_MAX; // Find the timer with shortest timeout. for ( i = 0; i < EMDRV_RTCDRV_NUM_TIMERS; i++ ) { if ( ( timer[ i ].running == true ) && ( timer[ i ].remaining < min ) ) { min = timer[ i ].remaining; } } rtcRunning = false; if ( min != UINT64_MAX ) { min = EFM32_MIN( min, RTC_CLOSE_TO_MAX_VALUE ); #if defined( _EFM_DEVICE ) if ( inTimerIRQ == false ) { lastStart = ( rtcCnt ) & RTC_COUNTER_MASK; } else #endif { lastStart = rtcCnt; } RTC_INTCLEAR( RTC_COMP_INT ); RTC_COMPARESET( rtcCnt + min ); #if defined( EMODE_DYNAMIC ) // When RTC is running, we can not allow EM3 or EM4. if ( sleepBlocked == false ) { sleepBlocked = true; SLEEP_SleepBlockBegin( sleepEM3 ); } #endif rtcRunning = true; // Reenable compare IRQ. RTC_INTENABLE( RTC_COMP_INT ); } }
/**************************************************************************//** * @brief * Parse a SCSI command. * A minimal, yet sufficient SCSI command subset is supported. *****************************************************************************/ static void ProcessScsiCdb(void) { MSDSCSI_Inquiry_TypeDef *cbI; MSDSCSI_RequestSense_TypeDef *cbRS; MSDSCSI_ReadCapacity_TypeDef *cbRC; MSDSCSI_Read10_TypeDef *cbR10; MSDSCSI_Write10_TypeDef *cbW10; EFM32_ALIGN(4) static MSDSCSI_ReadCapacityData_TypeDef ReadCapData __attribute__ ((aligned(4))); pCmdStatus->valid = false; pCmdStatus->xferType = XFER_MEMORYMAPPED; pCmdStatus->maxBurst = MAX_BURST; switch (pCbw->CBWCB[ 0 ]) { case SCSI_INQUIRY: cbI = (MSDSCSI_Inquiry_TypeDef*) &pCbw->CBWCB; if ((cbI->Evpd == 0) && (cbI->PageCode == 0)) { /* Standard Inquiry data request */ pCmdStatus->valid = true; pCmdStatus->direction = DIR_DATA_IN; pCmdStatus->pData = (uint8_t*) &InquiryData; pCmdStatus->xferLen = EFM32_MIN(SCSI_INQUIRYDATA_LEN, __REV16(cbI->AllocationLength)); } break; case SCSI_REQUESTSENSE: cbRS = (MSDSCSI_RequestSense_TypeDef*) &pCbw->CBWCB; if ((cbRS->Desc == 0) && (cbRS->Reserved1 == 0) && (cbRS->Reserved2 == 0) && (cbRS->Reserved3 == 0)) { pCmdStatus->valid = true; pCmdStatus->direction = DIR_DATA_IN; pCmdStatus->pData = (uint8_t*) pSenseData; pCmdStatus->xferLen = EFM32_MIN(SCSI_REQUESTSENSEDATA_LEN, cbRS->AllocationLength); pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &NoSenseData; } break; case SCSI_READCAPACITY: cbRC = (MSDSCSI_ReadCapacity_TypeDef*) &pCbw->CBWCB; if ((cbRC->Pmi == 0) && (cbRC->Lba == 0)) { ReadCapData.LogicalBlockAddress = __REV(MSDDMEDIA_GetSectorCount() - 1); ReadCapData.LogicalBlockLength = __REV(512); pCmdStatus->valid = true; pCmdStatus->direction = DIR_DATA_IN; pCmdStatus->pData = (uint8_t*) &ReadCapData; pCmdStatus->xferLen = SCSI_READCAPACITYDATA_LEN; } break; case SCSI_READ10: cbR10 = (MSDSCSI_Read10_TypeDef*) &pCbw->CBWCB; pCmdStatus->direction = DIR_DATA_IN; pCmdStatus->valid = MSDDMEDIA_CheckAccess(pCmdStatus, __REV(cbR10->Lba), __REV16(cbR10->TransferLength)); break; case SCSI_WRITE10: cbW10 = (MSDSCSI_Write10_TypeDef*) &pCbw->CBWCB; pCmdStatus->direction = DIR_DATA_OUT; pCmdStatus->valid = MSDDMEDIA_CheckAccess(pCmdStatus, __REV(cbW10->Lba), __REV16(cbW10->TransferLength)); break; case SCSI_TESTUNIT_READY: pCmdStatus->valid = true; pCmdStatus->direction = pCbw->Direction; pCmdStatus->xferLen = 0; break; case SCSI_STARTSTOP_UNIT: pCmdStatus->valid = true; pCmdStatus->direction = pCbw->Direction; pCmdStatus->xferLen = 0; pCmdStatus->xferType = XFER_INDIRECT; break; } if (!pCmdStatus->valid) { pCmdStatus->xferLen = 0; pCmdStatus->direction = pCbw->Direction; pSenseData = (MSDSCSI_RequestSenseData_TypeDef*) &IllegalSenseData; } }
/***************************************************************************//** * @brief * Start a timer. * * @note * It is legal to start an already running timer. * * @param[in] id The id of the timer to start. * @param[in] type Timer type, oneshot or periodic. See @ref RTCDRV_TimerType_t. * @param[in] timeout Timeout expressed in milliseconds. If the timeout value * is 0, the callback function will be called immediately and * the timer will not be started. * @param[in] callback Function to call on timer expiry. See @ref * RTCDRV_Callback_t. NULL is a legal value. * @param[in] user Extra callback function parameter for user application. * * @return * @ref ECODE_EMDRV_RTCDRV_OK on success.@n * @ref ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID if id has an illegal value.@n * @ref ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED if the timer is not reserved. ******************************************************************************/ Ecode_t RTCDRV_StartTimer( RTCDRV_TimerID_t id, RTCDRV_TimerType_t type, uint32_t timeout, RTCDRV_Callback_t callback, void *user ) { uint32_t timeElapsed, cnt, compVal, loopCnt = 0; uint32_t timeToNextTimerCompletion; // Check if valid timer ID. if ( id >= EMDRV_RTCDRV_NUM_TIMERS ) { return ECODE_EMDRV_RTCDRV_ILLEGAL_TIMER_ID; } INT_Disable(); if ( ! timer[ id ].allocated ) { INT_Enable(); return ECODE_EMDRV_RTCDRV_TIMER_NOT_ALLOCATED; } if ( timeout == 0 ) { if ( callback != NULL ) { callback( id, user ); } INT_Enable(); return ECODE_EMDRV_RTCDRV_OK; } cnt = RTC_COUNTERGET(); timer[ id ].callback = callback; timer[ id ].ticks = MSEC_TO_TICKS( timeout ); if (rtcdrvTimerTypePeriodic == type) { // Calculate compensation value for periodic timers. timer[ id ].periodicCompensationUsec = 1000 * timeout - (timer[ id ].ticks * TICK_TIME_USEC); timer[ id ].periodicDriftUsec = TICK_TIME_USEC/2; } // Add one tick in order to compensate if RTC is close to an increment event. timer[ id ].remaining = timer[ id ].ticks + 1; timer[ id ].running = true; timer[ id ].timerType = type; timer[ id ].user = user; if ( inTimerIRQ == true ) { // Exit now, remaining processing will be done in IRQ handler. INT_Enable(); return ECODE_EMDRV_RTCDRV_OK; } // StartTimer() may recurse, keep track of recursion level. if ( startTimerNestingLevel < UINT32_MAX ) { startTimerNestingLevel++; } if ( rtcRunning == false ) { #if defined( _EFM_DEVICE ) lastStart = ( cnt ) & RTC_COUNTER_MASK; #elif defined( _EFR_DEVICE ) lastStart = cnt; #endif RTC_INTCLEAR( RTC_COMP_INT ); compVal = EFM32_MIN( timer[ id ].remaining, RTC_CLOSE_TO_MAX_VALUE ); RTC_COMPARESET( cnt + compVal ); // Start the timer system by enabling the compare interrupt. RTC_INTENABLE( RTC_COMP_INT ); #if defined( EMODE_DYNAMIC ) // When RTC is running, we can not allow EM3 or EM4. if ( sleepBlocked == false ) { sleepBlocked = true; SLEEP_SleepBlockBegin( sleepEM3 ); } #endif rtcRunning = true; } else { // The timer system is running. We must stop, update timers with the time // elapsed so far, find the timer with the shortest timeout and then restart. // As StartTimer() may be called from the callbacks we only do this // processing at the first nesting level. if ( startTimerNestingLevel == 1 ) { timer[ id ].running = false; // This loop is repeated if CNT is incremented while processing. do { RTC_INTDISABLE( RTC_COMP_INT ); timeElapsed = TIMEDIFF( cnt, lastStart ); #if defined( _EFM_DEVICE ) // Compensate for the fact that CNT is normally COMP0+1 after a // compare match event. if ( timeElapsed == RTC_MAX_VALUE ) { timeElapsed = 0; } #endif // Update all timers with elapsed time. checkAllTimers( timeElapsed ); // Execute timer callbacks. executeTimerCallbacks(); // Set timer to running only after checkAllTimers() is called once. if ( loopCnt == 0 ) { timer[ id ].running = true; } loopCnt++; // Restart RTC according to next timeout. rescheduleRtc( cnt ); cnt = RTC_COUNTERGET(); timeElapsed = TIMEDIFF( cnt, lastStart ); timeToNextTimerCompletion = TIMEDIFF( RTC_COMPAREGET(), lastStart ); /* If the counter has passed the COMP(ARE) register value since we checked the timers, then we should recheck the timers and reschedule again. */ } while ( rtcRunning && (timeElapsed > timeToNextTimerCompletion)); } } if ( startTimerNestingLevel > 0 ) { startTimerNestingLevel--; } INT_Enable(); return ECODE_EMDRV_RTCDRV_OK; }
/***************************************************************************//** * @brief * Program the flash device. * * @note * It is assumed that the area to be programmed is erased. * * @param[in] addr * The first address in the flash to be programmed. * * @param[in] data * Pointer to the data to be programmed. * * @param[in] count * Number of bytes to be programmed. * * @return * @ref NORFLASH_STATUS_OK on success, an error code enumerated in * @ref NORFLASH_Status_TypeDef on failure. ******************************************************************************/ int NORFLASH_Program(uint32_t addr, uint8_t *data, uint32_t count) { int status; uint32_t sectorAddress, burst; if (!flashInitialized) { status = flashInterrogate(); if (status != NORFLASH_STATUS_OK) return status; } if (!NORFLASH_AddressValid(addr) || !NORFLASH_AddressValid(addr + count - 1)) { EFM_ASSERT(false); return NORFLASH_INVALID_ADDRESS; } /* Check if odd byte aligned */ if (addr & 1) { status = NORFLASH_ProgramByte(addr, *data); if (status != NORFLASH_STATUS_OK) return status; addr++; data++; count--; } while (count >= 2) { #if 0 /* Traditional write method, write one word at a time */ status = NORFLASH_ProgramWord16(addr, (*(data + 1) << 8) | *data); if (status != NORFLASH_STATUS_OK) return status; addr += 2; data += 2; count -= 2; #else /* "Write Buffer" write method */ sectorAddress = addr & ~(flashInfo.sectorSize - 1); /* Max 32 "words" at a time, must not cross sector boundary */ burst = EFM32_MIN(64U, sectorAddress + flashInfo.sectorSize - addr); burst = EFM32_MIN(burst, count & 0xFFFFFFFE); status = flashWriteBuffer(sectorAddress, addr, (uint16_t*) data, burst); if (status != NORFLASH_STATUS_OK) return status; addr += burst; data += burst; count -= burst; #endif } /* Check if a trailing odd byte aligned value must be programmed */ if (count) { status = NORFLASH_ProgramByte(addr, *data); } return status; }