Author Topic: ATMEGA328 adc , registors  (Read 5875 times)

0 Members and 1 Guest are viewing this topic.

Offline 0x007Topic starter

  • Contributor
  • Posts: 25
ATMEGA328 adc , registors
« on: April 29, 2016, 07:09:51 pm »
Hi everyone !
I have a bit of a miss understanding of the way I should address registers on AVRs .
I am playing with the mega328p and I just cant understand what the F**** is going on with the registers.
I am trying to use the ADC but I can't understand how to initialize the registers , I seen some code examples and they all look like this:
ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN); , why????
why can't it look like a normal register setup :"ADCSRA = 0x7f;", obviously it is not working for me....
I can copy it all like an idiot but I want to understand why I should declare it like this :ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);
sorry if this is confusing , I am really frustrated .
can someone explain this?
 

Offline firewalker

  • Super Contributor
  • ***
  • Posts: 2450
  • Country: gr
Re: ATMEGA328 adc , registors
« Reply #1 on: April 29, 2016, 07:31:33 pm »
Become a realist, stay a dreamer.

 

Offline MatthewEveritt

  • Supporter
  • ****
  • Posts: 136
  • Country: gb
Re: ATMEGA328 adc , registors
« Reply #2 on: April 29, 2016, 07:34:17 pm »
Haha, the idea is to make it easier to understand! You absolutely can just set a hex constant,  if that's what works for you.

What the example is doing is constructing the constant by shifting (<<) a 1 over to the specified position,  so for example  including (1<<ADEN) means turn on the adc enable bit. The binary or (|)  combines the selected bits into a single byte. The |= then updates the register, without upsetting what's already there.  You can use = to set it exactly if that's what you need.
 

Offline sleemanj

  • Super Contributor
  • ***
  • Posts: 3024
  • Country: nz
  • Professional tightwad.
    • The electronics hobby components I sell.
Re: ATMEGA328 adc , registors
« Reply #3 on: April 29, 2016, 11:46:52 pm »
Haha, the idea is to make it easier to understand! You absolutely can just set a hex constant,  if that's what works for you.

However, consider that if you should move to a different chip which is close-but-not-quite the same as the one you developed on that by using a hex constant you are hardcoding to that specific chip and also making it hard to do a search across your codebase for instances where you are fiddling a bit. 

Using the named bits is a better idea generally.

If the bit-shifting irritates, you can use the _BV() macro.

   ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0) | _BV(ADEN);
~~~
EEVBlog Members - get yourself 10% discount off all my electronic components for sale just use the Buy Direct links and use Coupon Code "eevblog" during checkout.  Shipping from New Zealand, international orders welcome :-)
 

Offline retrolefty

  • Super Contributor
  • ***
  • Posts: 1648
  • Country: us
  • measurement changes behavior
Re: ATMEGA328 adc , registors
« Reply #4 on: April 30, 2016, 12:47:04 am »
Hi everyone !
I have a bit of a miss understanding of the way I should address registers on AVRs .
I am playing with the mega328p and I just cant understand what the F**** is going on with the registers.
I am trying to use the ADC but I can't understand how to initialize the registers , I seen some code examples and they all look like this:
ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN); , why????
why can't it look like a normal register setup :"ADCSRA = 0x7f;", obviously it is not working for me....
I can copy it all like an idiot but I want to understand why I should declare it like this :ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);
sorry if this is confusing , I am really frustrated .
can someone explain this?

 Using register names instead of hard coded HEX values allow better portability between models of AVR. The 'names' get automatically converted to the approprate HEX values by the build tools supplied.
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 9946
  • Country: nz
Re: ATMEGA328 adc , registors
« Reply #5 on: April 30, 2016, 01:29:01 am »
It's just a way to easily change some of the bits to turn a feature on/off without effecting other bits.

It all makes perfect sense once you understand Bit mathematics in C.
Its not that complicated, it just looks like it.
Its well with sitting down and figuring out whats happening.
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline FreddyVictor

  • Regular Contributor
  • *
  • Posts: 164
  • Country: gb
Re: ATMEGA328 adc , registors
« Reply #6 on: April 30, 2016, 07:35:37 am »
"ADCSRA = 0x7f;"
and what the heck does that do?
oh well, it's off to the datasheet...

ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);
oh right, sets pre-scaler bits 0,1 + 2 and then enables it
 

Offline 0x007Topic starter

  • Contributor
  • Posts: 25
