Hallo

In een project ben ik bezig met het proberen van het configureren van een externe AD converter aan mijn ATmega16. Het gaat over een AD0831 en deze zou later vervangen kunnen worden door de AD0838. Nu is het probleem dat ik niet weet hoe de communicatie te starten. In de datasheet van de ADC staat er dat de CS lijn laag moet getrokken worden (dit is altijd het geval aangezien ik slechts 1 chipje via SPI bestuur). Vervolgens staat er dat de eerste conversie start door de eerste "1" die aan de DI ingang (waar mijn analoge ingang aanhangt) verschijnt. Komt deze "1" er vanaf er een spanning aan de DI klem wordt gezet? Via avrfreaks zag ik een voorbeeld om de AD0832 te interfacen dat je de data van de ADC kan inlezen in een variabele door variabele=SPI(0x00); Dit werkt echter niet. Heeft iemand ervaring met het gebruik van een AD083x? Ik programmeer in C met de avrgcc compiler. Ik kan eventueel mijn code die ik tot nu toe heb posten maar weet niet of dit iets uithaalt aangezien er slechts enkele lijnen code zijn.

thx, frederik

je mag je cs lijn niet continu laag houden, ook al heb je slechts 1 device aan spi bus.
Verder een kwestie van die SPI register van de atmega lezen en vullen volgens mij.

Ah ok, dit zal ik al aanpassen. Misschien moet ik het SPDR uitlezen in mijn variabele? Lijkt mij inderdaad logischer...

edit: maar blijkt ook niet te lukken... welk register moet ik uitlezen (waar staat mijn geconverteerde ana-> digi waarde? Geraakt de conversie gestart met enkel de CS laag te trekken?

[Bericht gewijzigd door kronoz op (45%)]

Je moet het ganse SPI-gedoe wel eerst initialiseren, voor je iets kan ontvangen. In de datasheet kan je lezen hoe je dat kan doen.

Ja dit heb ik natuurlijk gedaan. Ik zal voor de gemakkelijkheid mijn code (als je dat al code durft te noemen :)) even posten.

#include <avr/io.h> 
#include <avr/interrupt.h>
#include "SPI.h"

int junk,i,input;
void port_init (void) 

	{
 
	PORTB=0x00;  
	DDRB=0x00;   
	DDRA=0xFF;
	PORTA=0x00; 
	  
    }

int main (void)

	{

	port_init();
	SPIinit();

	while(1)
	
		{

		PORTB^=0<<4; // Zet CS laag
		//input=SPI(0x00); // Zo stond het op AVRfreaks.net
		input=SPDR; // Zo dacht ik dat het moest maar blijkbaar is het niet zo
		
		PORTA=input;
		PORTB^=1<<4; // Zet CS terug hoog

		}
	
	}

En mijn SPI header ziet er als volgt uit:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <inttypes.h>
#include <avr/iom16.h>

#include "SPI.h"

#define SPIDI	5	// PB6
#define SPIDO	4	// PB5
#define SPICLK	6	// PB7
#define SPICS	3	// PB4

void SPIinit(void) 

	{
	
	DDRB &= ~(1 << SPIDI);	// set port B SPI data input to input
	DDRB |= (1 << SPICLK);	// set port B SPI clock to output
	DDRB |= (1 << SPIDO);	// set port B SPI data out to output 
	DDRB |= (1 << SPICS);	// set port B SPI chip select to output
	SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
	//SPCR=0b01010010;
	PORTB &= ~(1 << SPICS);	// set chip select to low (MMC is selected)

	}

char SPI(char d) 
		
		{  // Stuur karakter over SPI
	
		char received = 0;
		SPDR = d;
		while(!(SPSR & (1<<SPIF)));
		received = SPDR;
		return (received);
		
		}

De initialisatie lijkt me OK. Verder zou


input=SPI(0x00);

moeten doen wat je verlangt. Vermits de ADC toch geen MOSI-pin heeft, maakt het uiteraard niets uit, wat je via SPI uitstuurt.

moet je niet eerst 8 bits uitklokken naar de ADC, zodat deze de data terug klokt in het SPDR register?
Heb je geen opcodes nodig voor je ADC?

Over opcodes vind ik toch niets terug in de datasheets van de AD083x.
Wat bedoel je moet 8 bits uitklokken naar de ADC?

@ pros: zou normaal de 8 bits geconverteerde waarde dan in de input variabele staan na input=SPI(0x00); ?

Dat zou moeten, ja. Als dat niets bruikbaars oplevert, kan je testen door de ADC even uit zijn voetje te halen, en MISO beurtelings met Vcc en met GND te verbinden. Als dat in het eerste geval 255 en in het 2e geval 0 oplevert, is je SPI-code OK en moet je de fout bij de ADC zoeken.

Ik heb de datasheet v/d ADC0831 eens (vluchtig) bekeken. Zo te zien verlangt dat ding meer dan 8 klokpulsen voor een ADC-conversie (zie ADC0831 Timing). Misschien is het handiger, als je daar een softwarematige SPI-techniek voor gebruikt. Daar heb je enkel 2 uitgangspinnen en 1 ingangspin voor nodig.

Ja het verbinden van MISO met VCC en GND levert inderdaad 255 en 0 op. De fout zit dus bij de ADC. Kan dit iets te maken hebben omdat er meer dan 8 klokpulsen nodig zijn?

EDIT: mijn signaal hangt aan de V(+) ingang, de V(-) ingang heb ik aan de GND gehangen omdat volgens de datasheets het verschil tussen deze twee ingangen wordt omgevormd en iets - 0 is nog steeds iets. Klopt deze redenering :)?

[Bericht gewijzigd door kronoz op (40%)]

Ik vermoed het. Hou er ook rekening mee, dat de ADC00831 bij de eerste klokpuls D7 uitspuwt, en bij de 8e klokpuls D0. Zelf zou ik zonder aarzelen voor software-SPI gaan.

Oh, ja, dat eerste bit (D7 dus) komt pas te voorschijn bij de neergaande flank van de tweede klokpuls!

En ja, in Single-ended mode moet de min-ingang aan GND.

Wat heeft dit als voordeel? Dan moet ik zelf een pen voor CS kiezen, een voor MISO en een voor de clock?

EDIT: Maakt dit uit dat dit pas de tweede klokpuls gebeurd? Ik zou toch zoizo iets moeten binnekrijgen? Soms krijg ik 255 en soms 0 maar nooit iets ertussen...

[Bericht gewijzigd door kronoz op (44%)]

Misschien draait je SPI-klok te snel? 400kHz is het maximum, zie ik (en 10kHz het minimum).

[Edit] Je hebt de traagste SPI-klok gekozen, zie ik. Dat kan moeilijk de oorzaak zijn.

Juist voor zulke situaties kan softwarematige SPI een uitkomst bieden, omdat je dan alles meer in de hand hebt. Daarna kan je nog steeds de hardware-SPI inzetten.

[Bericht gewijzigd door pros op (15%)]

Ja mijn uC draait aan 8MHz, ik heb de fosc/64 gedaan waardoor ik een klok van 125KHz zou moeten hebben maar als ik dit nameet klopt dit niet, ik zie enkel een zeer wazig en klein signaal. Ik heb ook met een 555 een klok gemaakt van 250KHz, dit signaal zit wel geod enkel is de duty cycle nog misschien te klein want het werkt ook hiermee niet. Ik ga nu eens proberen de duty cycle aan te passen.

EDIT: Nu heb ik een klok gemaakt met een 555 die tegen 135KHz draait, de duty cycle is nu ook inorde en het signaal gaat mooi van 0-5V itt het signaal dat door de uC zelf gegenereerd werd... Maar nog steeds geen verschil. Is er niet een bepaalde startsequentie die ik moet geven of iets waar ik over zie.

[Bericht gewijzigd door kronoz op (28%)]

OK, met de nieuwe klok blijkt hij toch al iets te doen, tenminste als ik in mijn code handmatig de chipselect hoog en laag maak. Of de conversies nu correct zijn weet ik echter niet. Bij 5V is PORTA volledig aangestuurd, bij 0V is er niets aangestuurd en tussenin is PORTA nogal redelijk onstabiel maar dat zal wel met onstoring/dendering te maken hebben denk ik?

Ik heb dit nog op de code-rommelzolder gevonden:



#define SPI_DDR    DDRB
#define SPI_PORT   PORTB
#define SPI_MISO   PB6
#define SPI_MOSI   PB5
#define SPI_SCK    PB7
#define SPI_SS     PB4


void init_spi_master(void)
{
    SPCR = (_BV(SPE) | _BV(MSTR) | _BV(SPR0) | _BV(SPR1));	// Enable SPI, CLK /= 128
    cbi(SPI_DDR, SPI_MISO);					// MISO = input
    SPI_DDR |= (_BV(SPI_MOSI) | _BV(SPI_SCK) | _BV(SPI_SS));	// SS, SCK en MOSI = output
    sbi(SPI_PORT, SPI_SS);					// Disable slave
}



unsigned char spi_mastertransmit(unsigned char c)
{
    cbi(SPI_PORT, SPI_SS);					// Enable slave
    SPDR = c;							// data -> transmitregister
    loop_until_bit_is_set(SPSR, SPIF);
    c = SPDR;
    sbi(SPI_PORT, SPI_SS);					// Disable slave
    return (c);							// Keer terug met antwoord
}

En dan in main()


    unsigned int ucnt = 0;
    init_spi_master();

    while (1) {
	ucnt = spi_mastertransmit(0);
    }

Dat levert op de CLK-uitgang een nette blokspanning met een puls/pauze-verhouding van 50% op. Raar dat dit bij jou niet het geval is. De SPI-programmeerkabel zit toch niet in de weg?

BTW: die sbi() en cbi() instructies werken enkel met avr-gcc, als je <compat/deprecated.h> mee included.

OK bedankt. De fout zat em ten eerste al in het feit dat in mijn SPI transmit functie de CS niet laag werd getrokken en dan weer hoog werd gemaakt. Dan de klok die zo'n raar signaal afgeeft, ik snap zelf niet hoe dit kan. Ik heb net uw instelling voor SPCR overgenomen, hier krijg ik nu wel een signaal dat ik kan meten. Het is wel slechts 50mV en dat lijkt mij vreemd? Nog een bijkomend vraagje: gaat het programma pas verder als heel het 8 bits woord ontvangen is of moet hier zelf een wacht lus voor voorzien worden?

EDIT:wat ook vreemd lijkt is dat als ik 5V aanleg als ingang aan de ADC, er 0 uit PORTA komt. Bij iets minder dan 5V zijner dan wel weer waarden voor PORTA. Kan dit aan de Vref liggen? Deze hangt nu aan VCC.

EDIT 2: aangezien DB7 eerst wordt ontvangen en DB0 laatst moet ik dan het resultaat dat in input sta verschuiven?

Dan de klok die zo'n raar signaal afgeeft, ik snap zelf niet hoe dit kan. Ik heb net uw instelling voor SPCR overgenomen, hier krijg ik nu wel een signaal dat ik kan meten. Het is wel slechts 50mV en dat lijkt mij vreemd?

Zeer vreemd. Hier wisselt de SPI-klok netjes tussen 0V en 5V. Staat er nergens in je code een vergeten regeltje dat die pin als ingang bestempelt?

EDIT:wat ook vreemd lijkt is dat als ik 5V aanleg als ingang aan de ADC, er 0 uit PORTA komt. Bij iets minder dan 5V zijner dan wel weer waarden voor PORTA. Kan dit aan de Vref liggen? Deze hangt nu aan VCC.

Ik denk dat je die 0 als "overflow" kan beschouwen.

EDIT 2: aangezien DB7 eerst wordt ontvangen en DB0 laatst moet ik dan het resultaat dat in input sta verschuiven?

Die bitvolgorde blijkt normaal te zijn bij SPI, als ik het goed begrijp. Maar D7 is onbruikbaar (zie ADC-datasheet) en D0 bekom je pas bij de 9e klokpuls.

Ja maar dient daar rekening mee gehouden te worden of is dit gewoon een eigenschap van de component? Ik bedoel maar moet ik iets in mijn software ondernemen of zou alles zo goed moeten gaan?

Dat is nu net het probleem met die hardware-SPI: ofwel lees je 1 byte, en heb je enkel D7 ... D1, of je leest er 2 waarbij het eerst-ingelezen bit v/d 2e byte D0 is. De ADC krijgt dan wel 16 klokpulsen, terwijl hij er 11 verwacht - of dat de werking verstoort, weet ik niet.

Worden hier

PORTB^=0<<4;
			input=SPI(0x00);
			PORTB^=1<<4;

8 bits ineens ingelezen of moet ik zelf 11 (of 16 in het geval van 2 bytes) klokpulsen maken door PORTB.4 hoog en laag te trekken en telkens de waarde in input te zetten en alles 1 plaats opschuiven zodat de nieuwe bit kan ingeladen worden in input?

Zodra je een byte in SPDR plaatst, worden de 8 bits van die byte uitgestuurd via MOSI en gelijktijdig worden 8 bits langs MISO ingelezen en in SPDR geplaatst. Je hoeft dus zelf geen klokpulsen op te wekken - dat gebeurt automagisch.

Misschien is dit bruikbaar om data van de ADC0831 in te lezen. Zeker ben ik er niet van - zonder die ADC valt het niet te testen:


unsigned char spi_ADC_in(void)
{
    unsigned char a, b;

    SPI_PORT |= (1 << SPI_SS);			// Enable slave
    SPDR = 0;					// dummy data -> transmitregister
    loop_until_bit_is_set(SPSR, SPIF);		// Even wachten...
    a = SPDR & 0x7F;				// a bevat nu bits 7 ... 1
    SPDR = 0;					// dummy data -> transmitregister
    a = a << 1;					// Bits 1 plaats opschuiven naar links
    loop_until_bit_is_set(SPSR, SPIF);		// Even wachten...
    b = SPDR & 0x80;				// We hebben enkel het MSb nodig,
    b = b >> 7;					// dat we 7 plaatsen naar rechts schuiven
    SPI_PORT &= ~(1 << SPI_SS);			// Disable slave
    a |= b;					// Samenvoegen
    return (a);					// Keer terug met antwoord
}

Nu ben ik weg; m'n fiets staat al een uur ongeduldig te trappelen in de gang... :-)

[Bericht gewijzigd door pros op (67%)]

@pros: ik heb uw voorbeeldcode even getest maar deze heeft dezelfde resultaten als de code die ik al had. Toch bedankt voor alle moeite/tijd die je er al hebt ingestoken!

@ iedereeen :): Waaraan kan het liggen dat mijn ADC niet mooi de waardes converteerd zoals de interne ADC dit wel doet?? Hij converteerd wel degelijk iets maar ik kan niet mooi in stapjes van 1 bit binair verhogen... Sterker nog, ik denk niet dat de geconverteerde waardes kloppen. Kan dit iets te maken hebben met Vref? Deze ligt aan Vcc en als mijn potmeter vol uitgestuurd is heeft deze exact dezelfde spanning als Vcc, dit zou toch voldoende moeten zijn? Verder zit er heel veel storing/ruis/... op de uitgang van de ADC, veel meer dan bij de interne ADC, dit is toch niet echt normaal? Ook heb ik een condensator van 0,1uF tussen Vcc en GND gezet vlak bij de ADC. Ik snap echt niet wat ik nog fout kan doen....

Hallo

Kan je eens zeggen wat ik hier moet aanpassen in de code want ik werk met een PIC controller en programmeer in C