Shop OBEX P1 Docs P2 Docs Learn Events
PWM — Parallax Forums

PWM

SailerManSailerMan Posts: 337
edited 2006-11-10 23:53 in General Discussion
Bean helped me with this code and I am not sure that I am using it Right.
It seems that no matter which channel I select, the same PWM happens on all 4 output pins. RB.0-RB.3
Thanks for any help.
Regards,
Eric

·I set in my program PWM_Values(0)=128
·I get a 50% Duty PWM on RB.0-RB.3


[noparse][[/noparse]edit] This Code Works Now... [noparse]:)[/noparse]
PWM_Count VAR BYTE
PWM_Values VAR BYTE (4)
 
 
INTERRUPT
 
ASM
                                  ' ** Setup variables
 MOV __PARAM1,#4                  ' (2) Number of bits per port
 MOV __PARAM2,#1                  ' (2) OR Mask for turning a bit ON
 MOV __PARAM3,#254                ' (2) AND Mask for turning a bit OFF
 INC PWM_Count                    ' (1) Adjust the PWM count 
                                  '     If the PWM value for each channel is
                                  '     less than PWM_Count then it should
                                  '     be high, othewise it should be low
 MOV __PARAM4,PWM_Count           ' (2) Make copy of PWM_Count in global memory
 MOV FSR,#PWM_Values              ' (2) Set FSR to address of 1st element of PWM values
:NEXT_BIT
  CJA IND,__PARAM4,@:MAKE_HIGH_B  ' (5/7) If this element is above PWM_Count
                                               ' Then jump to MAKE_HIGH_B
  AND RB,__PARAM3                 ' (2) Value is below or equal to PWM_Count so
                                  '      make it low.
  JMP @:DONE_BIT_B                ' (4) Jump over MAKE_HIGH code
:MAKE_HIGH_B
  OR RB,__PARAM2                  ' (2) Value is above PWM_Count so make it HIGH
  NOP                             ' (1) Waste time
  NOP                             ' (1) Waste time
:DONE_BIT_B
  STC                             ' (1) Need to shift a 1 into AND mask value
  RL __PARAM3                     ' (1) Shift AND mask value
  CLC                             ' (1) Need to shift a 0 into OR mask value
  RL __PARAM2                     ' (1) Shift OR mask value
  INC FSR                         ' (1) Point to next array element
 DJNZ __PARAM1,@:NEXT_BIT         ' (3/5) Repeat until done
ENDASM 
 

ReturnInt


Post Edited By Moderator (Bean (Hitt Consulting)) : 11/3/2006 1:13:50 PM GMT

