Go Down

Topic: Digital Outputs (Read 2138 times) previous topic - next topic

Hi there, my project requires me to generate a 1.2 MHz signal.  Is there a way to make a digital pin write out at that speed?

CrossRoads

Yes. Search  for "Direct Port Manipulation".
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

I'm using the code in the arduino page, as well as some code that someone else posted online, and I'm not getting any signal being outputted. 

Just to test, my code is:
int ledPin = 4;                 // LED connected to digital pin 13

void setup()
{
  pinMode(ledPin, OUTPUT);      // sets the digital pin as output
}

void loop()
{
  PORTD = B00010000;
  PORTD = B00000000;
}

I'm using a Mega2560, could that be why it's not working?  Or am I doing something completely wrong?

Graynomad

#3
Mar 09, 2012, 12:54 am Last Edit: Mar 09, 2012, 01:00 am by Graynomad Reason: 1
How do you know it's not working, you can't see that frequency.

Also it may not be possible to get exactly 1.2Mhz and using a loop the waveform won't be a 50% duty cycle. Does any of that matter to your application?

EDIT: On the 2560 pin 4 is PORTG bit 5. You are using PORTD bit 4 which is not connected to anything on the Mega.

Try PORTB bit 7 for the LED, but as I said at that speed you need instruments to see it.

Try

Code: [Select]

void setup()
{
  pinMode(13, OUTPUT);      // sets the digital pin as output
}

void loop()
{
  PORTB = B10000000;
  PORTB = B00000000;
  delay (1000);
}


To verify that the LED is blinking.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Awesome, thank you for all the help.  Duty cycle is not an issue, but I do need to be close to 1.2 MHz.  I will fiddle with it.  Is there any way I can induce a delay that's less than a microsecond to control the frequency?

I was using an oscilloscope to see the signal.

Graynomad

Quote
Is there any way I can induce a delay that's less than a microsecond to control the frequency?

At that frequency the only real option is to insert dummy instructions, for example

asm ("nop");

will insert a 62.5nS delay. So write the tightest loop you can (as above) then if it's too fast start adding NOPs. If it's too slow get an ARM :)

Bottom line is that your basic resolution is 62.5nS so you should be able to tune it to within that res.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

CrossRoads

I think you need
DDRB = B1111111; // for all 8 bits to be outputs on Port B

following this info:
Code: [Select]

/* Each port is controlled by three registers, which are also defined variables in the arduino language.
The DDR register, determines whether the pin is an INPUT or OUTPUT. (DDRx)
The PORT register controls whether the pin is HIGH or LOW.          (PORTx)
The PIN register reads the state of INPUT pins set to input with pinMode(). (PINx)

DDR and PORT registers may be both written to, and read.
PIN registers correspond to the state of inputs and may only be read.

Example
DDRD = B11111110;  // sets Arduino pins 1 to 7 as outputs, pin 0 as input
DDRD = DDRD | B11111100;  // this is safer as it sets pins 2 to 7 as outputs
                  // without changing the value of pins 0 & 1, which are RX & TX

PORTD = B10101000; // sets digital pins 7,5,3 HIGH
*/[code]
[/code]
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.


How do you know it's not working, you can't see that frequency.

Also it may not be possible to get exactly 1.2Mhz and using a loop the waveform won't be a 50% duty cycle. Does any of that matter to your application?

EDIT: On the 2560 pin 4 is PORTG bit 5. You are using PORTD bit 4 which is not connected to anything on the Mega.

Try PORTB bit 7 for the LED, but as I said at that speed you need instruments to see it.

Try

Code: [Select]

void setup()
{
  pinMode(13, OUTPUT);      // sets the digital pin as output
}

void loop()
{
  PORTB = B10000000;
  PORTB = B00000000;
  delay (1000);
}


To verify that the LED is blinking.

______
Rob


Awesome, that totally works!  Using your sample code, and getting rid of that delay(1000), I was able to get a 934kHz signal, which is usable.

My only question now is that this is done by NOT using ANY other commands.  I'm using my microcontroller to read in a signal and perform some calculations on it and output it to a serial, as well as creating this signal.  Once I introduce those other commands, it's going to slow this signal down right?  Is there any way around that, or is my knowledge of arduino code just too basic?

Graynomad

Quote
it's going to slow this signal down right?

Yep.

Quote
Is there any way around that

Not if you need the signal to be constant.

If you are happy to read and calculate outside this loop then you can do that, but if this extra work has to be done constantly you are in trouble.

In this case you are better using one of the hardware timers, they can be "set and forget" with no further software intervention and they are not affected by any code running.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

Magician

Quote
Is there any way around that, or is my knowledge of arduino code just too basic?

The way around is to program one of the timers to drive PWM pin.
http://arduino.cc/playground/Code/PwmFrequency

floresta

#10
Mar 13, 2012, 03:59 am Last Edit: Mar 13, 2012, 04:06 am by floresta Reason: 1
This can be done by using the 'Toggle OC2A on Compare Match' mode along with the 'Clear Timer on Compare Match (CTC)' mode of Timer/Counter2.  This sounds complicacted, but as I recall it only takes about four instructions, all in setup().

The disadvantage of this technique is that the output pin is fixed by the hardware.  It also ties up a timer (timer 2 in this case).

The advantage is that this technique does not use any interrupts and does not use any processor time once it is set up.

I have some (untested) code around here somewhere, I'll try to dig it up tomorrow and test it out.


Don

Thank you, I appreciate it.

I'll also try modifying the PWM timer.

floresta

Quote
I have some (untested) code around here somewhere, I'll try to dig it up tomorrow and test it out.


Here it is - it worked for me!

Code: [Select]
//  Arduino - v1.0
//  Arduino Duemilanove

#define outpin 11                                  // OC2A pin - cannot be changed
#define clockFrequency 16000000                    // Arduino clock frequency
#define outputFrequency 1000000                    // desired output frequency
#define prescaleFactor 1                           // the only choice to get 1 MHz out

void setup ()
{
  TCCR2B = B00000001;                               // set CS20, clear CS21, CS22 and WGM22
  TCCR2A = B01000010;                               // set COM2A0 and WGM21, clear COM2A1 and WGM20

  OCR2A  = ((float)clockFrequency / (outputFrequency * 2 * prescaleFactor)) -1;  // timer data

  pinMode(outpin, OUTPUT);                          // turn on output pin   

// the timer runs with no additional code and no use of interrupts
// do whatever else you want to do here

}

void loop ()
{
// do whatever else you want to do here
}




Don

That's perfect, exactly what I need, thank you!

Just to confirm, is that pin the same pin on the Mega2560?  Or is there a place I can check which pin is what online?

floresta

#14
Mar 13, 2012, 10:09 pm Last Edit: Mar 13, 2012, 10:26 pm by floresta Reason: 1
Quote
Just to confirm, is that pin the same pin on the Mega2560?  Or is there a place I can check which pin is what online?


I wouldn't count on it.  As far as I can tell from the hodgepodge that they call a 'Schematic' diagram it is Pin 10.

By the way you should be able to turn the square wave off with TCCR2B = B00000000;  and back on again with another TCCR2B = B00000001;.


Don

Edit:  It looks like it is indeed digital pin 10 according to:  http://arduino.cc/en/Hacking/PinMapping2560

Go Up