SOS: HELP PLEASE : GENERATING SEQUENCE AT 2MHZ

Good Day everyone,

I am relatively new to the arduino and getting a hang of it.

I actually want to generate a sequence that is {0,0,0,1,1,1,0,0,0,0,1,1,0,0,1,0,1,0,1,0,1,1,0,0,1,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,1,0}

at 2 MHz.

I went through the reference and was able to use delayMicroseconds and digitalWrite but the problem I am facing is that it is not being genrated at 2 MHz. the frequency sepatatin between two adjacent pulses is variable as well as not more than 250 KHz at any place.
the code I am using is:

int i;

int Myarray[43] = {0,0,0,1,1,1,0,0,0,0,1,1,0,0,1,0,1,0,1,0,1,1,0,0,1,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,1,0};
// the setup function runs once when you press reset or power the board
void setup() {

pinMode(13, OUTPUT);

}

// the loop function runs over and over again forever
void loop() {
for (i=0;i<100;i=i+1){
digitalWrite(13, LOW); //preceded by zeros
delayMicroseconds(1);}

for (i=0;i<43;i=i+1){
digitalWrite(13, Myarray*); //the required bit sequence*

  • delayMicroseconds(1);}*

}
Can anyone point out the error and help me out?
I would be highly grateful
Thanks in advance :slight_smile: :slight_smile:

To generate output at anywhere near 2MHz, you will NOT be using digitalRead() OR ANY kind of delay.

Direct Port Manipulation is the search term, and you need to determine how many nop calls to make, to get the frequency you want. You’ll need to learn how to call the assembler function, nop, from C++ code.

Not only direct port manipulation, but also no for-loops.
Will have to be a series of 43 port manipuations to get it fast enough

PORTB = PORTB | (Myarray[0] & 0b00000001); // D8 = 1 if array = 1, otherwise 0
PORTB = PORTB | (Myarray[1] & 0b00000001);
PORTB = PORTB | (Myarray[2] & 0b00000001);
:
:
PORTB = PORTB | (Myarray[40] & 0b00000001);
PORTB = PORTB | (Myarray[41] & 0b00000001);
PORTB = PORTB | (Myarray[42] & 0b00000001);

Hmm, have to think, will that actually clear a bit after it is high?

thanks a lot for the help. will look into it and try.

Hmm, have to think, will that actually clear a bit after it is high?

No, to make sure that the port bit is clear you need to use something like the following:

PORTB &= ~(1<<bit);

or

PORTB &= (0xFE); //clears bit 0

Rather than waste space in an array, I would pack bits into an unsigned variable and shift them out, for example for an 8-bit sequence (untested!)

   unsigned char bit, b = 0x31;

  //unroll loop over the 8 bits to send

   bit = b&1;
   if(bit) PORTB |= 1;
   else   PORTB &= ~(1);
   bit = b&2;
   if(bit) PORTB |= 1<<2;
   else   PORTB &= ~(1<<2);
   ...
   bit = b&0x80;
   if(bit) PORTB |= 0x80;
   else   PORTB &= ~(0x80);

Not sure if this will output bits at 2 MHz, though.

bit = b&1;
   if(bit) PORTB |= 1;
   else   PORTB &= ~(1);
   bit = b&2;
   if(bit) PORTB |= 1<<2;
   else   PORTB &= ~(1<<2);

To keep bit banging on pin 8 (PORTB0) the output mask should not be shifted.

 bit = b&1;
   if(bit) PORTB |= 1;
   else   PORTB &= ~(1);
   bit = b&2;
   if(bit) PORTB |= 1;
   else   PORTB &= ~(1);

Good point, thanks.

You can use SPI.transfer to output at 2 MHz, with SPI clock divisor set to 8, and send out 6 bytes that way to toggle the MOSI line:

SPI.transfer(Myarray[0]); // bit 0-7
SPI.transfer(Myarray[1]); // 8-15
SPI.transfer(Myarray[2]); // 16-23
SPI.transfer(Myarray[3]); // 24-31
SPI.transfer(Myarray[4]); // 32-39
SPI.transfer(Myarray[5]); // 40-47, with 4 don't care bits

You can use SPI.transfer to output at 2 MHz, with SPI clock divisor set to 8, and send out 6 bytes that way to toggle the MOSI line:

What is at the receiving end? Can it handle the breaks between the SPI bytes?

I think dlloyd has done some work on multibyte transfers with SPI and removing the breaks between bytes to achieve continuous data, but I can't find it right now.

EDIT: Here's the linkhttps://forum.arduino.cc/index.php?topic=404471.0

I blasted out 45 bytes at 8 MHz in a project using this method, timing out the in code:

spdr = Mydata[0]; nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = Mydata[1]; nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = Mydata[2]; nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
:
:
spdr = Mydata[42]; nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = Mydata[43]; nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;
spdr = Mydata[44]; nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;

Also had to disable interrupts during the transfer to keep the millis() / micros() interrupts from messing up the flow.
45 bytes that way takes just over 47.8uS to complete, just over a uS/byte, verified with high speed oscilloscope and logic analyzer.

CrossRoads:
Hmm, have to think, will that actually clear a bit after it is high?

You could initialize the bit in PORTB and then write a 1 to PINB when it's time to toggle the output. If the bit was initialized to 0 you would use 0001001000101011111110101011111011111111101 to get the desired sequence: 0001110000110010101011001101010010101010110.
OP:
What should the line be doing before the sequence starts?
Should the sequence repeat?
If so, can there be time between the sequences? If so, how long?
If the sequence ever ends, what should the line do after the end?

Thank You jremington, CrossRoads, cattledog, PaulS and johnwasser for your replies. I am going though the links you pointed to and will work on them

@ john wasser

What should the line be doing before the sequence starts?
Should the sequence repeat?
If so, can there be time between the sequences? If so, how long?
If the sequence ever ends, what should the line do after the end?

There is a continuous stream of zeros(0) before the sequence starts.
the sequence repeats after every 100 zeros or whatever is feasible. the line is always active. it is like a beacon signal it sends after every few secs.
there is no constraint on time between the sequences. it can be millisecs also or a few seconds also.
the sequence never ends, I mean, it is a fixed sequence but it repeats forever. the line always is active.
either it outputs LOW or the sequence.

Thanks

any help would be much appreciated

In theory this will do the job on Pin 13 of an Arduino UNO running at 16 MHz:

// On the Arduino UNO, Pin 13 is Port B bit 5  (0x20)
// Writing 0x20 to the PINB register will toggle Pin 13

#define nop  __asm__("nop\n\t")

void setup() {
  pinMode(13, OUTPUT);
  digitalWrite(13, 0);  // Initial line state
}

// Adjust the number of nop's to get the timing right.
void loop() {
  noInterrupts();  // Turn off interrupts
/*0*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*0*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*0*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*1*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*0*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*0*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
/*1*/ nop; nop; nop; nop; nop; nop; nop; nop; // Do nothing at 2 MHz
/*0*/ PINB = 0x20; nop; nop; nop; nop; nop; nop; nop; // Toggle Pin 13 at 2 MHz
  interrupts(); // Turn on interrupts
  delay(5);
}

johnwasser:
In theory this will do the job on Pin 13 of an Arduino UNO running at 16 MHz:

probably the first PINB = 0x20 will take two cycles and will become in assembly

ldi   r24, 0x20  // load 0x20 in register 24 (1 cycle)
out   0x09, r24 // memory 0x09 gets content of register 24 -> PIND = 0x20 (1 cycle)

Then the compiler is probably smart and will remember that r24 has been loaded with the 0x20 value so the next PIND = 0x20 will be translated into out  0x09, r24 directly.

so long story short, i would remove a nop from the first PIND action to really be at 2Mhz on that one

J-M-L:
probably the first PINB = 0x20 will take two cycles and will become in assembly

Since everything up to that point is nop, the first write taking two cycles won't make a difference. :slight_smile:

johnwasser:
Since everything up to that point is nop, the first write taking two cycles won't make a difference. :slight_smile:

smart answer :slight_smile: