Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

100% Duty cycle is not completely 100% (see picture) #48

Open
sylvanoMTL opened this issue Jan 26, 2021 · 6 comments
Open

100% Duty cycle is not completely 100% (see picture) #48

sylvanoMTL opened this issue Jan 26, 2021 · 6 comments

Comments

@sylvanoMTL
Copy link

sylvanoMTL commented Jan 26, 2021

here are part of the code I am using:

#define FAN_PWM_PIN 9 //9 or 10 for UNO
#define DEFAULT_DUTY_CYCLE 20
float dutyCycle = DEFAULT_DUTY_CYCLE;
void setup(){
  Timer1.initialize(40);  // 40 us = 25 kHz
}

void loop()
{
    Timer1.pwm(FAN_PWM_PIN, (dutyCycle / 100) * 1023);
}

here is a picture of the PWM :

So far I have no idea why it is causing this issue. (not I can dela with that) 
@sylvanoMTL
Copy link
Author

IssueTimerOne100p100Duty

@PaulStoffregen
Copy link
Owner

Some hardware simply isn't capable of 100% duty cycle. Typically those boards give a very short pulse, less than 1 timer cycle.

Or it could be a software bug.

Which board are you using? I can't tell from your text and photo.

@sylvanoMTL
Copy link
Author

sylvanoMTL commented Jan 26, 2021

Here is the other code I run on my Arduino Uno (same hardware as before): source

#include <Arduino.h>

#define MIN_DUTY_CYCLE 0
#define MAX_DUTY_CYCLE 100
#define DEFAULT_DUTY_CYCLE 20
#define DELAY 100  //ms


//---  PWM control
const byte OC1A_PIN = 9;
const byte OC1B_PIN = 10;

const word PWM_FREQ_HZ = 25000; //Adjust this value to adjust the frequency
const word TCNT1_TOP = 16000000/(2*PWM_FREQ_HZ);
            
int dutyCycle=DEFAULT_DUTY_CYCLE;

//--- Forward declaration
void setPwmDuty(byte duty);

void setup() {
  Serial.begin(9600);
  pinMode(OC1A_PIN, OUTPUT);

  // Clear Timer1 control and count registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  // Set Timer1 configuration
  // COM1A(1:0) = 0b10   (Output A clear rising/set falling)
  // COM1B(1:0) = 0b00   (Output B normal operation)
  // WGM(13:10) = 0b1010 (Phase correct PWM)
  // ICNC1      = 0b0    (Input capture noise canceler disabled)
  // ICES1      = 0b0    (Input capture edge select disabled)
  // CS(12:10)  = 0b001  (Input clock select = clock/1)
  
  TCCR1A |= (1 << COM1A1) | (1 << WGM11);
  TCCR1B |= (1 << WGM13) | (1 << CS10);
  ICR1 = TCNT1_TOP;

  //The fan is by default operating at DEFAULT_DUTY_CYCLE (instructions are given in the loop)
  Serial.print("Fan at default duty cycle: ");Serial.println(DEFAULT_DUTY_CYCLE);
}


void loop() {
    setPwmDuty(100);
}

void setPwmDuty(byte duty) {
  if(duty>MAX_DUTY_CYCLE)
  {
    duty = MAX_DUTY_CYCLE;
  }
  if(duty<MIN_DUTY_CYCLE)
  {
    duty = MIN_DUTY_CYCLE;
  }
  OCR1A = (word) (duty*TCNT1_TOP)/100;
}

IssueTimerOne100p100Duty_directTimer

@SteveGuidi
Copy link

Some hardware simply isn't capable of 100% duty cycle. Typically those boards give a very short pulse, less than 1 timer cycle.

Or it could be a software bug.

Which board are you using? I can't tell from your text and photo.

While this isn't an issue in my project, I did observe it on an Arduino Nano clone board (Atmega328p).

@michalwoz
Copy link

@PaulStoffregen @sylvanoMTL

same here, 1kHz, Arduino Mega 2560

I observed that on oscilloscope. First I thought that it was due to my mapping 0-100% to 0-1023 values or some floating point calculations. But I thied simplest possible code to directly set values 1023, 1024 and 0;

0: pin is always LOW
1023 oscilloscope reports 99.9% duty cycle. There is obvious little glitch at low state
1024: pin is always HIGH. It works perfect, but I'm not sure whether I will not overflow anything using 1024?
Maybe there is small calculation issue in library?

  Timer3.initialize(1000);
  Timer3.pwm(3, 1023);
  Timer3.pwm(2, 1024);
image

@michalwoz
Copy link

michalwoz commented Apr 22, 2024

@PaulStoffregen what do you think about that?

   //****************************
    //  PWM outputs
    //****************************
    void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) {
	unsigned long dutyCycle = pwmPeriod;
	dutyCycle *= duty;
	dutyCycle >>= 10;
	if (pin == TIMER1_A_PIN) {
		FTM1_C0V = dutyCycle;
	} else if (pin == TIMER1_B_PIN) {
		FTM1_C1V = dutyCycle;
	}
    }
dutyCycle = pwmPeriod * duty
          = 1000 * 1023
          = 1023000
dutyCycle >>= 10
          = 1023000 >> 10
          = 998.828125 <--- I think this is issue here! This is 999,0234375 or 998.828125 :D
dutyCycle = pwmPeriod * duty
          = 1000 * 1024
          = 1024000
dutyCycle >>= 10
          = 1024000 >> 10
          = 1000

When we do the operation of ">>= 10" we divide by 2^10 so divide by 1024. I think we will never get 100% when we provide 1023 as max duty cycle.

Anyway I think that the code for AVR id good, just the documentation needs to be updated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants