Sida 1 av 1

AVR atmega 644P ADC problem

Postat: 10 februari 2010, 20:29:25
av mrOh
Hej.

Jag har försökt slänga ihop en liten höjd och accelerations logger som ska kastas med lite olika former ur en helikopter på fredag. Det mesta har fungerat bra men AD konverteringarna strular. Det har fungerat jättebra med bara en kanal, men såfort jag kollar en i taget av de 7 jag använder så blir alla värden ungefär samma och fel. Det första värdet är ofta rätt (ca 800) men sen minskar det o stannar runt 60-70. se nedan.

Kod: Markera allt

ch: 1. Value: 816 :: ch: 2. Value: 632 :: ch: 3. Value: 496 :: ch: 4. Value: 487 :: ch: 5. Value: 352 :: ch: 6. Value: 334 :: ch: 7. Value: 327 :: 
ch: 1. Value: 319 :: ch: 2. Value: 304 :: ch: 3. Value: 287 :: ch: 4. Value: 287 :: ch: 5. Value: 286 :: ch: 6. Value: 255 :: ch: 7. Value: 255 :: 
ch: 1. Value: 255 :: ch: 2. Value: 252 :: ch: 3. Value: 231 :: ch: 4. Value: 224 :: ch: 5. Value: 224 :: ch: 6. Value: 224 :: ch: 7. Value: 199 :: 
ch: 1. Value: 192 :: ch: 2. Value: 192 :: ch: 3. Value: 192 :: ch: 4. Value: 176 :: ch: 5. Value: 167 :: ch: 6. Value: 167 :: ch: 7. Value: 167 :: 
ch: 1. Value: 152 :: ch: 2. Value: 143 :: ch: 3. Value: 152 :: ch: 4. Value: 152 :: ch: 5. Value: 127 :: ch: 6. Value: 127 :: ch: 7. Value: 127 :: 
ch: 1. Value: 127 :: ch: 2. Value: 113 :: ch: 3. Value: 112 :: ch: 4. Value: 120 :: ch: 5. Value: 113 :: ch: 6. Value: 99 :: ch: 7. Value: 103 :: 
ch: 1. Value: 112 :: ch: 2. Value: 103 :: ch: 3. Value: 96 :: ch: 4. Value: 97 :: ch: 5. Value: 103 :: ch: 6. Value: 88 :: ch: 7. Value: 88 :: 
ch: 1. Value: 96 :: ch: 2. Value: 96 :: ch: 3. Value: 79 :: ch: 4. Value: 88 :: ch: 5. Value: 96 :: ch: 6. Value: 79 :: ch: 7. Value: 79 :: 
ch: 1. Value: 92 :: ch: 2. Value: 92 :: ch: 3. Value: 71 :: ch: 4. Value: 79 :: ch: 5. Value: 88 :: ch: 6. Value: 71 :: ch: 7. Value: 71 :: 
ch: 1. Value: 88 :: ch: 2. Value: 81 :: ch: 3. Value: 71 :: ch: 4. Value: 79 :: ch: 5. Value: 88 :: ch: 6. Value: 71 :: ch: 7. Value: 71 :: 
ch: 1. Value: 79 :: ch: 2. Value: 79 :: ch: 3. Value: 63 :: ch: 4. Value: 71 :: ch: 5. Value: 79 :: ch: 6. Value: 63 :: ch: 7. Value: 67 :: 
ch: 1. Value: 79 :: ch: 2. Value: 71 :: ch: 3. Value: 63 :: ch: 4. Value: 71 :: ch: 5. Value: 79 :: ch: 6. Value: 63 :: ch: 7. Value: 67 :: 
[/size]

Specs. ATMega644P, drivs på 3.3V. AREF avkopplad mot GND med 0u1F. AVCC är LC kopplad mot VCC 10uH och 0u1F . Allt enligt min tolkning av databladet.

Så vad är inkopplat:
ADC ch: 1. Trycksensor, drivs på 5V. Spänningsdelad utgång, 20k och 39k mot GND.
ADC ch: 2,3,4. Accelerometer X, Y, Z. Analoga utsignaler upp till 3.3V.
ADC ch: 5,6,7. Hänger lösa i detta läge.
UART
ISP
En switch mot PortB pin 1 som om på jordar och startar sen loggern i log läge. Är den ej på så är PortB pin1 uppdragen och den startar i utläsning/debug läge.

Ordnar schema om någon önskar.

Är det någon som har en aning om vad det kan bero på så blir jag evigt tacksam.
Se koden nedan, den är ganska hastigt ihopslängd och i många fall stulen från olika källor så jag ber om ursäkt för hur den ser ut.

Kod: Markera allt

/*	Absolute Pressure/Altimeter and Acceleration logger
	Johan Juhlén 2010.								*/

#define F_CPU 8000000// Clock Speed
#define UART_BAUD  19200

#include <avr/io.h>
#include <stdio.h> 
#include <stdlib.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/eeprom.h>
#include <avr/sleep.h>
#include <string.h>

#include "uart.h"

#define ADC_CHANNELS_USED 7
#define PIN_B1 (PINB&1)

//unsigned int EEMEM loggedData[2000];
unsigned int eemem_address = 0;

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);

void inits(void);
void dataLog(void);
unsigned int readADC(uint8_t ch);

//set some timer to count seconds.
volatile char buffer[16];

ISR(TIMER1_COMPA_vect)
{
	dataLog();
}


int main(void)
{
	
	int i;
	int j = 2000;
	unsigned int input;
	inits();
	volatile char buffer[16];
	stdout = &mystdout;
	
	for(;;)
	{
		if (!PIN_B1)
		{
			TCCR1B |= (1<<WGM12);		//CTC 
			TCCR1B |= (1<<CS12);		//Prescaler 1024
			TCCR1B |= (1<<CS10);		// -'-
			TIMSK1 |= (1<<OCIE1A);		//interrupt at compare match 

			OCR1A = 0x0F42;				//period = ~1s at 8MHz
			sei();
			while(1)
				{;}
		}
		printf("Welcome.\n\rPress 'r' to read out memory. (w to do a testwrite to memory WILL ERASE ALL CURRENT DATA!)\n\r> ");
		input = uart_get_schar();
		printf(input);
		if (tolower(input) == 'w')
		{
			printf("Write\n\r");
			unsigned int temp =830;
			while (1)
			{
				printf("temp = %u\n\r", temp);
				eeprom_write_word ((uint16_t *)eemem_address, temp);
//				sprintf(buffer, "%u", readADC(0));
				printf("%u written to EEPROM address %u.\n\r", eeprom_read_word((uint16_t *)eemem_address),eemem_address);
				_delay_ms(200);
				temp++;
				eemem_address = eemem_address +2;
				if (eemem_address > 1999)
					{
					printf("EEPROM FULL\n\r");
					while(1);
					}
			}
		}
		if (tolower(input) == 'r')
		{
			printf("Read\n\r");
			while (1)
			{
				printf("Value in EEPROM: %u\n\r", eeprom_read_word((uint16_t *)eemem_address));
				_delay_ms(200);
				eemem_address = eemem_address +2;
				if (eemem_address > 1999)
					{
					printf("Read out finished.\n\r");
					while(1);
					}
			}
		}
		if (tolower(input) == 'a')
		{
			printf("Read out ADC\n\r");
			while (1)
			{
				for (i = 0; i<ADC_CHANNELS_USED; i++)
				{ 
					printf("ch: %u. Value: %u :: ",i+1,readADC(i));
					_delay_ms(200);
				}
				printf("\n\r");
			}
		}
	}
}

void dataLog(void)
{
	unsigned int i, temp;
	for (i = 0; i<ADC_CHANNELS_USED; i++)
	{
		temp = readADC(i);
		eeprom_write_word ((uint16_t *)eemem_address, temp);
		eemem_address = eemem_address +2;
		if (eemem_address > 1999)
			{
			printf("EEPROM FULL\n\r");
			TIMSK1 &= ~(1<<OCIE1A); // Turn off timer1
			while(1);
			}
	} 
}


void inits(void)
{
	// Init PORT A
	DDRA =0x00;	// Inputs
	PORTA = 0x00;	// Not pulled up
	DDRB = 0;
	PORTB = 0xFF;
	
	// Init UART
	uart_init();
	
	// ADC init
	ADMUX |= (1 << REFS0);
	ADCSRA |= (1 << ADEN);

	return;
}

unsigned int readADC(uint8_t ch)
{
	int i;
	ADMUX |= ch;
	ADCSRA |= (1 << ADSC);
	while(ADCSRA & (1<<ADSC));
	return ADC;
}

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 20:40:27
av eqlazer
Ska det vara "ADMUX |= ch;", blir det inte så att du bara ettställer bitarna hela tiden och aldrig nollar?

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 20:56:31
av sodjan
Behöver du inte vänta (lite) efter byte av ADC kanal ?

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 20:57:35
av mrOh
eqlazer. Jag älskar dig. ^_^)b

Stort tack, imponerande att du såg det så snabbt.

min snabbfulfix blev att börja varje ADC läsning med att nolla.

Kod: Markera allt

ADMUX &=~(0x07);
sen för säkerhetsskull köra två avläsningar varje gång, enligt databladet kan den vara lite konstig på första avläsningen efter att man ändrat mux.

Tack. Även ni som tog er tid att läsa tråden och fundera.

mvh / Johan

EDIT: Sojan, ja jag tror det, men somsagt så fullöser jag det genom att göra två läsningar. Jag har gott om tid så det stör inte funktionen.

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 21:01:01
av SvenW
Dessutom bör det vara en liten fördröjning från det att man ställer ADMUX tills dess att man startar omvandlingen, har jag för mig.

Alltså så här i readADC(uint8_t ch)

Kod: Markera allt

ADMUX = 0;
ADMUX |= ch;
_delay_us (2);
ADCSRA |= (1 << ADSC);
PS. Alltså som Sodjan redan sagt. Det går fort i tråden ibland!

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 21:12:10
av mrOh
hmm en delay är ju helt klart snyggare.

Aktuell readADC() som funkar:

Kod: Markera allt

unsigned int readADC(uint8_t ch)
{
	ADMUX &=~(0x07);
	ADMUX |= ch;
	_delay_us (2);
	ADCSRA |= (1 << ADSC);
	while(ADCSRA & (1<<ADSC))
		{;}
	return ADC;
}

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 21:16:24
av sneaky
Då problemet är löst så lånar jag tråden lite. Sånt här: ADMUX &=~(0x07); ger mig huvudvärk. Kan någon bena ut vad som sker där åt mig?

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 21:20:59
av SvenW
ADMUX &=~(0x07);

Det är 'AVR språk' Och - lika med - icke - sju.
Man drar ner de sista tre bitarna till noll.

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 21:23:32
av mrOh
Det är ett kortare sätt att skriva ADMUX = (ADMUX &~(0x07)); Kolla upp AND och OR mask.
Lite info finns här. http://sv.wikipedia.org/wiki/Mask_(dator)

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 22:09:43
av sneaky
Ah, det var negeringen av 0x07 som fick mig att se i kors. För mig känns det som ett extra onödigt steg. Själv hade jag nog skrivit masken direkt istället (och binärt). Jag föredrar fortfarande den, för mig, lite tydligare varianten &=0b111111000. Men det är väl för att jag inte har bankat in "tänket" hårt nog ännu hehe.

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 22:32:04
av AndLi
SvenW skrev:ADMUX &=~(0x07);

Det är 'AVR språk' Och - lika med - icke - sju.
Man drar ner de sista tre bitarna till noll.
Det enda som är AVR språk i den raden skulle vara ADMUX, resten är standard C

Det lär inte vara sista gången du ser en sån rad, ofta ser det ut ungefär som följer:

Kod: Markera allt

#define RED_LED 0x40    

PORTD &= ~RED_LED;  //nollar RED_LED biten
PORTD |= RED_LED; //sätter RED_LED biten
Utan ~ varianten skulle man behöva två olika defines, en för ettställa och en för nollställning. Bli mer omständigt och ökar felrisken

Re: AVR atmega 644P ADC problem

Postat: 10 februari 2010, 22:33:55
av eqlazer
Gott att det löste sig! Om ändå mina egna ADC-problem vore såhär enkla att lösa :) (på nån Texas DSP-variant)

Re: AVR atmega 644P ADC problem

Postat: 11 februari 2010, 09:08:26
av vfr
sneaky skrev:Jag föredrar fortfarande den, för mig, lite tydligare varianten &=0b111111000. Men det är väl för att jag inte har bankat in "tänket" hårt nog ännu hehe.
Det tycker jag du gör rätt i. Den visade varianten är mest bra om man redan råkar ha en konstant som motsvarar det värdet man behöver, t.ex från en inkluderingsfil eller liknande. I sådana fall kan det vara dumt att definiera samma sak på ett nytt ställe då det blir fler ställen att ändra på om man skulle göra en konstruktionsändring. D.v.s större risk för fel. Om man kan undvika, så ska man låta bli att ha samma sak definierad på flera ställen.

Så båda sätten är bra, på varsitt sätt.