ADC to PWM program glitch

Thread Starter

LaurenceR

Joined Feb 7, 2013
107
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:
.include "tn26def.inc"

.def Ra=r17

.def Rb=r18

.def Rc=r19

.def A=r16

.def I=r20

.def N=r22

.cseg

.org 000

rjmp Reset

Reset:



;Initial I/O

LDI Ra, 0b00000000

OUT DDRA, Ra ;Set port A pin 7 (PA4) to input

LDI Rb, 0b11111111

OUT DDRB, Rb ;Set port B to all output

;Initial PWM

LDI A, 0b01010011

OUT TCCR1A, A ;toggle OC1A and OC1B, reset to $00 OCR1A,B, compare OCRx.

LDI I, 0b00001100

OUT TCCR1B, I ;CK/16384

sei

Main:

;ADC_Init Dwell

ldi Rc,0b00100000

out ADMUX, Rc

ldi Ra, 0b10011011

out ADCSR,Ra

sbi ADCSR,ADSC

Wait:

sbis ADCSR,ADIF

rjmp Wait

NOP

NOP

IN I,ADCL

IN N,ADCH

OUT OCR1A, N ;portB PB0, PB1 connect to PB1 pin

OUT OCR1B, N ;portB PB3, PB2 connect to PB3 pin

;ADC_Init Rate

ldi Rc,0b00100001

out ADMUX, Rc

ldi Ra, 0b10011011

out ADCSR,Ra

sbi ADCSR,ADSC

Wait1:

sbis ADCSR,ADIF

rjmp Wait1

IN I,ADCL

IN Ra, ADCH

OUT OCR1C, Ra

rjmp Main
Mod edit: added code tags
 
Last edited by a moderator:

Thread Starter

LaurenceR

Joined Feb 7, 2013
107
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.
 

joeyd999

Joined Jun 6, 2011
5,236
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?
 

sailorjoe

Joined Jun 4, 2013
364
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.
 

Thread Starter

LaurenceR

Joined Feb 7, 2013
107
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.
 
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:
TCCR0A = B'00100011'         'Clear OCR0B on compare match, fast pwm on ocb
TCCR0B = B'00000010'         'PRESCALE 1/8, 9.6 MHz CK fuse, equals 4.5KHz??? PWM
And the duty cycle:
Code:
#define PWM_LED OCR0B
 
Ooops! Here is my setup for a ATTiny25. So I concur that preloading the OCR1C register sets the frequency.

Code:
#define PWM_LED OCR1B
dir PortB.4 out
TCCR1 = B'00000010'         'PRESCALE 1/2, 8 MHz CK fuse, equals 16.5KHz PWM
OCR1C = 210         'With PCK 1/2, GETS 20khZ
GTCCR = B'01100000'         'enable PWM1B
 
@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.
 

Thread Starter

LaurenceR

Joined Feb 7, 2013
107
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?
 

Picbuster

Joined Dec 2, 2013
1,047
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.
 

Picbuster

Joined Dec 2, 2013
1,047
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.
 

Thread Starter

LaurenceR

Joined Feb 7, 2013
107
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.
 

Picbuster

Joined Dec 2, 2013
1,047
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.
 

Thread Starter

LaurenceR

Joined Feb 7, 2013
107
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.
 

Thread Starter

LaurenceR

Joined Feb 7, 2013
107
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
 
Top