PWM and Register Counter?
UNO
I couldn’t find any examples of bit banging PWM without delay and this is what I have so far.
I’m reading Two frequencies and outputting a PWM signal. My first attempt used Interrupts to read an optical disk of a motor, software to read the control frequency, and analogWrite() for the PWM.
Motor optical disk is 0 to 1.6kHz. I’m including zero in the frequency range since the input will be zero when nothing is moving and the code has to handle that state.
The control frequency was low (0 to 60Hz) and I had issues with resolution. I decided to use a higher frequency range(0 to 3.2kHz). The higher frequencies were taxing and there were conflicts between the software and the interrupts.
I implemented Register Counters to read the control frequency. That was accurate. Using the registers for frequency counting disabled millis. I simply used the register timer for timing needed for the interrupt counter. Both counters are very accurate with no conflicts while testing on the bench with a function generator. Moving on to attaching the motor I found the PWM output is also disabled when using registers for counting. This is where I am now, trying to Bit bang without a delay.
I’m able to use Timer2 for the timing, but it increments each 1mS. I set the PWM time to 200mS but isn’t fast enough for smooth motor at slow speeds, needs to be uS. I left the PID portion out of this example, but it ran the motor at a crawl before I tried to use register counting. I tried to use Timer1 for timing, but when I added code to that section it broke it.
I have tried a few variations such as the attached example where the HIGH time follows the input frequency 1 to 200Hz, and the LOW time is the remaining time up to 200mS. I tried HIGH for 1mS and LOW time 200mS to 1mS, and then HIGH time 1mS to 200mS with LOW time for 1mS. And a hybrid of those two to get almost 700 points, but the motor is too jerky at slower speeds.
Does anyone know how to generate a PWM output while using the registers for frequency counting?
Is there a way to tap into the Timer1 without effecting the frequency counting, or is there a different reqister counter that frees up a register for PWM, or is there another way of bit banging PWM than what I am doing?
/*
*
* Use pin 2 for optical interrupt int. 0 (Motor optical disk)input pullUp
* Use pin 6 for input for register counter(128Kppm)
*
*/
const int optPin = 2; //Input Pin Interrupt for optical disk
int mover = 8;//Pin 8 pwm to motor
float frqIn = 0;//Input Frequency
float frq2 = 0;//Frequency of optical disk
float PWMtime = 0;
float driveCal = 0;//CALIBRATION*******************
float optical_speed = 0;//actual speed= frequency of the optical disk
#define driveCal (frqIn)//CALIBRATION********************************200 MAX
#define delayHi (PWMtime==driveCal)//PWM on time
unsigned long flag2=0;//iterates each optical disk pulse
int flag3=0;//flag for delayHi
float flag4=0;//1 second timer for serial output
float flag5=0;//mS timer for bit banging PWM
// set sampling period here (in milliseconds):
unsigned int samplingPeriod = 1000;
// Timer 1 overflows counter
volatile unsigned long overflow1;
void init_Timer1() {
overflow1 = 0; // reset overflow counter
// Set control registers (see datasheet)
TCCR1A = 0; // normal mode of operation
TCCR1B = bit(CS12) | bit(CS11) | bit(CS10); // use external clock source
TCNT1 = 0; // set current timer value to 0
TIMSK1 = bit(TOIE1); // enable interrupt on overflow
}
ISR(TIMER1_OVF_vect) {
overflow1++; // increment overflow counter
}
// Timer 2 overflows counter
volatile unsigned int overflow2;
void init_Timer2() {
overflow2 = 0; // reset overflow counter
GTCCR = bit(PSRASY); // reset prescalers
// Set control registers (see datasheet)
TCCR2A = bit(WGM21); // CTC mode
TCCR2B = bit(CS22) | bit(CS20); // prescaler set to 1/128, "ticks" at 125 kHz
OCR2A = 124; // counts from 0 to 124, then fire interrupt and reset;
TCNT2 = 0; // set current timer value to 0
TIMSK2 = bit(OCIE2A); // enable interrupt
}
// interrupt happens at each 125 counts / 125 kHz = 0.001 seconds = 1 ms
ISR(TIMER2_COMPA_vect) {
++PWMtime;//increments up to 1000 then resets to 0
++flag5;//Increment 200mS counter each 1mS timer for bit banging output
if (delayHi)// PWM Hi delay When PWMtime matches input frequency value
flag3 = PWMtime;// Capture PWMtime value when it matches input value
if (++overflow2 < samplingPeriod) // add an overflow and check if it's ready
return; // still sampling
unsigned long totalSamples = (overflow1 << 16) + TCNT1;
float freqHz = totalSamples * 1000.0 / samplingPeriod;
frqIn = freqHz;
frq2 = (flag2);//set frq2 to number of holes in 1000 ms and convert to 1 second
optical_speed = ((frq2/64)*60);//set optical_speed to number of revs per second based on 64 hole disk convert to Hz
flag2=0;//Resets flag each 1 second for next Optical disk pulse count
++flag4;//Increment second counter each 1S for serial output
// reset timers
TCNT1 = 0; overflow1 = 0;
TCNT2 = 0; overflow2 = 0;
PWMtime = 0;
}
void setup()
{
Serial.begin(115200);//enable serial monitor
pinMode(optPin, INPUT_PULLUP); //Declared Pin 2 as Input
pinMode(8, OUTPUT);//
attachInterrupt(0, N2, RISING);//pin3 Optical disk
// Disable Timer0; millis() will no longer work
TCCR0A = 0;//Pin 6
TCCR0B = 0;//Pin 5 Timer 1 input
// start timer 1 (count frequency)
init_Timer1();
init_Timer2();
}
void loop()
{
//Bit Banging PWM
if (flag5 <= flag3)// PWM Hi delay
{
digitalWrite(mover,HIGH);//
}
else
{
digitalWrite(mover,LOW);//
}
if (flag5 >= 200)//Set bit banging Time here
{
flag5 = 0;//Reset bit banging timer
}
if (flag4 == 1)//1 second
{
Serial.print(" Optical Disk is ");// CALIBRATION**********
Serial.print(frq2);//CALIBRATION***************************
Serial.print(" driveCal is ");//CALIBRATION*******************
Serial.println(driveCal);//CALIBRATION***********************
flag4 = 0;//reset second counter
}
}//end of void loop
void N2()
{
flag2++;//increment flag for Optical disk Routine
}