problemy s HW SPI

Zdravim,

vyskytol sa mi zaujimavy problem.

Mam odskusanu rutinu na hw SPI. velmi trivialnu podla datasheetu k Atmega32. Vyskusal som ju na inom HW aky som doteraz pouzival a cuduj sa svete nefunguje.
tak sm si spravil jednoduchy testovaci program pre zapis stavov 8 LED do 74595 ako v hw tak i v sw verzii. SW verzia bez problemov bezi. SW verzia sa aktivuje odREMovanim #define SW_SPI.
Progam zbehne priblizne 1x za 200ms a okrem odoslania bajtu cez SPI, este zmeni stav LEDky (SET_TLED, RES_TLED) takze vidim, ze sw facha.

HW verzia sa sprava tak cunde, ze ak nezaREMujem riadok

while(!(SPSR & (1<<SPIF)));

program sa na nom bezpecne zasekne a dalej nejde - LED neblika, iba sa rozsvieti.
Na SPI nemam okrem programatora a 74595 zapojene nic. Skusal som aj po odpojeni programatora (DRAGON).
Tento program pre HW SPI nebezi ani na ATmega32, 324P, 644P na danom HW - testovanych spolu 6 rovnakych dosiek.
Pre SW SPI absolutne bez problemov. S rychlostou IO problem nebude, problem je, ze procesor sa tvari, ze neodoslal bajt z registra.
Na SPI su len R o 5V a len ta 74595. Ak by bola chyba v mojom HW, nebezalo by ani SW SPI, ale to bezi bez problemov. Mam zaroven zistene, ze SPI periferia si alokovala prislusne piny procesora, lebo po inicializacii SPI uz s nimi nejde manipulovat (odskusane na inom testovacom kode).

Skusal som s rozne nastavenymi FUSE (Brown-out, ) Xtal 14.7456MHz, nastaveny CKOUT. S casovanim problem nie je, lebo LED blika pravidelne, tak ako je sw napisany. Napajanie 5,13V.

Ked som vsak presne tento program spustil na ATmega32 v inom HW (ale SPI je zapojene rovnako), bez problemov bezala SW SPI i HW SPI. Porad kto mozes. HW rozdiely medzi doskami som v oblasti SPI skumal a ziadne nenasiel.

Prikladam cely testovaci program, ziadne interrupty, cista slucka.

testovaci program


// tetovaci program na nefunkcnost HW SPI

#define XTAL 14745600
#ifndef F_CPU
#define F_CPU XTAL
#endif

#include <stdio.h>
#include <avr/io.h>
#include <util/delay.h>

// definuje, kde sa nachadzaju jednotlive piny, je urcite v poriadku
#define TLED 3 // PC3, TMS
#define OUT_TLED (DDRC |= (1<<TLED))
#define IN_TLED (DDRC &= ~(1<<TLED))
#define RES_TLED (PORTC |= (1<<TLED))
#define SET_TLED (PORTC &= ~(1<<TLED))
#define GET_TLED (!(PINC & (1<<TLED)))

#define SPI0_MISO 6 // PB6
#define OUT_SPI0_MISO (DDRB |= (1<<SPI0_MISO))
#define IN_SPI0_MISO (DDRB &= ~(1<<SPI0_MISO))
#define SET_SPI0_MISO (PORTB |= (1<<SPI0_MISO))
#define RES_SPI0_MISO (PORTB &= ~(1<<SPI0_MISO))
#define GET_SPI0_MISO (PINB & (1<<SPI0_MISO))

#define SPI0_MOSI 5 // PB5
#define OUT_SPI0_MOSI (DDRB |= (1<<SPI0_MOSI))
#define IN_SPI0_MOSI (DDRB &= ~(1<<SPI0_MOSI))
#define SET_SPI0_MOSI (PORTB |= (1<<SPI0_MOSI))
#define RES_SPI0_MOSI (PORTB &= ~(1<<SPI0_MOSI))
#define GET_SPI0_MOSI (PINB & (1<<SPI0_MOSI))

#define SPI0_CLK 7 // PB7
#define OUT_SPI0_CLK (DDRB |= (1<<SPI0_CLK))
#define IN_SPI0_CLK (DDRB &= ~(1<<SPI0_CLK))
#define SET_SPI0_CLK (PORTB |= (1<<SPI0_CLK))
#define RES_SPI0_CLK (PORTB &= ~(1<<SPI0_CLK))
#define GET_SPI0_CLK (PINB & (1<<SPI0_CLK))

#define SPI0_SET 4 // PC4
#define OUT_SPI0_SET (DDRC |= (1<<SPI0_SET))
#define IN_SPI0_SET (DDRC &= ~(1<<SPI0_SET))
#define SET_SPI0_SET (PORTC |= (1<<SPI0_SET))
#define RES_SPI0_SET (PORTC &= ~(1<<SPI0_SET))
#define GET_SPI0_SET (PINC & (1<<SPI0_SET))

//
// #define SW_SPI

int main()
{

static uint8_t pom_prem, hodnota;

for(; ; ) {

pom_prem++;
OUT_SPI0_SET;
SET_SPI0_SET;
OUT_SPI0_MOSI;
SET_SPI0_MOSI;
OUT_SPI0_CLK;
SET_SPI0_CLK;

//hodnota = pom_prem;
if (pom_prem & 0x01) {
#ifdef SW_SPI
hodnota = 0x55;
#else
hodnota = ~0x01;
#endif

OUT_TLED;
RES_TLED;
}
else {
#ifdef SW_SPI
hodnota = 0xAA;
#else
hodnota = ~0x02;
#endif

SET_TLED;
}
RES_SPI0_SET;

#ifdef SW_SPI
// *************** cast s SW SPI *********************

for( pom_prem2 = 0; pom_prem2 < 8; pom_prem2++) {
RES_SPI0_CLK;
if (hodnota & 0x01) SET_SPI0_MOSI;
else RES_SPI0_MOSI;
_delay_us(200);
SET_SPI0_CLK;
_delay_us(200);
hodnota = hodnota>>1;

}

// *************** END cast s SW SPI *********************
#else

// *************** cast s HW SPI *********************

DDRB = (1<<PB5)|(1<<PB7);
// Enable SPI, Master, set clock rate fck/16
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0);
SPDR = hodnota;
while(!(SPSR & (1<<SPIF)));

// *************** END cast s HW SPI *********************
#endif

//_delay_us(200);

SET_SPI0_SET;

_delay_ms(200);

}
}

******************************************** end of file ****************************

za kazdu radu ci nasmerovanie vopred dakujem.
Zdrojak

Martin

Tak sa problem nasiel.

Pre informovanost sirokej verejnosti uvadzam ze,
nejde o problem s optimalizaciu, v tomto smere sa na C nepravom hadzu hromy blesky (casovanie sa musi principialne robit inak :slight_smile: ).

Cely problem je v tom, ze /SS MUSI byt aspon na chvilu po restarte v log.1 pred tym ako bude prepnuty na vystup, aby SPI pracoval podla ocakavania. Da sa to dosiahnut aj dvojitym nastavenim na vystup, ale to treba odlaborovat. Potom je mozne s /SS cvicit ako s vystupnou nozickou do lubovole. A o tom, ake ma dosledky /SS ako IN pre SPI v rezime MASTER je napisane v datasheete.
Vymysleli to tak asi preto, aby cez SPI mohlo k jednej Flash prisupovat viac procesorov, co sa moze niekedy hodit, napriklad ak dva procaky komunikuju s jednou MMC kartou, alebo si cez SPI FRAM odovzdavaju haldy parametrov a ich priama komunikacia nie je priliz vhodna.

Len chcem podotknut, ze ak je /SS ako vstup a nastane zapis do vystupneho registra pocas urovne /SS v log.0 a nasledne po zapise nadobudne /SS hodnotu log.1, bajt sa z bufera automaticky neodvysiela. Treba si dat na to pri takychto aplikaciach bacha a nespoliehat sa iba na testovanie bitu SPIF, lebo to nie je ucinne a program sa tam moze krasne zaseknut (odskusane).

Myslim, ze ak sa navrhne HW tak, ze je /SS ako vystupny (napr. ako nejaky /CS) a ma odpor o 5V, s hore uvedenymi problemami sa nestretnete a vsetko bude spolahlivo fungovat.

Martin