character assignment

Go To Last Post
23 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In C is it possible to assign a character a hexidecimal value?

e.g. is something like:

char value = 0x4F;

valid?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes. Try it. I think it is ascii 'O'.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ok thanks, and I can also do things like:

if(value == 0x4F){
...
}

and 

value = REG;

where REG is an 8-bit register?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, sorry I have one more question. I want to write a function that takes five readings from an ADC register (once every 10ms), and returns the average value. Would the following be ok?

char ADC_read(void){
 int i;
 char total;
 for(i=0;i<5;i++){
 	ADCSRA |= (1<<ADSC);	//initiate single conversion
	while(!ADIF);		//wait for conversion complete flag
	total += ADCL;		//add lower ADC byte to total
	_delay_ms(10);		//wait 10ms
 }
 return total/5;	//return average reading over 50ms

}

I'm not sure whether arithmetic functions like these can be performed on chars, or whether I would need to use an array of chars or even integers?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I'm not sure whether arithmetic functions like these can be performed on chars, or whether I would need to use an array of chars or even integers?

I'd suggest you spend a bit of time doing a "course" in C programming, learning about data types and arithmetic and promotion rules and precedence and the like.

The fragment above has lots of problems. I'd suggest using a proven "read_adc()" primitive, from the Tutorials forum or elsewhere.

1) You are returning a "char" which is androgynous -- neither signed nor unsigned, or it could depend on context (compiler options?)

2) It is only 8 bits, or maybe 7 plus sign. The ADC gives a 10-bit result. Won't fit. You are adding 5 results that are 10 bit (or 8 bit as you are trying to do) and putting that into an 8-bit (or 7 + sign) accumulator. How do you expect that to fit?

3) You are using ADIF incorrectly-- it is a number like "4". (!4) is always false.

4) Even if you were using ADIF correctly, you never clear it.

5) Even if you were doing the above correctly, you aren't doing the result read correctly. Find the datasheet paragraph that contains "Once ADCL is read, ADC access to Data Registers is blocked."

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I guess that makes it something like:

char ADC_read(void){ 
 uint8_t i; 
 uint16_t total; 
 for(i=0;i<5;i++){ 
    ADCSRA |= (1<<ADSC);   //initiate single conversion 
   while(ADCSRA & (1<<ADSC));      //wait for conversion complete flag 
   total += ADCW;      //add ADC reading to total 
   _delay_ms(10);      //wait 10ms
 } 
 return total/5;   //return average reading over 50ms 
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
I guess that makes it something like:

char ADC_read(void){ 
 uint8_t i; 
 uint16_t total; 
 for(i=0;i<5;i++){ 
    ADCSRA |= (1<<ADSC);   //initiate single conversion 
   while(ADCSRA & (1<<ADSC));      //wait for conversion complete flag 
   total += ADCW;      //add ADC reading to total 
   _delay_ms(10);      //wait 10ms
 } 
 return total/5;   //return average reading over 50ms 
}

Hi thanks, I have a few questions. are 'uint8_t' and 'uint16_t' unsigned 8 bit and 16 bit integers respectively? Are they defined in a header file or did you define them yourself using #define statements? If so where is the header file, or how did you use the define the variables to set the bit length to 8 or 16 bits?

also, does ADCW read the entire 10-bits of the reading (ADCH and ADCL)? If so then the avreage could still be 10 bits long couldn't it, and therefore not fit in a char?

Finally I don't quite understand your statement for waiting for the conversion to complete, would you mind explaining?

Thanks alot

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

'uint8_t' and 'uint16_t' unsigned 8 bit and 16 bit integers respectively?

Yes (the names kind of gave the game away didn't they? in fact that's EXACTLY the point!)
Quote:

Are they defined in a header file

They are defined in a header called which is indirectly included when you include . Like all system header files it's in the /winavr/avr/include tree.
Quote:

also, does ADCW read the entire 10-bits of the reading (ADCH and ADCL)?

Yes.
Quote:

If so then the avreage could still be 10 bits long couldn't it, and therefore not fit in a char?

Correct - though on a binary computer 10bits is quite difficult to achieve 8bit (char=(u)int8_t) no problem. (u)int16_t no problem so to fit 10 bits then 16bits is the natural choice. As you are only adding 5 samples there's no question that the total could easily fit in 16 bits (you'd have to be adding more than 64 samples to have the possibility of overflowing 16 bits)
Quote:

Finally I don't quite understand your statement for waiting for the conversion to complete, would you mind explaining?

There are two ways to check for conversion complete. One easy and one hard. You picked the hard one, I picked the easy one.

The easy one is the fact that once you set ADSC it remains at 1 until the conversion is complete. So my while() loop was just watching it while it was still one. When it returns to 0 (the conversion is complete) the while() loop will end.

The difficult one is to use ADIF rather than ADSC. True it has the more natural property that it switches from 0 to 1 when the conversion is complete so your while could be:

while (!(ADCSRA & (1<<ADIF));

which remains false while the bit is 0 then ends when the bit becomes one. HOWEVER then what - ADIF, once set remains set until you do something about it. As it happens that "something" is writing a 1 bit to it but why put yourself to this overhead? The nice thing about ADSC was that it "auto-clears". You set it to 1 and it remains 1 until the conversion is complete then it automatically clears back to the 0 (non active) state. So you don't have to do anything more. In this way ADSC is preferable (in most people's opinion) to ADIF.

ADIF comes into play if you set up ADIE so that an interrupt is generated by the end of conversion. In this case the act of servicing the interrupt to read the value is enough to clear ADIF.

Cliff

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just a note, the return type of the function should be uint8_t too.

JW

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks alot, thats a great help. However, what I was talking about is that the function returns an 8bit char, and the average value will still result in a 10bit number (or the uint16_t which the 10bit result is stored in), so I guess I'd have to change the return type to uint16_t?

Also, say I have ADC0-ADC3 connected to 4 different channels, is there an easy way to jump through each of the channels using an index in a for loop? i.e. select ADC0, read, select ADC1, read ...etc

I know that channel selection is done by setting bits of the ADMUX register, but I've been unable to find any information about exactly which bits to set, and how. I've gathered that only 5 bits are used for channel selection, but haven't found which bits they are, and how to set them to select a particular channel.

I was thinking of something like:

for(i=0;i<3;i++){
  ADMUX |= (f(i));
  adc_read();
}

Wher f(i) is some expression involving i.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually I can just use this can't I?

for(i=0;i<3;i++){
  ADMUX = i;
  adc_read();
} 

Just worried about affecting the other bits in the register if I don't use bit masking.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Just worried about affecting the other bits in the register if I don't use bit masking.

Then do it properly--put in the desired option bits, and add the channel. (And the reference selection bits vary from model to model and I don't recall that we know the model.)

channel == ADMUX indeed in general for AVRs. For "simple" work. For differential channels, and internal channels, and etc., that may not be true.

Quote:

clawson wrote:
I guess that makes it something like:

I'd rarely do it that way, but suit yourself. If I must make a polling ADC primitive, then I'd make a general one and call it as needed rather than a special-purpose and sit there for about a millisecond.

More like

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | ADC_VREF_TYPE;

// Start the AD conversion
ADCSRA |= (1 << ADSC);
// Wait for the AD conversion to complete
while ((ADCSRA & (1 << ADIF))==0);
ADCSRA |= (1 << ADIF);
return ADCW;
}
...
unsigned int the_total;
#define SAMPLE_COUNT 5 // to avoid overflow, 64 or less
...
the_total = 0;
for (frog = 0; frog < SAMPLE_COUNT; frog++)
    {
    the_total += read_adc (MY_CHAN);
    }
the_average = the_total / SAMPLE_COUNT;

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I'd rarely do it that way, but suit yourself.

Me neither I was simply correcting the conceptual errors in OP's original code but retaining his algorithmic structure (which presumably he already understood)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, in this code:

ADMUX=adc_input | ADC_VREF_TYPE; 

is ADC_VREF_TYPE defined anywhere, or is that your own example variable? I still don't understand how this statement protects the VREF bits from changes in the channel selection bits. Can you explain in terms of the bits? I was thinking if bits 7-5 were the VREF bits then this meant use 0xE0 as ADC_VREF_TYPE, for example, but then the statement doesn't seem to make sense.

Also, I want a simple way of using a for loop to parse through each channel sucessively ADC0->ADC7. So on calling this function within a for loop, im assuming this function allows me to simply pass the index, i, as the parameter? (adc_input)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I still don't understand how this statement protects the VREF bits from changes in the channel selection bits.

It sets it every time. It rebuilds ADMUX. I don't know what else to explain about bits or otherwise. Yes, ADC_VREF_TYPE is built earlier from possible high-bit masks. It will always be more efficient than any &=~, |= sequence.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

More like

// Read the AD conversion result
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | ADC_VREF_TYPE;

// Start the AD conversion
ADCSRA |= (1 << ADSC);
// Wait for the AD conversion to complete
while ((ADCSRA & (1 << ADIF))==0);
ADCSRA |= (1 << ADIF);
return ADCW;
}
...
unsigned int the_total;
#define SAMPLE_COUNT 5 // to avoid overflow, 64 or less
...
the_total = 0;
for (frog = 0; frog < SAMPLE_COUNT; frog++)
    {
    the_total += read_adc (MY_CHAN);
    }
the_average = the_total / SAMPLE_COUNT;

Ok I see what you've done, my point is this though. lets say bits 7,6, and 5 of ADMUX are the reference selection bits, which weve already set, and 4-0 the channel selection bits. then lets say our mask ADC_VREF_TYPE = 0xE0, then a bitwise OR of our mask and a channel number would result in something like this:

1110 0000  //(0xE0) ADC_VREF_TYPE
0000 0010  //(0x02) channel number = ADC2

// OR'd together...

1110 0010  //ADMUX gets set with this result

so no matter what bits 7,6, and 5 were originally, they will always be set to 1. So actually they arent protected (say for example if they were set as 101, then they'd change to 111)

I think something like

for(i=0;i

would work better, since as long as the channel number is kept lower than a certain number in the for loop, then bits 7,6, and 5 wont be changed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What are you talking about? If you have bits in ANY byte to be protected while updating other bits you've always got two options:
1)

dest = unchanging_bits + changing_bits;
or
dest = unchanging_bits | changing_bits;

2)

dest &= ~(changing_bit_mask)
dest += changing_bits;
or
dest &= ~(changing_bit_mask)
dest |= changing_bits;

Which is "better" is simply a question of style.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Which is "better" is simply a question of style.

Not simply style, IMO.

-- Probably not in this case, but one must also consider possible RMW effects when doing "type 2)" operations on an I/O register.

-- 1) takes less cycles.
-- 1) takes fewer code words.
-- 1) probably uses less register(s); important in an ISR.

Call me paranoid, or belts-and-suspenders, or fixated on "What could possibly go wrong?". As with using >= instead of == in a test, if it costs nothing do it the way that "unchanging bits" get reset to the proper value, in case corrupted or forgot to initialize or some other code running amok is tromping on them. In these types of situations it costs nothing to carry out my paranoia.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
What are you talking about? If you have bits in ANY byte to be protected while updating other bits you've always got two options:
1)

dest = unchanging_bits + changing_bits;
or
dest = unchanging_bits | changing_bits;

OK, sorry if I'm not making much sense. But this is how I thought it worked - using your example, dest, unchnging_bits, and changing_bits are all bytes, right? so each is represented by 8 bits: xxxxxxxx.

Well lets say we want bits 7,6, and 5 in the dest byte to remain as they have been prviously set, but we want to assign a number between 0 and 31 (2^5-1) to bits 4 to 0. for examples sake lets say we want to assign 15. this is 0x0F or 00001111. also for examples sake lets say bits 7,6, and 5 of dest are set thusly: dest = 10100000

