Unable to serial print when using ADC

Hi beautiful people,

I am trying to print ADC values of an ATSAM3X8E (Arduino due) on the serial terminal of the computer. I am doing bare-metal programming in C (I am a noobie in this) on the sam3x8e using a JTAG in microchip studio framework. When I try print something on the terminal without the ADC functions the serial print gives output but if uncomment the adc functions it does not give output. I also found that when ADC is activated the gpios doesn´t work. Could you please help me on what I am going wrong?

I really appreciate your time and support

Many thanks :slight_smile:
CODE:

#include <asf.h>
#include <conf_board.h>
#include <string.h>
#include <stdio.h>

// UART definitions
#define CONF_UART_BAUDRATE   115200
#define CONF_UART_PARITY     UART_MR_PAR_NO
#define CONF_UART            CONSOLE_UART
#define CONF_USART_SERIAL_H



#define LED			IOPORT_CREATE_PIN(PIOD, 0)

float result;

static void configure_console(void)
{
	const usart_serial_options_t uart_serial_options = {
		.baudrate = CONF_UART_BAUDRATE,
		.paritytype = CONF_UART_PARITY
	};

	/* Configure console UART. */
	sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
	stdio_serial_init(CONF_UART, &uart_serial_options);
}

void led_blink(void)
{
	ioport_init();
	ioport_set_pin_dir(LED, IOPORT_DIR_OUTPUT);
	ioport_set_pin_level(LED, IOPORT_PIN_LEVEL_HIGH);
}

void adc_setup(void)
{
	//ADC clock setup
	adc_init(ADC, sysclk_get_main_hz(), sysclk_get_main_hz()/2, ADC_STARTUP_NORM);
	
	//Configure ADC tracking time, settling time and data transfer time
	adc_configure_timing(ADC, 1, ADC_SETTLING_TIME_0, 1);
	
	//Setting 12 bits of resolution for the ADC
	adc_set_resolution(ADC, ADC_MR_LOWRES_BITS_12);
	
	//Enabling ADC on channel 15 - temperature sensor
	adc_enable_channel(ADC, ADC_TEMPERATURE_SENSOR);
	
	//enable adc on channel 5,6,7
	adc_enable_channel(ADC, ADC_CHANNEL_5);
	adc_enable_channel(ADC, ADC_CHANNEL_6);
	adc_enable_channel(ADC, ADC_CHANNEL_7);
	
	//SW trigger with freerun on
	adc_configure_trigger(ADC, ADC_TRIG_SW, 1);
	
	//Enable End Of Conversion (EOC) interrupt on Channel 5,6,7
	adc_enable_interrupt(ADC, ADC_IER_EOC5);
	adc_enable_interrupt(ADC, ADC_ISR_EOC6);
	adc_enable_interrupt(ADC, ADC_ISR_EOC7);
	adc_enable_interrupt(ADC, ADC_IER_EOC15);  
	
	//Enable NVIC to accept interrupts from ADC
	NVIC_EnableIRQ(ADC_IRQn); 
	
	//Send peripheral clock to ADC block
	pmc_enable_periph_clk(ID_ADC);
	
	//Enable temperature sensor
	adc_enable_ts(ADC);
}

void ADC_Handler()
{
	
	// Lets read the value of ADC Temperature Sensor
 	uint32_t adc = adc_get_channel_value(ADC, ADC_CHANNEL_5);
	uint32_t adc_1 = adc_get_channel_value(ADC, ADC_CHANNEL_6);
	uint32_t adc_2 = adc_get_channel_value(ADC, ADC_CHANNEL_7);
	
	//uint32_t t_adc = adc_get_channel_value(ADC, ADC_TEMPERATURE_SENSOR);
	
	// Convert ADC values to voltage 
	float result		= adc	*0.0008056;
	float result_1		= adc_1 *0.0008056;
	float result_2		= adc_2 *0.0008056;
	
	//Convert ADC value to temperature
	float t			= (t_adc *0.0008056*27)/0.800;
	
}


int main (void)
{
	double result;
	/* Insert system clock initialization code here (sysclk_init()). */
	sysclk_init();
	board_init();
	adc_setup();
	adc_start(ADC);
	
	adc_disable_interrupt(ADC, ADC_IER_EOC5);
	NVIC_DisableIRQ(ADC_IRQn);
	
	configure_console();
	printf("hello_world");
	led_blink();

	while(1)
	{	
		//adc_start(ADC);
		printf("hello_world \r \n"); //debugging
		printf(result);
		
	}
} 

Bare-metal programming of SAM devices is not supported by Arduino.

Then you'll find better support in their forum.

3 Likes

@DrDiettrich Thank you for the response :slight_smile: :+1:

At a guess, there is an ADC interrupt flag that you need to clear explicitly. Eg, you turn on the temp sensor interrupt, but don't read that channel in your ISR, so it keeps interrupting.

Normally, using a framework like ASF (MSF?) is not considered "bare metal", BTW. I'll concede that it's barer than Arduino...

@westfw Hi, Thank you for the valid information. Could you please explain what is explicit clearing of ADC interrupt? I am sorry i am new to this and dont understand many things on first hand.

Thank you so much :slight_smile:

Hi @malamsthu

I'm not familiar with ASF/MSF, however in the ADC_Handler() function you need to test which ADC channel has completed its converstion.

Perhaps there's an equivalent function for it in ASF, but using the registers you'd do something like this:

void ADC_Handler() 
{
  if (ADC->ADC_ISR & ADC_ISR_EOC7)         // Check for the End Of Conversion on channel 7
  {    
    result = ADC->ADC_CDR[7];              // Read the result from the ADC, clears the EOC7 flag
    // Add the reset of your handler code here...

The code checks the End Of Conversion flag/bit on channel 7 (EOC7), which is in ADC's Interrupt Status Register (ISR). It does this to see if data on this channel is ready. If it is, then it reads the data from the ADC's Channel Data Register (CDR). Reading the CDR automatically clears the EOC7 flag/bit.

The same procedure can be used for the other channels within the ADC_Handler() function.

What is the problem? Do you mean that you have nothing printed on that line?

But you don't assign values to the result variable anywhere in the code.

@MartinL Thank you so much for the detailed explanation. What I understood is that EOC should be given in the Handler to check for end of conversion ? But if you see adc_setup() function i have already given the EOC in that. Any suggestions?

I Really appreciate your time.

Many thanks

Hi @b707,

Thanks for getting back. I cannot even print the hello world (debugg statement) on the terminal.
I have attached the snippet to show you. The values are updated in the varaible. the problem is with serial print.

You have a two independent result variables in the code - first the float result in the IRQ handler and the second double result in the loop() procedure. The values that you show in the debug screen - from the IRQ result variable, but the values that you try to print - is from the loop() result.
Changing the first result doesn't affected the second.
Read something about the variable scoping in C language.

@b707, I understand your point the double in the main was for a testcase. The issue is I am not able to see anything on the serial monitor. for instance the 'hello world´ in the main.

thank you :slight_smile:

No, the problem is not in double - float mismatch, if both variables were defined as float - everything would be the same.
Any variable declared inside curly braces is valid only inside this block. So your variable in the interrupt handler is local to the handler and is not visible anywhere else in the program.

I knowingly advised you to read about variable scope. It seems that you have a fundamental misunderstanding on this issue.

I get your point on global and local var. My only concern is that the serial monitor does not show anything, not even the debug message.

Many thanks :slight_smile:

Does it work with the ADC interrupt disabled?

@DrDiettrich Yes it works with adc interrupt disabled. when disabled i get the hello world on serial monitor

thanks

Hi people,

I found something online to read the temp of the arduino due on asf framework. But i didn´t understand it very well on how to take the raw ADC value? could anyone possibly help me out on this. I am pasting the code below. Could you please explain which part reads the raw adc here so that i can use it to display it on the serial terminal.

Many thanks :slight_smile:

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "asf.h"
#include "conf_board.h"

/** Size of the receive buffer and transmit buffer. */
#define BUFFER_SIZE     (100)

/** Reference voltage for ADC,in mv. */
#define VOLT_REF        (3300)

#if SAM3S || SAM4S || SAM3XA || SAM3N || SAM4C || SAM4CM
/* Tracking Time*/
#define TRACKING_TIME    1
/* Transfer Period */
#define TRANSFER_PERIOD  1
/* Startup Time*/
#if SAM4C || SAM4CM
#define STARTUP_TIME ADC_STARTUP_TIME_10
#else
#define STARTUP_TIME ADC_STARTUP_TIME_4
#endif
#endif

#if SAM3U
#ifdef ADC_12B
/* Start Up Time */
#define STARTUP_TIME               7
/* Off Mode Startup Time */
#define OFF_MODE_STARTUP_TIME      7
#else
#define STARTUP_TIME               3
#endif
/* Sample & Hold Time */
#define SAMPLE_HOLD_TIME           6
#endif

/** The maximal digital value */
#if SAM3S || SAM3XA || SAM4S
/** The maximal digital value */
#define MAX_DIGITAL     (4095)
#elif SAM3N || SAM4C || SAM4CM
#define MAX_DIGITAL     (1023)
#elif SAM3U
#ifdef ADC_12B
#define MAX_DIGITAL     (4095)
#else
#define MAX_DIGITAL     (1023)
#endif
#endif

#define STRING_EOL    "\r"
#define STRING_HEADER "-- ADC Temperature Sensor Example --\r\n" \
		"-- "BOARD_NAME" --\r\n" \
		"-- Compiled: "__DATE__" "__TIME__" --"STRING_EOL

/** adc buffer */
static int16_t gs_s_adc_values[BUFFER_SIZE] = { 0 };

/**
 * \brief Simple function to replace printf with float formatting.
 * One decimal with rounding support.
 */
static void print_temp(float temp)
{
	int16_t s_integer1 = 0;
	int32_t l_integer2 = 0;

	Assert(INT16_MAX > (temp * 100.0) && INT16_MIN < (temp * 100.0));

	/* Cast to integer */
	s_integer1 = (int16_t) (temp * 100.0);

	/* Rounding */
	l_integer2 = s_integer1 / 10;
	if ((s_integer1 - l_integer2 * 10) > 4) {
		s_integer1 = l_integer2 + 1;
	} else {
		if ((s_integer1 - l_integer2 * 10) < -4) {
			s_integer1 = l_integer2 - 1;
		} else {
			s_integer1 = l_integer2;
		}
	}

	/* Quotient */
	l_integer2 = s_integer1 / 10;
	/* Remainder */
	s_integer1 = s_integer1 - l_integer2 * 10;

	if (s_integer1 < 0) {
		printf("Temp:-%d.%d \n\r", (int16_t) ((l_integer2) * (-1)),
				(int16_t) ((s_integer1) * (-1)));
	} else {
		printf("Temp:%d.%d \n\r", (int16_t) l_integer2,
				(int16_t) s_integer1);
	}
}

/**
 * \brief Read converted data through PDC channel.
 *
 * \param pADC The pointer of adc peripheral.
 * \param pwBuffer The destination buffer.
 * \param dwSize The size of the buffer.
 */
static uint32_t adc_read_buffer(Adc * pADC, int16_t * pwBuffer, uint32_t dwSize)
{
	/* Check if the first PDC bank is free. */
	if ((pADC->ADC_RCR == 0) && (pADC->ADC_RNCR == 0)) {
		pADC->ADC_RPR = (uint32_t) pwBuffer;
		pADC->ADC_RCR = dwSize;
		pADC->ADC_PTCR = ADC_PTCR_RXTEN;

		return 1;
	} else {	/* Check if the second PDC bank is free. */
		if (pADC->ADC_RNCR == 0) {
			pADC->ADC_RNPR = (uint32_t) pwBuffer;
			pADC->ADC_RNCR = dwSize;

			return 1;
		} else {
			return 0;
		}
	}
}

/**
 * \brief Configure UART console.
 */
static void configure_console(void)
{
	const usart_serial_options_t uart_serial_options = {
		.baudrate = CONF_UART_BAUDRATE,
		.paritytype = CONF_UART_PARITY
	};

	/* Configure console UART. */
	sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
	stdio_serial_init(CONF_UART, &uart_serial_options);
}

/**
 * \brief Systick handler, start new conversion.
 */
void SysTick_Handler(void)
{
	if (adc_get_status(ADC) & (1 << ADC_TEMPERATURE_SENSOR)) {
		adc_start(ADC);
	}
}

/**
 * \brief ADC interrupt handler.
 */
void ADC_Handler(void)
{
	uint32_t ul_counter;
	int32_t l_vol;
	float f_temp;
	uint32_t ul_value = 0;
	uint32_t ul_temp_value = 0;

	if ((adc_get_status(ADC) & ADC_ISR_RXBUFF) == ADC_ISR_RXBUFF) {

		/* Multisample */
		for (ul_counter = 0; ul_counter < BUFFER_SIZE; ul_counter++) {
			ul_value += gs_s_adc_values[ul_counter];
		}
		/* Averaging */
		ul_temp_value = ul_value / 10;
		ul_value = ul_value / 100;
		ul_temp_value -= (ul_value * 10);
		/* Round for last decimal */
		if (ul_temp_value > 4) {
			ul_value++;
		}

		l_vol = ul_value * VOLT_REF / MAX_DIGITAL;
#if SAM3S || SAM3XA
		/* Using multiplication (*0.37736) instead of division (/2.65). */
		f_temp = (float)(l_vol - 800) * 0.37736 + 27.0;
#elif SAM4N
		/* Using multiplication (*0.21186) instead of division (/4.72). */
		f_temp = (float)(l_vol - 1440) * 0.21186 + 27.0;
#else
		/* Using multiplication (*0.21276) instead of division (/4.7). */
		f_temp = (float)(l_vol - 1440) * 0.21276 + 27.0;
#endif
		print_temp(f_temp);
		/* Clear the buffer. */
		memset(gs_s_adc_values, 0x0, sizeof(gs_s_adc_values));
		/* Start new pdc transfer. */
		adc_read_buffer(ADC, gs_s_adc_values, BUFFER_SIZE);

	}
}

/**
 * \brief adc_temp_sensor Application entry point.
 *
 * Initialize adc to 12-bit, enable channel 15,turn on
 * temp sensor, pdc channel interrupt for temp sensor
 * and start conversion.
 *
 * \return Unused (ANSI-C compatibility).
 */
int main(void)
{
	/* Initialize the SAM system. */
	sysclk_init();
	board_init();

	configure_console();

	/* Output example information. */
	puts(STRING_HEADER);

	/* 10 ms timer */
	if (SysTick_Config(sysclk_get_cpu_hz() / 100)) {
		puts("-F- Systick configuration error\r");
		while (1) {
		}
	}
	/* Enable peripheral clock. */
	pmc_enable_periph_clk(ID_ADC);
	/* Initialize ADC. */
	/*
	 * Formula: ADCClock = MCK / ( (PRESCAL+1) * 2 )
	 * For example, MCK = 64MHZ, PRESCAL = 4, then:
	 * ADCClock = 64 / ((4+1) * 2) = 6.4MHz;
	 */
	/* Formula:
	 *     Startup  Time = startup value / ADCClock
	 *     Startup time = 64 / 6.4MHz = 10 us
	 */
	adc_init(ADC, sysclk_get_cpu_hz(), 6400000, STARTUP_TIME);
	/* Formula:
	 *     Transfer Time = (TRANSFER * 2 + 3) / ADCClock
	 *     Tracking Time = (TRACKTIM + 1) / ADCClock
	 *     Settling Time = settling value / ADCClock
	 *
	 *     Transfer Time = (1 * 2 + 3) / 6.4MHz = 781 ns
	 *     Tracking Time = (1 + 1) / 6.4MHz = 312 ns
	 *     Settling Time = 3 / 6.4MHz = 469 ns
	 */
	adc_configure_timing(ADC, TRACKING_TIME
#if !SAM4C && !SAM4CM
			, ADC_SETTLING_TIME_3, TRANSFER_PERIOD
#endif
	);

	adc_configure_trigger(ADC, ADC_TRIG_SW, 0);

	adc_check(ADC, sysclk_get_cpu_hz());

	/* Enable channel for potentiometer. */
	adc_enable_channel(ADC, ADC_TEMPERATURE_SENSOR);

	/* Enable the temperature sensor. */
	adc_enable_ts(ADC);

	/* Enable ADC interrupt. */
	NVIC_EnableIRQ(ADC_IRQn);

	/* Start conversion. */
	adc_start(ADC);

	adc_read_buffer(ADC, gs_s_adc_values, BUFFER_SIZE);

	/* Enable PDC channel interrupt. */
	adc_enable_interrupt(ADC, ADC_ISR_RXBUFF);

	while (1) {
	}
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.