In C is it possible to assign a character a hexidecimal value?
e.g. is something like:
char value = 0x4F;
valid?
In C is it possible to assign a character a hexidecimal value?
e.g. is something like:
char value = 0x4F;
valid?
Yes. Try it. I think it is ascii 'O'.
David.
ok thanks, and I can also do things like:
if(value == 0x4F){ ... } and value = REG;
where REG is an 8-bit register?
Yes.
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?
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?
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."
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 }
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
'uint8_t' and 'uint16_t' unsigned 8 bit and 16 bit integers respectively?
Are they defined in a header file
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?
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
Just a note, the return type of the function should be uint8_t too.
JW
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.
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.
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.
clawson wrote:
I guess that makes it something like:
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;
I'd rarely do it that way, but suit yourself.
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)
I still don't understand how this statement protects the VREF bits from changes in the channel selection bits.
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.
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.
Which is "better" is simply a question of style.
-- 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.
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
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.
I simply thought the BITWISE OR operator "|" was literally a boolean OR operation between bytes
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
using your example, dest, unchnging_bits, and changing_bits are all bytes, right?
Thanks alot for the explanation I get it now thanks