Reading analog channel MANUALLY

Hi!

Me and my buddy are trying to read an analog chanel to no avail.

The task is part of a weather station that we are building for a course that requires it.
We do not have access to the libraries that include functions like;
AnalogRead(), PinMode(), DigitalWrite() etc so everything has to be done manually by setting bits on every register.

This is what we tried to read the analog channel:

*ADC_CHER = 1<<5; //Enables channel 5
*ADC_MR = 1<<7; //Enables freerun mode to convert continously
while(true)
{
     if(...key pressed...)
           printf("%d   \n", *ADC_CDR5 & 0xFFF); //Prints the converted data that's stored on the channels own register
}

The analog pin is hooked up to a photo resistor (LDR).
The program always prints 0 whenever I press the key.
We are working with "Arduino DUE"

What are we doing wrong?

You first need to setup something like this:

adc_init(ADC, 84000000, 84000000/2, 0);
adc_configure_timing(ADC, 1, ADC_SETTLING_TIME_3, 0);

As I said, we don't have access to those libraries. I.e. no adc_init and the other.

I would appreciate if you could copy the implementations of those 2 functions.
However, you say "like this" so I assume that you are only guessing.

rs1485,

The source is included with the Arduino distribution.

I believe it's all there, if you look for it.

You'll want to read the chip's datasheet as well.

Does the course project require that you not use Arduino's libraries? I ask because I wonder 'what could possibly be the point of all of this'?

If the use of the Arduino libraries is forbidden, then is is a great exercise. I just hope that it's not due tomorrow.

-Chris

rs1485:
As I said, we don't have access to those libraries. I.e. no adc_init and the other.

I would appreciate if you could copy the implementations of those 2 functions.
However, you say "like this" so I assume that you are only guessing.

I am not guessing as I am using it myself, but I am not sure of that kind of things are actually initialize by default. Anyway, here are the functions.
adc_init:

/**
 * \brief Initialize the given ADC with the specified ADC clock and startup time.
 *
 * \param p_adc Pointer to an ADC instance.
 * \param ul_mck Main clock of the device (value in Hz).
 * \param ul_adc_clock Analog-to-Digital conversion clock (value in Hz).
 * \param uc_startup ADC start up time. Please refer to the product datasheet
 * for details.
 *
 * \return 0 on success.
 */
uint32_t adc_init(Adc *p_adc, const uint32_t ul_mck,
		const uint32_t ul_adc_clock, const uint8_t uc_startup)
{
	uint32_t ul_prescal;

	/*  Reset the controller. */
	p_adc->ADC_CR = ADC_CR_SWRST;

	/* Reset Mode Register. */
	p_adc->ADC_MR = 0;

	/* Reset PDC transfer. */
	p_adc->ADC_PTCR = (ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS);
	p_adc->ADC_RCR = 0;
	p_adc->ADC_RNCR = 0;

	ul_prescal = ul_mck / (2 * ul_adc_clock) - 1;
	p_adc->ADC_MR |= ADC_MR_PRESCAL(ul_prescal) |
			((uc_startup << ADC_MR_STARTUP_Pos) &
			ADC_MR_STARTUP_Msk);
	return 0;
}

And adc_configure_timing:

/**
 * \brief Configure ADC timing.
 *
 * \param p_adc Pointer to an ADC instance.
 * \param uc_tracking ADC tracking time = uc_tracking / ADC clock.
 * \param uc_settling Analog settling time = (uc_settling + 1) / ADC clock.
 * \param uc_transfer Data transfer time = (uc_transfer * 2 + 3) / ADC clock.
 */
void adc_configure_timing(Adc *p_adc, const uint8_t uc_tracking,
		const enum adc_settling_time_t settling,const uint8_t uc_transfer)
{
	p_adc->ADC_MR |= ADC_MR_TRANSFER(uc_transfer)
			| settling | ADC_MR_TRACKTIM(uc_tracking);
}

chriskner:
rs1485,

The source is included with the Arduino distribution.

I believe it's all there, if you look for it.

You'll want to read the chip's datasheet as well.

Does the course project require that you not use Arduino's libraries? I ask because I wonder 'what could possibly be the point of all of this'?

If the use of the Arduino libraries is forbidden, then is is a great exercise. I just hope that it's not due tomorrow.

-Chris

Thank you very much for asking something that is answered in the original post.

Gericom:

rs1485:
As I said, we don't have access to those libraries. I.e. no adc_init and the other.

I would appreciate if you could copy the implementations of those 2 functions.
However, you say "like this" so I assume that you are only guessing.

I am not guessing as I am using it myself, but I am not sure of that kind of things are actually initialize by default. Anyway, here are the functions.
adc_init:

/**
  • \brief Initialize the given ADC with the specified ADC clock and startup time.

  • \param p_adc Pointer to an ADC instance.

  • \param ul_mck Main clock of the device (value in Hz).

  • \param ul_adc_clock Analog-to-Digital conversion clock (value in Hz).

  • \param uc_startup ADC start up time. Please refer to the product datasheet

  • for details.

  • \return 0 on success.
    */
    uint32_t adc_init(Adc *p_adc, const uint32_t ul_mck,
    const uint32_t ul_adc_clock, const uint8_t uc_startup)
    {
    uint32_t ul_prescal;

    /*  Reset the controller. */
    p_adc->ADC_CR = ADC_CR_SWRST;

    /* Reset Mode Register. */
    p_adc->ADC_MR = 0;

    /* Reset PDC transfer. */
    p_adc->ADC_PTCR = (ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS);
    p_adc->ADC_RCR = 0;
    p_adc->ADC_RNCR = 0;

    ul_prescal = ul_mck / (2 * ul_adc_clock) - 1;
    p_adc->ADC_MR |= ADC_MR_PRESCAL(ul_prescal) |
    ((uc_startup << ADC_MR_STARTUP_Pos) &
    ADC_MR_STARTUP_Msk);
    return 0;
    }



And adc_configure_timing:


/**

  • \brief Configure ADC timing.
  • \param p_adc Pointer to an ADC instance.
  • \param uc_tracking ADC tracking time = uc_tracking / ADC clock.
  • \param uc_settling Analog settling time = (uc_settling + 1) / ADC clock.
  • \param uc_transfer Data transfer time = (uc_transfer * 2 + 3) / ADC clock.
    */
    void adc_configure_timing(Adc *p_adc, const uint8_t uc_tracking,
    const enum adc_settling_time_t settling,const uint8_t uc_transfer)
    {
    p_adc->ADC_MR |= ADC_MR_TRANSFER(uc_transfer)
    | settling | ADC_MR_TRACKTIM(uc_tracking);
    }

Thanks!
Can you tell me what PDC stands for?
One more thing I would like to know is the function "AnalogRead" because that's what comes up whenever I try to google. I would be very thankful if you can paste the code of AnalogRead too!

PDC stands for Peripheral DMA Controller. You can find it in the datasheet just like adc stuff: http://www.atmel.com/images/doc11057.pdf
As for reading. Reading ADC_CDR5 should do it. I have done it as well.

rs1485:
Thank you very much for asking something that is answered in the original post.

You're quite welcome.

It was less about asking, as more about confirming your situation. You never explicitly said that using the libraries was forbidden for this class project. You simply stated that they weren't available.

I wanted to be certain that you had these rules to follow, and that you weren't misunderstanding the Arduino platform.

As for helping you, the answers really are in the source and datasheet.

The goal of any class project is to understand something that you yet do not (as is the point of all education). The fine folks here will certainly be happy to help you with specific problems as they arise, but they won't (and shouldn't) do your school work for you.

Read the docs. Work the problem. Test your ideas. Give examples of your failures. Provide theories about why perhaps things didn't work. Seek advice. Rinse. Repeat until success. Profit. Welcome to engineering.

Best of luck in your endeavor.

-Chris

chriskner:
-snip-

-Chris

We have been sitting with it for AT LEAST 10 hours trying to solve it, we asked the mentor 3 times already and there is no one else to ask because we're basically ahead of everyone else. What we received as information to what we are supposed to do in order to read the value is to:

Select/Enable channel with the ADC_CHER registry
Set the ADC clock to 14 MHZ with the ADC_MR registry
Start a conversion with the ADC_CR registry
Wait until ADC_SR's DRDY bit is 1
Read the converted data with either the ADC_LCDR or the ADC_CDRx register

We've tried a lot of different ways to approach this. We have tried setting the values that the adc_init and adc_config functions sets above in the other post, reading the internal temp sensor, turning on freerun mode.... We have tried so many different variations that I can't even remember all of them.

I am not asking anyone to do our school work for us. I am searching for guidance about exactly which registers we need to change in order to make the ADC work. We are not two stupid guys and are capable enough to understand what the different registers do, once we know which ones we need to use.

You can imagine how frustrated we get when all we get from hours of googling is "use AnalogRead".
We only need a small sample of code that is a MUST for using ADC. We obviously need to understand it because we're going to use it for temperature sensors and what not.

rs1485:

chriskner:
-snip-

-Chris

We have been sitting with it for AT LEAST 10 hours trying to solve it, we asked the mentor 3 times already and there is no one else to ask because we're basically ahead of everyone else. What we received as information to what we are supposed to do in order to read the value is to:

Select/Enable channel with the ADC_CHER registry

Set the ADC clock to 14 MHZ with the ADC_MR registry
Start a conversion with the ADC_CR registry
Wait until ADC_SR's DRDY bit is 1
Read the converted data with either the ADC_LCDR or the ADC_CDRx register




We've tried a lot of different ways to approach this. We have tried setting the values that the adc_init and adc_config functions sets above in the other post, reading the internal temp sensor, turning on freerun mode.... We have tried so many different variations that I can't even remember all of them.

I am not asking anyone to do our school work for us. I am searching for guidance about exactly which registers we need to change in order to make the ADC work. We are not two stupid guys and are capable enough to understand what the different registers do, once we know which ones we need to use. 

You can imagine how frustrated we get when all we get from hours of googling is "use AnalogRead".
We only need a small sample of code that is a MUST for using ADC. We obviously need to understand it because we're going to use it for temperature sensors and what not.

This is the adc sampling code I use for my USB Audio Interface. It actually contains everything for reading ADC values.

#include <Arduino.h>
#include <USB/USBAPI.h>
#include <USB/USBDesc.h>
#include <USB/Audio.h>

boolean B = false;

void startTimer(Tc *tc, uint32_t channel, IRQn_Type irq, uint32_t frequency) {
  pmc_set_writeprotect(false);
  pmc_enable_periph_clk((uint32_t)irq);
  TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1);
  uint32_t rc = VARIANT_MCK/2/frequency; //128 because we selected TIMER_CLOCK4 above
  TC_SetRA(tc, channel, rc/2); //50% high, 50% low
  TC_SetRC(tc, channel, rc);
  TC_Start(tc, channel);
  tc->TC_CHANNEL[channel].TC_IER=TC_IER_CPCS;
  tc->TC_CHANNEL[channel].TC_IDR=~TC_IER_CPCS;
  NVIC_EnableIRQ(irq);
}

void setup() {
  InitAdc();
  AUDIO_InitBuffers(512 * 10);
  while(true)
  {
    if(USBD_GetCurrentInterface() == 1) break;
  }
  udd_enable_endpoint_interrupt(AUDIO_ENDPOINT_DATA & 0xF);
  udd_enable_in_send_interrupt(AUDIO_ENDPOINT_DATA & 0xF);

  startTimer(TC1, 0, TC3_IRQn, 48000);
}

void InitAdc()
{
  adc_init(ADC, 84000000, 84000000/2, 0);
  adc_configure_timing(ADC, 1, ADC_SETTLING_TIME_3, 0); 
  adc_disable_all_channel(ADC);
  adc_enable_channel( ADC, (adc_channel_num_t)g_APinDescription[A0].ulADCChannelNumber ); 
  adc_enable_channel( ADC, (adc_channel_num_t)g_APinDescription[A11].ulADCChannelNumber ); 
  ADC->ADC_MR &= ~ADC_MR_FWUP;
  ADC->ADC_MR |= ADC_MR_FWUP_OFF;
  ADC->ADC_MR &= ~ADC_MR_LOWRES;
  //ADC->ADC_MR |= ADC_MR_LOWRES_BITS_10;
  ADC->ADC_MR &= ~ADC_MR_FREERUN;
  ADC->ADC_MR |= ADC_MR_FREERUN_ON;
  ADC->ADC_MR &= ~ADC_MR_PRESCAL_Msk;
  ADC->ADC_MR |= ADC_MR_PRESCAL(0);
  ADC->ADC_MR &= ~ADC_MR_SLEEP;
  ADC->ADC_MR |= ADC_MR_SLEEP_NORMAL;
  ADC->ADC_MR &= ~ADC_MR_STARTUP_Msk;
  ADC->ADC_MR |= ADC_MR_STARTUP_SUT0;
  ADC->ADC_MR &= ~ADC_MR_ANACH;
}

void loop() {
  
}

void TC3_Handler()
{
  TC_GetStatus(TC1, 0);

  int data1 = *((uint32_t*)0x400C006C);
  int data2 = *((uint32_t*)0x400C0084);
  //12288 is for centering. Math: (32767 / 2 + 32767 / 4) / 2
  short val1 = map(data1, 0, 4095, -32767, 32767) + 12288;
  short val2 = map(data2, 0, 4095, -32767, 32767) + 12288;

  AUDIO_WriteSample((uint16_t)val1, (uint16_t)val2);
}

I didn't edit anything. If you want a more clean example, just ask.

We were told that only the Channel Enable Register, Control Register and MR register is needed to make it work and we can then read from the LCDR or CDRx registers.

I noticed yours have things like startup time, settling time, the adc clock etc. We assumed those are unecessary to tamper with because the default should work too.

We don't have much experience with the terminology used in these kind of things so it's difficult to figure out what does what. That is why I need to know which registers are to be modified.

If you don't mind writing an example with direct values, i.e. remove any function call and replace it with a direct value, it would help us a lot.

Even if you only explained what the different things like settling, startup time and the others affect would help a lot too!

rs1485:
We were told that only the Channel Enable Register, Control Register and MR register is needed to make it work and we can then read from the LCDR or CDRx registers.

I noticed yours have things like startup time, settling time, the adc clock etc. We assumed those are unecessary to tamper with because the default should work too.

We don't have much experience with the terminology used in these kind of things so it's difficult to figure out what does what. That is why I need to know which registers are to be modified.

If you don't mind writing an example with direct values, i.e. remove any function call and replace it with a direct value, it would help us a lot.

Even if you only explained what the different things like settling, startup time and the others affect would help a lot too!

I will try to make a good example for you soon. But in the meanwhile, just take a look at the adc dection of this document: http://www.atmel.com/images/doc11057.pdf All registers are described here.

Indeed the datasheet is pretty good - but its a complex beast and you will need to
read the ADC section of the datasheet carefully 2 or 3 times (there is no magic
route to understanding a complex machine)

If its any consolation the ARM processors are as complex as
mainframe computers of the 60's and 70's, and they came with a whole
pallet full of manuals!

Gericom:

rs1485:
We were told that only the Channel Enable Register, Control Register and MR register is needed to make it work and we can then read from the LCDR or CDRx registers.

I noticed yours have things like startup time, settling time, the adc clock etc. We assumed those are unecessary to tamper with because the default should work too.

We don't have much experience with the terminology used in these kind of things so it's difficult to figure out what does what. That is why I need to know which registers are to be modified.

If you don't mind writing an example with direct values, i.e. remove any function call and replace it with a direct value, it would help us a lot.

Even if you only explained what the different things like settling, startup time and the others affect would help a lot too!

I will try to make a good example for you soon. But in the meanwhile, just take a look at the adc dection of this document: http://www.atmel.com/images/doc11057.pdf All registers are described here.

Here is what we got now:

In our Init file:
PMC_PCER1 = 0x1FFF; //We just turn on all of the clocks for comfort. (We will fix it later on)
ADC_MR = 2<<8 | 1<<7; //Prescaler to 2 (gives a clock of MCK / 4), freerun mode on
ADC_CHER = 1<<1; //Enable channel 1

When reading:
printf(..., ADC_CDR1 & 0xFFF); //Print the converted data that's stored in the channel's register

Output:
0
3xxx 
2xx
1xx
4x
4x
4x
4x
...and so on

Where:
3xxx is often somewhere around 3200

2xx is often somewhere around 220

1xx doesn't appear often but is somewhere around 130

4x is the value that varies between each reading. It's often sitting at 40, 41, 43 and sometimes at 38 or 39. It appears to be random but there is a slight limit to it, it rarely changes from a big value to a small value, i.e. from 43 to 38 or vice versa. It does , however, change by up to ~10 if we press a button on our keypad.

The output is the same if we have the photo sensor plugger in or not.

It's connected like this:
VCC -------- R -------PIN---------LDR--------GROUND
Note: R is connected to LDR and PIN is connected between them.

I suspect we need to edit more bits in the ADC_MR register before we can get this right.

Problem is, we have no idea how a high/low SETTLING time (amongst others) affect the ADC.