ADC to PWM program glitch

Discussion in 'Embedded Systems and Microcontrollers' started by LaurenceR, Dec 17, 2015.

  1. LaurenceR

    Thread Starter Member

    Feb 7, 2013
    97
    2
    Attached is the code. I am inputting two ADC channels to a pwm in order to have a variable frequency and duty cycle. The program works except there are these occasional stops for a brief moment. On channel feed OCR1C for the top value and the other adc feeds OCR1A and B for the compare value. I am using both A and B pwm outputs for no particular reason. Could use just one. I suspect it gets hung up slightly when updating or inputting from the TCNT1 register to ADCL and ADCH. Any help would be greatly appreciated.
    Thanks in advance
    Code (ASM):
    1.  
    2. .include "tn26def.inc"
    3.  
    4. .def Ra=r17
    5.  
    6. .def Rb=r18
    7.  
    8. .def Rc=r19
    9.  
    10. .def A=r16
    11.  
    12. .def I=r20
    13.  
    14. .def N=r22
    15.  
    16. .cseg
    17.  
    18. .org 000
    19.  
    20. rjmp Reset
    21.  
    22. Reset:
    23.  
    24.  
    25.  
    26. ;Initial I/O
    27.  
    28. LDI Ra, 0b00000000
    29.  
    30. OUT DDRA, Ra ;Set port A pin 7 (PA4) to input
    31.  
    32. LDI Rb, 0b11111111
    33.  
    34. OUT DDRB, Rb ;Set port B to all output
    35.  
    36. ;Initial PWM
    37.  
    38. LDI A, 0b01010011
    39.  
    40. OUT TCCR1A, A ;toggle OC1A and OC1B, reset to $00 OCR1A,B, compare OCRx.
    41.  
    42. LDI I, 0b00001100
    43.  
    44. OUT TCCR1B, I ;CK/16384
    45.  
    46. sei
    47.  
    48. Main:
    49.  
    50. ;ADC_Init Dwell
    51.  
    52. ldi Rc,0b00100000
    53.  
    54. out ADMUX, Rc
    55.  
    56. ldi Ra, 0b10011011
    57.  
    58. out ADCSR,Ra
    59.  
    60. sbi ADCSR,ADSC
    61.  
    62. Wait:
    63.  
    64. sbis ADCSR,ADIF
    65.  
    66. rjmp Wait
    67.  
    68. NOP
    69.  
    70. NOP
    71.  
    72. IN I,ADCL
    73.  
    74. IN N,ADCH
    75.  
    76. OUT OCR1A, N ;portB PB0, PB1 connect to PB1 pin
    77.  
    78. OUT OCR1B, N ;portB PB3, PB2 connect to PB3 pin
    79.  
    80. ;ADC_Init Rate
    81.  
    82. ldi Rc,0b00100001
    83.  
    84. out ADMUX, Rc
    85.  
    86. ldi Ra, 0b10011011
    87.  
    88. out ADCSR,Ra
    89.  
    90. sbi ADCSR,ADSC
    91.  
    92. Wait1:
    93.  
    94. sbis ADCSR,ADIF
    95.  
    96. rjmp Wait1
    97.  
    98. IN I,ADCL
    99.  
    100. IN Ra, ADCH
    101.  
    102. OUT OCR1C, Ra
    103.  
    104. rjmp Main
    105.  
    Mod edit: added code tags
     
    Last edited by a moderator: Dec 17, 2015
  2. LaurenceR

    Thread Starter Member

    Feb 7, 2013
    97
    2
    Made a mistake here. Meant to say could be hanging up when updating from ADCH to OCR1C. I am not sure from what I have read if OCR1C is the top value that determines the rate or if TCNT1 is the max value that set the frequency. My assumption is that OCR1C is the top value the counter counts to setting the frequency, OCR1A and B are the compare values that determine duty cycle.
     
  3. joeyd999

    AAC Fanatic!

    Jun 6, 2011
    2,672
    2,698
    I don't know AVR, so this is a random stab:

    First, you don't mention what a "brief moment" is. This makes it difficult to surmise reasons for the problem.

    But, many MCU's double-buffer the input to the PWM duty cycle register, but not to the timer that controls the frequency. Therefore, changing duty cycle tends to be glitchless, but modifying the timer period register (I suppose there is one in the AVR) resets the timer, causing a glitch.

    Could this be you problem?
     
  4. sailorjoe

    Member

    Jun 4, 2013
    361
    63
    Try rearranging your code so you sample the ADC's immediately AFTER setting your PWM registers. Then, while the registers are counting down, the ADC's will be doing their thing. They should finish before the timers do, so when they do, your new values will be ready to load without delay. It's a bit counterintuitive, but it does work IF your ADCs are faster than your counters.
     
  5. LaurenceR

    Thread Starter Member

    Feb 7, 2013
    97
    2
    Thanks both for your comments. The "brief moment" is approximately an extra half second. I notice the OFF time is what gets effected. The ON time remains consistent. Maybe it's important to note that these glitches occur after about every 10 to 15 ON pulses without changing any of the ADC inputs. I'll also try rearranging the code as it seems there is an issue here as to when it should be updating.
    Still not 100% clear on the difference between TCNT1 and OCR1C. I'm programming OCR1C as the top value to adjust frequency.
    Thanks again for your help.
     
  6. nickelflipper

    Active Member

    Jun 2, 2010
    280
    35
    Just to be clear about the duty cycle and frequency, at least as it pertains to an ATTiny13a, it will probably be a little different register setup for the ATTiny26. Notes from working code for setting up the type of PWM and the frequency:
    Code (Text):
    1. TCCR0A = B'00100011'         'Clear OCR0B on compare match, fast pwm on ocb
    2. TCCR0B = B'00000010'         'PRESCALE 1/8, 9.6 MHz CK fuse, equals 4.5KHz??? PWM
    And the duty cycle:
    Code (Text):
    1. #define PWM_LED OCR0B
     
  7. nickelflipper

    Active Member

    Jun 2, 2010
    280
    35
    Ooops! Here is my setup for a ATTiny25. So I concur that preloading the OCR1C register sets the frequency.

    Code (Text):
    1. #define PWM_LED OCR1B
    2. dir PortB.4 out
    3. TCCR1 = B'00000010'         'PRESCALE 1/2, 8 MHz CK fuse, equals 16.5KHz PWM
    4. OCR1C = 210         'With PCK 1/2, GETS 20khZ
    5. GTCCR = B'01100000'         'enable PWM1B
     
  8. atferrari

    AAC Fanatic!

    Jan 6, 2004
    2,644
    759
    No chances to simulate that?
     
  9. nickelflipper

    Active Member

    Jun 2, 2010
    280
    35
    @atferrari

    If you are talking to me, then unfortunately no. Can't find the file, I fear it is stuck on a old laptop that no longer has a working screen. Too much hassle to try and find it. I got the snippet from a forum post that I did.
     
  10. Picbuster

    Member

    Dec 2, 2013
    373
    50
    Are you using interrupts or are they enabled?
    Smother in an interrupt could be a reason for that brief moment.
     
  11. LaurenceR

    Thread Starter Member

    Feb 7, 2013
    97
    2
    Thank you for all your help everyone. I think there may be a problem with interrupts. I am setting up an interrupt but not really using it. I am triggering uploading the ADC results by checking for a flag. I might be triggering an interrupt but there is no ISR written. Every time I try to initialize the stack it shows an error. Not sure if this is ok to do or not. If you trigger an interrupt and don't have an ISR is that ok or could that be causing delays?
     
  12. Picbuster

    Member

    Dec 2, 2013
    373
    50
    Think how interrupt works the swing the lot up the stack and wheel it back when finished.
    That's why pwm thingies should be handled in an ISR and never in a loop living somewhere in the main.
     
  13. LaurenceR

    Thread Starter Member

    Feb 7, 2013
    97
    2
    I'll have to try getting rid of the loops and using an interrupt and ISR.
     
  14. Picbuster

    Member

    Dec 2, 2013
    373
    50
    I was as in a hurry made some spelling mistakes in previous text.
    however;
    in the interrupt on timer overflow do

    Repeat_freq++; // add up
    If (Repeat_freq>999) // hit the 1000 times or different if you want to.
    { Repeat_freq=0;) // this is your block
    // during this block a high is wanted for n x Repeat_freq/1000. Your density =1000 steps for 100% on
    // now you set high for Wanted_Pulse_With. Assume 100% should indicate 10V. result 10/1000 => 1/100 volt per pulse.
    // But remember a deviation occurs is when you end up with a decimal in the calculation hence a partial pulse is not possible.
    If (Repeat_freq<Wanted_Pulse_With)
    {
    Pin_out=1; // set high on NOT reached Wanted_Pulse_With
    }
    else
    {
    Pin-out=0; // set low on reached Wanted_Pulse_With
    }

    Good luck.
     
  15. LaurenceR

    Thread Starter Member

    Feb 7, 2013
    97
    2
    You'll have to pardon me as I do all my programming in assembly and am not good with C. I get that your suggesting I use an interrupt and no loos in "main". I don't quite understand the "Repeat_freq>999)// hit the 1000 times". I'll try writing an ISR and eliminate any loops in Man.
    Thanks.
     
  16. Picbuster

    Member

    Dec 2, 2013
    373
    50
    in the interrupt you make a counter.
    count till a value lets say 999 and then reset is to 0
    during that time you test separately ( during that time you want to keep output high)
    when you count till lets say 500 and keeping the pin high you have a pwm duty of 50%. ( I called this Wanted_Pulse_With)
    when to hit the Wanted_Pulse_With then set pin low.
    simple.
     
  17. LaurenceR

    Thread Starter Member

    Feb 7, 2013
    97
    2
    Thanks, I see what you are doing. That would make it short and to the point. However I am trying to load the values for "Top" value (OCR1C) and the duty cycle value (OCR!A) in from two separate ADC conversions. This has all been helpful, think I have some ideas I will try to do this.
    Appreciate everyone's help.
    Just chipping away.
     
  18. LaurenceR

    Thread Starter Member

    Feb 7, 2013
    97
    2
    Below is a copy of my cleaned up program that appears to be working great. No glitches and extremely smooth on the scope through all the duty cycle changes. Learned a lot from this experience.
    Thanks.
    Any comments are still welcome. Always learning.




    * _151229_Variable_Duty_cycle.asm
    * Created: 01/04/2015 12:11:35 AM
    */

    ;Variable frequency and duty cycle
    .include "tn26def.inc"
    .def ADCa=r16
    .def ADCb=r17
    .def ADCc=r18
    .def ADCd=r19
    .def PWa=r20
    .def PWb=r21
    .def PWc=r22
    .def PWd=r23
    .def PWe=r24
    .def TEMP=r25
    .cseg
    .org 000

    rjmp Reset
    Reset:

    ;Initial I/O
    LDI ADCa, 0b00000000 ;make port A all inputs
    OUT DDRA, ADCa
    LDI ADCb, 0b11111111 ;make port B all outputs
    OUT DDRB, ADCb
    ;Initial PWM
    LDI PWa, 0b10000010 ;COM1A0,(clear on compare OC1A output), PWM1A(enable)
    OUT TCCR1A, PWa
    LDI PWb,0b00000011 ;CTC1 (clear on compare), CS13&12(prescale/4)
    OUT TCCR1B, PWb
    LDI PWe, 0b11111111 ;Set Top Frequency to max count
    OUT OCR1C, PWe

    ;Initial ADC
    LDI ADCa, 0b00100000 ;left adjust. ADC0 enabled
    OUT ADMUX, ADCa
    LDI ADCb, 0b10100011 ;ADC enable, free running, prescale/8
    OUT ADCSR, ADCb
    sbi ADCSR, ADSC ;Start conversion
    NOP
    NOP
    Main:
    In ADCb, ADCL
    In ADCc, ADCH
    OUT OCR1A, ADCc
    CheckFlag:
    sbis ADCSR, ADIF
    rjmp CheckFlag
    Nop
    ldi ADCa, ADCH
    ldi ADCb, OCR1A
    cp ADCa, ADCb
    brne Main
    rjmp Main
     
Loading...