I'm having a hard time finding what I suspect is a pretty silly mistake in my code. I'm using an ATmega328.
What I need is to generate a PWM signal whose duty cycle varies not between 0% and 100%, but between two other values. This is taken into account by setting the macros duty_max
and duty_min
. What I'm doing in my code is reading the ADC, which has a resolution of 1024 values, and mapping the value read to the range of accepted values:
#define duty_max 239
#define duty_min 50
[...]
//Reading the ADC
ADMUX = 0b01000101;
ADCSRA |= (1<<ADSC);
while(!(ADCSRA & (1<<ADIF)));
ADCSRA |= (1<<ADIF);
//Setting the variable 'duty' to the value read by the ADC
cli();
duty = ADC;
sei();
//Mapping to the range of accepted values
duty = round((duty*(duty_max-duty_min))/1023 + duty_min);
//Generating the PWM signal
OCR0B = duty;
//Timer 0
TCCR0A = 0b00100011;
TCCR0B = 0b00000001;
The problem I'm having is that the signal is not working as it should. When sweeping the ADC reading from 0 to 1023 and measuring the output signal with an oscilloscope, I would like the signal to go from the minimum duty cycle stablished to the maximum. However, it goes from 0 to 40% (approximately) FOUR times. Namely, when the value of the ADC increases, at some time the duty cycle stops increasing and returns to 0, and then keeps increasing, until it gets to 0... Four times within the whole ADC range.
If I replace the mapping formula with duty = round(duty/4);
it works alright, but the duty cycle falls out of the accepted range (as it goes from 0% to 100%).
Why is this happening? The mistake must be in the mapping formula, but I can't find it and I have already been dealing with it for a while now.
I have been able to almost replicate your symptoms by truncating to 16 bits the result of
(duty*(duty_max-duty_min))/1023
When duty is 347, the output value drops from 114 (which is around 47% of maximum) to 50, which is the minimum duty cycle. This happens three times and a bit over the input range (not four).
To prevent this from happening you might try and, in this instance, divide both numbers by a common divisor: the duty difference, 189, is divisible by 3, and so is 1023. Try writing it as
duty = round((duty*63)/341) + duty_min;
and see whether this changes anything.