Bit Angle Modulation (BAM) in a PIC


Closed Thread
Results 1 to 40 of 151

Hybrid View

  1. #1
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    Hi Darrel,

    May I pass along my late congrats' on some brilliant work and share a couple ideas?

    Instead of using six instructions (6 cycles) per LED during each update interval you might consider updating one port at a time using an exclusive-or instruction (2 cycles) and data from an eight byte "toggle" array that's refreshed during that long 64T end-of-period interval. This reduces overhead from 288 cycles for 48 LEDs to just 12 cycles during each update interval. Using "toggle" data and an exclusive-or instruction lets us update MIBAM output bits without changing non-MIBAM bits on each port.

    Since the three '1T' intervals seem to be a major bottleneck we might consider combining the center |2T|1T|1T|1T|2T| intervals into a single '7T' interval and use small in-line isochronous delays to effect precise timing between updates. This would practically eliminate "overhead" effects on the center three '1T' intervals.

    Here's a pseudo C code driver example (sorry, I don't have PBP). I'm sure you realize that the "switch" function should be replaced with assembly language to reduce overhead and eliminate jitter and that the LEDs and "data benders" should probably be setup as macros as you did in your driver.

    An assembly language version I wrote supports 48 LEDs at decent refresh rates at almost any clock (4-MHz, 177 Hz, 1T = 22-usecs).

    Food for thought. Kind regards, Mike, K8LH

    Code:
    unsigned char interval = 0;     // ISR state machine var'
    
    unsigned char red = 44;
    unsigned char grn = 55;
    unsigned char blu = 66;
    
    volatile unsigned short ccpr1 @ 0xFBE;
    
    /*                                                                  *
     *  original intervals -> |64|32|16|8|4|2|1|1|1|2|4|8|16|32|64|     *
     *  modified intervals -> |64|32|16|8|4|----7----|4|8|16|32|64|     *
     *                                                                  */
    void interrupt()                // CCP1 "special event" interrupts
    { unsigned char Adat[8];        // porta "toggle" array
      unsigned char Bdat[8];        // portb "toggle" array
      pir1.CCP1IF = 0;              // clear CCP1 interrupt flag
      switch(interval++)            //
      { case 0:                     // 64T
          porta ^= Adat[7];         //
          portb ^= Bdat[7];         //
          ccpr1 = (64*tStep);       // ccpr1 = half 2^7 (64T)
          break;                    //
        case 1:                     // 32T
          porta ^= Adat[6];         //
          portb ^= Bdat[6];         //
          ccpr1 >>= 1;              // ccpr1 = half 2^6 (32T)
          break;                    //
        case 2:                     // 16T
          porta ^= Adat[5];         //
          portb ^= Bdat[5];         //
          ccpr1 >>= 1;              // ccpr1 = half 2^5 (16T)
          break;                    //
        case 3:                     // 8T
          porta ^= Adat[4];         //
          portb ^= Bdat[4];         //
          ccpr1 >>= 1;              // ccpr1 = half 2^4 (8T)
          break;                    //
        case 4:                     // 4T
          porta ^= Adat[3];         //
          portb ^= Bdat[3];         //
          ccpr1 >>= 1;              // ccpr1 = half 2^3 (4T)
          break;                    //
        case 5:                     // 7T (2T+1T+1T+1T+2T)
          porta ^= Adat[2];         //
          portb ^= Bdat[2];         //
          ccpr1 = (7*tStep);        // ccpr1 = 7*tStep  (7T)
          delayCy(2*tStep-8);       // 2T minus 8 cycles
          porta ^= Adat[1];         //
          portb ^= Bdat[1];         //
          delayCy(1*tStep-4);       // 1T minus 4 cycles
          porta ^= Adat[0];         //
          portb ^= Bdat[0];         //
          delayCy(1*tStep-4);       // 1T minus 4 cycles
          porta ^= Adat[0];         //
          portb ^= Bdat[0];         //
          delayCy(1*tStep-4);       // 1T minus 4 cycles
          porta ^= Adat[1];         //
          portb ^= Bdat[1];         //
          break;                    //
        case 6:                     // 4T
          porta ^= Adat[2];         //
          portb ^= Bdat[2];         //
          ccpr1 = (4*tStep);        // ccpr1 = half 2^3 (4T)
          break;                    //
        case 7:                     // 8T
          porta ^= Adat[3];         //
          portb ^= Bdat[3];         //
          ccpr1 <<= 1;              // ccpr1 = half 2^4 (8T)
          break;                    //
        case 8:                     // 16T
          porta ^= Adat[4];         //
          portb ^= Bdat[4];         //
          ccpr1 <<= 1;              // ccpr1 = half 2^5 (16T)
          break;                    //
        case 9:                     // 32T
          porta ^= Adat[5];         //
          portb ^= Bdat[5];         //
          ccpr1 <<= 1;              // ccpr1 = half 2^6 (32T)
          break;                    //
        case 10:                    // 64T (end-of-period)
          porta ^= Adat[6];         //
          portb ^= Bdat[6];         //
          ccpr1 <<= 1;              // ccpr1 = half 2^7 (64T)
          interval = 0;             // prep for new period
          Adat[0] = 0; Adat[1] = 0; // clear Adat[] array
          Adat[2] = 0; Adat[3] = 0; //
          Adat[4] = 0; Adat[5] = 0; //
          Adat[6] = 0; Adat[7] = 0; //
          Bdat[0] = 0; Bdat[1] = 0; // clear Bdat[] array
          Bdat[2] = 0; Bdat[3] = 0; //
          Bdat[4] = 0; Bdat[5] = 0; //
          Bdat[6] = 0; Bdat[7] = 0; //
    /*                                                                  *
     *  use one "data bender" for each LED (16 cycles each)             *
     *                                                                  */
          if(red.0) Bdat[0] |= 1;   // 2^0 data RB0 pin
          if(red.1) Bdat[1] |= 1;   // 2^1 data RB0 pin
          if(red.2) Bdat[2] |= 1;   // 2^2 data RB0 pin
          if(red.3) Bdat[3] |= 1;   // 2^3 data RB0 pin
          if(red.4) Bdat[4] |= 1;   // 2^4 data RB0 pin
          if(red.5) Bdat[5] |= 1;   // 2^5 data RB0 pin
          if(red.6) Bdat[6] |= 1;   // 2^6 data RB0 pin
          if(red.7) Bdat[7] |= 1;   // 2^7 data RB0 pin
    
          if(grn.0) Bdat[0] |= 2;   // 2^0 data RB1 pin
          if(grn.1) Bdat[1] |= 2;   // 2^1 data RB1 pin
          if(grn.2) Bdat[2] |= 2;   // 2^2 data RB1 pin
          if(grn.3) Bdat[3] |= 2;   // 2^3 data RB1 pin
          if(grn.4) Bdat[4] |= 2;   // 2^4 data RB1 pin
          if(grn.5) Bdat[5] |= 2;   // 2^5 data RB1 pin
          if(grn.6) Bdat[6] |= 2;   // 2^6 data RB1 pin
          if(grn.7) Bdat[7] |= 2;   // 2^7 data RB1 pin
    
          if(blu.0) Bdat[0] |= 4;   // 2^0 data RB2 pin
          if(blu.1) Bdat[1] |= 4;   // 2^1 data RB2 pin
          if(blu.2) Bdat[2] |= 4;   // 2^2 data RB2 pin
          if(blu.3) Bdat[3] |= 4;   // 2^3 data RB2 pin
          if(blu.4) Bdat[4] |= 4;   // 2^4 data RB2 pin
          if(blu.5) Bdat[5] |= 4;   // 2^5 data RB2 pin
          if(blu.6) Bdat[6] |= 4;   // 2^6 data RB2 pin
          if(blu.7) Bdat[7] |= 4;   // 2^7 data RB2 pin
    /*                                                                  *
     *  convert Bdat[] and Adat[] array "output" data to "toggle" data  *
     *                                                                  *
     *  red  0x2C 00101100 (on RB0)                                     *
     *  grn  0x37 00110111 (on RB1)                                     *
     *  blu  0x42 01000010 (on RB2)                                     *
     *                                                                  *
     *  Bdat 'output data'   Bdat 'toggle data'   Bdat 'toggle data'    *
     *   [0] 0x02 00000010    [0] 0x04 00000100    [0] 0x04 00000100    *
     *   [1] 0x06 00000110    [1] 0x05 00000101    [1] 0x05 00000101    *
     *   [2] 0x03 00000011    [2] 0x02 00000010    [2] 0x02 00000010    *
     *   [3] 0x01 00000001    [3] 0x03 00000011    [3] 0x03 00000011    *
     *   [4] 0x02 00000010    [4] 0x01 00000001    [4] 0x01 00000001    *
     *   [5] 0x03 00000011    [5] 0x07 00000111    [5] 0x07 00000111    *
     *   [6] 0x04 00000100    [6] 0x04 00000100    [6] 0x04 00000100    *
     *   [7] 0x00 00000000    [7] 0x00 00000000    [7] 0x01 00000001    *
     *                      portb 0x00 00000000  portb 0x01 00000001    *
     *                                                                  */
          asm {
          movf  _porta,W            // get current PORTA bits
          andlw 0                   // keep only MIBAM output bits
          xorwf _Adat+7,F           // create 2^7 toggle bits
          xorwf _Adat+7,W           // W simulates cumulative PORTA
          xorwf _Adat+6,F           // create 2^6 toggle bits
          xorwf _Adat+6,W           //
          xorwf _Adat+5,F           // create 2^5 toggle bits
          xorwf _Adat+5,W           //
          xorwf _Adat+4,F           // create 2^4 toggle bits
          xorwf _Adat+4,W           //
          xorwf _Adat+3,F           // create 2^3 toggle bits
          xorwf _Adat+3,W           //
          xorwf _Adat+2,F           // create 2^2 toggle bits
          xorwf _Adat+2,W           //
          xorwf _Adat+1,F           // create 2^1 toggle bits
          xorwf _Adat+1,W           //
          xorwf _Adat+0,F           // create 2^0 toggle bits
    
          movf  _portb,W            // get current PORTB bits
          andlw 7                   // keep only MIBAM output bits
          xorwf _Bdat+7,F           // create 2^7 toggle bits
          xorwf _Bdat+7,W           // W simulates cumulative PORTB
          xorwf _Bdat+6,F           // create 2^6 toggle bits
          xorwf _Bdat+6,W           //
          xorwf _Bdat+5,F           // create 2^5 toggle bits
          xorwf _Bdat+5,W           //
          xorwf _Bdat+4,F           // create 2^4 toggle bits
          xorwf _Bdat+4,W           //
          xorwf _Bdat+3,F           // create 2^3 toggle bits
          xorwf _Bdat+3,W           //
          xorwf _Bdat+2,F           // create 2^2 toggle bits
          xorwf _Bdat+2,W           //
          xorwf _Bdat+1,F           // create 2^1 toggle bits
          xorwf _Bdat+1,W           //
          xorwf _Bdat+0,F           // create 2^0 toggle bits
          }
          break;                    //
      }
    }
    Last edited by Mike, K8LH; - 26th July 2009 at 14:01.

  2. #2
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    WooHoo! Somebody's paying attention.

    Hi Mike,

    That's a VERY interesting idea!
    Not sure at this point how I can let the user assign the pins at random, in any order, and any number of them, on any chip. But this sounds good enough to see if I can find a way.

    For combining the smallest periods ... I already add the lsb from each side of the "mirror" into a single period to double the minimum interrupt time. Not sure where your 3rd one comes from. That would make the lsb worth 1.5.

    I had also tried combining the 2 lsb's on both sides, but that reduced the resolution to 7-bit instead of 8, so the results weren't very good.

    I'll see what I can do with the "Full PORT Press".

    Thanks for the great idea.

    Add: Hmmm, that would help minimze R-M-W issues too.
    <br>
    Last edited by Darrel Taylor; - 26th July 2009 at 16:56. Reason: RMW
    DT

  3. #3
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by Darrel Taylor View Post

    For combining the smallest periods ... I already add the lsb from each side of the "mirror" into a single period to double the minimum interrupt time. Not sure where your 3rd one comes from. That would make the lsb worth 1.5.
    I'm not sure I follow you here. The duty cycle b0 bit output is still 1T long smack in the middle and the b1 bit still generates two 1T outputs on either side of the b0 1T output so we're not losing any resolution. I've just combined those |2T|1T|1T|1T|2T| outputs into a single 7T interval so that our minimum ISR interval is now 4T instead of 1T which gives us more headroom and much higher refresh intervals (even with the overhead of full ISR context save/restore)

    Not sure at this point how I can let the user assign the pins at random, in any order, and any number of them, on any chip. But this sounds good enough to see if I can find a way.
    I'm not exactly sure how to do it either and then we would still need to dynamically come up with Amask, Bmask, Cmask constants or variables for each port output-to-toggle routine but like you, I'm excited enough to try and find a way.

    With the new smaller 4T minimum ISR interval my assembly language test driver can achieve Kilohertz range refresh rates with some of the higher clock frequencies, though I'm not sure if that's really useful (grin).

    Kind regards, Mike, K8LH

  4. #4
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Quote Originally Posted by Mike, K8LH View Post
    I'm not sure I follow you here. The duty cycle b0 bit output is still 1T long smack in the middle and the b1 bit still generates two 1T outputs on either side of the b0 1T output so we're not losing any resolution. I've just combined those |2T|1T|1T|1T|2T| outputs into a single 7T interval so that our minimum ISR interval is now 4T instead of 1T which gives us more headroom and much higher refresh intervals (even with the overhead of full ISR context save/restore)
    By combining those periods, you can't switch the LED for bit0, because it's combined in with bit1. That's what drops it down to 7-bit resolution. If it were a single output, you could combine them, but with multiple LED's, bit0 has to remain separate from bit1.

    With MIBAM, it looks like this, with the red line being the "mirror".
    Code:
    Bit- 1  0    0  1
        |2T|1T|1T|2T|
    The 1T's on either side can be combined into a single period equal to 2T, but they can't be combined with the bit1 (2T's).

    I'm excited enough to try and find a way.
    I think I know how now, but welcome any more thoughts.
    Just a matter of getting it done.

    Thanks,
    DT

  5. #5
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    Hi Darrel,

    I think we may be confusing each other with terminology and perhaps a slight difference in our design. I was using "1T" as the PWM step size. For example, if we were to use a step size of 10-usecs and 256 steps or levels then we'd have a 2560-usec period (256*10-usecs). The b0 output is 10-usecs or 1T long. The b1 output is 20-usecs or 2T long but I split it in half with 10-usecs or 1T on either side of the b0 10-usec (1T) output. That's how I get |2T|1T|1T|1T|2T| == |20us|10us|10us|10us|20us| == |half b2|half b1|full b0|half b1|half b2|. I thought this was how you were doing it but now I suspect I didn't look closely enough at your code and I apologize.

    It also seems you think I'm combining the b2, b1, and b0 outputs and reducing resolution but I'm not. I'm simply generating the b2, b1, and b0 outputs during a single interrupt interval.

    More later... I'm trying to see if I can upgrade a very old 12F683 Charlieplexed 5-pin 20-LED project from 64 levels per LED to 256 levels per LED using MIBAM. Wish me luck...

    Kind regards, Mike

    <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/AgQTBKcg2D4&color1=0xb1b1b1&color2=0xcfcfcf&hl=en& feature=player_embedded&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.youtube.com/v/AgQTBKcg2D4&color1=0xb1b1b1&color2=0xcfcfcf&hl=en& feature=player_embedded&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="425" height="344"></embed></object>

    Last edited by Mike, K8LH; - 28th July 2009 at 02:34.

  6. #6
    Join Date
    Feb 2008
    Location
    Michigan, USA
    Posts
    231


    Did you find this post helpful? Yes | No

    Default Is it possible to combine MIBAM and DT_INTS?

    Is it possible to combine MIBAM and DT_INTS?

    I am attempting to use MIBAM in a program where I have been using SPWM_INT. I understand that MIBAM need exclusive use of TMR1 and HI priority INTs. The thought was to see if I could still use DT_INTs to handle low priority ints for a pulse time capture and a HSER routine. I still have to play with it to decide if I can make them function with the probability of MIBAM cutting off the other two, but that is a separate problem.
    The issue that I see is that DT_INTS has:
    "DEFINE INTHAND INT_ENTRY_H"
    "DEFINE INTLHAND INT_ENTRY_L"
    and MIBAM has:
    "DEFINE INTHAND doBAM"
    Can the doBAM section be added to the INT_ENTRY_h section and eliminate the "DEFINE INTHAND doBAM"? If so how?

    Until I understand this better, I'm going to continue with the SPWM_INT version of the code and work on finishing the rest of the code that way. I may not need the advantages of MIBAM, but it sure is a cool routine. Even with my limited coding ability, DT's routines have multiplied my capabilities tremendously. Thank you Darrel and all the others that have put in the effort and patience and have been willing to share your wisdom so freely.


    Bo

  7. #7
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Since MIBAM uses a completely ASM interrupt handler, it would be much easier to make it compatible with DT_INTS instead of the other way around.

    However, if you're just measuring pulse widths ... the use of Timer3 and a CCP module (Capture mode) might be more applicable. And you won't need Interrupts for that, although they can still be used with LOW Priority. It'll be more accurate with the CCP too.

    The HIGH Priority interrupts will affect the "Timing" of LOW Priority ints.
    The resolution of a Pulse measurement using LP ints (without CCP) will always suffer.
    <br>
    DT

  8. #8
    Join Date
    Jul 2003
    Location
    Colorado Springs
    Posts
    4,959


    Did you find this post helpful? Yes | No

    Default

    Mike,

    Did you get anywhere with this ...
    I can not figure out a way to Charlieplex MIBAM.

    I need to finish this thing, and think I'm going to have to toss Charlie out the window (from a very tall building).

    DT

    Quote Originally Posted by Mike, K8LH View Post
    ...
    More later... I'm trying to see if I can upgrade a very old 12F683 Charlieplexed 5-pin 20-LED project from 64 levels per LED to 256 levels per LED using MIBAM. Wish me luck...

    Kind regards, Mike

    <object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/AgQTBKcg2D4&color1=0xb1b1b1&color2=0xcfcfcf&hl=en& feature=player_embedded&fs=1"></param><param name="allowFullScreen" value="true"></param><param name="allowScriptAccess" value="always"></param><embed src="http://www.youtube.com/v/AgQTBKcg2D4&color1=0xb1b1b1&color2=0xcfcfcf&hl=en& feature=player_embedded&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="425" height="344"></embed></object>

    DT

  9. #9
    Join Date
    Aug 2005
    Location
    Michigan, USA
    Posts
    224


    Did you find this post helpful? Yes | No

    Default

    What "thing" are you talking about tossing Darrel?

    I wrote and simulated the ISR driver for a 5-pin 20-LED demo' but that's as far as I got. You want to see that part Sir?

    <added>

    Tell me what chip you want it for and I'll give you a simple 5-pin (rb0..rb4) 20-LED sample Saturday that you can breadboard and play with.

    Mike
    Last edited by Mike, K8LH; - 13th November 2009 at 00:48.

Similar Threads

  1. decoding quadrature encoders
    By ice in forum mel PIC BASIC Pro
    Replies: 93
    Last Post: - 28th February 2017, 09:02
  2. Cordic trig assembly code for PIC18f
    By ScaleRobotics in forum mel PIC BASIC Pro
    Replies: 54
    Last Post: - 8th September 2015, 05:36
  3. AT/PS2 Keybord - PIC Interface?
    By Kamikaze47 in forum Code Examples
    Replies: 73
    Last Post: - 9th August 2009, 16:10
  4. MIBAM - (Mirror Imaged Bit Angle Modulation)
    By Darrel Taylor in forum Code Examples
    Replies: 2
    Last Post: - 15th February 2009, 16:02
  5. Bit Angle Modulation
    By BH_epuk in forum mel PIC BASIC Pro
    Replies: 1
    Last Post: - 18th November 2008, 07:01

Members who have read this thread : 3

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts