Go Down

Topic: Creating Square Waves with Programmable Frequencies? (Read 2 times) previous topic - next topic

GoForSmoke


Appreciate your help.  How about like this:

Code: [Select]

const int trimPot = A0;            //voltage divider to adjust speedometer output
const int speedPin = 21;          //pin for new speed pulse train
int pulseState = LOW;             // state used to set the pulse
volatile unsigned long currentMicros = 0;   //to measure new pulse   
volatile unsigned long previousMicros= 0;   //to mark prior pulse 
unsigned long currentSpeed = 0;    //to measure incoming pulse
unsigned long previousSpeed = 0;   //to mark prior pulse
unsigned long interval = 1;        //original pulse length
float adjustPercent = 1; //adjustment to original pulse length

void setup()
{
pinMode(speedPin, OUTPUT);   
pinMode(13, OUTPUT);
attachInterrupt (3, iSr, CHANGE);
}

void loop()
{
   currentMicros = micros();  //get the current micros count
   adjustPercent = (analogRead(trimPot)/512);
   interval=interval*adjustPercent;
   if(currentMicros - previousMicros > interval) //check to see if it's time to change
    {
    previousMicros = currentMicros;   //keep track of the last time pulse changed
    if (pulseState == LOW) pulseState = HIGH; else pulseState = LOW;
    digitalWrite(13, pulseState); //show pulse train on built in LED
    digitalWrite(speedPin, pulseState);  //flip the state of the new sqaure wave
    }
}

void iSr()
{
  currentSpeed=micros();
  interval=(currentSpeed-previousSpeed);  //how long since last change
  previousSpeed=currentSpeed;
}




interval needs to be volatile and decide what it's really for.

pulseState should be type byte.

Code: [Select]
   adjustPercent = (analogRead(trimPot)/512);
   interval=interval*adjustPercent;


The above code may run many times before interval is up, the dial does not set frequency and the ISR interval change is another mystery, also there's better ways than float.

Are you wanting to set the frequency with a dial? And the ISR to read the frequency? Or control frequency at least partly through the ISR?

Is that variable interval supposed to be changed by the ISR (triggered by writes in the code after interval is up) and then changed/initialized next time through loop()?

C code ^ is bitwise xor.
bool A xor bool B returns 1 if A and B are different, 0 if the same.

You could try
pulseState ^= 1; // flips pulseState between 0 and 1
or if that doesn't compile:
pulseState = pulseState ^ 1; // flips pulseState between 0 and 1


Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

dc42

You can use timer 1 to generate the signal you want directly. Here is the code I use:

Code: [Select]

#include "arduino.h"

#if defined(__AVR_ATtiny85__)

// Output is on physical pin 6 (= PB1/MISO/OCR1A)

#else

// Output is on Arduino pin 9 (= PB1/OC1A)
const int ocr1aPin = 9;

#endif

// Set the frequency that we will get on pin OCR1A
void setFrequency(uint16_t freq)
{
 uint32_t requiredDivisor = (F_CPU/2)/(uint32_t)freq;

#if defined(__AVR_ATtiny85__)
 // Code for attiny85
 uint16_t prescalerVal = 1;
 uint8_t prescalerBits = 1;
 uint32_t maxVal = 256;
 while (requiredDivisor > maxVal)
 {
   ++prescalerBits;
   maxVal <<= 1;
 }
 
 uint8_t top = ((requiredDivisor + (prescalerVal/2))/prescalerVal) - 1;
 TCCR1 = (1 << CTC1) | prescalerBits;
 GTCCR = 0;
 OCR1A = top;
#else
 // Code for atmega328p
 uint16_t prescalerVal;
 uint8_t prescalerBits;
 if (requiredDivisor < 65536UL)
 {
   prescalerVal = 1;
   prescalerBits = 1;
 }
 else if (requiredDivisor < 8 * 65536UL)
 {
   prescalerVal = 8;
   prescalerBits = 2;
 }
 else if (requiredDivisor < 64 * 65536UL)
 {
   prescalerVal = 64;
   prescalerBits = 3;
 }
 else if (requiredDivisor < 256 * 65536UL)
 {
   prescalerVal = 256;
   prescalerBits = 4;
 }
 else
 {
   prescalerVal = 1024;
   prescalerBits = 5;
 }

 uint16_t top = ((requiredDivisor + (prescalerVal/2))/prescalerVal) - 1;
 TCCR1A = 0;
 TCCR1B = (1 << WGM12) | prescalerBits;
 TCCR1C = 0;
 OCR1A = (top & 0xFF);
#endif
}

// Turn the frequency on
void on()
{
#if defined(__AVR_ATtiny85__)
 TCNT1 = 0;
 TCCR1 |= (1 << COM1A0);
#else
 TCNT1H = 0;
 TCNT1L = 0;  
 TCCR1A |= (1 << COM1A0);
#endif
}

// Turn the frequency off and turn off the output
void off()
{
#if defined(__AVR_ATtiny85__)
 TCCR1 &= ~(1 << COM1A0);
#else
 TCCR1A &= ~(1 << COM1A0);
#endif
}

void setup()
{
#if defined(__AVR_ATtiny85__)
#else
 digitalWrite(ocr1aPin, LOW);
 pinMode(ocr1aPin, OUTPUT);
#endif
}


Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

ArduinoTom

Thanks again.  I am trying to measure the frequency of a square wave on interrupt 3, alter it slightly with the trim pot, and output the altered frequency on pin 20.

GoForSmoke

Per frequency generated you need an start micros and interval micros.

If you want to adjust one, change the interval but do it where the adjustment won't get made as many times as there are micros in the interval.

Nick Gammon on multitasking Arduinos:
1) http://gammon.com.au/blink
2) http://gammon.com.au/serial
3) http://gammon.com.au/interrupts

Go Up