• AVR Freaks

PWM in Software

Author
ToothPick
New Member
  • Total Posts : 2
  • Reward points : 0
  • Joined: 2009/04/23 13:09:11
  • Location: 0
  • Status: offline
2009/04/23 13:21:58 (permalink)
0

PWM in Software

Ok, first post, complete newb so forgive any incorrect assumptions. I should point out I haven't programmed or used PICs before but I am a very experienced coder and have some rudimentary electronics knowledge.

I'm not looking for a specific solution but a general "yes that should be possible" from the gurus. I have a long road but would like to know I'm taking the right turn at the start ;)

I want to dim about 12 strings of LEDs individually via PWM. I would like to do this from a slave PIC via software. A master PIC will be used to interface to a user via touch screen and this will communicate (I2C?) to the slave PIC an array of values representing the PWM cycles for each LED. It is acceptable that during the communication the PWM cycle is broken.

I don't want to use SMD PIC chips.

As the slave PIC is purely dedicated to running a software loop controlling the PWM cycles is this feasible - and if so - what PIC chip would be best suited to this?

Open to better solutions :)
#1

7 Replies Related Threads

    dchisholm
    Super Member
    • Total Posts : 3805
    • Reward points : 0
    • Joined: 2003/12/14 15:30:12
    • Location: St Louis Mo
    • Status: offline
    RE: PWM in Software 2009/04/23 15:05:42 (permalink)
    0
    If the "slave" microcontroller (uC) is simply creating a set of PWM streams, you don't have much to worry about.  Software PWM - even multiple PWM channels - isn't a very complicated algorithm.  A single PWM signal can be created with around a couple dozen Assembly instructions; each additional signal adds about half that much code.  Even if you get elegant and stagger the start of each channel's waveform - to reduce power supply peak loading, or reduce RFI/EMI, or reduce visible flicker effects - the processor loading doesn't increase very much.  The entire PWM code will probably be significantly less than 1K instruction words.

    (The Fermi-style estimate of processor loading looks like this:
    • A basic PWM period of 20 mSec (50 Hz repetition rate) is a reasonable assumption for imperceptible flicker.
    • Allowing for 100 different, linearly spaced, brightness levels will require tending each of the PWM channels every (20 mSec/100) = 200 uSec
    • Tending each channel requires execution of 10 Assembly instructions, and there are 12 channels, so the processor must execute at least (12 * 10) = 120 instructions every 200 usec
    • The minimum instruction rate is (120 instructions/200 uSec) = 600K instructions/sec
    • It takes 4 clock cycles to execute one instruction.  A clock rate of 2.5 MHz or higher is, therefore, adequate for this task.
    • (You can investigate other combinations of number of PWM channels, PWM rate, PWM resolution, and processor speed by playing with the numbers in the above analysis.)
    • Many recent PIC designs have completely self-contained internal clock oscillators of 4 MHz or 8 MHz.  )
    That estimate only applies to creating some PWM waveforms.  I presume these would be used to modulate one of the switch-mode LED controller chips that are proliferating monthly.  If, on the other hand, the PIC is used as the control element in a full SMPS for each LED channel (such as Olin Lathrop did for his "KnurdLight" project (see also Here) then more pins and more processing cycles will be needed.

    Since PWM operation may be suspended while receiving and processing communication information, additional processor speed probably isn't necessary to support the communication function.  But depending on how elaborate your communications protocol is, the communication code may be larger than the PWM code. Things like assembling and parsing a message, error checking, handshaking between transmitter and receiver, etc can eat up code faster than you realize.  (Look, for instance, at the size of the LIN code in some of Microchip's application notes for an example of what it takes to implement a rather robust communications protocol.  Simpler protocols, of course, take significantly less code.)

    In addition to codespace and execution speed, the processor you choose must have enough pins to interface to your LED's, plus some pins for communications, and maybe a few for user interface (indicators, buttons, etc).  It will need a couple of timers - I think all of the 16Fxxx and 18Fxxx parts have enough for your needs.  It will definitely help if the PIC has either a USART (eg, for RS-232 style asynch comm) or SSP module (for I2C/SPI comm).  From there, you can compare processors with the Product Selector Guide.  One of the 20-pin processors from the 16F690 family may be adequate for your needs, but something from the 16F88x family can provide room to grow.  One of the 18F2xxx or 18F4xxx series might let you program in "C".

    Dale

    p.s. - Use the Forum's "Search" feature with "gooligum peatman" as the seed to find the advice I regularly dispense to new users.
    #2
    ToothPick
    New Member
    • Total Posts : 2
    • Reward points : 0
    • Joined: 2009/04/23 13:09:11
    • Location: 0
    • Status: offline
    RE: PWM in Software 2009/04/24 08:36:11 (permalink)
    0
    Great reply many thanks!

    Lots to get my teeth into there :)
    #3
    MBedder
    Circuit breaker
    • Total Posts : 6773
    • Reward points : 0
    • Joined: 2008/05/30 11:24:01
    • Location: Zelenograd, Russia
    • Status: offline
    RE: PWM in Software 2009/04/24 12:52:13 (permalink)
    0
    ORIGINAL: dchisholm

    ...A single PWM signal can be created with around a couple dozen Assembly instructions; each additional signal adds about half that much code  ....The entire PWM code will probably be significantly less than 1K instruction words.
    • Tending each channel requires execution of 10 Assembly instructions, and there are 12 channels, so the processor must execute at least (12 * 10) = 120 instructions every 200 usec
    • The minimum instruction rate is (120 instructions/200 uSec) = 600K instructions/sec
    ......

    No. No. No. No.

    A wisely written software multichannel PWM code for 8-bit PICs requires 2 (two) instructions per channel iteration. Here is a complete 12-channel 8-bit PWM code example which executes in the timer ISR:


    #define pwmport1 PORTA
    #define pwmport2 PORTB

            CBLOCK 0x20
    preset0  ; PWM0..11 required duty cycle values
    ; .......  (repeat same as above for other PWM channels)
    preset11 ;
     
    pwm0     ; PWM0..11 counters
    ; .......  (repeat same as above for other PWM channels)
    pwm11    ;
     
    pwmcntr  ; Common PWM cycle number counter
            ENDC
     
    timer_isr:
            decfsz  pwm0       ; Decrement PWMx counter
            bcf     pwmport1,0 ; Clear corresponding port bit if PWMx = 0
    ; ........                   (repeat same as above for other PWM channels)
            decfsz  pwm11
            bcf     pwmport2,3

            decfsz  pwmcntr    ; Decrement PWM cycle number counter
            retfie             ; Return if not all 256 PWM states passed
     
            movf    preset0,W  ; Otherwise reinitialize channels - copy PWMx preset values to PWMx counters for next PWM cycle
            movwf   pwm0
    ; ........                   (repeat same as above for other PWM channels)
            movf    preset11,W
            movwf   pwm11
         
            comf    pwmport1   ; Preset PWM port outputs to "1"s to start a new PWM cycle
            comf    pwmport2   ;

            retfie


    The complete code takes less than 3 machine cycles per channel including the interrupt latency and reinitialization overhead (once per 256 interrupts). Thus the PWM carrier frequency could be as fast as 5 MHz/12/256/3 =~ 550 Hz at 100% CPU load (assuming the MCU is running at 20 MHz/5 MIPS). With 50 Hz 12-ch 8-bit PWM the CPU load will be less than 10%.
    #4
    E_Blue
    Super Member
    • Total Posts : 352
    • Reward points : 0
    • Joined: 2009/03/02 18:02:17
    • Location: 0
    • Status: offline
    RE: PWM in Software 2009/04/25 07:53:30 (permalink)
    0
    I don't understand this part

    ORIGINAL: MBedder


    timer_isr:
           decfsz  pwm0       ; Decrement PWMx counter
           bcf     pwmport1,0 ; Clear corresponding port bit if PWMx = 0



    If pwm0 = 250 or any value except 1 when the first isr occur the port goes to zero.
    I think the code must be


    timer_isr:
           decfsnz  pwm0       ; Decrement PWMx counter and if not pwm = 0 then jump
           bcf     pwmport1,0 ; Clear corresponding port bit if PWMx = 0

    But decfsnz instruction is not available on 16F family. :\





    Electric Blue
    #5
    K8LH
    Super Member
    • Total Posts : 1872
    • Reward points : 0
    • Joined: 2004/03/26 05:12:34
    • Location: Michigan, USA
    • Status: offline
    RE: PWM in Software 2009/04/25 10:54:14 (permalink)
    0
    I understand what forum member MBedder is trying to say unfortunately his example is flawed.  If he had used decfsnz instructions in his example then LEDs with duty cycle settings of 0 would be lighted continuously.  There are also better soft PWM "counter method" algorithms that don't require extra processing "overhead" at end-of-period and don't require nearly so many variables.

    Here's a somewhat more efficient soft PWM "counter method" ISR driver example for 16-bit core devices;
    ;
    ;  soft PWM "counter method", 8 bits, 256 levels, 16 bit core
    ;
    ;  void tmr2_isr()
    ;  { pir1.CCP1IF = 0;                   //
    ;    if(dcy >= Led[0]) shadow.0 = 0;    //
    ;    if(dcy >= Led[1]) shadow.1 = 0;    //
    ;    if(dcy >= Led[2]) shadow.2 = 0;    //
    ;    if(dcy >= Led[3]) shadow.3 = 0;    //
    ;    if(dcy >= Led[4]) shadow.4 = 0;    //
    ;    if(dcy >= Led[5]) shadow.5 = 0;    //
    ;    if(dcy >= Led[6]) shadow.6 = 0;    //
    ;    if(dcy >= Led[7]) shadow.7 = 0;    //
    ;    latb = shadow;                     //
    ;    dcy++                              // inc duty cycle, 0..255
    ;    if(dcy == 0)                       // if end-of-period
    ;      shadow = 255;                    //
    ;  }
    ;
    ;  23 words, 26 cycles (isochronous), 10 bytes RAM
    ;
             bcf     PIR1,TMR2IF     ; clear timer 2 interrupt flag   
             movf    dcy,W           ; duty cycle counter, 0..255     
             cpfsgt  Led+0           ;                                
             bcf     Shadow,0        ;                                
             cpfsgt  Led+1           ;                                
             bcf     Shadow,1        ;                                
             cpfsgt  Led+2           ;                                
             bcf     Shadow,2        ;                                
             cpfsgt  Led+3           ;                                
             bcf     Shadow,3        ;                                
             cpfsgt  Led+4           ;                                
             bcf     Shadow,4        ;                                
             cpfsgt  Led+5           ;                                
             bcf     Shadow,5        ;                                
             cpfsgt  Led+6           ;                                
             bcf     Shadow,6        ;                                
             cpfsgt  Led+7           ;                                
             bcf     Shadow,8        ;                                
             movff   Shadow,LATB     ;                                
             infsnz  dcy,F           ; end-of-period? no, skip, else  
             setf    Shadow          ; reset shadow                   
             retfie  FAST            ;                                

    And here's a relatively efficient soft PWM "counter method" ISR driver example for 14-bit core devices;
    ;
    ;  soft PWM "counter method", 8 bits, 256 levels, 14 bit core
    ;
             org     0x0004
             radix   dec
    isr
             movwf   w_isr           ; save WREG                       |B?
             swapf   STATUS,W        ; don't change STATUS bits        |B?
             movwf   s_isr           ; save STATUS                     |B?
             clrf    STATUS          ; bank 0                          |B0
             movf    PCLATH,W        ;                                 |B0
             movwf   p_isr           ; save PCLATH                     |B0
             clrf    PCLATH          ; page 0                          |B0
             bcf     PIR1,TMR2IF     ; clear timer 2 interrupt flag    |B0
    ;
    ;  do 1/256th step, 26 cycles (isochronous)
    ;
             movf    led+0,W         ; led0 pwm value, 0..255          |B0
             subwf   dcy,W           ; C = led0 => dcy                 |B0
             rrf     shadow,F        ;                                 |B0
             movf    led+1,W         ; led1 pwm value, 0..255          |B0
             subwf   dcy,W           ; C = led1 => dcy                 |B0
             rrf     shadow,F        ;                                 |B0
             movf    led+2,W         ; led2 pwm value, 0..255          |B0
             subwf   dcy,W           ; C = led2 => dcy                 |B0
             rrf     shadow,F        ;                                 |B0
             movf    led+3,W         ; led3 pwm value, 0..255          |B0
             subwf   dcy,W           ; C = led3 => dcy                 |B0
             rrf     shadow,F        ;                                 |B0
             movf    led+4,W         ; led4 pwm value, 0..255          |B0
             subwf   dcy,W           ; C = led4 => dcy                 |B0
             rrf     shadow,F        ;                                 |B0
             movf    led+5,W         ; led5 pwm value, 0..255          |B0
             subwf   dcy,W           ; C = led5 => dcy                 |B0
             rrf     shadow,F        ;                                 |B0
             movf    led+6,W         ; led6 pwm value, 0..255          |B0
             subwf   dcy,W           ; C = led6 => dcy                 |B0
             rrf     shadow,F        ;                                 |B0
             movf    led+7,W         ; led7 pwm value, 0..255          |B0
             subwf   dcy,W           ; C = led7 => dcy                 |B0
             rrf     shadow,W        ; W = led 'step' output           |B0
    ;       xorlw   0xFF            ; for active hi outputs           |B0
             movwf   PORTB           ; update LEDs                     |B0
             incf    dcy,F           ; dcy++, 0..255                   |B0
    ;
    ;  restore context, 8 cycles (isochronous)
    ;
             movf    p_isr,W         ;                                 |B0
             movwf   PCLATH          ; restore PCLATH                  |B0
             swapf   s_isr,W         ;                                 |B0
             movwf   STATUS          ; restore STATUS                  |B?
             swapf   w_isr,f         ; don't screw up STATUS           |B?
             swapf   w_isr,W         ; restore WREG                    |B?
             retfie                  ; return from interrupt           |B?
     
    post edited by K8LH - 2009/04/26 08:10:34
    #6
    MBedder
    Circuit breaker
    • Total Posts : 6773
    • Reward points : 0
    • Joined: 2008/05/30 11:24:01
    • Location: Zelenograd, Russia
    • Status: offline
    RE: PWM in Software 2009/04/27 22:31:21 (permalink)
    0
    In my example above the PWM values should be presented in inverted format (0 = LED lighted continuously, 0xFF = LED off) - sorry for not mentioning it before. I've taken the code from a disassembled C working soft pwm example - those who can read Russian may see it on one of the Russian technical forums here).

    The K8LH PIC18 example above is pretty similar to the AVR soft PWM code which has been discussed on the same Russian forum years ago. There was a long PIC vs AVR discussion based on a comparison of the PIC and AVR implementations of the 24-channel soft PWM. Both PIC and AVR versions of the code were optimized up to 2.71 machine cycles per each of the 24 PWM channels including all ISR and related overhead.
    #7
    K8LH
    Super Member
    • Total Posts : 1872
    • Reward points : 0
    • Joined: 2004/03/26 05:12:34
    • Location: Michigan, USA
    • Status: offline
    RE: PWM in Software 2009/04/29 11:49:27 (permalink)
    0
    MBedder,

    I don't see how the code snippet you posted or the code in the link you posted can possibly generate a 256 step/led PWM output.  Perhaps someone else could take a look and tell me if I'm missing something?

    Mike, K8LH
    #8
    Jump to:
    © 2019 APG vNext Commercial Version 4.5