Generating 4 PWM at 100hz?

Hi everyone.
I'm currently making a project for the school and i need to generate 4 PWM but i dont have idea how, i currently have a code with 2 pwm using the library TimerOne but idk if i can use the other timer with this library im using an Arduino Uno.
Can anyone give me a hand with this ?/
tysm in advance

#include <TimerOne.h>
const int analogPin = A0,inputPin = 0;
int valor,ID;
void setup()
{
pinMode(9,OUTPUT);
pinMode(10,OUTPUT);
pinMode(inputPin, INPUT);
Serial.begin(9600);
Timer1.initialize(10000); 
Timer1.pwm(9,0);       
Timer1.pwm(10,0);
}

void loop()

{  ID = digitalRead(inputPin); 
  
    if (ID == 1) {
        
        // Control de Velocidad (ADC)
        valor = analogRead(analogPin);  
        Serial.print("S1,S4");
        Serial.print('\n');
        Serial.print(valor);
        Timer1.pwm(9,valor);      
        Timer1.pwm(10,0);
      
     }
    
    else if(ID == 0) {
      
        valor = analogRead(analogPin);  
        Serial.print("S2,S3");
        Serial.print('\n');
        Serial.print(valor);
        Timer1.pwm(9,0);      
        Timer1.pwm(10,valor);
     }
    
}

Can you say something more about the PWM signals.
The worst case is that all four can have different and independent frequencies, phases and duty cycles (and even *amplitudes)
*with external components

Sure im currently trying to generate the 4 pwm because i need to feed the transistors of a H bridge with them and i already did it with just two pwm but the current and voltage i got was to low for the motor also i need to vary the duty cycle according to the potentiometer.
All of the PWM need to be at 100Hz
image

That circuit would damage the Arduino pins.

You also have to say what voltage the motor is being driven at and whether this exercise allows you to change the design of the H-Bridge and/or add additional components.

Hi Mauro,

welcome to the Arduino-Forum.
Well done to post your code as a code-section in your frist post.

100 Hz is a pretty "low" frequency

The reason are:
there are no current-limiting resistors between IO-pin and base of the transistors
if the power-supply of the PNP-transistors has a higher voltage than 5V the voltage on the base might go above 5V and then damage the IO-pin

If all you want to do is driving the motor clockwise / counterclockwise a modified circuit will need only a single PWM and two or even only one IO-pin for controlling the direction.

How about using something like a DRV8871-board ?

  • active brake
  • overcurrent-protection needs just two PWMs as input
    best regards Stefan

The motor works with 5v & 190mA

Hi Stefan
Im already aware of the base resistors of the transistors and IRL i have them placed and already calculated but i just didn't put them in the simulation in order to make it faster also i know there could be an external circuit in order to drive the motor but "using the transistors its necessary for the project" professor words.
tysm for taking your time to answer this thread

You are using for NPN-transistors. I doubt that this will work
because the emitter of an NPN-transistor should be directly connected to ground.
The Emitters of the upper transistors are connected to the motor which means they are connected to nearly +Vcc because the big voltage-drop is across the motor.

This is the reason why the upper transistors must be PNP

For driving the h-bridge with four IO-pins always
two IO-pins on level to make two of the transistors **NON-**conductant
and the two others conductant. Which pair of diagonal transistors it is determines the rotation-direction

When using PNP-NPN-transistors
two IO-pins have to have the opposite logic level.
NPN is conductant on HIGH, PNP is conductant on LOW

otherwise you would switch on two transistors that are on the same side which would be a shortcut from Vcc to gound and immidiatly destroy the transistors through a much too high current.

So simply creating four PWM-signals is not sufficient two of the PWM-signals have to be phase-shiftet 180 degrees against the other two ones.

It is not nescessary to create four PWMs.
You have to switch two out of four IO-pins to make diagonal path "A" or diagonal path "B" of the H-Bridge conductant. The PWM-signal of these two channels must be inverted to each other (which is the same as a phase-shift of 180 degree)

The basic approach to realise this is to use a timer-interrupt at a pretty high frequency and then using a counter-variable to switch ON/OFF the IO-pins
At period-start Switch ON
count up 1,2,3,4,5,.....
if that number is reached that represents the duty-cycle then switch the IO-pins OFF

This works good fr lower frequencies like your 100 Hz.
here is the code

// this demo-code belongs to the public domain
// this code demonstrates how to blink the  onboard-LED
// of an Arduino-Uno (connected to IO-pin 13)
// in a nonblocking way using timing based on function millis()
// and creating a pwm-signal  to drive a transistor-H-Bridge "in the backround"
// through setting up a timer-interrupt through configuring timer2

// A T T E N T I O N !    R E M A R K
// some other libraries that make use of timer2 may conflict with this

/* H-Nridge

PNP_A(4)                PNP_B(6)
     \                   /
      \                 /
       x-----Motor-----x      
      /                 \
     /                   \
NPN_A(5)                NPN_B(7)
########################################################


rotate counterclockwise
LOW-PNP_A(4)             HIGH-PNP_B(6)
     \                   
      \                 
       x---+-Motor-----x      
                        \ 
                         \ 
LOW-NPN_A(5)              HIGH-NPN_B(7)

#############################################


rotate clockwise
HIGH-PNP_A(4)             LOW-PNP_B(6)
                         /
                        /
       x-----Motor-+---x      
      /                  
     /                    
HIGH-NPN_A(5)             LOW-NPN_B(7)

*/



// start of macros dbg and dbgi
#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);
// usage: dbgi("2:my fixed text",myVariable,1000);
// myVariable can be any variable or expression that is defined in scope
// third parameter is the time in milliseconds that must pass by until the next time a
// Serial.print is executed
// end of macros dbg and dbgi

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__));
  Serial.print( F("  compiled ") );
  Serial.print(F(__DATE__));
  Serial.print( F(" ") );
  Serial.println(F(__TIME__));
}


boolean TimePeriodIsOver (unsigned long &periodStartTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - periodStartTime >= TimePeriod ) {
    periodStartTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

unsigned long MyTestTimer =  0;                   // variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}

// defining self-explaining constants makes it easier to follow
#define NPN_OFF LOW
#define NPN_ON  HIGH

#define PNP_OFF HIGH
#define PNP_ON  LOW

const byte pwmPinPNP_A = 4;
const byte pwmPinNPN_A = 5;
const byte pwmPinPNP_B = 6;
const byte pwmPinNPN_B = 7;

const unsigned long pwmBaseFrequency = 10000;
const unsigned long pulseFreq = 100;

volatile unsigned long pwmCount = 0;
volatile unsigned long periodCount  = pwmBaseFrequency / pulseFreq;
byte dutyCycle = 0;
volatile unsigned long pwmOnTimeCnt;

const byte Clock_Pin   = 9;

#define clockWise 100
#define CounterClockWise 200

volatile byte MotorDirection = CounterClockWise;


void setupTimerInterrupt(unsigned long ISR_call_frequency) {
  const byte Prescaler___8 = (1 << CS21);
  const byte Prescaler__32 = (1 << CS21) + (1 << CS20);
  const byte Prescaler__64 = (1 << CS22);
  const byte Prescaler_128 = (1 << CS22) + (1 << CS20);
  const byte Prescaler_256 = (1 << CS22) + (1 << CS21);
  const byte Prescaler1024 = (1 << CS22) + (1 << CS21) + (1 << CS20);

  const unsigned long CPU_Clock = 16000000;
  const byte toggleFactor = 1;

  unsigned long OCR2A_value;

  cli();//stop interrupts

  TCCR2A = 0;// set entire TCCR2A register to 0
  TCCR2B = 0;// same for TCCR2B
  TCNT2  = 0;//initialize counter value to 0

  TCCR2A |= (1 << WGM21); // turn on CTC mode
  TIMSK2 |= (1 << OCIE2A); // enable timer compare interrupt

  // the prescaler must be setup to a value that the calculation
  // of the value for OCR2A is below 256
  TCCR2B = Prescaler___8;
  OCR2A_value = (CPU_Clock / ( 8 * ISR_call_frequency * toggleFactor) )  - 1;
  dbg("1 setup: timer ", OCR2A_value);

  if (OCR2A_value > 256) {  // if value too big
    TCCR2B = Prescaler__32; // set higher prescaler
    OCR2A_value = (CPU_Clock / ( 32 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 32", OCR2A_value);
  }

  if (OCR2A_value > 256) { // if value too big
    TCCR2B = Prescaler__64;// set higher prescaler
    OCR2A_value = (CPU_Clock / ( 64 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 64", OCR2A_value);
  }

  if (OCR2A_value > 256) { // if value too big
    TCCR2B = Prescaler_128;// set higher prescaler
    OCR2A_value = (CPU_Clock / ( 128 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 128", OCR2A_value);
  }

  if (OCR2A_value > 256) {  // if value too big
    TCCR2B = Prescaler_256; // set higher prescaler
    OCR2A_value = (CPU_Clock / ( 256 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 256", OCR2A_value);
  }

  if (OCR2A_value > 256) {   // if value too big
    TCCR2B = Prescaler1024;  // set higher prescaler
    OCR2A_value = (CPU_Clock / ( 1024 * ISR_call_frequency * toggleFactor) )  - 1;
    dbg("setup: prescaler 1024", OCR2A_value);
  }

  OCR2A = OCR2A_value; // finally set the value of OCR2A

  sei();//allow interrupts
  dbg("setup: timer done", OCR2A_value);

  dbg("setup", periodCount);
}



void setup() {
  Serial.begin(115200);
  Serial.println( F("Setup-Start") );
  PrintFileNameDateTime();
  pinMode(pwmPinPNP_A, OUTPUT);
  pinMode(pwmPinNPN_A, OUTPUT);
  pinMode(pwmPinPNP_B, OUTPUT);
  pinMode(pwmPinNPN_B, OUTPUT);

  pinMode(Clock_Pin, OUTPUT);

  setupTimerInterrupt(pwmBaseFrequency);
  pwmCount = 0;
  SetDuty(1);
  Serial.print("clockwise PNP_B on IO-Pin ");
  Serial.println(pwmPinPNP_B);
  Serial.print("clockwise NPN_A on IO-Pin ");
  Serial.println(pwmPinNPN_A);

  Serial.println();
  Serial.print("counterclockwise PNP_A on IO-Pin ");
  Serial.println(pwmPinPNP_A);
  Serial.print("counterclockwise NPN_B on IO-Pin ");
  Serial.println(pwmPinNPN_B);
}


void createPWM() {
  pwmCount++;

  // for debugging purposes create a clock-signal
  //digitalWrite(Clock_Pin, !digitalRead(Clock_Pin) );

  if (pwmCount == periodCount) { // if new period starts
    pwmCount = 0; // reset counter
    //digitalWrite(Clock_Pin, !digitalRead(Clock_Pin) );
    // shut off transistors
    digitalWrite(pwmPinPNP_A, PNP_OFF); // switch PNP-A channel OFF through HIGH
    digitalWrite(pwmPinNPN_B, NPN_OFF); // switch NPN-B channel OFF through LOW

    digitalWrite(pwmPinPNP_B, PNP_OFF); // switch PNP-B channel OFF through HIGH
    digitalWrite(pwmPinNPN_A, NPN_OFF); // switch NPN-A channel OFF through LOW
  }

  if (pwmCount == 1) { // current-flow start

    if (MotorDirection == clockWise) {
      //digitalWrite(Clock_Pin, !digitalRead(Clock_Pin) );
      digitalWrite(Clock_Pin, HIGH);

      digitalWrite(pwmPinPNP_A, PNP_OFF); // switch PNP-B channel OFF through HIGH
      digitalWrite(pwmPinNPN_B, NPN_OFF); // switch NPN-A channel OFF through LOW

      digitalWrite(pwmPinPNP_B, PNP_ON); // switch PNP-A channel ON through LOW
      digitalWrite(pwmPinNPN_A, NPN_ON); // switch NPN-B channel ON through HIGH
    }


    if (MotorDirection == CounterClockWise) {
      //digitalWrite(Clock_Pin, !digitalRead(Clock_Pin) );

      digitalWrite(pwmPinPNP_B, PNP_OFF); // switch PNP-A channel OFF through HIGH
      digitalWrite(pwmPinNPN_A, NPN_OFF); // switch PNP-B channel OFF through LOW

      digitalWrite(pwmPinPNP_A, PNP_ON); // switch PNP-B channel ON through HIGH
      digitalWrite(pwmPinNPN_B, NPN_ON); // switch NPN-A channel ON through LOW
    }

  }

  if (pwmCount == pwmOnTimeCnt) { // if ON-time is reached
    digitalWrite(Clock_Pin, !digitalRead(Clock_Pin) );

    digitalWrite(pwmPinPNP_A, PNP_OFF); // switch PNP-A channel OFF through HIGH
    digitalWrite(pwmPinNPN_B, NPN_OFF); // switch NPN-B channel OFF through LOW

    digitalWrite(pwmPinPNP_B, PNP_OFF); // switch PNP-B channel OFF through HIGH
    digitalWrite(pwmPinNPN_A, NPN_OFF); // switch NPN-A channel OFF through LOW
  }
}


void SetDuty(byte p_duty) {

  if (p_duty > 100) {
    p_duty = 100;
  }
  pwmOnTimeCnt = (periodCount * p_duty) / 100;

  //dbg("3:", pwmOnTimeCnt);
}


ISR(TIMER2_COMPA_vect) {
  createPWM();
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 200);

  if (TimePeriodIsOver(MyTestTimer, 100) ) {
    // if 100 milliseconds have passed by
    dutyCycle++;
    if (dutyCycle > 100) {
      dutyCycle = 0;
    }
    SetDuty(dutyCycle);
  }
}

best regards Stefan

Hi, @mauro_28
Your circuit in post#1, will not allow full current to flow through the motor because you are using NPN transistors in the top of the H-Bridge.
The top NPN will never be able to turn fully ON because you have the motor in the Emitter circuit influencing the base current.
724e70f15bd7fd204611c94d24c7862f4f58f21a
Can you please tell us your electronics, programming, arduino, hardware experience?

Tom... :smiley: :+1: :coffee: :australia:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.