/* * ======== USBMSC_checkMSCInsertionRemoval ======== * * This function checks for insertion/removal of the card. If either is * detected, it informs the API by calling USBMSC_updateMediaInformation(). Whether * it detects it or not, it returns non-zero if the card is present, or zero if * not present */ uint8_t USBMSC_checkMSCInsertionRemoval (void) { //Check card status -- there or not? uint8_t newCardStatus = detectCard(); if ((newCardStatus) && (mediaInfo.mediaPresent == USBMSC_MEDIA_NOT_PRESENT)){ //An insertion has been detected -- inform the API mediaInfo.mediaPresent = USBMSC_MEDIA_PRESENT; mediaInfo.mediaChanged = 0x01; //Get the size of this new medium DRESULT SDCard_result = disk_ioctl(0, GET_SECTOR_COUNT, &mediaInfo.lastBlockLba); USBMSC_updateMediaInformation(0, &mediaInfo); } if ((!newCardStatus) && (mediaInfo.mediaPresent == USBMSC_MEDIA_PRESENT)){ //A removal has been detected -- inform the API mediaInfo.mediaPresent = USBMSC_MEDIA_NOT_PRESENT; mediaInfo.mediaChanged = 0x01; USBMSC_updateMediaInformation(0, &mediaInfo); } return ( newCardStatus) ; }
SDCardLib_Status SDCardLib_detectCard(SDCardLib * lib) { //Use software to detect card present if (lib->interface->sdDetectCard == NULL) { return (SDCardLib_Status)detectCard(); } else { return (SDCardLib_Status)(lib->interface->sdDetectCard()); } }
/* * This function initializes the MSC data variables */ void USBMSC_initMSC(void) { //SD-cards must go through a setup sequence after powerup. //This FatFs call does this. disk_initialize(0); //The API maintains an instance of the USBMSC_RWbuf_Info structure.This is // a shared resource between the API and application; the application must //request the pointer. RWbuf_info = USBMSC_fetchInformationStructure(); //USBMSC_updateMediaInformation() must be called prior to USB connection. We //check if the card is present, and if so, pull its size and bytes per //block. if (detectCard()){ mediaInfo.mediaPresent = USBMSC_MEDIA_PRESENT; } else { mediaInfo.mediaPresent = USBMSC_MEDIA_NOT_PRESENT; } mediaInfo.mediaChanged = 0x00; mediaInfo.writeProtected = 0x00; //Returns the number of blocks (sectors) in the media. disk_ioctl(0,GET_SECTOR_COUNT,&mediaInfo.lastBlockLba); //Block size will always be 512 mediaInfo.bytesPerBlock = BYTES_PER_BLOCK; USBMSC_updateMediaInformation(0, &mediaInfo); //The data interchange buffer (used when handling SCSI READ/WRITE) is //declared by the application, and registered with the API using this //function. This allows it to be assigned dynamically, giving //the application more control over memory management. USBMSC_registerBufferInformation(0, &RWbuf[0], NULL, sizeof(RWbuf)); }
void USBMSC_processMSCBuffer(void) { //Call USBMSC_pollCommand() to initiate handling of any received SCSI commands. //Disable interrupts //during this function, to avoid conflicts arising from SCSI commands //being received from the host //AFTER decision to enter LPM is made, but BEFORE it's actually entered //(in other words, avoid //sleeping accidentally). __disable_interrupt(); if ((USBMSC_pollCommand() == USBMSC_OK_TO_SLEEP) && (!bDetectCard)){ //Enable interrupts atomically with LPM0 entry __bis_SR_register(LPM0_bits + GIE); } __enable_interrupt(); //Verify SD card before attempting to read. If this condition is not there Windows displays //a message about 'Reformatting the disk' when SD card is not in the drive. if(detectCard()){ //Bug number 6754 bDetectCard = 0x01; //update the flag. There seems to be a timing issue between when the main //application calls USBMSC_processMSCBuffer() and when the card is detected. Setting //the global flag here prevents the Windows message 'Reformat Disk' from //showing up when an SD card is inserted into an empty drive. //If the API needs the application to process a buffer, it will keep the //CPU awake by returning USBMSC_PROCESS_BUFFER //from USBMSC_pollCommand(). The application should then check the 'operation' //field of all defined USBMSC_RWbuf_Info //structure instances. If any of them is non-null, then an operation //needs to be processed. A value of //USBMSC_READ indicates the API is waiting for the application to fetch //data from the storage volume, in response //to a SCSI READ command from the USB host. After the application does //this, it must indicate whether the //operation succeeded, and then close the buffer operation by calling //USBMSC_processBuffer(). while (RWbuf_info->operation == USBMSC_READ) { hal_led_a(GREEN); //A READ operation is underway, and the app has been requested to access //the medium. So, call file system to read //to do so. Note this is a low level FatFs call -- we are not //attempting to open a file ourselves. The host is //in control of this access, we're just carrying it out. DRESULT dresult = disk_read(0, //Physical drive number (0) RWbuf_info->bufferAddr, //Pointer to the user buffer RWbuf_info->lba, //First LBA of this buffer operation RWbuf_info->lbCount); //The number of blocks being requested //as part of this operation hal_led_a(0); //The result of the file system call needs to be communicated to the //host. Different file system software uses //different return codes, but they all communicate the same types of //results. This code ultimately gets passed to the //host app that issued the command to read (or if the user did it the //host OS, perhaps in a dialog box). switch (dresult) { case RES_OK: RWbuf_info->returnCode = USBMSC_RW_SUCCESS; break; //In FatFs, this result suggests the medium may have been removed //recently. case RES_ERROR: //This application function checks for the SD-card, //and if missing,calls USBMSC_updateMediaInformation() to inform //the API if (!USBMSC_checkMSCInsertionRemoval()){ RWbuf_info->returnCode = USBMSC_RW_MEDIA_NOT_PRESENT; } break; case RES_NOTRDY: RWbuf_info->returnCode = USBMSC_RW_NOT_READY; break; case RES_PARERR: RWbuf_info->returnCode = USBMSC_RW_LBA_OUT_OF_RANGE; break; } hal_led_a(RED); USBMSC_processBuffer(); hal_led_a(0); } //Everything in this section is analogous to READs. Reference the //comments above. while (RWbuf_info->operation == USBMSC_WRITE) { hal_led_a(GREEN); DRESULT dresult = disk_write(0, //Physical drive number (0) RWbuf_info->bufferAddr, //Pointer to the user buffer RWbuf_info->lba, //First LBA of this buffer operation RWbuf_info->lbCount); //The number of blocks being requested //as part of this operation hal_led_a(0); switch (dresult) { case RES_OK: RWbuf_info->returnCode = USBMSC_RW_SUCCESS; break; case RES_ERROR: if (!USBMSC_checkMSCInsertionRemoval()){ RWbuf_info->returnCode = USBMSC_RW_MEDIA_NOT_PRESENT; } break; case RES_NOTRDY: RWbuf_info->returnCode = USBMSC_RW_NOT_READY; break; case RES_PARERR: RWbuf_info->returnCode = USBMSC_RW_LBA_OUT_OF_RANGE; break; default: RWbuf_info->returnCode = USBMSC_RW_NOT_READY; break; } hal_led_a(RED); USBMSC_processBuffer(); hal_led_a(0); } } }
void main(void) { WDTCTL = WDTPW + WDTHOLD; // Stop the watchdog __enable_interrupt(); // Enable general interrupts Board_init(); // Configure's the F5529 EXP board's I/Os // Initialize power/clocks for use with USB SetVCore(3); // The USB module requires that VCore be set to highest setting, independent of MCLK freq ClockUSB(); disk_initialize(0); // SD-cards must go through a setup sequence after powerup. This FatFs call does this. USB_init(); // Initializes the USB API, and prepares the USB module to detect USB insertion/removal events USB_setEnabledEvents(kUSB_allUsbEvents); // Enable all USB events // The data interchange buffer (used when handling SCSI READ/WRITE) is declared by the application, and // registered with the API using this function. This allows it to be assigned dynamically, giving // the application more control over memory management. USBMSC_registerBufInfo(&RW_dataBuf[0], NULL, sizeof(RW_dataBuf)); // The API maintains an instance of the USBMSC_RWbuf_Info structure. If double-buffering were used, it would // maintain one for both the X and Y side. (This version of the API only supports single-buffering, // so only one structure is maintained.) This is a shared resource between the API and application; the // application must request the pointers. RWbuf_info = USBMSC_fetchInfoStruct(); // USBMSC_updateMediaInfo() must be called prior to USB connection. We check if the card is present, and if so, pull its size // and bytes per block. // LUN0 mediaInfo.mediaPresent = 0x01; // The medium is present, because internal flash is non-removable. mediaInfo.mediaChanged = 0x00; // It can't change, because it's in internal memory, which is always present. mediaInfo.writeProtected = 0x00; // It's not write-protected mediaInfo.lastBlockLba = 774; // 774 blocks in the volume. (This number is also found twice in the volume itself; see mscFseData.c. They should match.) mediaInfo.bytesPerBlock = BYTES_PER_BLOCK; // 512 bytes per block. (This number is also found in the volume itself; see mscFseData.c. They should match.) USBMSC_updateMediaInfo(0, &mediaInfo); // LUN1 if(detectCard()) mediaInfo.mediaPresent = kUSBMSC_MEDIA_PRESENT; else mediaInfo.mediaPresent = kUSBMSC_MEDIA_NOT_PRESENT; mediaInfo.mediaChanged = 0x00; mediaInfo.writeProtected = 0x00; disk_ioctl(0,GET_SECTOR_COUNT,&mediaInfo.lastBlockLba); // Returns the number of blocks (sectors) in the media. mediaInfo.bytesPerBlock = BYTES_PER_BLOCK; // Block size will always be 512 USBMSC_updateMediaInfo(1, &mediaInfo); // At compile-time for this demo, there will be one file on the volume. The root directory and data cluster // for this file need to be initialized. //flashWrite_LBA(Root_Dir, (BYTE*)Root_Dir_init); //flashWrite_LBA(Data559, (BYTE*)Data559_init); // Configure Timer_A0 to prompt detection of the SD card every second TA0CCTL0 = CCIE; // Enable interrupt TA0CCR0 = 32768; // Clock will be 32kHz, so we set the int to occur when it counts to 32768 TA0CTL = TASSEL_1 + MC_1 + TACLR; // ACLK = 32kHz, up mode, clear TAR... go! // If USB is already connected when the program starts up, then there won't be a USB_handleVbusOnEvent(). // So we need to check for it, and manually connect if the host is already present. if (USB_connectionInfo() & kUSB_vbusPresent) { if (USB_enable() == kUSB_succeed) { USB_reset(); USB_connect(); } } while(1) { BYTE ReceiveError=0, SendError=0; WORD count; switch(USB_connectionState()) { case ST_USB_DISCONNECTED: __bis_SR_register(LPM3_bits + GIE); // Enter LPM3 until VBUS-on event // Check if the reason we woke was a button press; and if so, log a new piece of data. if(fS1ButtonEvent) { // Build string char str[14] = "Data entry #0\n"; str[12] = logCnt++; // Number the entries 0 through....? memcpy(RW_dataBuf, Data559, BYTES_PER_BLOCK); // Copy data block 559 from flash to RAM buffer memcpy(&RW_dataBuf[DataCnt], str, sizeof(str)); // Write the new entry to the RAM buffer flashWrite_LBA((PBYTE)Data559, RW_dataBuf); // Copy it back to flash DataCnt += sizeof(str); // Increment the index past the new entry if((DataCnt + sizeof(str)>= BYTES_PER_BLOCK)) // Roll index back to 0, if no more room in the block DataCnt = 0; fS1ButtonEvent = 0; } break; case ST_USB_CONNECTED_NO_ENUM: break; case ST_ENUM_ACTIVE: // Call USBMSC_poll() to initiate handling of any received SCSI commands. Disable interrupts // during this function, to avoid conflicts arising from SCSI commands being received from the host // AFTER decision to enter LPM is made, but BEFORE it's actually entered (in other words, avoid // sleeping accidentally). __disable_interrupt(); if((USBMSC_poll() == kUSBMSC_okToSleep) && (!bDetectCard)) { __bis_SR_register(LPM0_bits + GIE); // Enable interrupts atomically with LPM0 entry } __enable_interrupt(); // If the API needs the application to process a buffer, it will keep the CPU awake by returning kUSBMSC_processBuffer // from USBMSC_poll(). The application should then check the 'operation' field of all defined USBMSC_RWbuf_Info // structure instances. If any of them is non-null, then an operation needs to be processed. A value of // kUSBMSC_READ indicates the API is waiting for the application to fetch data from the storage volume, in response // to a SCSI READ command from the USB host. After the application does this, it must indicate whether the // operation succeeded, and then close the buffer operation by calling USBMSC_bufferProcessed(). while(RWbuf_info->operation == kUSBMSC_READ) { switch(RWbuf_info->lun) { case 0: RWbuf_info->returnCode = Read_LBA(RWbuf_info->lba, RWbuf_info->bufferAddr, RWbuf_info->lbCount); // Fetch a block from the medium, using file system emulation USBMSC_bufferProcessed(); // Close the buffer operation break; case 1: read_LUN1(); break; } } // Everything in this section is analogous to READs. Reference the comments above. while(RWbuf_info->operation == kUSBMSC_WRITE) { switch(RWbuf_info->lun) { case 0: RWbuf_info->returnCode = Write_LBA(RWbuf_info->lba, RWbuf_info->bufferAddr, RWbuf_info->lbCount); // Write the block to the medium, using file system emulation USBMSC_bufferProcessed(); // Close the buffer operation break; case 1: write_LUN1(); break; } } // Every second, the Timer_A ISR sets this flag. The checking can't be done from within the timer ISR, because the // checking enables interrupts, and this is not a recommended practice due to the risk of nested interrupts. if(bDetectCard) { checkInsertionRemoval(); bDetectCard = 0x00; // Clear the flag, until the next timer ISR } if(bHID_DataReceived_event) //Message is received from HID application { bHID_DataReceived_event = FALSE; // Clear flag early -- just in case execution breaks below because of an error count = hidReceiveDataInBuffer((BYTE*)dataBuffer,BUFFER_SIZE,HID0_INTFNUM); strncat(wholeString," \r\nRx->",7); strncat(wholeString,(char*)dataBuffer,count); strncat(wholeString," \r\n ",4); if(cdcSendDataInBackground((BYTE*)wholeString,strlen(wholeString),CDC0_INTFNUM,1)) // Send message to other CDC App { SendError = 0x01; break; } memset(wholeString,0,MAX_STR_LENGTH); // Clear wholeString } if(bCDC_DataReceived_event) //Message is received from CDC application { bCDC_DataReceived_event = FALSE; // Clear flag early -- just in case execution breaks below because of an error cdcReceiveDataInBuffer((BYTE*)wholeString,MAX_STR_LENGTH,CDC0_INTFNUM); ASCII(wholeString); if(hidSendDataInBackground((BYTE*)wholeString,strlen(wholeString),HID0_INTFNUM,1)) // Send message to HID App { SendError = 0x01; // Something went wrong -- exit break; } memset(wholeString,0,MAX_STR_LENGTH); // Clear wholeString } break; case ST_ENUM_SUSPENDED: __bis_SR_register(LPM3_bits + GIE); // Enter LPM3, until a resume or VBUS-off event break; case ST_ENUM_IN_PROGRESS: break; case ST_ERROR: break; default:; } if(ReceiveError || SendError) { //TO DO: User can place code here to handle error } } }
/* * ======== main ======== */ int main (void) { WDT_A_hold(WDT_A_BASE); // Stop watchdog timer // Minumum Vcore setting required for the USB API is PMM_CORE_LEVEL_2 . PMM_setVCore(PMM_CORE_LEVEL_2); USBHAL_initPorts(); // Config GPIOS for low-power (output low) USBHAL_initClocks(MCLK_FREQUENCY); // Config clocks. MCLK=SMCLK=FLL=MCLK_FREQUENCY; ACLK=REFO=32kHz hal_sd_pwr_on(); initTimer(); USB_setup(FALSE, TRUE); // Init USB & events; if a host is present, connect __enable_interrupt(); // Enable interrupts globally // GPS_init(); // state machine while (1) { switch( state ) { case sIDLE: hal_led_a(0); hal_led_b(0); hal_gps_pwr_off(); hal_sd_pwr_off(); UCS_turnOffXT2(); /* USB connected */ if(USB_getConnectionInformation() & USB_VBUS_PRESENT) { hal_led_a(CYAN); //PMM_setVCore(PMM_CORE_LEVEL_2); hal_sd_pwr_on(); shortDelay(); USBMSC_initMSC(); // Initialize MSC API, and report media to the host if (USB_enable() == USB_SUCCEED){ state = sUSB; hal_led_a(GREEN); //hal_sd_pwr_on(); //detectCard(); USB_reset(); USB_connect(); //generate rising edge on DP -> the host enumerates our device as full speed device } break; // don't enter sleep } /* start GPS */ if(hal_button_event()) { /* delay for starting */ hal_led_a(RED); uint8_t timeout = 16; while( hal_button_status() == 1 && --timeout ) { shortDelay(); } hal_led_a(0); if( hal_button_status() == 0 ) break; state = sGPS; hal_led_a(CYAN); hal_sd_pwr_on(); timeout = 8; while( --timeout ) { shortDelay(); } detectCard(); Timer_A_startCounter(TIMER_A0_BASE, TIMER_A_UP_MODE); gps_start(); hal_button_event(); break; // don't enter sleep } USB_disable(); //Disable hal_gps_rtc_on(); // saves around 7uA Timer_A_stop(TIMER_A0_BASE); //UCS_turnOffSMCLK(); //PMM_setVCore(PMM_CORE_LEVEL_0); __bis_SR_register(LPM4_bits + GIE); _NOP(); //UCS_turnOnSMCLK(); //PMM_setVCore(PMM_CORE_LEVEL_2); break; case sGPS: /* stop GPS */ if((USB_getConnectionInformation() & USB_VBUS_PRESENT)) { state = sIDLE; gps_stop(); break; } if(hal_button_event()) { /* delay for stopping */ uint8_t timeout = 16; while( hal_button_status() == 1 && --timeout ) { hal_led_a(RED); shortDelay(); hal_led_a(RED); } hal_led_a(0); if( hal_button_status() == 0 ) break; state = sIDLE; gps_stop(); break; } if (bDetectCard){ USBMSC_checkMSCInsertionRemoval(); // Clear the flag, until the next timer ISR bDetectCard = 0x00; } while( gps_check() ) { gps_do(); } __bis_SR_register(LPM0_bits + GIE); _NOP(); break; case sUSB: if(!(USB_getConnectionInformation() & USB_VBUS_PRESENT)) { state = sIDLE; break; } /* check state of chareger? */ if( hal_charge_status()) hal_led_b(RED); else hal_led_b(GREEN); hal_button_event(); // clear button event switch (USB_getConnectionState()) { case ST_ENUM_ACTIVE: USBMSC_processMSCBuffer(); // Handle READ/WRITE cmds from the host // Every second, the Timer_A ISR sets this flag. The // checking can't be done from within the timer ISR, because it // enables interrupts, and this is not a recommended // practice due to the risk of nested interrupts. if (bDetectCard){ USBMSC_checkMSCInsertionRemoval(); // Clear the flag, until the next timer ISR bDetectCard = 0x00; } break; // These cases are executed while your device is disconnected from // the host (meaning, not enumerated); enumerated but suspended // by the host, or connected to a powered hub without a USB host // present. case ST_PHYS_DISCONNECTED: case ST_ENUM_SUSPENDED: case ST_PHYS_CONNECTED_NOENUM_SUSP: hal_led_a(BLUE); //state = sIDLE; break; // The default is executed for the momentary state // ST_ENUM_IN_PROGRESS. Usually, this state only last a few // seconds. Be sure not to enter LPM3 in this state; USB // communication is taking place here, and therefore the mode must // be LPM0 or active-CPU. case ST_ENUM_IN_PROGRESS: default:; } break; } } }