[START][L21 xplained] START ADC ASYNC driver causing MCU to crash

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

Hello all,

 

I am designing a proportional buck converter controller using the SAM L21 for practice (final goal is to design MPPT controller, this is to test parts for prototype). Unfortunately the START ADC driver is giving me some issues.

 

My main code looks like this:

#include <atmel_start.h>
#include <stdio.h>
#include "user_hal.h"

#define TARGET_V 2000	//mVolts
#define K_CN		1	//Kc numerator
#define K_CD		1       //Kc denom
#define VOLTAGE_DIVIDER_VALUE 570 //R1 = 470ohm, R2 = 100 ohm
#define PWM_PERIOD 480*4
#define U_BIAS	PWM_PERIOD/2		//half pwm

uint8_t adc0_value[2];
uint8_t teststring[8] = "test";
uint32_t output_voltage;
int16_t error_voltage;
uint32_t new_pwm;
uint32_t last_pwm = U_BIAS;

int main(void)
{
	/* Initializes MCU, drivers and middleware */

	atmel_start_init();
	spi_m_sync_enable(&SPI_INSTANCE);
	gfx_mono_init();
	pwm_set_parameters(&PWM_0, 480, 240);
	pwm_enable(&PWM_0);
	delay_ms(5);
	timer0_init();
	ADC_init();
	delay_ms(10); //gives the internal reference time to stabalize
	adc_async_start_conversion(&ADC_0);

	int adc_target = (TARGET_V*4096)/(VOLTAGE_DIVIDER_VALUE*10);
	last_pwm = U_BIAS;

	while (1) {

		if (adc_async_read_channel(&ADC_0, 0, adc0_value, 2))
		{

			output_voltage = adc0_value[0] | adc0_value[1] << 8;
			error_voltage = adc_target - output_voltage;
			//int P = P_calc(error_voltage); not implemented yet
			//int I = I_calc(error_voltage);
			last_pwm = new_pwm;
			new_pwm = ( last_pwm + (((K_CN*error_voltage * PWM_PERIOD))/(K_CD*4096)));
			if (new_pwm > PWM_PERIOD)
			{
				new_pwm = PWM_PERIOD;
			}

			if (new_pwm < 0)
			{
				new_pwm = 0;
			}
			pwm_set_parameters(&PWM_0, PWM_PERIOD, new_pwm);
			adc_async_start_conversion(&ADC_0);
			gpio_set_pin_level(DGI_GPIO3, true);
		}

	}
}

The issue is that _adc_async_get_data_size() is sometimes returning unreasonable values, like 106, instead of the 2 that it should return.

 

Here's the function code:

uint8_t _adc_async_get_data_size(const struct _adc_async_device *const device)
{
	return hri_adc_read_CTRLC_RESSEL_bf(device->hw) == ADC_CTRLC_RESSEL_8BIT_Val ? 1 : 2;
}

I don't understand how that could possibly return 106. shouldn't it only return a 1 or 2?

 

I've attached the rest of my code just in case it's helpful.

 

the only other C file in my code that I've written is my HAL file:

 

#include <atmel_start.h>
#include "user_hal.h"
#include <stdio.h>

#define VOLTAGE_DIVIDER_VALUE 570

extern bool ADC_DONE_FLAG;
extern uint16_t output_voltage;

static struct timer_task TIMER_0_screen_task;

static void ADC_cb(const struct adc_async_descriptor *const descr, const uint8_t channel)
{
	gpio_set_pin_level(DGI_GPIO3, false);
}

void print_integer(uint32_t i, int x, int y) {
	char int_string[0];
	i = (VOLTAGE_DIVIDER_VALUE*10*i)/(4096);
	sprintf(int_string, "%04ld", i);
	gfx_mono_text_draw_string(&MONOCHROME_TEXT_0_desc, (uint8_t*)int_string, x, y, &basic_6x7);

}

static void TIMER_0_screen_cb(const struct timer_task *const timer_task)
{
	gpio_set_pin_level(DGI_GPIO2, true);
	print_integer(output_voltage, 5, 5);
	gpio_set_pin_level(DGI_GPIO2, false);
}

void ADC_init() {
	adc_async_register_callback(&ADC_0, 0, ADC_ASYNC_CONVERT_CB, ADC_cb);
	adc_async_enable_channel(&ADC_0, 0);

}

void timer0_init() {
	TIMER_0_screen_task.interval = 500;
	TIMER_0_screen_task.cb       = TIMER_0_screen_cb;
	TIMER_0_screen_task.mode     = TIMER_TASK_REPEAT;

	timer_add_task(&TIMER_0, &TIMER_0_screen_task);
	timer_start(&TIMER_0);
}

int16_t P_calc(int16_t eV) {
	return 0;
}

int16_t I_calc(int16_t eV) {
	return 0;
}

 

Attachment(s): 

Last Edited: Wed. Jun 13, 2018 - 11:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It seems like some of my post got eaten when I edited a spelling mistake. the _adc_async_get_data_size() function is called by the adc_async_read_channel() function.

int32_t adc_async_read_channel(struct adc_async_descriptor *const descr, const uint8_t channel, uint8_t *const buffer,
                               const uint16_t length)
{
	uint8_t  data_size, index;
	uint32_t num;
	uint16_t was_read = 0;

	ASSERT(descr && buffer && length);
	ASSERT(descr->channel_max >= channel);
	data_size = _adc_async_get_data_size(&descr->device);
	ASSERT(!(length % data_size));
	(void)data_size;

	index                                         = descr->channel_map[channel];
	struct adc_async_channel_descriptor *descr_ch = &descr->descr_ch
; CRITICAL_SECTION_ENTER() num = ringbuffer_num(&descr_ch->convert); CRITICAL_SECTION_LEAVE() while ((was_read < num) && (was_read < length)) { ringbuffer_get(&descr_ch->convert, &buffer[was_read++]); } descr_ch->bytes_in_buffer -= was_read; return was_read; }

 

It's the line 

ASSERT(!(length % data_size));

that's causing the program to stop, because the line above it is returning an impossible value. Hope that clarifies the problem. My apologies for poor proofreading.

Last Edited: Thu. Jun 14, 2018 - 01:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

UPDATE: commenting out the assert causes the program to run, and perfectly fine as far as I can tell, but it has to be there for a reason, right?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void print_integer(uint32_t i, int x, int y) {
	char int_string[0];
	i = (VOLTAGE_DIVIDER_VALUE*10*i)/(4096);
	sprintf(int_string, "%04ld", i);
	gfx_mono_text_draw_string(&MONOCHROME_TEXT_0_desc, (uint8_t*)int_string, x, y, &basic_6x7);

}

Zero size for the buffer there would be a problem if this function is called. Should be at least 5 bytes.

/Lars