PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Hilfe bei Kommunikation über ISP-Schnittstelle



Bumbum
12.03.2013, 17:31
Hallo,

ich bin an der Entwicklung eines Programms für einen Tiny25. Ich bentöige dabei Werte, die ich über den integrierten ADC einlese irgendwo angezeigt. Da der Tiny25 so wenige Pins hat habe ich mir überlegt diese Werte über die ISP-Schnittstelle zu senden, da ich diese eh "onboard" habe um den Controller darüber zu programmieren.

Meine Idee war es also einen kleinen Umsetzer zu bauen, der mir die ISP-Daten auf eine serielel Schnittstelle übersetzt und umgekehrt. Für dieses Projekt habe ich einen Tiny2313 verwendet, da der noch rumlag. Die ISP-Schnittstelle ist ebenfalls so verdrahtet, dass ich den Tiny2313 darüber programmieren kann mit der Ausnahme das ich die Reset-Leitung per Jumper trennen kann und die Reset-Leitung von "außen" auf einen Eingangspin geht, damit ich die ISP-Schnittstelle abschalten kann solange der Tiny25 programmiert wird. Wenn also der externe Reset auf Low geht möchte ich die ISP-Schnittstelle abschalten.

Leider ist aus den Datenblättern für mich nicht so ganz ersichtlich wie ich das ganze programmieren soll. Den Umsetzer mit dem Tiny2313 habe ich mir als ISP-Slave gedacht. Hier mal mein Code dafür:



#define AVRGCC/**
ISP_RS232:
Interface um Daten zwischen ISP und RS232 zu übersetzen.
IO:
PB0 out LED gelb (Daten RS232 vorhanden)
PB1 out LED grün (Daten ISP vorhanden)
PB2 frei
PB3 frei
PB4 frei
PB5 MOSI
PB6 MISO
PB7 SCK
PD0 RXD
PD1 TXD
PD2 in disable (ext. Reset)
PD3 frei
PD4 frei
PD5 frei
PD6 Out LED rot (Buffer Überlauf)
**/

#include <avr/io.h>
#include <compiler.h>
#include <avr/interrupt.h>

//F_CPU = 8000000
#define BAUD 9600l
#define UBRRValue (F_CPU / (BAUD * 16) - 1)
#define BufferSize 16
#define LED_ge_0 PORTB = ~(~PORTB | (1<<PB0))
#define LED_ge_1 PORTB = (PORTB | (1<<PB0))
#define LED_gn_0 PORTB = ~(~PORTB | (1<<PB1))
#define LED_gn_1 PORTB = (PORTB | (1<<PB1))
#define LED_rt_0 PORTD = ~(~PORTD | (1<<PD6))
#define LED_rt_1 PORTD = (PORTD | (1<<PD6))
#define disable ((PIND & 1<<PD2) == 0)
#define RS232ready ((UCSRA & (1<<UDRE)) != 0)
#define AccessRead 0
#define AccessWrite 1
#define BufferRS232 0
#define BufferISP 1

volatile bool currentState = FALSE;
volatile U8 InBuffer[2][BufferSize];
volatile U8 Pointer[2][2];


void sendMsg (char *Msg, bool addCR)
{
while (*Msg != 0)
{
while ( !(UCSRA & (1<<UDRE)));
UDR = *Msg++;
}
if (addCR)
sendMsg ("\r\n", FALSE);
}

void incPointer (U8 Buffer, U8 Access)
{
if (++Pointer[Buffer][Access] >= BufferSize)
Pointer[Buffer][Access] = 0;
}
bool BufferDone (U8 Buffer)
{
return (Pointer[Buffer][AccessRead] == Pointer[Buffer][AccessWrite]);
}
void add2Buffer (U8 Buffer, U8 value)
{
U8 tmp = Pointer[Buffer][AccessWrite];
if (++tmp >= BufferSize)
tmp = 0;

if (tmp != Pointer[Buffer][AccessRead])
{
Pointer[Buffer][AccessWrite] = tmp;
InBuffer[Buffer][tmp] = value;
}
else
LED_rt_1;
}
U8 readBuffer (U8 Buffer)
{
return (InBuffer[Buffer][AccessRead]);
}

ISR (USART_RX_vect)
{
add2Buffer (BufferRS232, UDR);
}

ISR (USI_OVERFLOW_vect)
{
U8 value = USIDR;
if (currentState)
{
add2Buffer (BufferISP, value);
if (!BufferDone (BufferRS232))
incPointer (BufferRS232, AccessRead);
if (!BufferDone (BufferRS232))
USIDR = readBuffer (BufferRS232);
else
USIDR = 0;
}
else
LED_rt_1;
}

int main (void)
{
DDRB = 0b00000011;
PORTB = 0b00000000;

DDRD = 0b01000000;
PORTD = 0b00000000;
UCSRB = (1<<RXCIE) | (1<<TXEN) | (1<<RXEN);
UCSRC = (1<<UCSZ1) | (1<<UCSZ0);
UBRRH = (U8)(UBRRValue>>8);
UBRRL = (U8)UBRRValue;
Pointer[BufferRS232][AccessRead] = 0;
Pointer[BufferRS232][AccessWrite] = 0;
Pointer[BufferISP][AccessRead] = 0;
Pointer[BufferISP][AccessWrite] = 0;
sei ();

sendMsg ("", TRUE);
sendMsg ("RESET", TRUE);

while (1)
{
if (disable)
{
if (currentState)
{
USICR = 0;
sendMsg ("TARGET RESET", TRUE);
}
currentState = FALSE;
}
else
{
if (!currentState)
{
USICR = (1<<USIWM0) | (1<<USICS1) | (1<<USICLK) |(1<<USIOIE);
sendMsg ("TARGET START", TRUE);
if (!BufferDone (BufferRS232))
USIDR = readBuffer (BufferRS232);
else
USIDR = 0;
}
currentState = TRUE;
}



if (RS232ready & !BufferDone (BufferISP))
{
UDR = readBuffer (BufferISP);
incPointer (BufferISP, AccessRead);
}

if (BufferDone (BufferISP))
LED_gn_0;
else
LED_gn_1;
if (BufferDone (BufferRS232))
LED_ge_0;
else
LED_ge_1;
}

return (0);
}


Die RS232-Kommunikation läuft. RESET wird gemeldet und TARGET RESET bzw. TARGET START wird auch gemeldet. Und wenn ich etwas sende geht die gelbe LED als Anzeige an, dass etwas im RS232-Buffer vorhanden ist. (Nach 16 Bytes geht dann auch die rote LED an)

Der Tiny25 soll dann als ISP-Master funktionieren. Hier mal mein Test-Code um etwas zu senden:



#define AVRGCC
/**
Voltage Detector:

IO:
PB0 MOSI
PB1 MISO out LED
PB2 SCK
PB3 analog in (ADC3)
PB4 frei
**/

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

//F_CPU = 1000000
#define LED0 PORTB = ~(~PORTB | (1<<PB1))
#define LED1 PORTB = (PORTB | (1<<PB1))

int get_analog_input (void)
{
ADCSRA |= (1<<ADSC);
while ((ADCSRA & (1<<ADSC)) > 0);
return ((ADCH<<8) | ADCL) - 512;
}

int main (void)
{
DDRB = 0b00000010;
PORTB = 0b00000000;
DIDR0 = (1<<ADC3D);
ADMUX = (1<<REFS1) | (1<<REFS2) | (1<<MUX1) | (1<<MUX0);
ADCSRA = (1<<ADEN);
USICR = (1<<USIWM0);

USIDR = 'A';
U8 i1;
for (i1 = 0; i1 < 8; i1++)
{
USICR |= (1<<USICLK);
_delay_ms (1);
}

while (1);

return (0);
}


Leider kommt beim Umsetzer nichts an (grüne LED bleibt aus). Wenn ich den Tiny25 programmiere erkennt der Umsetzer aber brav die Reset-Leitung und meldet "TARGET RESET" und "TARGET START".

Wo könnte mein Problem liegen? Liegt es an der Verdrahtung? Kann ich vielleicht gar keine Kommunikation bei einer 1:1 verdrahteten ISP-Schnittstelle aufbauen? So wie ich das Verstehe müsste es gehen, da die Pins ja MISO (Master In Slave Out) und MOSI (Master Out und Slave In) heißen. Allerdings habe ich in der Beschreibung der Register für die USI-Schnittstelle nirgendwo gesehen wie ich Master und Slave konfiguriere.

Viele Grüße
Andreas

Bumbum
21.03.2013, 08:30
Guten Morgen,

dieses Problem ist immer noch aktuell und benötigt eine Lösung. Da ich zur Zeit für andere Projekte auf Teile warte habe ich selbst noch mal etwas Zeit investiert und bin ein Stück weiter. Die erste Erkenntnis nach genauer Studie des Datenblattes ist, dass der Master den Takt für die ISP-Schnittstelle per Software selbst generieren muss. Das finde ich schade. Ich dachte ich schiebe mein Datenbyte ins USIDR Register und setze ein Flag "Start" und erhalte dann, wie von RS232 gewohnt, einen Interrupt sobald die Übertragung abgeschlossen ist.
Aber vielleicht finde ich da noch eine Lösung. (Ich hoffe, dass ich das im Datenblatt noch übersehen habe)

Auf jeden Fall habe ich den Tiny25 zum Master gemacht mit folgendem Code:


#define AVRGCC
#include <avr/io.h>
#include <util/delay.h>
#include <compiler.h>
//F_CPU = 1000000
U8 ISP_send (U8 value)
{
USISR |= (1<<USIOIF);
USICR = (1<<USIWM0) | (1<<USITC);
USIDR = value;
while ((USISR & (1<<USIOIF)) == 0)
{
USICR = (1<<USIWM0) | (1<<USITC) |(1<<USICLK);
_delay_ms (100);
USICR = (1<<USIWM0) | (1<<USITC);
_delay_ms (100);
}
return (USIDR);
}
int main (void)
{
DDRB = 0b00000110;
PORTB = 0b00000000;
U8 value = 'A';
while (1)
{
ISP_send (value++);
if (value > 'Z')
value = 'A';
_delay_ms (5000);
}
return (0);
}


Es wird mit sehr langsam Takt die Buchstaben des Alphabets mit jeweils 5 Sekunden Pause zwischen den Buchstaben gesendet, damit man die Bits quasi noch mal dem Auge verfolgen kann. Das scheint zu funktionieren, zumindest tut sich was auf den Datenleitungen. Hier mal ein Video:

http://www.car-mp3.de/RNetz/ISP.zip

Die grün LED ist der Takt und gelb die Daten. Die linke/obere Platine ist der Master mit dem Tiny25. Rechts/unten ist das ISP/RS232-Interface mit dem Tiny2313 als Slave. Hier der Code für das Interface:



#define AVRGCC
#include <avr/io.h>
#include <compiler.h>
#include <avr/interrupt.h>
//F_CPU = 8000000
#define BAUD 9600L
#define UBRRValue (F_CPU / (BAUD * 16) - 1)
#define LED_ge_0 PORTB = ~(~PORTB | (1<<PB0))
#define LED_ge_1 PORTB = (PORTB | (1<<PB0))
#define LED_gn_0 PORTB = ~(~PORTB | (1<<PB1))
#define LED_gn_1 PORTB = (PORTB | (1<<PB1))
#define RS232ready ((UCSRA & (1<<UDRE)) != 0)
void sendNumber (U32 Number, bool addCR)
{
if (Number == 0)
{
while ( !(UCSRA & (1<<UDRE)));
UDR = '0';
}
else
{
U32 ZifferGrundWert = 1000000000;
bool ZifferWert;
bool started = FALSE;

while ((Number > 0) | (started & (ZifferGrundWert > 0)))
{
if (ZifferGrundWert == 1)
{
ZifferGrundWert = 0;
ZifferWert = Number;
Number = 0;
}
else
{
ZifferWert = 0;

while (Number >= ZifferGrundWert)
{
Number = Number - ZifferGrundWert;
ZifferWert++;
}
ZifferGrundWert = ZifferGrundWert / 10;
}

while ( !(UCSRA & (1<<UDRE)));
if ((ZifferWert > 0) | started)
{
UDR = '0' + ZifferWert;
started = TRUE;
}
else
{
if (started)
UDR = '0';
}
}
}
if (addCR)
sendMsg ("\r\n", FALSE);
}
ISR (USI_OVERFLOW_vect)
{
USISR |= (1<<USIOIF);
sendNumber (USIDR, TRUE);
}
int main (void)
{
DDRB = 0b00000011; // Alternativ: DDRB = 0b00100011; Richtig müsste es eigentlich 0b01000011 sein
PORTB = 0b00000000;

DDRD = 0b01000000;
PORTD = 0b00000000;
UCSRB = (1<<RXCIE) | (1<<TXEN) | (1<<RXEN);
UCSRC = (1<<UCSZ1) | (1<<UCSZ0);
UBRRH = (U8)(UBRRValue>>8);
UBRRL = (U8)UBRRValue;
USISR |= (1<<USIOIF);
USICR = (1<<USIWM0) | (1<<USICS1) | (1<<USIOIE);
sei ();
while (1)
{
if ((PINB & 1<<PB7) == 0)
LED_gn_0;
else
LED_gn_1;
if ((PINB & 1<<PB6) == 0)
LED_ge_0;
else
LED_ge_1;
}
return (0);
}


Wie man sieht werden die Leds im Code geschaltet und hängen nicht direkt an den Datenleitungen. Es kommt also was am Slave an. Und wenn der Master das Datenbyte übertragen hat wird auch der USI_OVERFLOW_vect Interrupt ausgelöst. Die ISP-Initialisierung ist zumindest teilweise korrekt. Nur leider ist der empfangene Datenwert immer 255. Wenn ich die Alternative DDRB Initialisierung, wie im Code kommentiert verwende empfange ich immer als Datenwert 0.

Es scheint so, als ob die Datenleitungen noch vertauscht werden müssen. Das zeigt auch die Initialisierung von DDRB. Normalerweise müsste es ja 0b01000011 heißen. Dann bleibt aber die gelbe LED dunkel und die beiden Atmels kämpfen mit ihren Ausgängen "gegeneinander".
Ich habe es im Datenblatt leider nicht gefunden: Kann man die ISP-Schnittstelle so konfigurieren, dass die Pins als ISP-Slave verwendet werden? Also MOSI und MISO vertauscht? Müsste ja eigentlich gehen, da die Leitungen ja schon so heißen und man einfach 1:1 durchverdrahten kann.

Viele Grüße
Andreas

Bumbum
21.03.2013, 17:17
So, ich beende meinen Monolog. Das Problem ist gelöst. Nach längerem Googeln habe ich herausgefunden, dass der Tiny25 nicht als SPI-Master verwendet werden kann, zumindest nicht mit den integrierten Funktionen des Schieberegisters.
Man muss das Schiebe-Register selbst im Code nachbilden. Damit läuft es nun.

Viele Grüße
Andreas