I was under the impression that the BITWISE OR ("|") operator would OR corresponding bits in the two target bytes, and put the result in the destination byte. i.e. bit n of target byte 1, is OR'd with bit n of target byte 2, and the result is put in bit n of the destination byte - bitwise operation.

so then by the code you gave:

10100000  //original dest byte

//bits 7,6,and 5 want to remain the same so
//use 0xE0 (11100000) for "unchanging_bits"
//byte:

11100000  //unchanging_bits

00001111  //changing_bits 0x0F (15)

//when we OR together the two bytes we get:

11101111  //new value of "dest" byte

//bits 7,6,5 will always be set to 1 in the 
//new "dest" byte, since 1 OR'd with anything
//is always 1.

//but we WANT:

10101111  //bits 7,6,5 unchanged

//this is NOT whats given by:

dest = unchanging_bits | changing_bits;

//and for adding them we get:

 11100000  //unchanging_bits
+00001111  //changing_bits
---------
 11101111  //new value of "dest"

//this is given by your first example:

dest = unchanging_bits + changing_bits;

//...the same wrong result

//now look at my example:

ADMUX += channel_number;

//staying consistant this would be:

dest += changing_bits;

//we then have:

 10100000  //original "dest" byte
+00001111  //"changing_bits"
---------
 10101111  //new "dest" value

//here, bits 7,6, and 5 will remain the same
//but we have set bits 4-0 with the new 
//value. we then just need to reset "dest"
//when we have finished counting up, by 
//resetting bits 0-4 back to 0:

dest &= ~0x1F;

I simply thought the BITWISE OR operator "|" was literally a boolean OR operation between bytes, and the addition was literally a boolean addition, as I have used them both above. If my interpretation is wrong please tell me why.

You are obviously both more experienced AVR programmers than me, but your explanations don't line up with my understanding, which seems to be fairly intuitive.

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

I simply thought the BITWISE OR operator "|" was literally a boolean OR operation between bytes, and the addition was literally a boolean addition, as I have used them both above. If my interpretation is wrong please tell me why.

You are wrong. Well, actually, you are right, but missing a step. Here is why:

Quote:

I simply thought the BITWISE OR operator "|" was literally a boolean OR operation between bytes

It is indeed that. But you aren't applying it properly. Construct a mask with the unchanging bit positions; as you said, that might be 0xe0. Apply the mask to the current value. OR the new bits to the lower part. The top bits are >>not<< 111 but 101 using your example above.

Why not just pick your reference type, always use that constant as the "base", and then add in the channel reconstructing ADMUX each time? I outlined 3.5 reasons why I fell it is better this way. Anyway, to use your example numbers going from channel 3 to channel 4:

// Starting with ref selection bits of 101 in the high three bits,
// and channel 3 in the low bits, and an ADMUX value of 0xB3 or 0b10100011.
//
#define ADMUX_REF_MASK 0xe0
...
new_channel = 4;
...
scratch = ADMUX & ADMUX_REF_MASK; // scratch now has 0xb0
scratch |= new_channel;           // scratch now has 0xb4, not oxe4
ADMUX = scratch;

The compiler will use an intermediate anyway, but if you really want more
I/O stores/reads...

ADMUX &= ADMUX_REF_MASK;
ADMUX |= new_channel;

but I'd rather just do the assignment; compare the generated code...

#define ADMUX_MY_REF 0xb0
...
ADMUX = ADMUX_MY_REF | new_channel;

In most (nearly all?) of my apps the reference selection stays constant throughout the app. In the few where range-changing is done, then a variable can be used for the ref bits, or selection of a constant mask depending on the app state.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

using your example, dest, unchnging_bits, and changing_bits are all bytes, right?

Err no. There was a clue in the name. I had a picture in my mind of something like unchanging_bits=bits7..5 and changing_bits=bits4..0 - but see Lee's explanation above.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks alot for the explanation I get it now thanks