How to read/write into specific bits of a unsigned char - c

I want to read and write from/to an unsigned char according to the table below:
for example I have following variables:
unsigned char hsi_div = 0x01; /* HSI/2 */
unsigned char cpu_div = 0x05; /* Fmaster/32 */
I want to write hsi_div to bits 4,3 and cpu_div to bits 2,1,0 (imagine the whole char is named CLK_DIVR):
CLK_DIVR |= hsi_div << 4; //not correct!
CLK_DIVR |= cpu_div << 2; //not correct!
And lets say I want to read the register back to make sure I did it correct:
if( ((CLK_DIVR << 4) - 1) & hsi_div) ) { /* SET OK */ }
if( ((CLK_DIVR << 2) - 1) & cpu_div) ) { /* SET OK */ }
Is there something wrong with my bitwise operations!? I do not get correct behaviour.

I assume CLK_DIVR is a hardware peripheral register which should be qualified volatile. Such registers should be set up with as few writes as possible. You change all write-able bits, so just
CLK_DIVR = (uint8_t)((hsi_div << 3) | (cpu_div << 0));
Note using fixed width type. That makes mentioniong it is an 8 bit register unnecessary. According to the excerpt, the upper bits are read-only, so they are not changed when writing. The cast keeps the compiler from issuing a truncation warning which is one of the recommended warnings to always enable (included in -Wconversion for gcc).
The shift count is actually the bit the field starts (the LSbit). A shift count of 0 means "no shifting", so the shift-operator is not required. I still use it to clarify I meant the field starts at bit 0. Just let the compiler optimize, concentrate on writing maintainable code.
Note: Your code bit-or's whatever already is in the register. Bit-or can only set bits, but not clear them. Addiionally the shift counts were wrong.
Not sure, but if the excerpt is for an ARM Cortex-M CPU (STM32Fxxxx?), reducing external bus-cycles becomes more relevant, as the ARM can take quite some cycles for an access.

For the HSIDIV bit fields you want:
hw_register = (hw_register & 0x18) | (hsi_value & 0x03) << 0x03;
This will mask the value to 2 bits wide then shift to bit position 3 and 4.
The CPUDIV fields are:
hw_register = (hw_register & 0x7) | (cpu_value & 7);
Reading the register:
hsi_value = (hw_register & 0x18) >> 3;
cpu_value = hw_register & 0x07;

Just
CLK_DIVR |= hsi_div << 3;
CLK_DIVR |= cpu_div << 0;
Since hsi_div is a 2-digit binary, you have to move it three positions to skip the CPUDIV field. And the cpu_div is already at the end of the field.

Related

MSP430 I2C read multiple bytes communication problem

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);
}

Bitwise operations in C coding

I'm trying to learn how to write drivers for GPIO pins in STM32F4 Discovery. I was going through the tutorials and I came across this line:
static void hal_gpio_configure_pin_mode (GPIO_TypeDef *GPIOx, uint16_t pin_no, uint32_t mode)
{
GPIOx->MODER |= (mode << (2* pin_no));
}
I am relatively new to embedded systems and don't know what GPIOX->MODER |= (mode << (2* pin_no)); does. Also, I don't know why pin_no is 16 bit while mode is 32 bit. I know that << is the left shift bitwise operator. But I still don't get the complete context.
Can anybody explain all this?
Trying to explain the line GPIOx->MODER |= (mode << (2* pin_no));:
GPIOx is a pointer to a structure
GPIOx->MODER accesses the member MODER inside this structure
x |= y is an abbreviation of x = x | y, which means "perform a logic OR of x and y, and write the result back to x
mode << (2* pin_no) means left-shift the content of variable mode by twice pin_no bits.
Hence, the line means "take the content of GPIOx->MODER, bitwise-OR it with the left-shiftet content of mode.

Copy a bit value from one variable to different bit position in another variable

I want to set or clear the bit number 3 in a register named TCCR0B depending on the value of the bit number 2 in a variable named ‘mode’. If bit 2 is high in mode, bit 3 in TCCR0B has to be set without disturbing other bits. In case bit 2 in mode is low, I want to clear bit 3 in TCCR0B. Basically I want to copy one bit to other bit. I thought it is going to be simple, but now I feel we need conditional statement to do this. I am not sure if I am making this code complex. Is there any easy method to achieve this? I wrote below code to test this.
#define WGM02 3
#define WGM02_IN_MODE 2
int main (void)
{
unsigned int TCCR0B;
unsigned int mode;
while(1)
{
printf("enter the TCCRB\n");
fflush(stdin);
scanf("%X",&TCCR0B);
printf("TCCRB = %x\n",TCCR0B);
fflush(stdin);
printf("enter the mode\n");
scanf("%X",&mode);
mode=((mode>>WGM02_IN_MODE)&0x01);
if(mode)
{
TCCR0B = (TCCR0B & ~(1<<WGM02)) | (mode<<WGM02);
}
else
{
TCCR0B = (TCCR0B & ~(1<<WGM02)) & ~(mode<<WGM02);
}
printf("TCCRB = %x\n",TCCR0B);
}
}
Edit: I looked at the post in How do you set, clear, and toggle a single bit?. But it is related to setting clearing etc of individual bits and not copy from one to another.
Given:
#define TCCR0B_WGM02_MASK (1 << 3)
#define MODE_WGM02_MASK (1 << 2)
then:
TCCR0B = (mode & MODE_WGM02_MASK)) == 0 ? // If mode WGM02 bit is zero...
TCCR0B & ~MODE_WGM02_MASK : // clear WGM02 in TCCR0B,
TCCR0B | MODE_WGM02_MASK ; // otherwise set WGM02 in TCCR0B.

C shift zero or shift by zero

I am learning program AVR uCs.
An example code snippet contains this line:
DDRC |= (0 << 0) | (0 << 1); // PIN C0, C1 as input for buttons
I don't understand what sense
(0 << 0)
makes and in general what this line logically does.
I know what the DDRC is I only want to understand this logical operation.
May be the comment helps.
The code does nothing and might be wrong.
The author could have meant
DDRC |= (1 << 0) | (1 << 1);
which is short for
DDRC |= 3;
which reads the DDRC, sets the bottommost two bits, and writes that value back. The syntax using << and | attempts to make that clearer.
An alternative interpretation would be that the author intends to clear these two bits. In that case, the code is wrong, because it doesn't do that.
If the DDRC is the Data Direction Register for port C, chances are that writing a zero bit switches the direction to input, in which case the intention appears to be clearing the bits.
The appropriate code for that would be
DDRC &= ~( (1 << 0) | (1 << 1) );

C bit operations / copy one bit from one byte to another byte

I know how to set a bit, clear a bit , toggle a bit, and check if a bit is set.
But, how I can copy bit, for example nr 7 of byte_1 to bit nr 7 in byte_2 ?
It is possible without an if statement (without checking the value of the bit) ?
#include <stdio.h>
#include <stdint.h>
int main(){
int byte_1 = 0b00001111;
int byte_2 = 0b01010101;
byte_2 = // what's next ?
return 0;
}
byte_2 = (byte_2 & 0b01111111) | (byte_1 & 0b10000000);
You need to first read the bit from byte1, clear the bit on byte2 and or the bit you read earlier:
read_from = 3; // read bit 3
write_to = 5; // write to bit 5
the_bit = ((byte1 >> read_from) & 1) << write_to;
byte2 &= ~(1 << write_to);
byte2 |= the_bit;
Note that the formula in the other answer (if you extend it to using variables, instead of just bit 7) is for the case where read_from and write_to are the same value.

Resources