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