(8)Arduino IDE+Due/Uno->Audio: Bit Manipulation

Following the revelation discussed in the previous post, this meant I had to figure out the maximum baud rate which can be used with the code required to process, send, and receive data.  After discussing with a few individuals I decided that direct bit manipulation of the memory allocated to variables would be the most efficient.

This is because when the compiler converts high level code into in assembly code bit operations such as AND and NOT can be done in a single line.  Contrary to direct bit manipulation things such as if, while, and for require an extensive amount of code to execute.

For example.  Turning R1=128 into R1=512 would only require one line of code.

ADD R1, R1, R2 where R2 is a bitmask.

However a simple for loop to add 128 to itself until 512 is reached could look something like the following.

for(i=0; i<n ; i++){
x=x+x;
}
ADD R1, R1, R1 ;add R1 to itself
ADD R2, R2, -1 ;decrement counter
BRz R2 #R2 contains a counter

But how do I encode data I read into such a form in which it can be easily transmitted and understood?  Recalling that the data read from an Analog In port on an Arduino meausres a value from 0~1023 (10 bit) and that serial transmission transmits 1 byte words, I can turn a 10 bit word into two 5 bit words with a 3 bit header which marks where the data came from.

bool interruptCtr = true;

void setup(){
Serial.begin(5200000);
Serial2.begin(5200000);
}unsigned int in_A=0;
unsigned int in_B=0;
uint8_t A_1=0;
uint8_t A_2=0;
uint8_t B_1=0;
uint8_t B_2=0;

void loop(){
if( interruptCtr++ >= 1 ){//check whether to interrupt
interruptCtr = 1;//update interrupt value

//parse 0-1023 (10 bit) into 2 five bit words
//serial transmit transmits with 8 bit words
//split each 10 bit word into 2 bytes with 3 bit headers
//if from chA mark in[0-2] with 1
//if from chB mark in[0-2] with 0
//use integer comparison to determine the ch.
//if in>=2^7+2^6+2^5 (224) then it must be from chA
//chA has 111xxxxx or 110xxxxx in[2]=1 first 5 bit in[2]=0 second 5 bit
//chB has 001xxxxx or 000xxxxx

in_A=analogRead(7);
//save 1st five bits in A1/B1
A_1=(in_A>>5)|224;//xxxxxyyyyy->00000xxxxx
//00000xxxxx|0011100000->00111xxxxx
Serial2.write(A_1);

A_2=(in_A&31)|192;//xxxxxyyyyy&0000011111->00000yyyyy
//00000yyyyy|0011000000->00110yyyyy
Serial2.write(A_2);

in_B=analogRead(8);

B_1=(in_B>>5)|32;//xxxxxyyyyy->00000xxxxx
//00000xxxxx|0011100000->00111xxxxx
Serial2.write(B_1);

B_2=in_B&31;//xxxxxyyyyy&0000011111->00000yyyyy
//00000yyyyy|0011000000->00110yyyyy
Serial2.write(B_2);
}
}

 

The code above for the transmitter will read from two ports, split each port’s data into two 5 bit words and mark the header with 111 for first part of port A 110 for second part of port A and 001 for first part of port B and 000 for second part of port B.

One thing to note is the optimization done by condensing all bit operations into one line of code.  The compiler will try to do all written on one line in as little assembly as possible.  If the same operations were split on different lines (marked with 😉 there will be a much longer list of assembly instructions required.

The second thing to note is the variable types used.  Although for most programs that do not require heavy optimization a simple “int”, allowing for both + – integers (in 32 bit form for the Due), due to the inefficient manner of the Arduino IDE using something such as an uint_8 (unsigned 8 bit integer) will save memory and require less processing.

For the receiver a similar thing was done to optimize the code.

void setup() {
Serial.begin(5200000);
Serial1.begin(5200000);
analogWriteResolution(10);//set resolution to 10 bit (0-1023)
}//used to mark the difference between first and second parts
unsigned int A_1=0;
unsigned int A_2=0;
unsigned int B_1=0;
unsigned int B_2=0;

//flags are used to mark whether a certain part was recieved
bool flagA1=false;
bool flagA2=false;
bool flagB1=false;
bool flagB2=false;

bool two=false;//mark whether both parts are complete
bool two1=false;

bool switch_flag=false;

unsigned int serial_in=0;

void loop() {
//determine whether its chA or chB
//chA has 111xxxxx or 110xxxxx
//chB has 001xxxxx or 000xxxxx
//11100000=224 11000000=192
flagA1=flagA2=flagB1=flagB2=two=two1=false;

//check A/B and 1st 2nd byte conditions
while(two==false && Serial1.available()){
serial_in=Serial1.read();

switch(serial_in & 224){
case 224: //111xxxxx
flagA1=true;
A_1=(serial_in&31)<<5;
break;
case 192: //110xxxxx
flagA2=true;
A_1=A_1|(serial_in&31);
break;
case 32: //001xxxxx
flagB1=true;
B_1=(serial_in&31)<<5;
break;
case 0: //000xxxxx
flagB2=true;
B_1=B_1|(serial_in&31);
break;
}
two=flagA1 && flagA2 && flagB1 && flagB2;

}
analogWrite(DAC1, A_1);
analogWrite(DAC0, B_1);

}

The code here uses flags to mark whether each of the first and second parts were received and once all 4 parts are received both pieces of data will be written to both DAC pins of the Rx Due at the same time.

Originally I had planned to use if and else loops such as the following to determine whether each part had been recieved

while(flagB1==false && flagB2==false &&flagA1==false && flagA2==false && Serial1.read()){
serial_in=Serial1.read();
if(serial_in>=224){ //111xxxxx
flagA1=true;
A_1=serial_in;
A_1=A_1&31;
//analogWrite(DAC1, A_1);
A_1=A_1<<5;

}
else if((serial_in>=192) && (serial_in<224)){ //110xxxxx
flagA2=true;
A_2=serial_in;
A_2=A_2&31;

}
else if((serial_in>=32) && (serial_in<64)){ //001xxxxx
flagB1=true;
B_1=serial_in;
B_1=(B_1&31)<<5;
}
else if((serial_in>=0) && (serial_in<32)){ //000xxxxx
flagB2=true;
B_2=serial_in;
B_2=B_2&31;
}
}

However upon further research on optimization due to the problems with “if and else” conditions the switch method is much more efficient.  This is due to the compiler reading and converting switch statements into JMP.

This means that in less lines the processor can decide to jump to which subroutine (a separate function) stored somewhere else in memory.  Given that the subroutine is the same as whatever was inside the if else braces the switch statement is definitely more efficient.

Even with all the developments and optimizations, the maximum baud rate the processor can handle is 5.25 million bits per second which given the code provided above is a measly 7.5kHz which is lackluster to say the least.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s