Jump to content
43oh

oPossum

Members
  • Content Count

    925
  • Joined

  • Last visited

  • Days Won

    103

Posts posted by oPossum

  1. This code will print a 64 bit unsigned integer in 5 characters using 3 significant digits and and an SI prefix. Several revisions are shown beginning with the obvious implementation and then optimizing it to improve speed and decrease code size. Compilers used are TI 4.4.4 and GCC 4.9.1 Test hardware is the F5529 Launchpad running at the default clock of 1.016 MHz.

     

    The general strategy of the code is:

    - Count the number of digits in the decimal representation

    - Divide the value to reduce it to 3 digits.

    - Print the 3 digits with proper formatting and SI prefix.

    - Handle the special case of values of 99 or less that must be printed with 2 or 1 digits and no decimal.

     

    A series of values from zero to the maximum possible 64 bit value are used to test the performance and correct operation of the code...

        static const uint64_t td[] = {
            0ULL,
            1ULL,
            12ULL,
            123ULL,
            1234ULL,
            12345ULL,
            123456ULL,
            1234567ULL,
            12345678ULL,
            123456789ULL,
            1234567890ULL,
            12345678901ULL,
            123456789012ULL,
            1234567890123ULL,
            12345678901234ULL,
            123456789012345ULL,
            1234567890123456ULL,
            12345678901234567ULL,
            123456789012345678ULL,
            1234567890123456789ULL,
            12345678901234567890ULL,
            18446744073709551615ULL
        };
    
    The first version uses C standard library functions for a very simple implementation. Decimal digits are counted using log10(). Dividing the digit count by three using the div() function provides values for formatting, division, and the SI prefix. The divisor needed to reduce the value to three digits is calculated with pow(). The special case of values of 99 or less is handled by using a fixed digit count for values less than 1000 rather than the actual base ten digit count. Conversion from uint64_t to double will have a loss of precision for large values due to float having a 52 bit significand. This is not a problem for this code because only three significant digits are printed.

    Code size for the TI compiler is 20,636 bytes and the execution time for the test case is 1.22 seconds. The GCC compiler does not produce working code.

    static void sprint_f3d(char * s, double f)
    {
        static char const * const fmt[3] = { "%1.2f%c", "%2.1f%c", "%4.0f%c" };
    
        div_t const d = div((f < 1000.0) ? 2 : (int)log10(f), 3);
    
        sprintf(s, fmt[d.rem], f / pow(1000.0, d.quot), " kMGTPEZY"[d.quot]);
    }
    
    The C standard library does not have integer versions of log10() and pow(), so another strategy will be used. The value is divided by 10 until it is less than 1000. The number of divisions is counted to determine the number of base ten digits. Stopping the division when the value is less than 1000 handles the special case for values of 99 or less by limiting the digit count to 3 or more. The resulting 3 digit value will be split as necessary for formatting using the div() library function.

    Code size for the TI compiler is 5,346 bytes and the execution time for the test case is 3.24 seconds.

    Code size for the GCC compiler is 36,040 bytes and the execution time for the test case is 981 milliseconds.

    The poor performance of the TI compiler is due the an inefficient intrinsic 64 bit unsigned divide.

    static void sprint_u64_a(char *s, uint64_t n)
    {
        unsigned d = 2;
    
        while(n >= 1000) n /= 10, ++d;
    
        div_t qr = div(d, 3);
    
        char const u = " kMGTPE"[qr.quot];
    
        switch(qr.rem) {
            case 0: qr = div((int)n, 100); sprintf(s, "%i.%02i%c", qr.quot, qr.rem, u); break;
            case 1: qr = div((int)n, 10); sprintf(s, "%2i.%i%c", qr.quot, qr.rem, u); break;
            case 2: sprintf(s, "%4i%c", (int)n, u); break;
        }
    }
    
    Using this 64 bit unsigned divide provides much better performance on the TI compiler, but much worse on the GCC compiler.

    Code size for the TI compiler is 5,666 bytes and the execution time for the test case is 396 milliseconds.

    Code size for the GCC compiler is 36,116 bytes and the execution time for the test case is 4.06 seconds.

    Future revisions will use intrinsic divide for GCC and this divide function for TI.

    static uint64_t divu64(uint64_t n, uint64_t d)
    {
        if((n < d) || (!d)) return 0;
    
        uint64_t register b = 1;
        if(((uint16_t)(n >> 48)) & 0x8000) {
            while(!(((uint16_t)(d >> 48)) & 0x8000)) d <<= 1, b <<= 1;
            if(n < d) d >>= 1, b >>= 1;
        } else {
            d <<= 1;
            while(n >= d) d <<= 1, b <<= 1;
            d >>= 1;
        }
    
        uint64_t q = b;
        n -= d;
        while(!(b & 1)) {
            d >>= 1;
            b >>= 1;
            if(n >= d) n -= d, q |= b;
        }
    
        return q;
    }
    
    The number of 64 bit divides can be reduced by using constants of 10 to the power of 2 to the power of N rather than iterative division by 10.

    Code size for the TI compiler is 5,886 bytes and the execution time for the test case is 160 milliseconds.

    Code size for the GCC compiler is 36,256 bytes and the execution time for the test case is 223 milliseconds.

        unsigned d = 2;
        if(n >= 1000000000000000000ULL) n = divu64(n, 10000000000000000ULL), d += 16;
        if(n >= 10000000000ULL) n = divu64(n, 100000000ULL), d += 8;
        if(n >= 1000000ULL) n = divu64(n, 10000ULL), d += 4;
        if(n >= 10000ULL) n = divu64(n, 100ULL), d += 2;
        if(n >= 1000ULL) n = divu64(n, 10ULL), ++d;
    
    The number of 64 bit divides can be reduced to one by using a table to determine the number of base ten digits. The table is searched for the highest value that is less than or equal to the value to be printed. The value is then divided by the table entry that is 1/100th of the value representing the base ten digit count to reduce the value to 3 digits.

    Code size for the TI compiler is 5,886 bytes and the execution time for the test case is 143 milliseconds.

    Code size for the GCC compiler is 36,096 bytes and the execution time for the test case is 143 milliseconds.

        static uint64_t const pt[] = {	// Powers of 10
    //	18446744073709551615		//  2^64 - 1
            10000000000000000000ULL,	// 10^19  NN.N E
             1000000000000000000ULL,	// 10^18  N.NN E
              100000000000000000ULL,	// 10^17   NNN P
               10000000000000000ULL,	// 10^16  NN.N P
                1000000000000000ULL,	// 10^15  N.NN P
                 100000000000000ULL,	// 10^14   NNN T
                  10000000000000ULL,	// 10^13  NN.N T
                   1000000000000ULL,	// 10^12  N.NN T
                    100000000000ULL,	// 10^11   NNN G
                     10000000000ULL,	// 10^10  NN.N G
    //           	  4294967295		//  2^32 - 1
                      1000000000ULL,	// 10^9   N.NN G
                       100000000ULL,	// 10^8    NNN M
                        10000000ULL,	// 10^7   NN.N M
                         1000000ULL,	// 10^6   N.NN M
                          100000ULL,	// 10^5    NNN k
                           10000ULL,	// 10^4   NN.N k
                            1000ULL,	// 10^3   N.NN k
                             100ULL,	// 10^2    NNN
                              10ULL,	// 10^1     NN
                               1ULL,	// 10^0	     N
        };
    
        unsigned d = 2;
        if(n >= 1000) {
            d = 19;
            uint64_t const *p = pt;
            while (n < *p) ++p, --d;
            n = divu64(n, p[2]);
        }
    
    The sprintf() function takes significant space, so it is replaced with decimal to ASCII conversion using the div() library function.

    Code size for the TI compiler is 1,660 bytes and the execution time for the test case is 25.8 milliseconds.

    Code size for the GCC compiler is 3,384 bytes and the execution time for the test case is 82.0 milliseconds.

        div_t qr; qr.rem = (int)n;
        div_t const dp = div(d, 3);
        if(dp.rem == 2) *s++ = ' ';
        if(qr.rem < 100) {
            *s++ = ' '; qr.quot = 0;
        } else {
            qr = div(qr.rem, 100); *s++ = '0' + qr.quot;
        }
        if(dp.rem == 0) *s++ = '.';
        if((!qr.quot) && (qr.rem < 10)) {
            *s++ = ' ';
        } else {
            qr = div(qr.rem, 10); *s++ = '0' + qr.quot;
        }
        if(dp.rem == 1) *s++ = '.';
        *s++ = '0' + qr.rem;
        *s++ = " kMGTPE"[dp.quot];
        *s = 0;
    
    The final optimization removes all division. The base ten tables values are used with iterative subtraction to create the ASCII string.

    Code size for the TI compiler is 1,368 bytes and the execution time for the test case is 10.8 milliseconds.

    Code size for the GCC compiler is 2,452 bytes and the execution time for the test case is 19.9 milliseconds.

        uint64_t const *p = pt + 17;
        if(n >= 1000) {
            p = pt;
            while (n < *p) ++p;
        }
        // n += (p[2] >> 1);	// optional rounding
        int dp = p - pt; while (dp > 2) dp -= 3;
        if (dp == 2) *s++ = ' ';
        char c;
        if(n < 100) {
            c = ' ';
        } else {
            c = '0'; while(n >= *p) n -= *p, ++c;
        }
        *s = c; ++p;
        if(dp == 1) *++s = '.';
        if((*s == ' ') && (n < 10)) {
            c = ' ';
        } else {
            c = '0'; while(n >= *p) n -= *p, ++c;
        }
        *++s = c; ++p;
        if(dp == 0) *++s = '.';
        c = '0'; while (n >= *p) n -= *p, ++c;
        *++s = c;
        *++s = "  EEPPPTTTGGGMMMkkk "[p - pt];
        *++s = 0;
    
    Performance summary

     function        TI                 GCC
    ----------------------------------------------
    sprint_u64     1368  10.8 ms     2452  19.9 ms
    sprint_u64_e   1660  25.8 ms     3384  82.0 ms
    sprint_u64_d   5886   143 ms    36096   143 ms
    sprint_u64_c   5886   160 ms    36256   223 ms
    sprint_u64_b   5666   396 ms    36116  4.06 s
    sprint_u64_a   5346  3.24 s     36040   981 ms
    sprint_f3d    20636  1.22 s     non-functional
    
    Network bandwidth display (lower right) using 3 digits.

     

    Complete code

    #include <msp430.h> 
    #include <stdint.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <stdio.h>
    
    
    #define PU64 sprint_u64
    //#define PU64 sprint_u64_e
    //#define PU64 sprint_u64_d
    //#define PU64 sprint_u64_c
    //#define PU64 sprint_u64_b
    //#define USE_DIV_FUNC
    //#define PU64 sprint_u64_a
    //#define PU64 sprint_f3d
    //#define PU64 sprint_u64_null
    
    
    static void sprint_u64(char *s, uint64_t n)
    {
        static uint64_t const pt[] = {	// Powers of 10
    //	18446744073709551615		//  2^64 - 1
            10000000000000000000ULL,	// 10^19  NN.N E
             1000000000000000000ULL,	// 10^18  N.NN E
              100000000000000000ULL,	// 10^17   NNN P
               10000000000000000ULL,	// 10^16  NN.N P
                1000000000000000ULL,	// 10^15  N.NN P
                 100000000000000ULL,	// 10^14   NNN T
                  10000000000000ULL,	// 10^13  NN.N T
                   1000000000000ULL,	// 10^12  N.NN T
                    100000000000ULL,	// 10^11   NNN G
                     10000000000ULL,	// 10^10  NN.N G
    //		  4294967295		//  2^32 - 1
                      1000000000ULL,	// 10^9   N.NN G
                       100000000ULL,	// 10^8    NNN M
                        10000000ULL,	// 10^7   NN.N M
                         1000000ULL,	// 10^6   N.NN M
                          100000ULL,	// 10^5    NNN k
                           10000ULL,	// 10^4   NN.N k
                            1000ULL,	// 10^3   N.NN k
                             100ULL,	// 10^2    NNN
                              10ULL,	// 10^1     NN
                               1ULL,	// 10^0	     N
        };
    
        uint64_t const *p = pt + 17;
        if(n >= 1000) {
            p = pt;
            while (n < *p) ++p;
        }
        // n += (p[2] >> 1);	// optional rounding
        int dp = p - pt; while (dp > 2) dp -= 3;
        if (dp == 2) *s++ = ' ';
        char c;
        if(n < 100) {
            c = ' ';
        } else {
            c = '0'; while(n >= *p) n -= *p, ++c;
        }
        *s = c; ++p;
        if(dp == 1) *++s = '.';
        if((*s == ' ') && (n < 10)) {
            c = ' ';
        } else {
            c = '0'; while(n >= *p) n -= *p, ++c;
        }
        *++s = c; ++p;
        if(dp == 0) *++s = '.';
        c = '0'; while (n >= *p) n -= *p, ++c;
        *++s = c;
        *++s = "  EEPPPTTTGGGMMMkkk "[p - pt];
        *++s = 0;
    }
    
    static uint64_t divu64(uint64_t n, uint64_t d)
    {
    #if defined( __GNUC__) & !defined(USE_DIV_FUNC)
    
        return n / d;
    
    #else
    
        if((n < d) || (!d)) return 0;
    
        uint64_t register b = 1;
        if(((uint16_t)(n >> 48)) & 0x8000) {
            while(!(((uint16_t)(d >> 48)) & 0x8000)) d <<= 1, b <<= 1;
            if(n < d) d >>= 1, b >>= 1;
        } else {
            d <<= 1;
            while(n >= d) d <<= 1, b <<= 1;
            d >>= 1;
        }
    
        uint64_t q = b;
        n -= d;
        while(!(b & 1)) {
            d >>= 1;
            b >>= 1;
            if(n >= d) n -= d, q |= b;
        }
    
        return q;
    
    #endif
    }
    
    static void sprint_u64_e(char *s, uint64_t n)
    {
        static uint64_t const pt[] = {	// Powers of 10
    //	18446744073709551615		//  2^64 - 1
            10000000000000000000ULL,	// 10^19  NN.N E
             1000000000000000000ULL,	// 10^18  N.NN E
              100000000000000000ULL,	// 10^17   NNN P
               10000000000000000ULL,	// 10^16  NN.N P
                1000000000000000ULL,	// 10^15  N.NN P
                 100000000000000ULL,	// 10^14   NNN T
                  10000000000000ULL,	// 10^13  NN.N T
                   1000000000000ULL,	// 10^12  N.NN T
                    100000000000ULL,	// 10^11   NNN G
                     10000000000ULL,	// 10^10  NN.N G
    //   		  4294967295		//  2^32 - 1
                      1000000000ULL,	// 10^9   N.NN G
                       100000000ULL,	// 10^8    NNN M
                        10000000ULL,	// 10^7   NN.N M
                         1000000ULL,	// 10^6   N.NN M
                          100000ULL,	// 10^5    NNN k
                           10000ULL,	// 10^4   NN.N k
                            1000ULL,	// 10^3   N.NN k
                             100ULL,	// 10^2    NNN
                              10ULL,	// 10^1     NN
                               1ULL,	// 10^0	     N
        };
    
        unsigned d = 2;
        if(n >= 1000) {
            d = 19;
            uint64_t const *p = pt;
            while (n < *p) ++p, --d;
            n = divu64(n, p[2]);
        }
        div_t qr; qr.rem = (int)n;
        div_t const dp = div(d, 3);
        if(dp.rem == 2) *s++ = ' ';
        if(qr.rem < 100) {
            *s++ = ' '; qr.quot = 0;
        } else {
            qr = div(qr.rem, 100); *s++ = '0' + qr.quot;
        }
        if(dp.rem == 0) *s++ = '.';
        if((!qr.quot) && (qr.rem < 10)) {
            *s++ = ' ';
        } else {
            qr = div(qr.rem, 10); *s++ = '0' + qr.quot;
        }
        if(dp.rem == 1) *s++ = '.';
        *s++ = '0' + qr.rem;
        *s++ = " kMGTPE"[dp.quot];
        *s = 0;
    }
    
    static void sprint_u64_d(char *s, uint64_t n)
    {
        static uint64_t const pt[] = {	// Powers of 10
    //	18446744073709551615		//  2^64 - 1
            10000000000000000000ULL,	// 10^19  NN.N E
             1000000000000000000ULL,	// 10^18  N.NN E
              100000000000000000ULL,	// 10^17   NNN P
               10000000000000000ULL,	// 10^16  NN.N P
                1000000000000000ULL,	// 10^15  N.NN P
                 100000000000000ULL,	// 10^14   NNN T
                  10000000000000ULL,	// 10^13  NN.N T
                   1000000000000ULL,	// 10^12  N.NN T
                    100000000000ULL,	// 10^11   NNN G
                     10000000000ULL,	// 10^10  NN.N G
    //   		  4294967295		//  2^32 - 1
                      1000000000ULL,	// 10^9   N.NN G
                       100000000ULL,	// 10^8    NNN M
                        10000000ULL,	// 10^7   NN.N M
                         1000000ULL,	// 10^6   N.NN M
                          100000ULL,	// 10^5    NNN k
                           10000ULL,	// 10^4   NN.N k
                            1000ULL,	// 10^3   N.NN k
                             100ULL,	// 10^2    NNN
                              10ULL,	// 10^1     NN
                               1ULL,	// 10^0	     N
        };
    
        unsigned d = 2;
        if(n >= 1000) {
            d = 19;
            uint64_t const *p = pt;
            while (n < *p) ++p, --d;
            n = divu64(n, p[2]);
        }
    
        div_t qr = div(d, 3);
    
        char const u = " kMGTPE"[qr.quot];
    
        switch(qr.rem) {
            case 0: qr = div((int)n, 100); sprintf(s, "%i.%02i%c", qr.quot, qr.rem, u); break;
            case 1: qr = div((int)n, 10); sprintf(s, "%2i.%i%c", qr.quot, qr.rem, u); break;
            case 2: sprintf(s, "%4i%c", (int)n, u); break;
        }
    }
    
    static void sprint_u64_c(char *s, uint64_t n)
    {
        unsigned d = 2;
        if(n >= 1000000000000000000ULL) n = divu64(n, 10000000000000000ULL), d += 16;
        if(n >= 10000000000ULL) n = divu64(n, 100000000ULL), d += 8;
        if(n >= 1000000ULL) n = divu64(n, 10000ULL), d += 4;
        if(n >= 10000ULL) n = divu64(n, 100ULL), d += 2;
        if(n >= 1000ULL) n = divu64(n, 10ULL), ++d;
    
        div_t qr = div(d, 3);
    
        char const u = " kMGTPE"[qr.quot];
    
        switch(qr.rem) {
            case 0: qr = div((int)n, 100); sprintf(s, "%i.%02i%c", qr.quot, qr.rem, u); break;
            case 1: qr = div((int)n, 10); sprintf(s, "%2i.%i%c", qr.quot, qr.rem, u); break;
            case 2: sprintf(s, "%4i%c", (int)n, u); break;
        }
    }
    
    static void sprint_u64_b(char *s, uint64_t n)
    {
        unsigned d = 2;
    
        while(n >= 1000) n = divu64(n, 10), ++d;
    
        div_t qr = div(d, 3);
    
        char const u = " kMGTPE"[qr.quot];
    
        switch(qr.rem) {
            case 0: qr = div((int)n, 100); sprintf(s, "%i.%02i%c", qr.quot, qr.rem, u); break;
            case 1: qr = div((int)n, 10); sprintf(s, "%2i.%i%c", qr.quot, qr.rem, u); break;
            case 2: sprintf(s, "%4i%c", (int)n, u); break;
        }
    }
    
    static void sprint_u64_a(char *s, uint64_t n)
    {
        unsigned d = 2;
    
        while(n >= 1000) n /= 10, ++d;
    
        div_t qr = div(d, 3);
    
        char const u = " kMGTPE"[qr.quot];
    
        switch(qr.rem) {
            case 0: qr = div((int)n, 100); sprintf(s, "%i.%02i%c", qr.quot, qr.rem, u); break;
            case 1: qr = div((int)n, 10); sprintf(s, "%2i.%i%c", qr.quot, qr.rem, u); break;
            case 2: sprintf(s, "%4i%c", (int)n, u); break;
        }
    }
    
    static void sprint_f3d(char * s, double f)
    {
        static char const * const fmt[3] = { "%1.2f%c", "%2.1f%c", "%4.0f%c" };
    
        div_t const d = div((f < 1000.0) ? 2 : (int)log10(f), 3);
    
        sprintf(s, fmt[d.rem], f / pow(1000.0, d.quot), " kMGTPEZY"[d.quot]);
    }
    
    static void sprint_u64_null(char *s, uint64_t n)
    {
        *s = 0;
    }
    
    static void print(char const *s)
    {
        while(*s) {
            while(!(UCA1IFG & UCTXIFG));
            UCA1TXBUF = *s++;
        }
    }
    
    
    #define smclk_freq	(32768UL * 31UL) 	// SMCLK frequency in hertz
    #define bps			(9600UL)	// Async serial bit rate
    
    int main(void)
    {
        WDTCTL = WDTPW | WDTHOLD;	                // Stop watchdog timer
                                                    //
        P4SEL = BIT4 | BIT5;			// Enable UART pins
        P4DIR = BIT4 | BIT5;			//
                                                    //
                                                    // Initialize UART
        UCA1CTL1 = UCSWRST;				// Hold USCI in reset to allow configuration
        UCA1CTL0 = 0;				// No parity, LSB first, 8 bits, one stop bit, UART (async)
        const unsigned long brd = (smclk_freq + (bps >> 1)) / bps;	// Bit rate divisor
        UCA1BR1 = (brd >> 12) & 0xFF;		// High byte of whole divisor
        UCA1BR0 = (brd >> 4) & 0xFF;		// Low byte of whole divisor
        UCA1MCTL = ((brd << 4) & 0xF0) | UCOS16;	// Fractional divisor, oversampling mode
        UCA1CTL1 = UCSSEL_2;			// Use SMCLK for bit rate generator, release reset
        
        static const uint64_t td[] = {
            0ULL,
            1ULL,
            12ULL,
            123ULL,
            1234ULL,
            12345ULL,
            123456ULL,
            1234567ULL,
            12345678ULL,
            123456789ULL,
            1234567890ULL,
            12345678901ULL,
            123456789012ULL,
            1234567890123ULL,
            12345678901234ULL,
            123456789012345ULL,
            1234567890123456ULL,
            12345678901234567ULL,
            123456789012345678ULL,
            1234567890123456789ULL,
            12345678901234567890ULL,
            18446744073709551615ULL
        };
    
    
        char s[32];
        int n;
        uint64_t const *p;
    
    
        // Print test array to application UART
        print("\r\n");
        n =  sizeof(td) / sizeof(td[0]);
        p = td;
        do {
            PU64(s, *p++);
            print(s);
            print("\r\n");
        } while(--n);
    
    
        // Time the test array - make string only - do not print
        TA0EX0 = 7;
        TA0CTL = TASSEL_2 | ID_3 | MC_2;
        TA0CTL |= TACLR;
        n =  sizeof(td) / sizeof(td[0]);
        p = td;
        do {
            PU64(s, *p++);
        } while(--n);
        uint64_t et = TA0R * 63ULL;		// Elapsed time in microseconds
        PU64(s, et); print(s); print(" us\r\n");
    
    
        for(;;
    
        return 0;
    }
    
  2. Checking for proper sequence...

    (only changed code shown)

     

    struct Fastpacket {
      unsigned char *bufferEnd;		// End of buffer + 1
      unsigned char *bufferPos;		// Next char goes here
      unsigned char seq;                    // Next char in sequence
    };
    
    struct Fastpacket *newFastpacket(size_t const size){
    
        // Allocate enough memory for the structure and the buffer
        struct Fastpacket *retVal = malloc(sizeof(struct Fastpacket) + (size * sizeof(unsigned char)));
    
        if(retVal) {
              // Buffer begins after the structure
    	  retVal->bufferPos = (unsigned char *)(retVal + 1);
    	  retVal->bufferEnd = retVal->bufferPos + size;
              retVal->seq = 0x40;
        }
    
        return retVal;
    }
    
    int appendFrame(struct Fastpacket *fastpacket, unsigned char const *msg){
    
        if(msg[0] != fastpacket->seq) { // Check if incorrect sequence
           fastpacket->seq = 0x40;      // Start over
           return -1;                   // Return error
        }
    
        ++fastpacket->seq;              // Increment sequence
    
        unsigned char const * const msgEnd = msg + 8;
    
        while((msg < msgEng) && (fastpacket->bufferPos < fastpacket->bufferEnd))
            *(fastpacket->bufferPos)++ = *msg++;
    
        return 0;
    }
    
  3. Nothing wrong with what you have written.

     

    If you want it a little faster and smaller with a little less memory fragmentation, here is some optimized code...

     

    struct Fastpacket {
      unsigned char *bufferEnd;		// End of buffer + 1
      unsigned char *bufferPos;		// Next char goes here
    };
    
    struct Fastpacket *newFastpacket(size_t const size){
    
        // Allocate enough memory for the structure and the buffer
        struct Fastpacket *retVal = malloc(sizeof(struct Fastpacket) + (size * sizeof(unsigned char)));
    
        if(retVal) {
              // Buffer begins after the structure
    	  retVal->bufferPos = (unsigned char *)(retVal + 1);
    	  retVal->bufferEnd = retVal->bufferPos + size;
        }
    
        return retVal;
    }
    
    void delFastpacket(struct Fastpacket *fastpacket){
        if(fastpacket) free(fastpacket);
    }
    
    void appendFrame(struct Fastpacket *fastpacket, unsigned char const *msg){
    
        unsigned char const * const msgEnd = msg + 8;
    
        while((msg < msgEng) && (fastpacket->bufferPos < fastpacket->bufferEnd))
            *(fastpacket->bufferPos)++ = *msg++;
    }
    
    int main(int argc, char *argv[]) {
    
      int i;
      struct Fastpacket *fastpacket = newFastpacket(88);
      if(!fastpacket) return -1;
     
      appendFrame(fastpacket, msg01);
      appendFrame(fastpacket, msg02);
      appendFrame(fastpacket, msg03);
      appendFrame(fastpacket, msg04);
      appendFrame(fastpacket, msg05);
      appendFrame(fastpacket, msg06);
      appendFrame(fastpacket, msg07);
      appendFrame(fastpacket, msg08);
      appendFrame(fastpacket, msg09);
      appendFrame(fastpacket, msg10);
      appendFrame(fastpacket, msg11);
    
      unsigned char const *b = (unsigned char*)(fastpacket + 1);
      int const len = fastpacket->bufferPos - b;
      for(i = 0; i < len; ++i)
        printf("%02X%s" , *b++, ((i & 7) == 7) ? "\n" : " ");
    
      printf("\nBuffer position is currently at: %d\n", len);
    
      delFastpacket(fastpacket);
      return 0;
    }
    
  4.     unsigned int pgn = ((pgn_field.bytes.byte4 >> 4) << 16) +
                            (pgn_field.bytes.byte3 << 8) +
                            (pgn_field.bytes.byte2);
    
    
    should probably be

     

        unsigned int pgn = ((pgn_field.bytes.byte4 & 0x1F) << 16) |
                            (pgn_field.bytes.byte3 << 8) |
                            (pgn_field.bytes.byte2);
    
    
  5. Looks like this is the code that does the printing in your candump example...

     

    void sprint_long_canframe(char *buf , struct can_frame *cf, int view) {
    	/* documentation see lib.h */
    
    	int i, j, dlen, offset;
    	int dlc = (cf->can_dlc > 8)? 8 : cf->can_dlc;
    
    	if (cf->can_id & CAN_ERR_FLAG) {
    		sprintf(buf, "%8X  ", cf->can_id & (CAN_ERR_MASK|CAN_ERR_FLAG));
    		offset = 10;
    	} else if (cf->can_id & CAN_EFF_FLAG) {
    		sprintf(buf, "%8X  ", cf->can_id & CAN_EFF_MASK);
    		offset = 10;
    	} else {
    		sprintf(buf, "%3X  ", cf->can_id & CAN_SFF_MASK);
    		offset = 5;
    	}
    
    	sprintf(buf+offset, "[%d]", dlc);
    	offset += 3;
    
    .....
    
    So it does mask off the address bits and also prints 3 or 8 hex digits as needed.
  6. Bit 31 of can_frame.can_id indicates if an 11 or 29 bit address is used. It is probably being masked by the candump program because it is not actually part of the address.

     

    It would be nice if the candump program used 3 or 8 hex digits to indicate the address size in use.

     

    Using a stream (socket) for a block based protocol seems like a bad idea. Much that can go wrong.

  7. @@oPossum Heya, how you been ?

    I have recovered from alcoholism, but my eyesight has become worse.

     

    You may prefer the Windows way of doing this

    I have never said this, and never will. I use Linux, OS-X, and Windows. They are all very frustrating at times and all lack features I would like them to have.

     

    I was just pointing out that Windows and OS-X both support disk images without any additional software. They both allow command line or GUI tools to be used. You are correct that it is not exactly the same as using dd. The Windows Virtual Hard Drive (vhd) and OS-X Disk Image (dmg) go beyond simple byte-for-byte copies of a chunk of media. The GUI tools will often allow using these capabilities without consulting any documentation. It is difficult for me to see how Linux is easier to use in this specific case.

  8. Granted some of these tools are also availible on Windows even, but it is the flexibility in use on Linux( or even just *NIX in general ) that sets these tools apart.

    Many of them are available with cygwin. Windows goes far beyond these simple utils with powershell.

     

    Using MS Windows - How easy do you all think it would be to make a 1:1 disk copy - saved as an image file, mounting this image file( or even just one partition in it- assuming multiples ) as if it was it;s own partition(s) / file system, and then sharing this same file system over a network so a remote system could use it ? Let us make it even more interresting. Only using standard command line tools. Neigh impossible ? So assuming it is not impossible, which it probably isn't, how much do you think that would cost ? I'm betting more than the average hobbyist would want to invest. Much more.

    Trivial.

     

    You can use command line or GUI to make images, mount them, and even boot them.

     

    https://technet.microsoft.com/en-us/library/gg318052(v=ws.10).aspx

     

     

    Sharing them over the network can also be done with GUI or command line.

     

     

    Same for OS-X using Disk Utility as a GUI or command line tool.

  9. In Windows there are several timing parameters for async serial (COM) ports that are configured with the SetCommTimeouts() API call.

     

    https://msdn.microsoft.com/en-us/library/windows/desktop/aa363437

    https://msdn.microsoft.com/en-us/library/windows/desktop/aa363190

     

    These settings in combination with the buffer sizes use for tx/rx can have a significant impact on latency. This latency is in addition to that of the USB to serial hardware.

     

    I suspect that it is not possible to modify this stuff when using LabView. Not sure about C#. It is, of course, possible to adjust all this when using C or C++.

     

    As a practical matter you should probably be using a windowing protocol to limit the impact of latency.

     

    http://en.wikipedia.org/wiki/Sliding_window_protocol

     

    If you really need a low latency async serial connection, you could try a USB to MIDI cable. There is a 14 bit limit on most messages. See Firmata for an example of repurposing the MIDI protocol.

     

    https://github.com/firmata/protocol

  10. This code quickly determines if a date is within daylight savings time (DST). A companion function adjusts the date and time if necessary.

     

    There are many ways to determine if a date is within DST. This code begins by checking large time spans (months) and works down to shorter time spans (hours) as necessary. So for most of the year the decision is made very quickly. The worst cases occur on the days of the transition.

     

    BCD representation is used for compatibility with most hardware RTC chips. The day of week (T_RTC.dow) must be set properly for this code to work. Day of week is a value of 1 (Sunday) to 8 (Saturday).

     

     

    typedef struct {                                        // RTC data structure
      uint8_t sec;                                          //
      uint8_t min;                                          //
      uint8_t hour;                                         //
      uint8_t dow;                                          //
      uint8_t day;                                          //
      uint8_t month;                                        //
      uint8_t year;                                         //
    } T_RTC;                                                //
    
    // Starting in 2007, most of the United States and Canada observe DST from the second Sunday in March to the first Sunday in November
    int rtc_is_dst(T_RTC const * const rtc)                 // --- Check if time & date are within DST
    {                                                       //
        if((rtc->month > 0x03) && (rtc->month < 0x11)) return 1; // After March and before November is DST
        if((rtc->month < 0x03) || (rtc->month > 0x11)) return 0; // Before March or after November is not DST
        if(rtc->month == 0x03) {                            // March
            if(rtc->day > 0x15) return 1;                   // After second week, is DST
            if(rtc->day < 0x08) return 0;                   // Before second week, is not DST
            const int d = ((rtc->day >> 4) * 10) + (rtc->day & 0x0F); // Integer day of month
            int s = d - rtc->dow + 1;                       // Current or previous Sunday as day of month
            if(s < 8) s += 7;                               // Make sure Sunday is in second week
            if(d < s) return 0;                             // Before Sunday, is not DST
            if(d > s) return 1;                             // After Sunday, is DST
            if((rtc->hour & 0x3F) < 0x02) return 0;         // Before 2:00, is not DST
            return 1;                                       // 2:00 or after, is DST
        } else { // rtc->month == 0x11                      // November
            if(rtc->day > 0x07) return 0;                   // After first week, not DST
            const int d = ((rtc->day >> 4) * 10) + (rtc->day & 0x0F); // Integer day of month
            int s = d - rtc->dow + 1;                       // Current or previous Sunday as day of month
            if(s < 0) s += 7;                               // Make sure Sunday is in first week
            if(d < s) return 1;                             // Before Sunday, is DST
            if(d > s) return 0;                             // After Sunday, is not DST
            if((rtc->hour & 0x3F) < 0x02) return 1;         // Before 2:00, is DST
            return 0;                                       // 2:00 or after, is not DST
        }                                                   //
    }                                                       //
                                                            //
    void rtc_adjust_dst(T_RTC * const rtc)                  // --- Correct RTC structure for DST if necessary
    {                                                       //
      static const uint8_t dm[19] = { 0, 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0, 0, 0, 0, 0, 0, 0x31, 0x30, 0x31 };
      if(rtc_is_dst(rtc)) {                                 // If DST
        ++rtc->hour;                                        // Increment hour
        if((rtc->hour & 0x0F) > 9) rtc->hour += 6;          // Adjust for BCD
        if(rtc->hour > 0x23) {                              // If next day
          rtc->hour = 0;                                    // Set hour to 0
          ++rtc->dow;                                       // Increment day of week
          if(rtc->dow > 7) rtc->dow = 1;                    // Adjust for wrap around
          ++rtc->day;                                       // Increment day of month
          if((rtc->day & 0x0F) > 9) rtc->day += 6;          // Adjust for BCD
          if(rtc->day > dm[rtc->month]) {                   // Check if next month
            rtc->day = 0x01;                                // Wrap to first day of next month
            ++rtc->month;                                   // Increment month
            if((rtc->month & 0x0F) > 9) rtc->month += 6;    // Adjust for BCD
          }                                                 //
        }                                                   //
      }                                                     //
    }                                                       //
    
  11. Those old 8 bit ECUs have support chips that help with all the timing critical stuff. With a G2553 you have a faster CPU, but only wo very basic timers. Each can only do two full featured compare outputs, so your need for three outputs is just beyond what it can easily do. The F5529 has a timer A with five outputs, and a timer B with 7 outputs, so it is much better able to do what you need.

  12. No, not due to sales. Sales are fine at Parallax. They have been releasing a FPGA bitstream for the Propeller 2 that has been under development. The community really like being able to test a next gen chip before it became available. The release of a VHDL and bitstream for a Propeller 1 workalike will in no way harm sales of the Propeller chip. The FPGA is much more expensive then a propeller chip, and it would not be cost effective for a competitor to have an ASIC made. It is simply something they are doing for a few in the community that can hack away at the VHDL and do cool stuff.

  13. char *strtok_r(char *str, const char *delim, char **saveptr);

    On the first call to strtok_r(), str should point to the string to be parsed, and the value of saveptr is ignored. In subsequent calls, str should be NULL, and saveptr should be unchanged since the previous call.

      char *p = text;
      char *q;
      char *str;
    
      while ((str = strtok_r(p, ",", &q)) != NULL) // delimiter is comma
      {
        p = NULL;
        Serial.println(str);
        delay(500);
      }
    
  14. The FTDI is capable of jitter free operation at 3,000,000 / N bps. It can use fractional division for operation at more bit rates, but that causes some jitter, so I don't recommend it. It has no special knowledge of 'standard' bit rates like some other chips - it is a simple divider.

     

    So the highest reliable bit rates for the FTDI chips are 1.5M, 1M, 750k, 600k, 500k, etc...

×
×
  • Create New...