Arduino read 20 bit number from 3 registers - arduino

I have an ADXL355 (EVAL-ADXL355-PMDZ) that I am trying to test against a very expensive industrial grade sensor. I am using I2C and I am able to read the device properties and settings as described in the datasheet.
The issue I'm having is how to read the 3 ZDATA (or XDATA, YDATA) registers as a single value. I have tried two approaches. Here is the first:
double values[3];
Wire.beginTransmission(addr);
Wire.write(0x08); // ACCEL_XAXIS
Wire.endTransmission();
Wire.requestFrom(addr, 9, true); // Read 9, 3 for each axis
byte x1, x2, x3;
for (int i = 0; i < 3; ++i){
x3 = Wire.read();
x2 = Wire.read();
x1 = Wire.read();
unsigned long tempV = 0;
unsigned long value = 0;
value = x3;
value <<= 12;
tempV = x2;
tempV <<= 4;
value |= tempV;
tempV = x1;
tempV >>= 4;
value |= tempV;
values[i] = SCALEFACTOR * value;
}
This will produce values that approach 1g for negative gravity and 3g for positive gravity. Also the unloaded axes will sometimes show offscale high instead of -0.0g. They bounce from 0.0 to 4.0 g's. This tells me I have a sign problem which I'm sure comes from using unsigned long. So I attempted to read it as a 16 bit value and retain the sign.
double values[3];
Wire.beginTransmission(addr);
Wire.write(0x08); // ACCEL_XAXIS
Wire.endTransmission();
Wire.requestFrom(addr, 9, true); // Read 9, 3 for each axis
byte x1, x2, x3;
for (int i = 0; i < 3; ++i){
x3 = Wire.read();
x2 = Wire.read();
x1 = Wire.read();
long tempV = 0;
long value = 0;
value = x3;
value <<= 8;
tempV = x2;
value |= tempV;
values[i] = SCALEFACTOR * value;
}
This produced values are good in terms of sign but they are (as expected) much lower in magnitude than they are supposed to be. I tried to create a 20 bit number like this long value:20; but I received
expected initializer before ':' token
same error for int.
How do I properly read from 3 registers to obtain a correct 20 bit value?

First of all, you really want to use unsigned types when using the left and right shift operators (see this question).
Taking a look to the avr-gcc type layout we learn that long are represented on 4 bytes (i.e. 32 bits) so they are long enough (no pun intended) to "hold" your 20 bits numbers (XDATA, YDATA, and ZDATA). On the other hand, int are represented on 2 bytes (i.e. 16 bits) and thus should not be used in your case.
According to the datasheet you linked page 33, the numbers are formatted as two's complement. Your first example correctly set the last 20 bits of your unsigned, 32 bits long value (in particular the left justification handling — right-shifting x1 by four — already looks correct) but the "new" 12 most significants bits are always set to 0.
To perform sign extension, you need to set the "new" 12 most significant bits to 0 if the number is a positive value, 1 if the number is a negative value (adaptation of your first example):
...
value |= tempV;
if (x3 & 0x80) /* msb is 1 so the number is a negative value */
value |= 0xFFF00000;
From there, what you should observe is about the same behaviour as previously: high positive values instead of small negative ones (but even higher than previously). This is caused by the fact that while your value is correct bitwise speaking, it is still intepreted as unsigned. This can be worked around by forcing the compiler to use value as signed:
values[i] = SCALEFACTOR * (long)value;
And now it should be working.
Note that this answer use the fact that your C/C++ implementation use two's complement to represent negative integers. While very rare in practice, the standard allow other representations (see this question for examples).

Here is one way to make it work. It does use bitshifting on a signed value. Various sources have said that this is a potential bug as it is implementation defined. It worked on my platform.
typedef union {
byte bytes[3];
long value:24;
} accelData;
double values[3];
Wire.beginTransmission(addr);
Wire.write(0x08); // ACCEL_XAXIS
Wire.endTransmission();
Wire.requestFrom(addr, 9, true); // Read 9, 3 for each axis
accelData raw;
for (int i = 0; i < 3; ++i){
raw.bytes[2] = Wire.read();
raw.bytes[1] = Wire.read();
raw.bytes[0] = Wire.read();
long temp = raw.value >> 4;
values[i] = SCALEFACTOR * (double)temp;
}
I prefer the solution presented by Alexandre Perrin.

union
{
struct
{
unsigned : 4;
unsigned long uvalue : 20;
};
struct
{
unsigned : 4;
signed long ivalue : 20;
};
unsigned char rawdata[3];
}raw;
for (int i = 0; i < 3; ++i){
raw.bytes[2] = Wire.read(); //if most significant part is transfered first
raw.bytes[1] = Wire.read();
raw.bytes[0] = Wire.read();
values[i] = SCALEFACTOR * (double)raw.ivalue;
}

Related

Strange magnetometer readout

I am attempting to readout the data from my MPU-9150 magnetometer and getting some odd numbers. I have accessed the magnetometer within the IMU and am getting data which changes with the orientation of the IMU but is not within the range specified by the product specification guide. I'm think its probably something to do with either the variable type i am using to store the data or the method i am using to manipulate the twos compliment data to make it readable, so here is the code anyway...
void MPU9150::getMag(double* mag_X, double* mag_Y, double* mag_Z){
uint8_t asax, asay, asaz;
I2Cdev::writeByte(MPU9150_ADDRESS, INT_PIN_CFG, 0x02);//Set i2c bypass enable pin to true to access magnetometer.
I2Cdev::writeByte(MPU9150_MAG_ADDRESS, MPU9150_MAG_CNTRL, 0x0f);//Fuse Rom access mode.
I2Cdev::readBytes(MPU9150_MAG_ADDRESS, MPU9150_MAG_ASAX, 3, buffer);//Get sensitivity adjustment values.
asax = buffer[0];
asay = buffer[1];
asaz = buffer[2];
// Serial.print("asax = "); Serial.print(asax); Serial.print("\n");
// Serial.print("asay = "); Serial.print(asay); Serial.print("\n");
// Serial.print("asaz = "); Serial.print(asaz); Serial.print("\n\n");
I2Cdev::writeByte(MPU9150_MAG_ADDRESS, MPU9150_MAG_CNTRL, 0X01);//Enable the magnetometer.
delay(10);
I2Cdev::readBytes(MPU9150_MAG_ADDRESS, MPU9150_MAG_XOUT_L, 6, buffer);//Read magnetometer readings.
mx = ((((int16_t)buffer[0]) << 8) | buffer[1]) * 0.3;
my = ((((int16_t)buffer[2]) << 8) | buffer[3]) * 0.3;
mz = ((((int16_t)buffer[4]) << 8) | buffer[5]) * 0.3;
*mag_X = mx * ((((asax - 128)*0.5)/(128)) + 1);//Adjust readings with sensitivity adjustment values.
*mag_Y = my * ((((asay - 128)*0.5)/(128)) + 1);
*mag_Z = mz * ((((asaz - 128)*0.5)/(128)) + 1);
}
The decimal range for each axis should be between 4096 and -4096, and there are 6 data registers each containing 8 bit high and low values for each axis. The data that i am getting seems to be in the range >10000 to <-10000 with the highest value i have seen at 9830. If anyone has any ideas they would be appreciated, thanks :)
EDIT: Buffer is a private integer array, used to hold the bytes from the axis registers.
private:
uint8_t buffer[14];
Also reversing the order of the bytes (as i have realised should be done as the first byte in each register is low and the second high) has resulted in the range of the output reduced to a range of about -170 to 170...
mx = (((int16_t)buffer[1]) << 8) | buffer[0];
my = (((int16_t)buffer[3]) << 8) | buffer[2];
mz = (((int16_t)buffer[5]) << 8) | buffer[4];

Sum calculation error in Arduino Mega 2560

I am using Arduino Mega 2560 to communicate with the server.
I create a byte array, uses the first digit as a indicator (to tell the server this message is from a arduino device) and the last digit for check sum.
// for creating msg
void createmsg(){
int index = 0;
memset(MSGpack,0,sizeof(MSGpack));
byte sum;
MSGpack[0] = 0x23; // for identifing it is arduino
// for current readings
index = 14;
for (int i = 0; i < 7; i++){
float voltage = readcurrent(i);
injectByte(voltage, index);
index = index + 4;
}
////////////////////////////////////////////////////////////DATE
myRTC.updateTime();
index = 162;
int timeVAR = myRTC.dayofmonth;//reporting day
injectByte(timeVAR, index);
timeVAR = myRTC.month;
injectByte(timeVAR, 166); //reporting month
timeVAR = myRTC.year;
injectByte(timeVAR, 170); //reporting year
timeVAR = myRTC.year + myRTC.month + myRTC.dayofmonth; //sum of date
injectByte(timeVAR, 158);
////////////////////////////////////////////////////////////DATE
////////////////////////////////////////////////////////////TIME
myRTC.updateTime();
timeVAR = myRTC.hours;
injectByte(timeVAR, 146); //reporting hour
timeVAR = myRTC.minutes;
injectByte(timeVAR, 150); //reporting second
timeVAR = myRTC.hours + myRTC.minutes;
injectByte(timeVAR, 154); //sum of time
////////////////////////////////////////////////////////////TIME
//to pass buffer verification
for (int i = 0; i < 186; i++) {
sum += MSGpack[i];
}
MSGpack[186] = sum;
}
void injectByte(float value, int index){
byte * b = (byte *) &value;
MSGpack[index] = b[3];
MSGpack[index + 1] = b[2];
MSGpack[index + 2] = b[1];
MSGpack[index + 3] = b[0];
}
At the server side, it checks if the last digit equals to the sum of all the previous digit, if yes, it identify the received package is valid.
The problems is, if I comment out the date and time data, the server could identify the package as valid. But if I add the data back into the package, the server says the package is not valid.
So I conclude it is check sum error at the Arduino side.
According to here, "some constant calculations may overflow" && "Know at what point your variable will "roll over" " etc at "Programming tips:"
A byte stores an 8-bit unsigned number, from 0 to 255. So what if the check sum calculated is larger than 255? What will be resulted?
And how should I solve this issue and let the server receive a valid package? Thanks!
I would suggest changing your byte array to being an unsigned byte array
then change sum to an unsigned int or unsigned short (16 bits will suffice)
Where you calculate the sum:
sum = (sum + MSGpack[i]) & 0xFF;
MSGpack[186] = (unsigned byte)sum;
I think the issue is that you are adding signed numbers together and also not limiting the output to 8 bits. As you are using the bytes as unsigned, it's best to tell the compiler explicitly so it doesn't make rash assumptions.

why fundamental Frequency and magnitude are not null when microphone is off?

I would like to make real time audio processing with Qt and display the spectrum using FFTW3.
What I've done in steps:
I capture any sound from computer device and fill it into the buffer.
I assign sound samples to double array
I compute the fundamental frequency.
when I'm display the fundamental frequency and Magnetitude when the microphone is on but no signal(silence) , the fundamental frequency is not what I expected , the code don't always return zero , sometimes the code returns 1500Hz,2000hz as frequency
and when the microphone is off (mute) the code don't return zero as fundamamental frequency but returns a number between 0 and 9000Hz. Any help woulbd be appreciated
here is my code
QByteArray *buffer;
QAudioInput *audioInput;
audioInput = new QAudioInput(format, this);
//Check the number of samples in input buffer
qint64 len = audioInput->bytesReady();
//Limit sample size
if(len > 4096)
len = 4096;
//Read sound samples from input device to buffer
qint64 l = input->read(buffer.data(), len);
int input_size= BufferSize;
int output_size = input_size; //input_size/2+1;
fftw_plan p3;
double in[output_size];
fftw_complex out[output_size];
short *outdata = (short*)m_buffer.data();// assign sample into short array
int data_size = size_t(outdata);
int data_size1 = sizeof(outdata);
int count = 0;
double w = 0;
for(int i(chanelNumber); i < output_size/2; i= i + 2) //fill array in
{
w= 0.5 * (1 - cos(2*M_PI*i/output_size)); // Hann Windows
double x = 0;
if(i < data_size){
x = outdata[i];
}
if(count < output_size){
in[count] = x;// fill Array In with sample from buffer
count++;
}
}
for(int i=count; i<output_size; i++){
in[i] = 0;
}
p3 = fftw_plan_dft_r2c_1d(output_size, in, out, FFTW_ESTIMATE);// create Plan
fftw_execute(p3);// FFT
for (int i = 0; i < (output_size/2); i++) {
long peak=0;
double Amplitudemax=0;
double r1 = out[i][0] * out[i][0];
double im1 = out[i][3] * out[i][4];
double t1 = r1 + im1;
//double t = 20*log(sqrt(t1));
double t = sqrt(t1)/(double)(output_size/2);
double f = (double)i*8000 / ((double)output_size/2);
if(Magnitude > AmplitudeMax)
{
AmplitudeMax = Magnitude;
Peak =2* i;
}
}
fftw_destroy_plan(p3);
return Peak*(static_cast<double>(8000)/output_Size);
What you think is silence might contain some small amount of noise. The FFT of random noise will also appear random, and thus have a random magnitude peak. But it is possible that noise might come from equipment or electronics in the environment (fans, flyback transformers, etc.), or the power supply to your ADC or mic, thus showing some frequency biases.
If the noise level is low enough, normally one checks the level of the magnitude peak, compares it against a threshold, and cuts off frequency estimation reporting below this threshold.

how to round an odd integer towards the nearest power of two

Add one to or subtract one from an odd integer such that the even result is closer to the nearest power of two.
if ( ??? ) x += 1; else x -= 1;// x > 2 and odd
For example, 25 through 47 round towards 32, adding one to 25 through 31 and subtracting one from 33 through 47. 23 rounds down towards 16 to 22 and 49 rounds up towards 64 to 50.
Is there a way to do this without finding the specific power of two that is being rounded towards. I know how to use a logarithm or count bits to get the specific power of two.
My specific use case for this is in splitting odd sized inputs to karatsuba multiplication.
If the second most significant bit is set then add, otherwise subtract.
if ( (x&(x>>1)) > (x>>2) ) x += 1; else x -= 1;
It isn't a big deal to keep all of the powers of 2 for a 32 bit integer (only 32 entries) do a quick binary search for the location it's supposed to be in. Then you can easily figure out which number it's closer to by subtracting from the higher and lower numbers and getting the abs. Then you can easily decide which one to add to.
You may be able to avoid the search by taking the log base 2 of your number and using that to index into the array
UPDATE: reminder this code is not thoroughly tested.
#include <array>
#include <cmath>
#include <iostream>
const std::array<unsigned int,32> powers =
{
1,1<<1,1<<2,1<<3,1<<4,1<<5,1<<6,1<<7,1<<8,1<<9,1<<10,1<<11,1<<12,1<<13,1<<14,
1<<15,1<<16,1<<17,1<18,1<<19,1<<20,1<<21,1<<22,1<<23,1<<24,1<<25,1<<26,1<<27,
1<<28,1<<29,1<<30,1<<31 -1
};
std::array<unsigned int,32> powers_of_two() {
std::array<unsigned int,32> powers_of_two{};
for (unsigned int i = 0; i < 31; ++i) {
powers_of_two[i] = 1 << i;
}
powers_of_two[31]=~0;
return powers_of_two;
}
unsigned int round_to_closest(unsigned int number) {
if (number % 2 == 0) return number;
unsigned int i = std::ceil(std::log2(number));
//higher index
return (powers[i]-number) < (number - powers[i-1]) ?
++number:--number;
}
int main() {
std::cout << round_to_closest(27) << std::endl;
std::cout << round_to_closest(23) << std::endl;
return 0;
}
Since I can't represent 2 ^ 31 I used the closest unsigned int to it ( all 1's) this means that 1 case out of all of them will produce the incorrect result, I figured that's not a big deal.
I was thinking that you could use a std::vector<bool> as a very large lookup table on wether to add 1 or subtract 1, seems like overkill to me for an operation that seems to run quite fast.
As #aaronman pointed out, if you are working with integers only the fastest way to do this is to have all powers of 2 in table as there are not that many. By construction, in an unsigned 32 bit integer there are 32 powers of 2 (including the number 1), in a 64 bit integer there are 64 and so on.
But if you want to do it on the fly for a generic case you can easily calculate the surrounding powers of 2 of any number. In c/c++:
#include <math.h>
(...)
double bottom, top, number, exponent;
number = 1234; // Set the value for number
exponent = int(log(number) / log(2.0)); // int(10.2691) = 10
bottom = pow(2, exponent); // 2^10 = 1024
top = bottom * 2; // 2048
// Calculate the difference between number, top and bottom and add or subtract
// 1 accordingly
number = (top - number) < (number - bottom) ? number + 1 : number - 1;
For nearest (not greatest or equal) - see this:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
unsigned int val = atoi(argv[1]);
unsigned int x = val;
unsigned int result;
do {
result = x;
} while(x &= x - 1);
if((result >> 1) & val)
result <<= 1;
printf("result=%u\n", result);
return 0;
}
if you need greatest or equal - change:
if((result >> 1) & val)
to
if(result != val)

Arduino converting between uint32_t and unsigned chars

I'm using the rainbowduino and it has some methods that take individual r g b values as unsigned chars, and some that take a 24bit rgb colour code.
I want to convert r g b values into this 24bit colour code of type uint32_t (so that all my code only has to use r g b values.
Any ideas?
I have already tried uint32_t result = r << 16 + g << 8 + b;
r = 100 g =200 b=0 gave green, but r=0 g=200 b=0 gave nothing
Rb.setPixelXY(unsigned char x, unsigned char y, unsigned char colorR, unsigned char colorG, unsigned char colorB)
This sets the pixel(x,y)by specifying each channel(color) with 8bit number.
Rb.setPixelXY(unsigned char x, unsigned char y, unit32_t colorRGB)
This sets the pixel(x,y)by specifying a 24bit RGB color code.
The drivers code is:
void Rainbowduino::setPixelXY(unsigned char x, unsigned char y, uint32_t colorRGB /*24-bit RGB Color*/)
{
if(x > 7 || y > 7)
{
// Do nothing.
// This check is used to avoid writing to out-of-bound pixels by graphics function.
// But this might slow down setting pixels (remove this check if fast disply is desired)
}
else
{
colorRGB = (colorRGB & 0x00FFFFFF);
frameBuffer[0][x][y]=(colorRGB & 0x0000FF); //channel Blue
colorRGB = (colorRGB >> 8);
frameBuffer[1][x][y]=(colorRGB & 0x0000FF); //channel Green
colorRGB = (colorRGB >> 8);
frameBuffer[2][x][y]=(colorRGB & 0x0000FF); //channel Red
}
}
So I would think similar to the above :
uint8_t x,y,r,b,g;
uint32_t result = (r << 16) | (g << 8) | b;
Rb.setPixelXY(x, y, result);
should work. It I think the above likely needs the parenthesis, to ensure proper ordering, as "+" is higher than "<<". Also likely won't hurt but the "|" is better, as not to prevent undesired carry's.
P.S. Remember when shifting to be unsigned, unless you want arithmetic shift versus logical.
and on that note I don't like shifts as they are often messed up and inefficient. Rather a union is simple and efficient.
union rgb {
uint32_t word;
uint8_t byte[3];
struct {
uint8_t blue;
uint8_t green;
uint8_t red;
} color ;
}rgb ;
// one way to assign by discrete names.
rbg.color.blue = b;
rbg.color.green = g;
rbg.color.red = r;
//or assign using array
rgb.byte[0] = b;
rgb.byte[1] = g;
rgb.byte[2] = r;
// then interchangeably use the whole integer word when desired.
Rb.setPixelXY(x, y, rgb.word);
no messing with keeping track of shifts.
One way to approach this would be to shift the bits to the left...
uint32_t result = r << 16 + g << 8 + b;

Resources