Re: ATMEGA328 adc , registors
« Reply #7 on: April 30, 2016, 07:55:33 am »
ah, ok that's explains it, I see now it makes sense.
It drove me nuts because I am not used to it, I learnd mcu programming thru 8051 chips they are much more straight forward(and don't have as much registers).
Thank you very much, I'll try to implement this knowledge now but I have one little question :
I want to use adc channel 0 of the atmega328p chip, I need to do the next:
1. hook up the lead to the 23rd pin of the chip.
2. feed ADMUX register 0 , because I am using the channel 0 and the external reference.
3. set ADCSRA to ADEN=1,ADSC=0(not starting conversion yet),ADATE=0,ADIF=(leaving it alone),ADIE=0,ADSPS = 011(it doesn't meter set the clock to/8).
4. when I want to start a conversion I ADSC to 1 and whit for ADIF to become logic 1.
5. then I read the ADCL and ADCH registers ,int val=0, val|=ADCL, val<<=4,val |=ADCL.
Am I correct ?
and I don't quite get how DIDR1 and DIDR0 effect the adc?
 

Offline old gregg

  • Regular Contributor
  • *
  • Posts: 130
  • Country: 00
Re: ATMEGA328 adc , registors
« Reply #8 on: April 30, 2016, 10:56:34 am »
DIRD0 is the register to disable the hardware logic circuitry of the D port. You can use it to reduce power consumption while using the ADC (cf p.252 datasheet).

ADMUX shouldn't be 0, only the last 4 bits significate the channel you're using. ADMUX also sets the reference voltage.

take a look at this series of video they're very nice :

https://www.youtube.com/playlist?list=PLE72E4CFE73BD1DE1

 

Offline 0x007Topic starter

  • Contributor
  • Posts: 25
Re: ATMEGA328 adc , registors
« Reply #9 on: April 30, 2016, 04:21:08 pm »
I maneged to get it working, but for some reason in order to take a reading I need to reset the chip each time .
this is the function:
Code: [Select]
int ADC_get(){
   unsigned int AVal=0;
   ADMUX = 0x40;
   ADCSRA = 0xc5;
   Delay_ms(8);
   while(!ADIF);
   AVal|=ADCH,Aval<<=8,Aval|=ADCL;
   return AVal;
}
and for some reason it wont work with out the delay .
long story short  |O
what have I done wrong ?
 

Offline PeterFW

  • Frequent Contributor
  • **
  • Posts: 577
  • Country: de
    • Stuff that goes boom
Re: ATMEGA328 adc , registors
« Reply #10 on: April 30, 2016, 04:52:54 pm »
what have I done wrong ?

You do not give the µC time to finish the conversion, check the ADSC bit in the ADCSRA register wich gets cleared after the conversion is finished in single conversion mode, if you do not use free running:

while(ADCSRA & (1<<ADSC));

Alternatively, flash the arduino bootloader on the chip and use the Arduino IDE wich spares a lot of trouble and grief at the expense of code size. 
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3240
  • Country: gb
Re: ATMEGA328 adc , registors
« Reply #11 on: May 01, 2016, 07:30:20 pm »
what have I done wrong ?

You do not give the µC time to finish the conversion, check the ADSC bit in the ADCSRA register wich gets cleared after the conversion is finished in single conversion mode, if you do not use free running:

while(ADCSRA & (1<<ADSC));

Alternatively, flash the arduino bootloader on the chip and use the Arduino IDE wich spares a lot of trouble and grief at the expense of code size.

The OP is trying to poll the ADIF bit to check for end of conversion which can be done (though polling ADSC is easier overall), but there are two problems:

1) ADIF is a bit position, not a register, so while(ADIF) will always be true.
2) ADIF must be cleared by software after it's set.

If you want to use ADIF for a particular reason, something like this should work */

Code: [Select]
int ADC_get(){
   ADCSRA |= _BV(ADIF); /* Clear the ADC interrupt flag by writing a '1' to the bit */
   ADMUX = 0x40;
   ADCSRA = 0xc5;
   while( (ADCSRA & _BV(ADIF)) == 0x00 ); /* ADIF will be set when conversion has completed */
  /* ADCW is a 16 bit pseudo register which reads the ADC bytes in the correct order.
      Works in (at least) GCC, IAR, ImageCraft, Rowley and Codevision compilers */
   return ADCW;
}
 

Offline Neverther

  • Regular Contributor
  • *
  • Posts: 129
Re: ATMEGA328 adc , registors
« Reply #12 on: May 01, 2016, 08:00:40 pm »
I had the same, one conversion and second one did not work.

I noticed is that the example for reading ADCH and ADCL separately did not work for some reason. Use the ADC/ADCW instead as it will get it right (I guess gcc optimizer or something broke it?). With that change my code worked.

It is supposed to read low byte first, that will lock the registers and they can be written only when the high byte is retrieved.

Read high first, then low = the registers are locked and new conversions wont save.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8221
  • Country: 00
Re: ATMEGA328 adc , registors
« Reply #13 on: May 01, 2016, 08:06:43 pm »
Code: [Select]
AVal|=ADCH,Aval<<=8,Aval|=ADCL;

I would do
Code: [Select]
AVal = ADC;

or
Code: [Select]
AVal = (ADCH << 8) | ADCL;

Faster ways exist.
================================
https://dannyelectronics.wordpress.com/
 

Offline Neverther

  • Regular Contributor
  • *
  • Posts: 129
Re: ATMEGA328 adc , registors
« Reply #14 on: May 02, 2016, 05:51:50 am »
I would do
..
Code: [Select]
AVal = (ADCH << 8) | ADCL;
...

This type of assigment was the one that failed for me.
 

Offline mikerj

  • Super Contributor
  • ***
  • Posts: 3240
  • Country: gb
Re: ATMEGA328 adc , registors
« Reply #15 on: May 03, 2016, 10:30:06 am »
Code: [Select]
AVal|=ADCH,Aval<<=8,Aval|=ADCL;

I would do
Code: [Select]
AVal = ADC;


ADC does not exist as a virtual register in all compilers, primarily because of potential conflict with the ADC instruction.  ADCW does and is the preferred solution.

or
Code: [Select]
AVal = (ADCH << 8) | ADCL;


Bearing in mind that the registers need to be read in a specific order this has no guarantee of working, and no guarantee of the same behaviour across different compilers or even compiler versions.  Not a good solution in this case.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf