⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mp3srial.c

📁 Frank s MP3 Player Source Files
💻 C
📖 第 1 页 / 共 3 页
字号:
    // Cleanup - Ensure StreamFileC stopped & MP3dataQ empty
   	STREAMFILE_CMD = StreamFile_Cmd_Stop;                       // command StreamFileC to STOP
    StreamFileC();
   	while (STREAMFILE_ST!=StreamFile_St_Idle) StreamFileC();    // wait until StreamFileC idle
    Q_clear_C((u08*)&MP3DATAQ[0]);                              // empty MP3 data Q
    return 0;
}






void ClearLCDlines (u08 startline, u08 numlines)
// Clears the specified lines on the LCD display, starting with startline (1-8)
// for the specified number of lines (1-8)
{
    // Set cursor to start of specified line number
	UART1_TxCharWaitC(0x5c);
	UART1_TxCharWaitC(0x42);
	UART1_TxCharWaitC(32);
	UART1_TxCharWaitC(31+startline);              // position cursor at start of startline (1-8)

    while (numlines--)
        uart1_putwaitPROGstr(PSTR("                        "));  // clear 24 characters, ie 1 line
}



void PositionLCDcursor (u08 linenum, u08 columnnum)
// Sets the cursor position on the LCD display. linenum is 1-8. columnnum is 1-24.
{
	UART1_TxCharWaitC(0x5c);
	UART1_TxCharWaitC(0x42);
	UART1_TxCharWaitC(31+columnnum);            // column
	UART1_TxCharWaitC(31+linenum);              // line
}



void PrintASCIIword (u16 num, u08 uart)
// This routine accepts a 16-bit word and prints its ASCII form (eg 2109) out the specified
// serial port (0 for uart0 the debug port, 1 for uart1 the lcd). This routine supresses
// the printing of any leading zeros, however it will always print at least a single digit, even
// if it's zero (ie 0). Put another way, this routine will always print at least one ASCII character.
{
    u16     numcopy;     
    u08     mod;
    void    (*PrintFn)(u08);                // PrintFn is a pointer to a function of type: void FnName (u08)

    if (uart==0)
        PrintFn = UART_TxCharWaitC;
    else
        PrintFn = UART1_TxCharWaitC;        // select which "print to uart" function to use 

	numcopy = num;	    					/* need to remember what num originally was */
	
	if (numcopy >= 10000) {
		mod = (u08)(num / 10000);			/* calculate & display ten-thousands digit */
		PrintFn(mod+48);					/* if it exists */
		num = num - mod*10000;
		}

	if (numcopy >= 1000) {
		mod = (u08)(num / 1000);			/* calculate & display thousands digit */
		PrintFn(mod+48);					/* if it exists */
		num = num - mod*1000;
		}			

	if (numcopy >= 100) {
		mod = (u08)(num / 100);				/* calculate & display hundreds digit */
		PrintFn(mod+48);					/* if it exists */
		num = num - mod*100;
		}	
	
	if (numcopy >= 10) {
		mod = (u08)(num / 10);				/* calculate & display tens digit */
		PrintFn(mod+48);					/* if it exists */
		num = num - mod*10;
		}	

	PrintFn(num+48);						/* display ones digit (always exists) */	
}





// DivRound
//
// This routine performs an integer divide, but rounds the result to the nearest unit. As compared
// to using a simple "/", which truncates the result. For example, 154 / 5 returns 30, but
// DivRound (154, 5) returns 31. In the case of an even split this routine rounds down. For
// example DivRound (10, 4) returns 2. All numbers are 16 bit.
u16 DivRound (u16 x, u16 y)
{
    u16     result;

    result = x / y;

    if ( (x%y) > (y>>1) )
        result++;

    return result;
}



// AbsDiff8
//
// Returns the absolute value of the difference between two u08 numbers
u08 AbsDiff8 (u08 a, u08 b)
{
    if (a > b)
        return (a-b);
    else
        return (b-a);
}





/******************************************************************************
    
    IR_Decode

    This routine performs the decoding of the received IR remote-
    control periods. 

    The remote control is considered to send nothing more complex than a series
    of "periods", ie a period of carrier, followed by a period of no carrier,
    follwed by another period of carrier, etc. 
    (See mp3menus.c for the IR training (or learning) routine which captures a
    complete IR sequence of periods and stores all results in EEPROM.)

    The input capture 3 interrupt service routine measures the length of each
    incoming period (IR carrier or IR no-carrier) and puts the result in IR_RX_period. 
	A sequence of periods constitutes a remote control command.

	This routine knows about multiple sequences. For example one period sequence
	can mean "play", another may mean "stop", etc.
	
    The sequences to look for are stored in EEPROM, starting at eeprom address
    IRcode_Start (see fvhmp3.h file). There are IR_numcodes of these sequences.
    Each sequence is a maximum of 62 periods long.
    To remember where we are in those sequences the variable IRdecode_cnt is
    used; starting from zero it provides the offset into the eeprom period
    sequences for where we're currently looking. There is an IRdecode_cnt for
    each sequence; this is so if a sequence is found to not match (ie a period
    mismatch is found) then its count is reset to zero. 

    At the beginning of each period sequence in eeprom is a length byte, which states 
	how long the sequence is (ie how many periods it contains) and also a
    "ButtonPress byte" (so to speak); the command byte to write into the ButtonPress
    variable if that sequence is fully detected (for example the PLAY command).
    Each EEPROM entry is 126 bytes long (length byte + buttonpress byte + upto 62 period words).

    The "length" byte is a "1 to xx" number (eg 41 if there are 41 periods in the
    eeprom sequence. Whereas the IRdecode_cnt number is a "0 to yy" number; it starts
    at zero. So a value of IRdecode_cnt of 5 is actually checking the 6th element (word)
    in the eeprom period sequence, for example.

    Note that each sequence can be of different length. For example one could be
    40 periods long, another 50 periods long, and yet another could be zero periods
    long, ie, not present.

    Note that the eeprom contains a sequence of "desired period lengths". But an
    acceptable period length can be a range around that, based upon a couple of factors.
    One factor is the length of the period itself; longer periods can have more leeway.
    Another is the period type. "No IR Carrier" periods stretch when the remote is held
    further away, and the "IR Carrier Present" periods tend to shorten when the remote
    is further away. In the period sequence they alternate, with the first period being
    "IR Carrier Present", the second period being "No IR Carrier", etc.

    This routine called from the interrupt service routine, so it only executes when a
    received period is received. (It's never called when there is no period to process.)

    We keep track of a timeout. We do this by storing the current time. If nothing is 
    detected after a certain period of time we restart the period counter back to zero, 
    so that when new IR data starts appearing we'll be decoding from the beginning of the 
    period sequences, not halfway through. This routine only stores the time; a main-loop
    routine IR_CheckTimeout watches for, and handles, the timeout. (Because IR_Decode is
    only called when an interrupt occurs, ie when a period is detected, the time stored is
    the time of the last period. So then IR_CheckTimeout is able to see how long it's been
    since a period was last detected. If it's been several hundred milliseconds since the
    last period, IR_CheckTimeout will reset all the period counters IRdecode_cnt[] to zero.)

    This is the pseudocode for the IR_decode routine:

    store current time (in IRdecode_time)
    FOR each period sequence (there are IR_numcodes of them) DO
        IF we're past the end of this period sequence (IRdecode_cnt > period sequence length-1) THEN
            move on to next period sequence
        ELSE  (we're within this period sequence)
            Get current "desired" period from EEPROM
            Calculate acceptable period range for it
            Compare IR_RX_period to this acceptable range        
            IF received period acceptable THEN
                IF that was last period in sequence THEN
                    Put command byte in ButtonPress
                    Force timeout (see note in comments below)
                    exit
                ELSE  (period acceptable but it wasn't the last period in the sequence)
                    increment function period counter (IRdecode_cnt)
                    move on to next period sequence
            ELSE  (received period not acceptable)
                zero function period counter (IRdecode_cnt)
                move on to next period sequence


    Looks complex but it's not that bad. 
    For each of the possible period sequences it checks to see if the newly
    received period matches what that period sequence is expecting. If it matches
    it moves it along; if the period doesn't match it resets that sequence to zero.
    If a sequence reaches the end then "it's the one" so write into ButtonPress.

    "Force timeout" trick. Here's the problem. When you press the remote control
    button it continually sends the sequence until you lift your finger. That means
    this routine will receive the same code multiple times. This is a problem.
    (Imagine pressing "next" and having the player jump forward multiple tracks
    instead of just the one track you were expecting.) To counter this, what we want
    is that once a code has been fully received and written into ButtonPress, we want
    to wait for a while before looking for the next code. This is accomplished here by
    incrementing the function period counter beyond the normal value. This will cause
    the test 
        if (IRdecode_cnt[sequencenum] < length)
    to fail, as IRdecode_cnt[sequencenum] will be equal to "length". It will seems
    we're past the end of that sequence. So that sequence will not execute any more, 
    until the main-loop routine IR_CheckTimeout finally sets all of the 
    IRdecode_cnt[sequencenum] values to zero a few hundred milliseconds later. 
    At that point the sequence will be capable of working again, because the test 
    will again pass. So that's the trick: force the sequence into what's effectively
    a "dead state" until the timeout comes along and resets it.
                  
******************************************************************************/
void IR_decode (void)
{
    u08     sequencenum, length;
	u16		expected, high, low, eepromoffset;


    IRdecode_time = Tick100ms;                  // remember current time (restart timeout timer)
    sequencenum = IR_numcodes;                  // number of period sequences to check against
    while (sequencenum--) {                     // for each period sequence do the following....
        length = EEPROM_ReadC(((u16)sequencenum<<7) + IRcode_Start);  // get length of current period sequence
        if (IRdecode_cnt[sequencenum] < length) {
            // We're within the period sequence and current sequence isn't zero length (ie absent) so do the following...
            // (if we're beyond the end of this sequence, just skip it all & move along to the next sequence)
            eepromoffset = IRcode_Start + ((u16)sequencenum<<7) + 2 + ((IRdecode_cnt[sequencenum])<<1);
			expected  = EEPROM_ReadC(eepromoffset);  			// get desired period length (low byte) for current sequence from eeprom
            expected += (EEPROM_ReadC(eepromoffset + 1))<<8;  	// get desired period length (high byte)
			// Calculate acceptable values (high & low) for IR_RX_period. Basically, if we're in a "carrier present"
			// period then accept shorter periods (because when you move the remote further away, the amount of carrier
			// we receive gets less). If we're in a "carrier absent" period then accept longer periods. 
			// IRdecode_cnt[sequencenum] tells us whether carrier is present or not; even = carrier, odd = no carrier.
			if (IRdecode_cnt[sequencenum] & 0x01) {
				// execute here if carrier absent; allow period to stretch a fair bit
				low  = expected - (expected>>2);
				high = (expected<<1) + (expected>>1);
			}
			else {
				// execute here because carrier present; allow period to shrink a great deal
				low = expected>>4;
				high = expected + (expected>>2);
			}
			

			if ( (IR_RX_period >= low) && (IR_RX_period <= high) ) {
				// Execute here if received period length is acceptable; it matches up with stored eeprom period length
                if (length - 1 == IRdecode_cnt[sequencenum]) {
                    // Execute here if we just did the last period in the eeprom sequence; we've successfully detected a complete IR sequence!
                    ButtonPress = EEPROM_ReadC(((u16)sequencenum<<7) + IRcode_Start + 1);       // get ButtonPress code from eeprom and give to player; emulate that pushbutton being pressed
                    IRdecode_cnt[sequencenum]++;            // IRdecode_cnt[sequencenum] now equals length; see note about "force timeout trick"
                    goto IRdecode_end;                      // I hate goto's, but what else to do when this deeply nested?
                }   
                else {
                    // Execute here if received period acceptable but not the last period in the eeprom sequence
                    IRdecode_cnt[sequencenum]++;            // increment period sequence counter
                    // we now return to the next sequencenum; we're done checking this particular eeprom sequence
                }
            }
            else {
                // Execute here if received period length did NOT match up with stored eeprom period length; this eeprom sequence is not a match
                IRdecode_cnt[sequencenum] = 0;              // zero sequence counter (restart checking this sequence from the beginning)
                // we now return to the next sequencenum; we're done checking this particular eeprom sequence
            }
        }
    }

    // End of the IRdecode function. 
    IRdecode_end:
    ;                               // a do-nothing semicolon just to keep the compiler happy
}




                  
/******************************************************************************

    IR_CheckTimeout

    Checks to see if the IR remote control received sequence timeout has
    occured (ie if a too-long period has elapsed since the last receive
    interrupt). If the timeout has expired it clears out all the receive
    sequence counters, so that they'll start decoding again from the beginning.

******************************************************************************/
void IR_CheckTimeout (void)
{
    u08     sequencenum;

    if (Tick100ms - IRdecode_time >= 3) {       // current time minus last stored time
        // we just hit timeout expiry; clear all the function period counters
        sequencenum = IR_numcodes;
        while (sequencenum--)
            IRdecode_cnt[sequencenum] = 0;
    }
}




// DumpEEPROM
//
// This routine dumps the contents of the EEPROM to the debug port. At the moment it doesn't dump the
// entire 4k simply because most of it is unused.
void DumpEEPROM (void)
{
    u16     eepromaddr;
    u08     i, j;


    uart0_putwaitPROGstr(PSTR("\r\nEEPROM Contents in Hex:\r\n"));

    eepromaddr = 0;
    while (eepromaddr <= 0x4ff) {
        // first print eeprom address followed by two spaces
        UART_PutHexWaitC((u08)(eepromaddr>>8));        
        UART_PutHexWaitC((u08)eepromaddr);
        uart0_putwaitPROGstr(PSTR("  "));
        // then print 4 eeprom bytes separated by spaces, 4 times (separated by 2 spaces)
        i = 4;
        while (i--) {           // outer loop
            j = 4;
            while (j--) {       // inner loop
                UART_PutHexWaitC( EEPROM_ReadC(eepromaddr++) );
                uart0_putwaitPROGstr(PSTR(" "));
            }        
            uart0_putwaitPROGstr(PSTR(" "));        // extra space between groups of 4 bytes
        }
        uart0_putwaitPROGstr(PSTR("\r\n"));         // end of line CR/LF
    }
    uart0_putwaitPROGstr(PSTR("\r\n"));             // additional CR/LF at end of eeprom dump
}



⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -