I am working on converting a program that runs on a specific micro-controller and adapt it to run on the raspberry pi. I have successfully been able to pull values from the sensor I have been working with but now I have run into a problem and I think it is caused by a few lines of code I am having trouble understanding. I have read up on what they are but am still scratching my head. The code below I believe is supposed to modify the number that gets stored in the X,Y,Z variables however I don't think this is occurring in my current program. Also I had to change byte to an INT to get the program to compile with out errors. This is the unmodified code from the original code I have converted. Can someone tell me if this is even modifying the number in anyway?
void getGyroValues () {
byte MSB, LSB;
MSB = readI2C(0x29);
LSB = readI2C(0x28);
x = ((MSB << 8) | LSB);
MSB = readI2C(0x2B);
LSB = readI2C(0x2A);
y = ((MSB << 8) | LSB);
MSB = readI2C(0x2D);
LSB = readI2C(0x2C);
z = ((MSB << 8) | LSB);
}
Here is the original readI2C function:
int readI2C (byte regAddr) {
Wire.beginTransmission(Addr);
Wire.write(regAddr); // Register address to read
Wire.endTransmission(); // Terminate request
Wire.requestFrom(Addr, 1); // Read a byte
while(!Wire.available()) { }; // Wait for receipt
return(Wire.read()); // Get result
}
I2C is a 2-wire protocol used to talk to low-speed peripherals.
Your sensor should be connected over the I2C bus to your CPU. And you're reading 3 values from the sensor - x, y and z. The values for these are accessible from the sensor as 6 x 8-bit registers.
x - Addresses 0x28, 0x29
y - Addresses 0x2A, 0x2B
z - Addresses 0x2C, 0x2D
ReadI2C() as the function name implies, reads a byte of data from a given address from your sensor and returns the data being read. The code in ReadI2C() is dependent on how your device's I2C controller is setup.
A byte is 8-bits of data. The MSB (Most-Significant-Byte) and LSB(Least-Significant-Byte) denote 8-bits each read over I2C.
It looks like you're interested in a 16-bit data (for x, y and z). To construct the 16-bit data from the 2 pieces of 8-bit data, you shift the MSB by 8-bits to the left and then perform a logical-OR operation with the LSB.
For example:
Let us assume: MSB = 0x45 LSB = 0x89
MSB << 8 = 0x4500
(MSB << 8) | LSB = 0x4589
Look at my comments inline as well:
void getGyroValues () {
byte MSB, LSB;
MSB = readI2C(0x29);
LSB = readI2C(0x28);
// Shift the value in MSB left by 8 bits and OR with the 8-bits of LSB
// And store this result in x
x = ((MSB << 8) | LSB);
MSB = readI2C(0x2B);
LSB = readI2C(0x2A);
// Do the same as above, but store the value in y
y = ((MSB << 8) | LSB);
MSB = readI2C(0x2D);
LSB = readI2C(0x2C);
// Do the same as above, but store the value in z
z = ((MSB << 8) | LSB);
}
Related
I'm trying to write a VM (LC-3), and on this ADD instruction I encountered this statement. Basically the "register0" is the DR register, but I don't really understand what is actually shifting and why 9. Also the AND operator with the 0x7 value.
|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1|0|
| 0001 | DR | SR1 |0| 00| SR2 |
Could anyone please explain it to me in detail?
ADD {
/* destination register (DR) */
uint16_t r0 = (instr >> 9) & 0x7;
/* first operand (SR1) */
uint16_t r1 = (instr >> 6) & 0x7;
/* whether we are in immediate mode */
uint16_t imm_flag = (instr >> 5) & 0x1;
if (imm_flag) {
uint16_t imm5 = sign_extend(instr & 0x1F, 5);
reg[r0] = reg[r1] + imm5;
} else {
uint16_t r2 = instr & 0x7;
reg[r0] = reg[r1] + reg[r2];
}
update_flags(r0);
}
What it's doing is isolating the 3 bits that represent the DR register so they become a standalone number.
Let's say the entire sequence looks like this:
1110101101101011
^^^
DR
Shifting 9 bits right gives this:
1110101
and & 0x7 (bitwise AND) isolates the 3 lowest bits:
101
Similar operations are performed to isolate the values of SR1 and the immediate mode flag. Depending on that flag, SR2 may also be required, but as it's already in the lowest 3 bits, no shifting is needed.
I'm trying to use a temperature sensor(PCT2075) by MSP430F249
To get a temperature, I get a 2bytes from this sensor.
I wrote a code from this link.
https://e2e.ti.com/support/microcontrollers/msp430/f/166/t/589712?MSP430FR5969-Read-multiple-bytes-of-data-i2c-with-repeated-start-and-without-interrupts
I'm using MSP430F249. so I modified a code from this link.
Howerver, I got just two same value. I think that it is MSByte.
Is there any way to get 2bytes from sensor.
my code here
void i2c_read_multi(uint8_t slv_addr, uint8_t reg_addr, uint8_t l, uint8_t *arr)
{
uint8_t i;
while(UCB0STAT & UCBBUSY);
UCB0I2CSA = slv_addr; // set slave address
UCB0CTL1 |= UCTR | UCTXSTT; // transmitter mode and START condition.
while(UCB0CTL1 & UCTXSTT);
UCB0TXBUF = reg_addr;
while(!(UCB0CTL1 & UCTXSTT));
UCB0CTL1 &= ~UCTR; // receiver mode
UCB0CTL1 |= UCTXSTT; // START condition
while(UCB0CTL1 & UCTXSTT); // make sure start has been cleared
for (i = 0; i < l; i++) {
while(!(IFG2 & UCB0RXIFG));
if(i == l - 1){
UCB0CTL1 |= UCTXSTP; // STOP condition
}
arr[i] = UCB0RXBUF;
}
while(UCB0CTL1 & UCTXSTP);
}
There are two issues ...
The linked to code assumes that the port only needs to read one byte for each output value.
But, based on the sensor documentation you've shown, for each value output to the array, we need to read two bytes (one for MSB and one for LSB).
And, we need to merge those two byte values into one 16 bit value. Note that arr is now uint16_t instead of uint8_t. And, l is now the number of [16 bit] samples (vs. number of bytes). So, the caller of this may need to be adjusted accordingly.
Further, note that we have to "ignore" the lower 5 bits of lsb. We do that by shifting the 16 bit value right by 5 bits (e.g. val16 >>= 5). I assume that's the correct way to do it. Or, it could be just val16 &= ~0x1F [less likely]. You may have to experiment a bit.
Here's the refactored code.
Note that this assumes the data arrives in "big endian" order [based on my best guess]. If it's actually little endian, reverse the msb = and lsb = statements.
Also, the placement of the "STOP" condition code may need to be adjusted. I had to guess as to whether it should be placed above the LSB read or MSB read.
I chose LSB--the last byte because that's closest to how the linked general i2c read is done. (i.e.) i2c doesn't know about or care about the MSB/LSB multiplexing of the device in question. It wants the STOP just before the last byte [not the 16 bit sample].
void
i2c_read_multi(uint8_t slv_addr, uint8_t reg_addr, uint8_t l,
uint16_t *arr)
{
uint8_t i;
uint8_t msb;
uint8_t lsb;
uint16_t val16;
while (UCB0STAT & UCBBUSY);
// set slave address
UCB0I2CSA = slv_addr;
// transmitter mode and START condition.
UCB0CTL1 |= UCTR | UCTXSTT;
while (UCB0CTL1 & UCTXSTT);
UCB0TXBUF = reg_addr;
while (!(UCB0CTL1 & UCTXSTT));
// receiver mode
UCB0CTL1 &= ~UCTR;
// START condition
UCB0CTL1 |= UCTXSTT;
// make sure start has been cleared
while (UCB0CTL1 & UCTXSTT);
for (i = 0; i < l; i++) {
while (!(IFG2 & UCB0RXIFG));
msb = UCB0RXBUF;
while (!(IFG2 & UCB0RXIFG));
// STOP condition
if (i == l - 1) {
UCB0CTL1 |= UCTXSTP;
}
lsb = UCB0RXBUF;
val16 = msb;
val16 <<= 8;
val16 |= lsb;
// use only most 11 significant bits
// NOTE: this _may_ not be the correct way to scale the data
val16 >>= 5;
arr[i] = val16;
}
while (UCB0CTL1 & UCTXSTP);
}
In the Code below
void I2C_Write(uint8_t v_i2cData_u8)
{
uint8_t i;
for(i=0;i<8;i++) // loop 8 times to send 1-byte of data
{
SDA_PIN = v_i2cData_u8 & 0x80; // Send Bit by Bit on SDA line
i2c_Clock(); // Generate Clock at SCL
v_i2cData_u8 = v_i2cData_u8<<1;// Bring the next bit to be transmitted to MSB position
}
i2c_Clock();
}
in the statement: SDA_PIN = v_i2cData_u8 & 0x80; it is told that data will be sent bit by bit,if data is sent bit by bit then what will be stored in SDA_PIN,will SDA_PIN have the value = 0x80 or 1?
SDA_PIN will be assigned either 0x80 (128) or 0 depending on the high order bit of v_i2cData_u8 in that loop. If you want to insure a 0x01 byte is written, you would need to do this:
SDA_PIN = (v_i2cData_u8 & 0x80) ? 1 : 0;
I'm trying to read the pressure value from MPL3115.
At chapter 14.3 the ds says:
The pressure data is stored as a 20-bit unsigned integer with a fractional part. The
OUT_P_MSB (01h), OUT_P_CSB (02h) and bits 7 to 6 of the OUT_P_LSB (03h)
registers contain the integer part in Pascals. Bits 5 to 4 of OUT_P_LSB contain the
fractional component. This value is representative as a Q18.2 fixed point format where
there are 18 integer bits and two fractional bits.
Here my (testing) code for Microchip XMEGA (GCC):
#define MPL3115_ADDRESS 0x60
#define MPL3115_REG_PRESSURE_DATA 0x01
bool _ReadRegister(uint8_t address, uint8_t *readBuf, uint8_t readCount)
{
TWI_MasterWriteRead(&twiMaster, MPL3115_ADDRESS, &address, 1, readCount);
while (twiMaster.status != TWIM_STATUS_READY);
memcpy(readBuf, (void *) twiMaster.readData, readCount);
return true;
}
bool MPL3115_ReadPressure(float *value) {
uint8_t raw[3];
_ReadRegister(MPL3115_REG_PRESSURE_DATA, raw, 3);
uint32_t data = (((uint32_t) raw[0]) << 16) | (((uint32_t) raw[1]) << 8) | raw[2];
uint32_t q18n2 = data >> 4;
*value = (double) q18n2 / 4;
return true;
}
I'm pretty sure the i2c line is working because I can successfully read the temperature from the same chip.
I configured it in barometer mode and 128 oversampling (CTRL_REG1 = 0x38).
In debug mode I read the following values:
raw0 = 0x18
raw1 = 0x25
raw2 = 0x70
data = 1582448
to obtain the pressure in Pascal I have to right shift data of 4 bits:
q18n2 = 98908
now to convert the Q18.2 to float I should multiply for 2^-n or divide for 4:
value = 24727 Pa
This should be in Pascal, so divide for 100 and get mBar = 247.27 mBar... it's unlikely I have such a pressure here! By the way now should be around 1008 mBar.
Are there any mistakes in my thoughts?
you must to right shift data of 6 bits and then add fractional part (2 bits * 0.25).
*value = (raw0 << 10) | (raw1 << 2) | (raw2 >> 6);
*value += 0.25 * ((raw2 >> 4) & 0x03);
I'm trying to get a hold of bit manipulation in embedded c programming.
I have the following code
#define X_POS (8UL)
#define X_MASK (0x1FUL<<X_POS)
#define Y_POS (16UL)
#define Y_MASK (0x3UL<<Y_POS)
typedef struct {
uint32_t res[6];
uint32_t config[10];
} myStruct_type;
myStruct_type myStruct;
void configure (uint32_t n, uint32_t x, uint32_t y)
{
myStruct.config[n] &= ~(X_MASK | Y_MASK); // A
myStruct.config[n] |= ((x<<X_POS) & X_MASK) | ((y<<Y_POS) & Y_MASK); // B
}
int main (void)
{
configure(3, 18, 2);
while (1) { }
}
I understand that the line marked with the comment A is used for setting the bits of interest to 0, i.e. clearing the bits.
Also I understand that on the line marked with B the bits of interest are set to wanted values.
But what is the purpose of the X_MASK and Y_MASK on line B??? Aren't the values set by
(x<<X_POS) | (y<<Y_POS)
The purpose of the masking on line B is to ensure that only the bits you want set will be set. e.g. if x is 0xFFFFFFFF, the & with X_MASK will stop the bits above what you are interested in from being set.
So assuming config[0] starts out at 0, x is 0xFFFFFFFF and y is 0, without the maskingin line B, you would have
myStruct.config[0] &= ~(X_MASK | Y_MASK); // config[0] is still 0
myStruct.config[0] |= (0xFFFFFFFF << 8) | (0<< 16);
// config[0] is now 0xFFFFFF00
with the masks, the second line is
myStruct.config[0] |= ((0xFFFFFFFF<< 8) & 0x1F00) | (( 0 << 16) & 3 << 16); // B
which is
myStruct.config[0] |= 0x1F00 | 0);
If you only use (x<<X_POS) | (y<<Y_POS) you simply assign the values x and y shifted to the left.
The mask eliminates the unwanted bits. For example:
X_MASK = 0x1FUL << 8UL = 0x1F00000000 = 0b111110000...
With the logical & (AND) you set all bits to zero that are zero on your mask:
X_MASK 0b00000001111100000000.....
x<<X_POS 0b01010101010100000000.....
& ________________________
result 0b00000001010100000000.....
On a higher level you can say, that the X_MASK sets all bits of x to zero except the lowest 5 and the Y_MASK sets all bits of y to zero, except the lowest 2. Afterwards your result gets bit shifted to the left. So the mask will clear the higher bits.