Comments

  • BeanBean Posts: 8,129
    edited 2006-11-03 00:16
    Oops,
    Now I see it, I forgot to put "INC FSR" just before the "DJNZ __PARAM1,@:NEXT_BIT".
    That should fix it.

    Sorry about that...

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cheap used 4-digit LED display with driver IC·www.hc4led.com

    Low power SD Data Logger www.sddatalogger.com
    SX-Video Display Modules www.sxvm.com

    "People who are willing to trade their freedom for·security deserve neither and will lose both." Benjamin Franklin
    ·
  • SailerManSailerMan Posts: 337
    edited 2006-11-03 00:28
    Thanks... so much!!
  • BeanBean Posts: 8,129
    edited 2006-11-03 13:07
    You had asked about an SX/B version of the PWM code.
    Here it is. Now this is NOT·very effecient code, but it does show what's going on...

    PWM_Count VAR BYTE
    PWM_Values VAR BYTE (4) 
     
     
    INTERRUPT
     
      INC PWM_Count ' This will automatically wrap from 255 back to 0
     
      IF PWM_Values(0) > PWM_Count THEN
        RB.0 = 1
      ELSE
        RB.0 = 0
      ENDIF
     
      IF PWM_Values(1) > PWM_Count THEN
        RB.1 = 1
      ELSE
        RB.1 = 0
      ENDIF 
     
      IF PWM_Values(2) > PWM_Count THEN
        RB.2 = 1
      ELSE
        RB.2 = 0
      ENDIF 
     
      IF PWM_Values(3) > PWM_Count THEN
        RB.3 = 1
      ELSE
        RB.3 = 0
      ENDIF
     
    RETURNINT
    
    
    
    

    Bean.



    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cheap used 4-digit LED display with driver IC·www.hc4led.com

    Low power SD Data Logger www.sddatalogger.com
    SX-Video Display Modules www.sxvm.com

    "People who are willing to trade their freedom for·security deserve neither and will lose both." Benjamin Franklin
    ·
  • SailerManSailerMan Posts: 337
    edited 2006-11-03 16:07
    Looking at the SX/B code it looks so simple and the ASM looks so hard... Now I can compare this to the above ASM Code.

    It's a good way to learn!!

    Thanks for everything.
  • PJMontyPJMonty Posts: 983
    edited 2006-11-03 16:32
    SailorMan,

    One thing to keep in mind is that the assembly in the first post was machine generated by the SX/B compiler. Machine generated assembly code is harder for humans to read since it doesn't make human comprehension a priority. I have gone through with a plain old text editor and done a search and replace to convert the unfriendly variable names with human friendly versions. There are two things to notice here:

    1 - Just changing variables names to something with meaning makes assembly code easier to read.

    2 - ALWAYS use good variables names (such as "PwmCount") and not weak variable names (such as "Count" or "Cnt" or "C" to make the code easier to comprehend at a glance.

    Here's the easier to read version:

    INTERRUPT
     
    ASM
                                      ' ** Setup variables
     MOV NumBitsPerPort,#4            ' (2) Number of bits per port
     MOV BitOn_OrMask,#1              ' (2) OR Mask for turning a bit ON
     MOV BitOff_AndMask,#254          ' (2) AND Mask for turning a bit OFF
     INC PWM_Count                    ' (1) Adjust the PWM count 
                                      '     If the PWM value for each channel is
                                      '     less than PWM_Count then it should
                                      '     be high, othewise it should be low
     MOV PwmCount_Global,PWM_Count    ' (2) Make copy of PWM_Count in global memory
     MOV FSR,#PWM_Values              ' (2) Set FSR to address of 1st element of PWM values
    :NEXT_BIT
      CJA IND,PwmCount_Global,@:MAKE_HIGH_B  ' (5/7) If this element is above PWM_Count
                                             ' Then jump to MAKE_HIGH_B
      AND RB,BitOff_AndMask                  ' (2) Value is below or equal to PWM_Count so
                                      '      make it low.
      JMP @:DONE_BIT_B                ' (4) Jump over MAKE_HIGH code
    :MAKE_HIGH_B
      OR RB,BitOn_OrMask              ' (2) Value is above PWM_Count so make it HIGH
      NOP                             ' (1) Waste time
      NOP                             ' (1) Waste time
    :DONE_BIT_B
      STC                             ' (1) Need to shift a 1 into AND mask value
      RL BitOff_AndMask               ' (1) Shift AND mask value
      CLC                             ' (1) Need to shift a 0 into OR mask value
      RL BitOn_OrMask                 ' (1) Shift OR mask value
      INC FSR                         ' (1) Point to next array element
     DJNZ NumBitsPerPort,@:NEXT_BIT   ' (3/5) Repeat until done
    ENDASM
     
    ReturnInt
    




    BTW, you can make this even easier to read by copying it into the SX-Key IDE or any text editor that uses a non-proportional font. That will makes the comments line up correctly.
      Thanks, PeterM
  • SailerManSailerMan Posts: 337
    edited 2006-11-04 00:09
    Hey Thanks... [noparse]:)[/noparse] Definately helpful.
  • crgwbrcrgwbr Posts: 614
    edited 2006-11-09 14:34
    Sailerman, I would love to use this code in one of my own projects; may I? If so, to use it, I assign PWM_Values(1-4) a number 0-255, right? How many times per second would you recomend for the interupt?

    Thanks
    crgwbr

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    NerdMaster
    For
    Life
  • BeanBean Posts: 8,129
    edited 2006-11-09 15:08
    crgwbr,
    I would assume all code posted in the forums is public domain unless otherwise noted.

    It takes 256 interrupts to get 1 pulse of PWM. So you need to know how many pulses per second you want. Then just multiply that by 256 to get the interrupt rate.

    For example a LED needs to be updated at least 50 or 60 times a second so you don't see the flicker. So I would probably use 100 for the PWM update rate. That means the interrupt must occur 25,600 times a second. The faster the interrupt rate, the less processor time your main program gets, so don't over do it.

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cheap used 4-digit LED display with driver IC·www.hc4led.com

    Low power SD Data Logger www.sddatalogger.com
    SX-Video Display Modules www.sxvm.com

    "People who are willing to trade their freedom for·security deserve neither and will lose both." Benjamin Franklin
    ·
  • crgwbrcrgwbr Posts: 614
    edited 2006-11-09 16:13
    Thanks

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    NerdMaster
    For
    Life
  • SailerManSailerMan Posts: 337
    edited 2006-11-09 18:41
    I am using am Interupt·Prescaler of 2. I guess that equates to and interupt occuring every 10.24 us. If I go any slower, my motors make too much noise and it limits the motor Torque.. I guess I could do away·with the Prescaler, but I am·doing serial·coms and reading Encoders... It all seems to work well.
    ·

    I'm still learning about PWM.

    I've learn alot from Bean's code.

    Just incase you are using serial or time sensitive subroutines, you really need to remember to take your ISR time into account!!

    Thanks bean for answering the question. It's your code.·smile.gif



    Post Edited (SailerMan) : 11/9/2006 6:45:01 PM GMT
  • crgwbrcrgwbr Posts: 614
    edited 2006-11-09 19:47
    Wow, thats odd!· I'm also controlling two motors, reading encoders, and doing serial communication.· Sorry, but I'm totally new to using interupts; what do you mean by prescaler, or ISR time?

    Thanks


    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    NerdMaster
    For
    Life
  • SailerManSailerMan Posts: 337
    edited 2006-11-09 20:36
    I am totally new also. So I could be giving false information.

    But the prescaler divides the RTCC so an interupt will happen less frequently.

    ISR just stands for Interupt Service Routine.... And if it takes longer to run your ISR than the time between ISR calls (Real Time Clock Counter (RTCC) Ticks) there will be no time left over for the balance of your program. I divided my Interupt call with Setting Option to $80.

    I hope that made sense and I hope I am correct in this thinking.

    So I decided to only call the ISR as often as I needed to run my program and left as much processing power for the balance of the program which is where the Serial in/out takes place.
  • BeanBean Posts: 8,129
    edited 2006-11-09 21:40
    The easy way is to just use the "INTERRUPT rate" syntax.

    If you want your interrupt to occur 25600 times a second just do:

    INTERRUPT 25600
    ' Your code here
    RETURNINT

    The SX/B compiler will automatically enable interrupts and set the prescaler and the RETURNINT value for you.

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cheap used 4-digit LED display with driver IC·www.hc4led.com

    Low power SD Data Logger www.sddatalogger.com
    SX-Video Display Modules www.sxvm.com

    "People who are willing to trade their freedom for·security deserve neither and will lose both." Benjamin Franklin
    ·
  • PLJackPLJack Posts: 398
    edited 2006-11-10 22:45
    Bean (Hitt Consulting) said...
    The easy way is to just use the "INTERRUPT rate" syntax.

    If you want your interrupt to occur 25600 times a second just do:

    INTERRUPT 25600
    ' Your code here
    RETURNINT

    The SX/B compiler will automatically enable interrupts and set the prescaler and the RETURNINT value for you.

    Bean.

    What! How did I miss that.
    So to set an interrupt to 60 Hertz I would just type INTERRUPT 120?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    - - - PLJack - - -



    Perfection in design is not achieved when there is nothing left to add.
    It is achieved when there is nothing left to take away.

    Post Edited (PLJack) : 11/11/2006 12:14:35 AM GMT
  • BeanBean Posts: 8,129
    edited 2006-11-10 23:53
    The interrupt rate must be within the range obtainable by using the prescaler and RETURNINT value.
    So for 60 Hz the clock must be 60*65536 or less, so that is about 3.9MHz.
    Otherwise you will have to use a multiple and use a counter in the interrupt.

    Bean.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Cheap used 4-digit LED display with driver IC·www.hc4led.com

    Low power SD Data Logger www.sddatalogger.com
    SX-Video Display Modules www.sxvm.com

    "People who are willing to trade their freedom for·security deserve neither and will lose both." Benjamin Franklin
    ·
Sign In or Register to comment.