Forum: Mikrocontroller und Digitale Elektronik TSIC und ATMega


von Matthias P. (matthias_p65)


Lesenswert?

Hallo zusammen,

ich versuche mich gerade am Auslesen eines TSIC Sensors mittels ZACWIRE.

Ehrlichgesagt tue ich mich gerade etwas schwer, vielmehr habe ich einen
ziemlichen Knoten im Kopf. Zu meinem Verständnisproblem:

Im Manual steht:

When the falling edge of the start bit occurs, it causes the mC to 
branch to its ISR. The
ISR enters a counting loop incrementing a memory location (Tstrobe) 
until it sees a rise on the ZACwireTM
signal. When Tstrobe has been acquired, the ISR can simply wait for the 
next 9 falling edges (8 for data, 1 for
parity). After each falling edge, it waits for Tstrobe to expire and 
then samples the next bit.

So weit so gut. Ich soll also die Strobe Time messen, da diese 
auschlaggebend für die Messung der nachfolgenden Bits ist und sich mit 
der Temperatur ändert.

Die Frage, die sich mir stellt: Woher weiss ich denn, WELCHE fallende 
Flanke ich da gerade habe? Es kommt ja mit jedem Bit eine fallende 
Flanke, wie kann ich also wissen, wo ich mich gerade befinde, um die 
Zeit zu messen?

Gruß
Matthias

von Bernie (Gast)


Lesenswert?

Welche fallende Flanke?

Der TSIC306 sendet (etwa) alle 100 ms für 2,6 ms.

Zu Anfang kann man da auch mal falsch liegen. Na und?
Danach ist 97 ms Ruhe und dann kommt die nächste
komplette Übertragung.

Lass das alles einen IRQ machen...

von Matthias P. (matthias_p65)


Lesenswert?

Bernie schrieb:
> Welche fallende Flanke?

Matthias P. schrieb:
> When the falling edge of the start bit occurs...

Ok, so weit so gut, also muss ich im Prinzip nur solange warten bis die 
ersten 97ms der Interrupt Pin auf HIGH war?

von Bernie (Gast)


Lesenswert?

Willst du es per Interrupt machen, oder etwa im Hauptprogramm
drauf warten?

Geht es um einen TSIC306? Es gibt auch andere ZACWIRE-Sensoren,
denen man sogar mitteilen kann, wie oft sie Daten liefern sollen.

Ich habe es mal mit PINCHANGE-Interrupt am tiny24 gemacht,
dazu brauchte ich noch einen freilaufenden Counter, dessen
Zählerstand jeweils eingelesen wurde, um die Daten-Erfassung im
PINCHANGE-Interrupt mit Hilfe einiger reservierter Register
zu bewerkstelligen. In einem Register wird dem Hauptprogramm
mitgeteilt, wenn ein gültiger Wert fertig vorliegt.

von MSP (Gast)


Lesenswert?

Matthias P. schrieb:
> Ok, so weit so gut, also muss ich im Prinzip nur solange warten bis die
> ersten 97ms der Interrupt Pin auf HIGH war?

Der TSIC macht zwischen den Übertragungen eine Pause. Man sucht eine 
Pause und reagiert auf die nächste fallende Flanke. Da beginnt das 
Startbit.

Im Datenblatt gibt es auch einen anderen Hinweis: Die Synchronisation 
kann durch Schalten der Versorgungsspannung erfolgen.

von Matthias P. (matthias_p65)


Lesenswert?

Bernie schrieb:
> Willst du es per Interrupt machen, oder etwa im Hauptprogramm
> drauf warten?

Per INT1 auf der fallenden Flanke

Bernie schrieb:
> Geht es um einen TSIC306?

Ja.

MSP schrieb:
> Der TSIC macht zwischen den Übertragungen eine Pause. Man sucht eine
> Pause und reagiert auf die nächste fallende Flanke. Da beginnt das
> Startbit.
>
> Im Datenblatt gibt es auch einen anderen Hinweis: Die Synchronisation
> kann durch Schalten der Versorgungsspannung erfolgen.

Ok, da war ich ja schon auf dem richtigen Weg. ich werde dann beides mal 
probieren. Letzteres ist ja schon insofern schick, als das man den 
Stromverbrauch senken kann.

Danke für eure Antworten.

von Bernie (Gast)


Lesenswert?

Suche auf jeden fall mal nach der App-Note für ZAC-Wire!

Egal ob du den µC durch Warten blockierst, oder eine
pfiffige Interrupt-Routine baust: Es werden nicht einfach
alle Datenbits rausgeschoben, die Bits sind auf 2 Gruppen
(mit kleiner Pause und neuem Startbit) verteilt.

Allerdings ist das beigefügte PIC-Beispiel nur tauglich,
wenn der µC sonst nichts zu tun hat. WARTEN mit NOP....

Die Synchronisation durch Schalten der Versorgungsspannung
ist "nett gemeint" - man darf dann 65...85 ms WARTEN (!),
bis die Datenübertragung (< 3 ms) beginnt.

Die Stromersparnis kommt erst zum Tragen, wenn man frühestens
nach einigen Sekunden die Temperatur erneut abruft und wenn
es wirklich auf 50 µA ankommt .

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> Die Synchronisation durch Schalten der Versorgungsspannung
> ist "nett gemeint" - man darf dann 65...85 ms WARTEN (!),
> bis die Datenübertragung (< 3 ms) beginnt.

Mensch Berniechen, sei doch nicht so einfältig. Auch wenn der Sensor 
frei läuft, hast du die knapp 100ms Pause. Nach Reset muss sich der µC 
mit dem Sensor synchronisieren und wieder können es bis knapp 100ms 
werden. Der TSIC ist in einem Kunststoffgehäuse untergebracht. Das ist 
relativ träge. Was meinst du, wäre eine sinnvolle Messfrequenz?

von Bernie (Gast)


Lesenswert?

@MSP:

Was hat es mit Einfalt zu tun, wenn ich die Bearbeitung per
Interrupt empfehle, um das WARTEN zu VERMEIDEN?

Die Enrgieeinsparung bei seltenerer "Ablesung" habe ich doch
nicht in Frage gestellt.

Wer einen µC einsetzt, will den aber fast immer noch andere
Sachen erledigen lassen, als "WARTE AUF SENSORSIGNAL"...

65...85 ms kann man kaum als echten Zeitgewinn gegenüber 100 ms
verkaufen!

Oder hast du noch nicht verstanden, dass Interrupts bei Nicht-
Auslösung alle Zeit dem Hauptprogramm, oder anderen Interrupts
überlassen?  ;-)

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> @MSP:
> Wer einen µC einsetzt, will den aber fast immer noch andere
> Sachen erledigen lassen, als "WARTE AUF SENSORSIGNAL"...

Mensch Bernie, jetzt verkaufts du dich noch d...er als oben.

Wo bitte schön habe ich geschrieben, dass keine Interrupts verwendet 
werden sollen???
Der Tipp mit der geschalteten Versorgung erleichtert die 
Synchronisation!!!

> 65...85 ms kann man kaum als echten Zeitgewinn gegenüber 100 ms
> verkaufen!

In meinem Post schrieb ich, dass der TSIC träge ist. Die Auswertung 
mit 100ms Messzyklen ist völlig übertrieben. Es ist also völlig egal, ob 
20ms, 40ms, ..., 100ms. Ich würde den TSIC nur minimal alle 10s 
abfragen. Vorher ist eine Temperaturänderung doch gar nicht im Inneren 
des Sensors angekommen.

von Bernie (Gast)


Lesenswert?

Was braucht's eine Synchchronisation, wenn die Interrupt-
Routine von ganz allein meldet: Neuer Wert vorhanden!

@ MSP: Schlaf weiter!
Die geschaltetete Versorgungsspannung spart bei Batterieprojekten
Strom und kostet ansonsten einen µC-Pin.

Außerdem:
Das Gegrantel bringt nix: Der TO hat schon lang nicht mehr
vorbeigeschaut. Gute Nacht!

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> Was braucht's eine Synchchronisation, wenn die Interrupt-
> Routine von ganz allein meldet: Neuer Wert vorhanden!

Du bist ja der wahre Trollmaster. Und wer bitte schön schreibt die ISR? 
Fällt die vom Himmel?

Ein embedded System hat nicht nur den Zustand "run". Es gibt auch 
weitere. Irgendwann musst du schon die beiden ICs synchronisieren.

Dann schlaf mal gut und lass die großen Jungs das machen.

von Bernie (Gast)


Lesenswert?

@  MSP (Gast)

Zeig doch mal deine ISR,
ich hab schon eine!

;-)

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> Zeig doch mal deine ISR,
> ich hab schon eine!

Ahhhh, der Obertroll hat also ein Geheimnis. Du kennst den Sinn eines 
Forums?

Der gute Matthias braucht Unterstützung bei der Auswertung eines TSIC. 
Was helfen ihm wohl deine Belobigungen über deinen unveröffentlichen 
Code?

Erklär einmal, wie du synchronisierst. Und zwar bei allen 
Systemzuständen. Dann hätten wir etwas zur fachlichen Diskussion.

von Bernie (Gast)


Lesenswert?

Warum sollte ich dir was verraten, wenn du mich als
Obertroll beschimpfst?

Jemand, der fähig ist, daraus zu lernen, mag doch nach deinen
Meckereien nicht mehr weiterlesen.

Trotzdem kleiner Tipp:
- 5..6 der Register 0..15 reservieren und initialisieren.
- INT1: Any logical change on INT1 generates an interrupt request.
- CTR0: freilaufend im Takt von 1 MHz

Flanke an INT1 -> IRQ
Negative Flanke: IRQ testet Overflow-Flag von CTR0 und resettet
ihn und das Overflow-Flag.
Lag ein Overflow vor: Start einer neuen Übertragung!
(Die Abstände innerhalb der Übertragung sind unter 256 µs.)

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> Warum sollte ich dir was verraten,

Mir musst du nichts verraten, aber du beteiligst dich an einer Frage von 
Matthias.


> Flanke an INT1 -> IRQ
> Negative Flanke: IRQ testet Overflow-Flag von CTR0 und resettet
> ihn und das Overflow-Flag.
> Lag ein Overflow vor: Start einer neuen Übertragung!
> (Die Abstände innerhalb der Übertragung sind unter 256 µs.)

Na also, geht doch. Warum nicht gleich so?

von Matthias P. (matthias_p65)


Lesenswert?

Hatte gar nicht bemerkt, daß hier noch Diskussion stattgefunden hatte. 
;)

Ich habe mich die letzten Tage mit dem TSIC beschäftigt, bin aber nicht 
wirklich weitergekommen. Irgendwie stelle ich mich zu dumm an.

Erstmal interessiert mich ja die TStrobe Zeit. Ich hatte mir folgendes 
überlegt:

Davon ausgehend das der TSIC am INT1 Pin hängt:

INT1 auf fallende Flanke
TSIC einschalten per µC
Bei fallender Flanke TIMER1 normal starten
INT1 auf steigende Flanke
bei steigender Flanke auf INT1 denselben wieder auf fallende Flanke 
setzen
Bei der nächsten fallenden Flanke ist das Startbit komplett, also TCNT1 
merken und Timer 1 im CTC Modus starten mit dem Wert aus TCNT1 in OCR1A.

Soweit müsste dann ja zumindest die TStrobe Zeit zu messen sein, ich 
wollte dan zum Test den PINC4 in der ISR (TIMER1_COMPA_vect) toggeln. 
Leider kommt da völliger Mist raus und der Pin toggelt mit etwa 200kHz 
statt der etwa erwarteten 8kHz.

Konkret:

irgendwo in main.c
1
  // enable INT0 and INT1
2
  GICR = 1 << INT0 | 1 << INT1;
3
4
  // VUSB defaults for INT0 and trigger on falling edge for INT1
5
  MCUCR =  (1 << ISC11) | (0 << ISC10) | (1 << ISC00) | (1 << ISC01);
6
7
  TCNT1 = 0;
8
9
  zac_on();
10
   _delay_us(125);
11
12
  sei();


ein paar Hilfs Methoden:
1
void timer1_start_normal() {
2
  TCCR1B = (0 << CS12) | (0 << CS11) | (1 << CS10); // no prescaler
3
  TIMSK |= (1 << TOIE1); // timer overflow interrupt enable
4
}
5
void timer1_stop() {
6
  TCCR1B = (0 << CS12) | (0 << CS11) | (0 << CS10); // stop timer
7
  TIMSK &= ~(1 << TOIE1); // stop overflow interrupt
8
  TIFR |= (1 << TOV1); // clear timer overflow flag
9
}
10
11
void timer1_stop_ctc() {
12
  TIMSK &= ~(1 << OCIE1A); // stop ctc interrupt
13
  TIFR |= (1 << TOV1); // clear timer overflow flag
14
}
15
16
void timer1_start_ctc(uint16_t cmp) {
17
  TCCR1B = (1 << WGM12) | (0 << CS12) | (0 << CS11) | (1 << CS10); // ctc, no prescaler
18
  OCR1A = cmp; // set value to output compare register
19
  //Enable the Output Compare A interrupt
20
  TIMSK |= (1 << OCIE1A);
21
}
22
23
void int1_falling_edge(void) {
24
  MCUCR &= ~(1 << ISC10);
25
  GIFR |= (1 << INTF1); // clear external interrupt flag
26
}
27
28
void int1_rising_edge(void) {
29
  MCUCR |= (1 << ISC10);
30
  GIFR |= (1 << INTF1); // clear external interrupt flag
31
}

und dann die ISRs:
1
ISR (TIMER1_COMPA_vect) {
2
  PORTC ^= ( 1 << PC4 );
3
}
4
5
ISR (INT1_vect) {
6
7
  if (state == ZAC_READING_START_BIT_LOW) {
8
    timer1_start_normal();
9
    int1_rising_edge();
10
    state = ZAC_READING_START_BIT_HIGH;
11
  }
12
  if (state == ZAC_READING_START_BIT_HIGH) {
13
    int1_falling_edge();
14
    state = ZAC_ACQUIRING_TSTROBE;
15
  }
16
  if (state == ZAC_ACQUIRING_TSTROBE) {
17
    timer1_stop();
18
    timer_value = TCNT1;
19
                TCNT1 = 0;
20
    timer1_start_ctc(timer_value);
21
    state = ZAC_READING_HIGH_BYTE;
22
  }
23
  if (state == ZAC_READING_HIGH_BYTE) {
24
    // hier dann anfangen zu samplen
25
  }
26
27
}

Aber es funktioniert nicht. Egal ob ich das 125µs Delay am Anfang 
weglasse oder nicht. Ich hab den INT1 auch mal auf "Any logical change" 
gestellt, aber ohne Erfolg,

Ist das hier grober Bockmist, oder habe ich einfach nur eine Kleinigkeit 
übersehen?

von Matthias P. (matthias_p65)


Lesenswert?

Ok, der Depp sitzt immer VOR dem Bildschirm:
1
ISR (INT1_vect) {
2
3
  if (state == ZAC_READING_START_BIT_LOW) {
4
    timer1_start_normal();
5
    int1_rising_edge();
6
    state = ZAC_READING_START_BIT_HIGH;
7
  }
8
  else if (state == ZAC_READING_START_BIT_HIGH) {
9
    int1_falling_edge();
10
    state = ZAC_ACQUIRING_TSTROBE;
11
  }
12
  else if (state == ZAC_ACQUIRING_TSTROBE) {
13
    timer1_stop();
14
    timer_value = TCNT1;
15
    TCNT1 = 0;
16
    timer1_start_ctc(timer_value);
17
    state = ZAC_READING_HIGH_BYTE;
18
  }
19
  else if (state == ZAC_READING_HIGH_BYTE) {
20
    PORTC |= 1 << PINC3;
21
  }
22
23
}

Wenn man die if/else Konstruktion vernünftig schreibt, klappts auch mit 
der Strobe Messung. ;)

von Matthias P. (matthias_p65)


Angehängte Dateien:

Lesenswert?

So, rein theoretisch sollte es jetzt tun, leider kommt nur Unfug bei der 
Messung raus. Ich probiere jetzt schon einige Stunden hin und her.

Zumindest bin ich ziemlich sicher, nach Messung mit dem Oszi am Debug 
Pin, daß die TSIC Messung etwa alle 100ms stattfindet. So weit so gut.

Ich vemute inzwischen, daß das Polling und der INT0 für den USB Bus die 
Messung durcheinanderbringen, sicher bin ich aber nicht. Ich kenne mich
dafür auch zuwenig aus.

Ich benutze im Wesentlichen die Schaltung im Anhang. Der TSIC hängt am 
INT0 Pin des Mega8 und wird über PINC5 eingeschaltet. Zum Auslesen über 
USB verwende ich das C-Programm des HID-data Beispiels aus der VUSB Lib.

Zumindest das funktioniert ohne Probleme.

Etwas Quelltext:

main.c
1
#include <avr/io.h>
2
#include <avr/wdt.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
#include <avr/eeprom.h>
6
7
#include <avr/pgmspace.h>
8
#include "usbdrv.h"
9
#include "oddebug.h"
10
#include "zacwire.h"
11
12
zacstate state = ZAC_READING_START_BIT_LOW;
13
14
/* ------------------------------------------------------------------------- */
15
/* ----------------------------- USB interface ----------------------------- */
16
/* ------------------------------------------------------------------------- */
17
18
PROGMEM const char usbHidReportDescriptor[22] = { /* USB report descriptor */
19
0x06, 0x00, 0xff, // USAGE_PAGE (Generic Desktop)
20
    0x09, 0x01, // USAGE (Vendor Usage 1)
21
    0xa1, 0x01, // COLLECTION (Application)
22
    0x15, 0x00, //   LOGICAL_MINIMUM (0)
23
    0x26, 0xff, 0x00, //   LOGICAL_MAXIMUM (255)
24
    0x75, 0x08, //   REPORT_SIZE (8)
25
    0x95, 0x80, //   REPORT_COUNT (128)
26
    0x09, 0x00, //   USAGE (Undefined)
27
    0xb2, 0x02, 0x01, //   FEATURE (Data,Var,Abs,Buf)
28
    0xc0 // END_COLLECTION
29
    };
30
/* Since we define only one feature report, we don't use report-IDs (which
31
 * would be the first byte of the report). The entire report consists of 128
32
 * opaque data bytes.
33
 */
34
35
/* The following variables store the status of the current data transfer */
36
static uchar currentAddress;
37
// remaining bytes of a bus transfer
38
static uchar bytesRemaining;
39
40
unsigned char buffer[129];
41
42
// half the value of tstrobe
43
volatile uint16_t half_timer_value = 0;
44
// high byte of a zacwire paxcket
45
volatile uchar zac_high_byte = 0;
46
// low byte of a zacwire paxcket
47
volatile uchar zac_low_byte = 0;
48
// the bit of the byte currently being read including parity
49
volatile uchar zac_current_bit = 8;
50
51
/**
52
 * Start timer1 in normal mode
53
 */
54
void timer1_start_normal();
55
/**
56
 * Stop timer1
57
 */
58
void timer1_stop();
59
/**
60
 * Stop timer1 from ctc mode
61
 */
62
void timer1_stop_ctc();
63
/**
64
 * Start timer1 in ctc mode and load the output compare register (OCR1A)
65
 */
66
void timer1_start_ctc(uint16_t cmp);
67
/**
68
 * Select falling edge for INT1 pin
69
 */
70
void int1_falling_edge(void);
71
/**
72
 * Select rising edge for INT1 pin
73
 */
74
void int1_rising_edge(void);
75
/**
76
 * Turn INT1 off
77
 */
78
void int1_enable(void);
79
/**
80
 * Turn INT1 on
81
 */
82
void int1_disable(void);
83
84
/* ------------------------------------------------------------------------- */
85
86
/* usbFunctionRead() is called when the host requests a chunk of data from
87
 * the device. For more information see the documentation in usbdrv/usbdrv.h.
88
 */uchar usbFunctionRead(uchar *data, uchar len) {
89
  if (len > bytesRemaining)
90
    len = bytesRemaining;
91
92
  // eeprom_read_block(data, (uchar *)0 + currentAddress, len);
93
94
  int i;
95
96
  // copy outgoing data from buffer to host
97
  for (i = 0; i < len; i++) {
98
    data[i] = buffer[i + currentAddress];
99
  }
100
101
  currentAddress += len;
102
  bytesRemaining -= len;
103
  return len;
104
}
105
106
/* usbFunctionWrite() is called when the host sends a chunk of data to the
107
 * device. For more information see the documentation in usbdrv/usbdrv.h.
108
 */uchar usbFunctionWrite(uchar *data, uchar len) {
109
  if (bytesRemaining == 0)
110
    return 1; /* end of transfer */
111
  if (len > bytesRemaining)
112
    len = bytesRemaining;
113
114
  // eeprom_write_block(data, (uchar *)0 + currentAddress, len);
115
116
  int i;
117
118
  // copy incoming data from host to device
119
  for (i = 0; i < len; i++) {
120
    buffer[i + currentAddress] = data[i];
121
  }
122
123
  currentAddress += len;
124
  bytesRemaining -= len;
125
126
  return bytesRemaining == 0; /* return 1 if this was the last chunk */
127
}
128
129
/* ------------------------------------------------------------------------- */
130
131
usbMsgLen_t usbFunctionSetup(uchar data[8]) {
132
  usbRequest_t *rq = (void *) data;
133
134
  if ((rq->bmRequestType & USBRQ_TYPE_MASK) == USBRQ_TYPE_CLASS) { /* HID class request */
135
    if (rq->bRequest == USBRQ_HID_GET_REPORT) { /* wValue: ReportType (highbyte), ReportID (lowbyte) */
136
      /* since we have only one report type, we can ignore the report-ID */
137
      bytesRemaining = 128;
138
      currentAddress = 0;
139
      return USB_NO_MSG; /* use usbFunctionRead() to obtain data */
140
    } else if (rq->bRequest == USBRQ_HID_SET_REPORT) {
141
      /* since we have only one report type, we can ignore the report-ID */
142
      bytesRemaining = 128;
143
      currentAddress = 0;
144
      return USB_NO_MSG; /* use usbFunctionWrite() to receive data from host */
145
    }
146
  } else {
147
    /* ignore vendor type requests, we don't use any */
148
  }
149
  return 0;
150
}
151
152
ISR (TIMER1_COMPA_vect) {
153
154
  // stop the counter and reset timer count
155
  timer1_stop_ctc();
156
  TCNT1 = 0;
157
158
  // reading the HIGH byte of a zacwire packet
159
  if (state == ZAC_READING_HIGH_BYTE) {
160
    // read bits ignoring the last bit (parity)
161
    // actually reading 9 bits in total
162
    if ((PIND & (1<<PIND3)) && zac_current_bit > 1) {
163
      zac_high_byte |= (1 << (zac_current_bit - 1));
164
    }
165
    // decrement current bit every trigger
166
    if (zac_current_bit > 0) {
167
      zac_current_bit--;
168
    }
169
    // have read all bits from HIGH byte, switch
170
    // to LOW byte
171
    else {
172
      state = ZAC_READING_LOW_BYTE;
173
      // now we have one bit more since there comes another stop bit
174
      zac_current_bit = 9;
175
    }
176
177
  }
178
179
  // after reading the parity bit of the first byte, we can omit
180
  // the stop bit, since the next falling edge does not
181
  // happen until the next start bit (LOW byte)
182
183
  // reading the LOW byte of a zacwire packet
184
  else if (state == ZAC_READING_LOW_BYTE) {
185
    // read bits ignoring the first bit (start bit) and the last bit (parity)
186
    // actually reading 10 bits in total
187
    if ((PIND & (1<<PIND3)) && zac_current_bit > 1 && zac_current_bit < 9) {
188
      zac_low_byte |= (1 << (zac_current_bit - 2));
189
    }
190
    // decrement current bit every trigger
191
    if (zac_current_bit > 0) {
192
      zac_current_bit--;
193
    }
194
    // all bits read => back to the beginning
195
    else {
196
      zac_current_bit = 8;
197
      state = ZAC_READING_START_BIT_LOW;
198
    }
199
  }
200
201
202
}
203
204
ISR (INT1_vect) {
205
206
  /**
207
   * Beginning of zacwire packet, first falling edge
208
   * start measuring tstrobe time
209
   */
210
  if (state == ZAC_READING_START_BIT_LOW) {
211
    /*
212
     *  if there was one measurement cycle, store data
213
     *  from zacwire in usb transfer buffer
214
     */
215
    if (zac_high_byte > 0)
216
      buffer[0] = zac_high_byte;
217
    if (zac_low_byte > 0)
218
      buffer[1] = zac_low_byte;
219
    // reset measured values
220
    zac_high_byte = 0;
221
    zac_low_byte = 0;
222
    // start timer1 and use rising edge for int1
223
    // if the next rising edge happens this is the second half of the start bit
224
    timer1_start_normal();
225
    int1_rising_edge();
226
    state = ZAC_READING_START_BIT_HIGH;
227
    // toggle pin for debigging purposes
228
    PORTC ^= ( 1 << PC3 );
229
  }
230
  else if (state == ZAC_READING_START_BIT_HIGH) {
231
    // rising edge of the start bit
232
    // select falling edge, thus the startbit is complete
233
    // on next falling edge
234
    int1_falling_edge();
235
    state = ZAC_ACQUIRING_TSTROBE;
236
  }
237
  else if (state == ZAC_ACQUIRING_TSTROBE) {
238
    // falling edge of start bit occured
239
    // stop standard timer, reset timer count
240
    timer1_stop();
241
    // we need half the strobe time in order to detect
242
    // the individual level for each bit
243
    half_timer_value = TCNT1 / 2;
244
    TCNT1 = 0;
245
    // we now start reading the first byte
246
    state = ZAC_READING_HIGH_BYTE;
247
    // start timer 1 in ctc mode, timer interrupt then occurs
248
    // each tstrobe/2 (50% duty cycle)
249
    timer1_start_ctc(half_timer_value);
250
  }
251
  else if (state == ZAC_READING_HIGH_BYTE || state == ZAC_READING_LOW_BYTE) {
252
    // now we restart the ctc timer on each falling edge for bit detection
253
    timer1_start_ctc(half_timer_value);
254
  }
255
256
}
257
258
void timer1_start_normal() {
259
  TCCR1B = (0 << CS12) | (0 << CS11) | (1 << CS10); // no prescaler
260
  TIMSK |= (1 << TOIE1); // timer overflow interrupt enable
261
}
262
void timer1_stop() {
263
  TCCR1B = (0 << CS12) | (0 << CS11) | (0 << CS10); // stop timer
264
  TIMSK &= ~(1 << TOIE1); // stop overflow interrupt
265
  TIFR |= (1 << TOV1); // clear timer overflow flag
266
}
267
268
void timer1_stop_ctc() {
269
  TIMSK &= ~(1 << OCIE1A); // stop ctc interrupt
270
  TIFR |= (1 << TOV1); // clear timer overflow flag
271
}
272
273
void timer1_start_ctc(uint16_t cmp) {
274
  TCCR1B = (1 << WGM12) | (0 << CS12) | (0 << CS11) | (1 << CS10); // ctc, no prescaler
275
  OCR1A = cmp; // set value to output compare register
276
  //Enable the Output Compare A interrupt
277
  TIMSK |= (1 << OCIE1A);
278
}
279
280
void int1_falling_edge(void) {
281
  MCUCR &= ~(1 << ISC10);
282
  GIFR |= (1 << INTF1); // clear external interrupt flag
283
}
284
285
void int1_rising_edge(void) {
286
  MCUCR |= (1 << ISC10);
287
  GIFR |= (1 << INTF1); // clear external interrupt flag
288
}
289
290
void int1_enable(void) {
291
  GICR |= 1 << INT1;
292
}
293
294
void int1_disable(void) {
295
  GICR &= ~(1 << INT1);
296
}
297
298
int main(void) {
299
300
  uchar i;
301
302
  wdt_enable(WDTO_1S);
303
  /* Even if you don't use the watchdog, turn it off here. On newer devices,
304
   * the status of the watchdog (on/off, period) is PRESERVED OVER RESET!
305
   */
306
  /* RESET status: all port bits are inputs without pull-up.
307
   * That's the way we need D+ and D-. Therefore we don't need any
308
   * additional hardware initialization.
309
   */
310
  odDebugInit(); DBG1(0x00, 0, 0); /* debug output: main starts */
311
  usbInit();
312
  usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */
313
  i = 0;
314
  while (--i) { /* fake USB disconnect for > 250 ms */
315
    wdt_reset();
316
    _delay_ms(1);
317
  }
318
  usbDeviceConnect();
319
320
  DDRC = 0xFF;
321
322
  // enable INT0 and INT1
323
  GICR = 1 << INT0 | 1 << INT1;
324
  // VUSB defaults for INT0 and trigger on falling edge for INT1
325
  MCUCR =  (1 << ISC11) | (0 << ISC10) | (1 << ISC00) | (1 << ISC01);
326
327
  TCNT1 = 0;
328
329
  // turn TSIC on
330
  zac_on();
331
  _delay_us(125);
332
333
  // enable interrupts
334
  sei();
335
336
  DBG1(0x01, 0, 0); /* debug output: main loop starts */
337
338
  for (;;) { /* main event loop */
339
    DBG1(0x02, 0, 0); /* debug output: main loop iterates */
340
    wdt_reset();
341
    usbPoll();
342
  }
343
344
  return 0;
345
}

zakwire.h
1
#ifndef ZACKWIRE_H_
2
#define ZACWIRE_H_
3
4
#define ZACPORT PORTC
5
#define ZACPIN  PINC5
6
#define ZACINT INT0
7
8
void zac_on(void);
9
void zac_off(void);
10
11
typedef enum state {
12
  ZAC_READING_START_BIT_LOW,
13
  ZAC_READING_START_BIT_HIGH,
14
  ZAC_ACQUIRING_TSTROBE,
15
  ZAC_STROBE_ACQUIRED,
16
  ZAC_READING_PARITY,
17
  ZAC_WAITING_STOPBIT,
18
  ZAC_READING_LOW_BYTE,
19
  ZAC_READING_STOP_BIT,
20
  ZAC_READING_HIGH_BYTE,
21
  ZAC_WAITING_NEXT
22
} zacstate;
23
24
extern zacstate state;
25
26
#endif /* ZACWIRE_H_ */
1
#include <avr/io.h>
2
#include <util/delay.h>
3
#include <avr/interrupt.h>
4
#include <stdint.h>
5
#include <stdio.h>
6
7
#include "zacwire.h"
8
9
void zac_on() {
10
  ZACPORT |= 1 << ZACPIN;
11
}
12
13
void zac_off() {
14
  ZACPORT &= 1 & ZACPIN;
15
}

Mein Problem ist wie gesagt, daß bei der Messung ziemlich seltsame Werte 
herauskommen, die nicht dem Paket auf dem jeweiligen Oszillogramm 
entsprechen.

Kann hier beizeiten mal jemand drüberschauen und mir einen Tip geben, wo 
ich noch nach dem Fehler suchen könnte?

Bin für jede Hilfe dankbar.

Gruß
Matthias

von Matthias P. (matthias_p65)


Lesenswert?

Hat niemand eine Idee?

von Matthias P. (matthias_p65)


Lesenswert?

Also, ich habe den ganzen USB Krams mal rausgeworfen, mir scheint, daß 
die Synchronisation nicht klappt. Die Methode "Einschalten, dann Messen" 
taugt offenbar nichts.

Bernie schrieb:
> Trotzdem kleiner Tipp:
> - 5..6 der Register 0..15 reservieren und initialisieren.
> - INT1: Any logical change on INT1 generates an interrupt request.
> - CTR0: freilaufend im Takt von 1 MHz
>
> Flanke an INT1 -> IRQ
> Negative Flanke: IRQ testet Overflow-Flag von CTR0 und resettet
> ihn und das Overflow-Flag.
> Lag ein Overflow vor: Start einer neuen Übertragung!
> (Die Abstände innerhalb der Übertragung sind unter 256 µs.)

Kannst Du mir das mal etwas genauer erklären?

Woher weiss ich in der ISR dann, um welche Flanke es sich handelt und 
wie reserviere ich Register in C?

von Bernie (Gast)


Lesenswert?

Nanu? Warum fragst du nicht "MSP (Gast)"? Der tut doch hier
so oberschlau!

Scherz beiseite, ich schrieb natürlich, dass es eine "schlaue"
Interruptroutine sein muss. Das soll nicht heißen, dass sie
schwer verständlich ist, sondern dass sie einiges - Schritt für
Schritt - bei jedem Pegelwechsel des TSIC bearbeitet.

Unser Verständnisproblem wäre jetzt, dass ich es in ASM
verwirklicht habe - und du es in C machen willst. Da kann ich
dir leider nicht sooo exakt helfen, besonders weil sich das
Tutorial um die Prozessor-Register R0...R31 elegant rumdrückt.

Fangen wir mal mit der Synchronisierung an:
-------------------------------------------
- Mach den mega8 ERST MAL frei von USB- oder sonstigen
  nicht überschaubaren Aufgaben.

- Reserviere einige GLOBALE Variablen nur für diese
  TSIC-Bearbeitung.
  Erst mal eine für COUNT-OLD.

- Sorge dafür, dass einer der zwei 8-Bit-Timer/Counter des
  mega8 mit etwa 1 MHz frei läuft.

- Schließe den TSIC an INT1 an und stelle den INT1 so ein,
  dass er auf positive UND negative Flanken reagiert.

- Wird der INT1 ausgelöst, frage sofort PinD-Bit3 ab.
  (Ist PinD-Bit3 = 0 liegt eine negative Flanke vor.)

- Lese den Zählerstand des Timer/Counters aus.
- Schau nach, ob Timer-Overflow dieses Zählers gesetzt wurde.
- Diesen Timer-Overflow sofort resetten.

- Ist bei negativer Flanke der neue Zählerstand abzüglich
  COUNT-OLD größer als 200 (µs)? (Bei Timer-Overflow kannst du
  diese Differenz um mindestens 256 (µs) erhöhen.)
  Dann bist du am Startpunkt einer neuen Übertragung des TSIC!!!
  Schalte JETZT an irgendeinem Portpin eine LED an!
  Sonst (!) setze diesen Portpin jedes mal auf LED = aus.

- Vor dem Ende der INT1-Routine den neu gelesenen Zählerstand
  in COUNT-OLD speichern.

Nun schau mal, ob die LED immer aufblitzt, wenn am Oszilloskop
gerade eine neue Übertragung abläuft.

Wenn du interessiert bist, melde dich wieder - ES IST MACHBAR!
Der Rest ist eher einfacher.

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> Warum fragst du nicht "MSP (Gast)"?

Du hast mich gerufen Bernie, hier bin ich. Das Thema ist 
Synchronisation? (Hast du das wieder unterm Teppich hergeholt?)

1. per Portpin den TSIC einschalten und Eieruhr mit ca 20ms (Wert ist 
unkritisch, s. Datenblatt) aufziehen: z. B. mit Timer Interrupt.
2. Bei abgelaufener Eieruhr (Timer Interrupt) den Portpin mit TSIC Daten 
auf fallende Flanke Interrupt einstellen
3. Bei fallender Flanke beginnt das Starbit

das war 's :-P

Bernie schrieb:
> Ist bei negativer Flanke der neue Zählerstand abzüglich
>   COUNT-OLD größer als 200 (µs)?

Woher kommt der Zeitwert. Die Bitrate vom TSIC unterliegt sehr starken 
Schwankungen. Feste Werte sind mit Vorsicht zu genießen.

von Matthias P. (matthias_p65)


Lesenswert?

Bernie schrieb:
> - Mach den mega8 ERST MAL frei von USB- oder sonstigen
>   nicht überschaubaren Aufgaben.

Ja, das habe ich jetzt mal gemacht, sonst seh' ich auch den Wald vor 
lauter Bäumen nicht mehr.

Bernie schrieb:
> - Wird der INT1 ausgelöst, frage sofort PinD-Bit3 ab.
>   (Ist PinD-Bit3 = 0 liegt eine negative Flanke vor.)

Ok, da hätte ich Depp ja auch mal dran denken können.

Bernie schrieb:
> Wenn du interessiert bist, melde dich wieder - ES IST MACHBAR!
> Der Rest ist eher einfacher.

Bin ich. Und ich bin natürlich davon ausgegangen das es machbar ist. 
Offenbar mache ich noch einiges falsch, sonst würde ja etwas Sinnvolles 
herauskommen.

Dank euch beiden, ich werde mich heute Abend dann wieder damit 
beschäftigen und mich melden.

von Bernie (Gast)


Lesenswert?

Oh, "MSP (Gast)" ist wieder dabei! Und schreibt falsch ab:
Von 20 ms steht nichts in der App-Note, sondern 65...85 ms.

Welchen Timer mit welcher Zählfrequenz benutzt du denn für die
Decodierung des "TSIC-Telegramms"?

Die Hardware für die "reine" Interrupt-Lösung (INT1 und einen
Timer/Counter) muss man auch bei der langweiligen WARTE-Lösung
für die Code-Erkennung belegen...

Wenn man die Temperatur seltener auslesen will, kann man die
"reine" Interrupt-Lösung natürlich auch Sekunden- oder Minuten-lang
ruhen lassen.

Oh, "MSP (Gast)" verbreitet Furcht vor großen Toleranzen -
machen wir es eben mit REICHLICHST Toleranz!

Nominelle Ausgaberate 10 Hz, Bitdauer 125 µs.
Kennt jemand Exemplare, die langsamer, als 50%, oder schneller,
als 200% sind?

Also Annahme: Bitdauer 62...250 µs (Ausgaberate 5...20 Hz).

Nehmen wir bei 8 MHz µC-Clock den Timer1 mit Prescaler 1/32.
Wenn wir seinen Zählerstand UND sein Overflow-Flag bei jeder
negativen INT1-Flanke auslesen und danach auf Null setzen, können wir
Zeiten von 0...1 ms mit 4 µs Auflösung erfassen.
Zeiten länger als 1 ms zeigen sich durch das Overflow-Flag.
Mehr Sicherheit für die Erkennung der Pause vorm Startbit, ohne
Langeweile beim µC, geht garnicht!

Erkennung von Logisch 1 und 0:
Bei positiver Flanke lesen wir nur den Zählerstand aus.
Das Startbit hat 50% duty cycle bei 62...250 µs Bitdauer: 31...125 µs.
=> Timer1-Stand nach der positive Flanke = 7...32. (Referenzwert!)

Somit lassen sich locker die duty cycle von 25% und 75% unterscheiden.
Logisch 0 / 1 bei Zählerstand  < Referenzwert / > Referenzwert.

Auch bei Timer0 mit Prescaler 1/64 (Auflösung nur 8 µs) gäbe es keine
Probleme! Logisch 0 / 1 bei Zählerstand < 3 / > 3 ... < 16 / > 16.


@  Matthias P.:
Bei Interesse gibts dann auch Hinweise zur Auwertung.

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> Und schreibt falsch ab:
> Von 20 ms steht nichts in der App-Note, sondern 65...85 ms.

Und Bernie stellt sich wieder d... an. Denn ich schrieb
MSP schrieb:
> ca 20ms (Wert ist unkritisch
und warum? Es handelt sich um die Zeit, die ich dem TSIC gönne, damit er 
sich nach Power On stabilisiert. Die Werte im Datenblatt werden nicht 
immer eingehalten. Probier das ruhig einmal aus.

Bernie schrieb:
> Welchen Timer mit welcher Zählfrequenz benutzt du denn für die
> Decodierung des "TSIC-Telegramms"?
>
> Die Hardware für die "reine" Interrupt-Lösung (INT1 und einen
> Timer/Counter) muss man auch bei der langweiligen WARTE-Lösung
> für die Code-Erkennung belegen...
>
> Wenn man die Temperatur seltener auslesen will, kann man die
> "reine" Interrupt-Lösung natürlich auch Sekunden- oder Minuten-lang
> ruhen lassen.
???
Keine Ahnung wo ich dir hier helfen kann. Es ging doch um 
Synchronisation.
und ich schrieb
MSP schrieb:
> Bei abgelaufener Eieruhr (Timer Interrupt)

Bernie schrieb:
> Furcht vor großen Toleranzen -
> machen wir es eben mit REICHLICHST Toleranz!
Zacwire ist ein Protokoll mit Übertragung der Baudrate. Feste Werte für 
Zeiten sind nicht sinning und auch nicht erforderlich.

Bernie schrieb:
> Also Annahme: Bitdauer 62...250 µs
Wie lang sind die Stop Bits zwischen den Daten? Aber nicht nach 
Datenblatt sondern selbst gemessen.

Man merkt, dass dir meine Synchronosation gefällt. Sie hat ja Vorteile:
- unabhängig von der Baudrate
- schlank und kurz
- funktioniert in jedem Fall, denn es gibt nur einen Fall. Mit der 
ersten fallenden Flanke beginnt das Startbit

von Bernie (Gast)


Lesenswert?

@ MSP (Gast)

Hast du wieder keine Antwort, außer "Warteschleife"?

Das ist DEINE BESTE Lösung: Schlanke kurze WARTESCHLEIFEN.

Wer AHNUNG hat, kann andere Vorgehensweisen VERSTEHEN.
Kannst du das nicht?

Es ist wohl nicht mal DEINE Lösung, da du keine Auskunft
geben kannst, welche µC-Resourcen du dafür und für die
Dekodierung benutzt hast.

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> Wer AHNUNG hat, kann andere Vorgehensweisen VERSTEHEN.

Na Bernie, dann bemühe dich doch endlich. Denn das

Bernie schrieb:
> Hast du wieder keine Antwort, außer "Warteschleife"?
>
> Das ist DEINE BESTE Lösung: Schlanke kurze WARTESCHLEIFEN.

zeigt, dass du den Text nicht gelesen oder nicht verstanden hast.

Bernie schrieb:
> welche µC-Resourcen du dafür und für die
> Dekodierung benutzt

Für Synchronisation und Datenempfang werden
   Ausgang für ON/OFF TSIC
   Eingang für Daten (interruptfähig)
   Timer
benötigt.

Der TSIC ist Master der Übertragung. Es braucht eine Synchronisation mit 
dem Slave. Und das muss in allen Fällen funktionieren. Den Master 
kontrolliere ich über die Spannungsversorgung. Und die erste fallende 
Flanke ist garantiert das Startbit. Für die Synchronisation wird keine 
Rechenleistung benötigt.
Ich hoffe, jetzt hast du es verstanden.

Und da dir die Verschwendung von Rechenleistung wichtig ist:
Bei deiner Variante führt jede Flanke zu einer Berechnung und das können 
über 30 Flanken sein.

Für die Synchronisation benötigt deine Variante nur die fallenden 
Flanken. Das lässt sich später auf beide Flanken umkonfigurieren.

Die absoluten Zeiten wurden schon angesprochen.

von Bernie (Gast)


Lesenswert?

@ MSP (Gast)
Hast du den ganzen Abend gewartet, dass dich jemand lobt?
Gibt leider nichts loben:


  Ein µC in Warteschleife braucht keine Rechenleistung?
- Falsch:
In Warteschleifen steht die Rechenleistung nicht zur
Verfügung, die vielleicht anderswo gebraucht wird.


  30 Flanken?
- Falsch:
Es sind genau 40 Flanken (20 neg, 20 pos.) pro Messwert.
Bis zur Startflanke braucht meine Variante nur ein paar
µC-Takte für die Initialisierung. Deine steht mindestens
160.000 (eher 680.000) µC-Takte dumm rum.

Falls du überhaupt schon mal daran gedacht hast, die
Datenbits zu dekodieren, musst du die auch bearbeiten.

Wenn meine Interruptlösung in der Pause auf das
Startbit wartet, wird der µC überhaupt nicht belastet.
Es kommt doch nichts - da kann man auch gleich beide
Flanken des INT1 aktivieren.


  Du willst dekodieren, ohne auf die positiven
  Flanken zu reagieren?
- Falsch:
Die Zeit von negativer zu positiver Flanke wird benötigt,
um beim Startbit die "Baudrate" zu bestimmen. Danach, um
den Bitwert (0/1) zu bestimmen.


Für mich erscheinst du einfach ignorant.
Das Wort kannst du bei WIKI nachlesen. Bei dir ist es aber
keine Absicht! - Dazu würde ja ein Mindestmaß an Wissen
gehören...

von MSP (Gast)


Lesenswert?

Nun Bernie, was ist mit dir los?
Weist du nicht, wie Interrupts in einer CPU verarbeitet werden? Oder 
bist du ignorant und enttäuscht, weil deine mühsamm und mit viel Einsatz 
gestrickte SW noch Verbesserungspotenial besitzt?

Aber ich will dir gerne helfen.

Bernie schrieb:
> Ein µC in Warteschleife braucht keine Rechenleistung?
Wie kommst du immer wieder auf Warteschleife? Bei meiner Variante gibt 
es keine Warteschleifen. Es wird ein Portpin gesetzt und der Interrupt 
für eine negative Flanke aktiviert. Das war es. Jetzt kann der µC machen 
was er will.

Bernie schrieb:
> 30 Flanken?
MSP schrieb:
> das können
> über 30 Flanken sein
Wenn du beim µC die Interrupts aktiviertst, weisst du nicht, was der 
TSIC gerade macht. Im ungüstigen Fall passiert das genau im ersten 
Startbit. Dann springst du bei jeder der über 30 Flanken in deine ISR 
und rechnest.
Ob das kritisch ist, musst du selbst entscheiden. Dir war aber die 
Verschwendung von Rechenleistung wichtig, oder?

Bernie schrieb:
> Wenn meine Interruptlösung in der Pause auf das
> Startbit wartet, wird der µC überhaupt nicht belastet.
> Es kommt doch nichts - da kann man auch gleich beide
> Flanken des INT1 aktivieren.
Doch, du benötigst Rechenleistung, siehe oben.

Bernie schrieb:
> Du willst dekodieren, ohne auf die positiven
>   Flanken zu reagieren?
MSP schrieb:
> Für die Synchronisation benötigt deine Variante nur die fallenden
> Flanken. Das lässt sich später auf beide Flanken umkonfigurieren.
Für die Synchronisation braucht man keine positiven Flanken. Wenn man 
sie für die Datenbits braucht, konfiguriert man nach der Synchronisation 
die Flanken um.

von Bernie (Gast)


Lesenswert?

@ MSP (Gast)
Von klugen Menschen lass ich mich ungern "d... " nennen.

Von Menschen, wie dir, die nicht verstehen, worum es geht,
schon garnicht.

Bei meiner Lösung kostet der sichere (!) Empfang bei 8 MHz
mit Einschalten des TSIC 200 µs,
sonst (worst case) 400 µs.

Viel Spaß in deiner Warteschleife!

Biete was besseres, oder hasta la vista, Baby!

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> Viel Spaß in deiner Warteschleife!
> Biete was besseres, oder hasta la vista, Baby!
Ok, es ist also dein angekratzter Stolz.

Deine Oberflächlichkeit bei Erkennung der Systemzustände lässt auf einen 
Hobby Bastler schließen. Im industriellen Umfeld muss eben mehr beachtet 
werden.

Bernie schrieb:
> mit Einschalten des TSIC
Aber schön, dass du doch andere Ideen annimmst. Auch wenn es einen 
weiteren Portpin kostet, hat es noch weitere Vorteile.

von Bernie (Gast)


Lesenswert?

@ MSP (Gast) Mein Stolz? Deine Professionalität?

- Warum lauerst du denn hier tagelang auf ein bisschen
  Anerkennung?

- Warum bist du so "d... " und kannst nicht über deine
  einmal (abgekupferte?) Lösung hinausblicken?

Bei mir ist es eher die Freude, mit der Verbreitung von
Wissen über resourcensparende, sonst mindestens
gleichwertige Denkansätze, anderen bei der Problemlösung
zu helfen. - Darum antworte ich dir überhaupt noch.

Hab ich das Einschalten des TSIC abgelehnt?
- Nein, deine alberne Warteschleife dabei!
- Und dass man einen Portpin gegen << 0,1 mA Strom-
  erparnis abwägen muss. Das sollten "Profis im
  industriellen Umfeld" schon mal können!

Wer für triviale Probleme gleich zum nächstgrößeren µC
greift, hat noch nie Kostendruck gespürt. Das kostet
nicht nur die paar Cent des größeren Chips...

Weitere Vorteile?
Deine (zeitliche) µC-Belastung?
- Da kommt mal wieder nix. (Woher denn auch...)

von MSP (Gast)


Lesenswert?

Bernie schrieb:
> Hab ich das Einschalten des TSIC abgelehnt?
> - Nein, deine alberne Warteschleife dabei!
Und wenn du es 100mal wiederholst, wird es nicht realer. Tja, wenn 's 
fachlich fehlt, dann ...

Bernie schrieb:
> Bei mir ist es eher die Freude, mit der Verbreitung von
> Wissen
Bernie, Bernie, dann fang doch bitte an. Von dir kommt nichts als 
Selbstbelobing. Welche wissenwerte Dinge hast du denn verbreitet?

Den TSIC zu koppeln ist nicht gerade der höchste Schwierigkeitsgrad. Das 
haben schon Hunderte gemacht. Und zu 99% nach einer der beiden 
Möglichkeiten:
a) Zeit des Low-Pegel messen und mit der halben Bitzeit vergleichen
b) in der Mitte des Bits den Pegel abfragen (wie beim UART)

Bernielein hat Variante a) gewählt und glaubt damit das Ei des Columbus 
gefunden zu haben. lol

Aber wie ich schon schrieb, ist das nur ein Teil der Systemzustände. Da 
gibt es noch etwas darum herum.

von Knetling (Gast)


Lesenswert?

He, hier gibts ja grade einen aktiven Thread fürn TSIC-Tempratursensor.
Ist der schwer einzuprogrammieren? Oder streitet ihr aus Spaß?

Bin an einer Steuerung mit AVR4313 und soll noch einen TSIC ranhängen, 
weil man für einiges Temperaturkompensieren muss. 3 Ports sind noch 
frei, USART aber nicht. Geht das und ohne Interrupt? Die Tempratur kommt 
doch 10 mal die Sekunde.

danke J. Knetling

von Bernie (Gast)


Lesenswert?

Schwer ist das nicht. Beim 4313 kann ja fast jeder Port-Pin
einen Interrupt auslösen - das brauchst du für die Erfassung
des Telegramms. Hast du noch einen Timer frei, oder einen
der durchgängig zählt?

Streiten? Man kann es auf verschiedene Weisen machen.
Manche Leute meinen, es gibt nur EINEN Weg. Aber wenn du
schon Steuerung kannst, wirst du schon sehen, was bei dir
am besten passt.

Machst du es in ASM oder C?

von bingo (Gast)


Lesenswert?

Ich verstehe den ganzen Thread nicht. ihr macht hier einen Popanz um 
eines der einfachsten protokolle, die es überhaupt gibt:

Jedes Bit beginnt mit einer fallenden Flanke, das Startbit hat einen DC 
von 50%, ist also leicht von den anderen Bits zu unterscheiden.

Hier gibt es fertige Codes für AVR und PIC 
http://www.mikrocontroller.net/articles/Temperatursensor#TSic, einem 
Beispiel für den PIC habe ich die folgende Erklärung entnommen:

1
;                       Duty-Cycle:
2
; Startbit                   50%
3
; Daten-/Parity-Bit logic 0: 25%
4
;                   logic 1: 75%
5
; typ. Länge eines Bits: 125 usec
6
;
7
; Messprinzip:
8
; Die Bitlänge ist temperaturabhängig, deshalb bestimmt man die Bitlänge bei jedem Byte durch Messung des Startbits
9
; dieses hat einen Duty-Cycle von 50%. Wenn das halbe Startbit rum ist (LO), wird die Zeit t_strobe gemessen.
10
; Diese Zeit wartet man vom Start eines jeden Daten-Bytes, und nimmt dann den Zustand des Daten-Pins auf. Ist er
11
; LO, dann ist das Daten-Bit 0 (25% Duty-Cycle), ist er HI, dann ist das Daten-Bit 1 (75% Duty-Cycle)

Absolut easy, oder ...

von MSP (Gast)


Lesenswert?

Knetling schrieb:
> Ist der schwer einzuprogrammieren?
Nein, keine große Nummer.

Knetling schrieb:
> weil man für einiges Temperaturkompensieren muss
Was für Reaktionszeiten bei Temperaturänderung benötigst du? Der TSIC in 
seinem Kunststoffgehäuse muss ja der Änderung folgen können. Evtl. kann 
das durch Kopplung mit einer Wärmeleitfläche (Kühlkörper) verbessert 
werden.

Knetling schrieb:
> Die Tempratur kommt
> doch 10 mal die Sekunde.
Aber so schnell wird sie sich im TSIC (also am Messpunkt) nicht 
ändern. Also brauchst du nicht jeden Datenrahmen empfangen und bei 
knappen Resourcen solltest du das auch nicht.

Bernie schrieb:
> Schwer ist das nicht.
Da hatte ich bei dir einen anderen Eindruck. Ich dachte du hättest eine 
Doktorarbeit über den TSIC geschrieben.

bingo schrieb:
> eines der einfachsten protokolle, die es überhaupt gibt
bingo schrieb:
> Absolut easy, oder ...
full agree

von Knetling (Gast)


Lesenswert?

Voll super, wird schon klar!

Geht das gut mit Versorgung ausm Portpin?
Also den TSIC alle Sekunden von einem Port eingeschalten, und einen 
PCINT für die Daten aktivieren.
Timer0 muss dann für die Zeitgeschichten ran.

PCINT kommt bei jeder Flanke, da kommt mal keine bis zum Start-Bit. Dann 
kommt die 50% Zeit, mess ich mit Timer0. Damit die andern Bits nach 
fallende Flanke vergleichen.

Worauf muss ich 20ms warten?

Und wieso ist die Bitlänge tempraturabhängig? Dachte die Temperatur ist 
in den 2 Datenbytes.
( Steht so im Datenblatt. )

von MSP (Gast)


Lesenswert?

Knetling schrieb:
> Geht das gut mit Versorgung ausm Portpin?
Ja, aber Spannung anhand der Datenblätter kontrollieren.

Knetling schrieb:
> Flanke, da kommt mal keine bis zum Start-Bit
Ja, aufm Labortisch. Besser ist es dem TSIC ein wenig Zeit zum 
Stabilisieren zu geben. Die Zeit ist unkritisch, Bsp. 2ms ... 20ms.

Knetling schrieb:
> Worauf muss ich 20ms warten?
Gar nicht, der Floh kommt von "Ich kann nicht lesen Bernie".

Knetling schrieb:
> Und wieso ist die Bitlänge tempraturabhängig?
Intern im TSIC gibt es eine Takterzeugung. Die ist nicht 
temperaturstabil. Das ist aber völlig egal, da die Bitzeit vom Startbit 
abgeleitet wird.

Knetling schrieb:
> Damit die andern Bits nach
> fallende Flanke vergleichen.
oder ab dem halben Startbit bei den nächsten vollen Bitlängen auf den 
Pegel gucken.
Für Bernie: Das dürften ein paar Maschinenbefehle weniger sein. Aber 
eigentlich egal, wenn man die Zeit damit vergeudet, alle 100ms Daten zu 
lesen.    Klick?

von bingo (Gast)


Angehängte Dateien:

Lesenswert?

Ich habe mal meinen LA angeworfen und das Protokoll aufgezeichnet. 
Daraus kann man sehr schön sehen, dass es sehr wohl Sinn macht, die 
Versorgung des TSIC über einen zusätzlichen Port ein und aus zu 
schalten.

In Image1 wird der TSIC eingeschaltet (Vdd geht HI und wenige ns danach 
auch Data), etwa 35 ms später kommen die 2 fast zusammenhängenden Blöcke 
mit den Daten. Diese sind in Image 2 dargestellt: je ein Startbit (S), 8 
Datenbits (0/1) und ein Paritybit (P). Wenn man per Timer die LO-Phase 
des Starbits ausmisst (hier sind es etwa 62µs) und dann nach jeder 
fallenden Flanke genau diese Zeit abwartet, braucht man nur den Zustand 
von Data (0/1) aufzunehmen und hat das Datenbit.

In Image 3 ist der Verlauf aufgezeichnet, den man im frei laufenden 
Modus hat: etwa alle 100 ms zwei fast zusammenhängende Datenblöcke.

Der Modus mit ein/aus geht also wesentlich schneller (nach 35 ms hat man 
die Daten) als der frei laufende Modus (im Mittel dauert es 50 ms, bis 
der Datenblock da ist). Da man im ein/aus-Modus sicher sein kann, dass 
als erstes ein Startbit kommt, kann man die Daten einfach per Polling 
aufnehmen, das sind nur wenige Befehle in ASM oder C. Im frei laufenden 
Modus muss man erst mal auf eine Pause von einer gewissen Länge warten 
(z.B. 60-80 ms) und dann pollen oder man nimmt per Interrupt auf und 
muss entscheiden, ob ein Start- oder ein Daten-Bit vorliegt.

Zudem fällt die Erwärmung des Dies im TSIC weg, die angesichts des 
geringen Stromverbrauch nur gering, aber messbar ist.

von MSP (Gast)


Lesenswert?

bingo schrieb:
> etwa 35 ms später kommen
Aha, und was steht im Datenblatt? Ich kann die größeren Abweichungen 
bestätigen.

bingo schrieb:
> dann nach jeder
> fallenden Flanke genau diese Zeit abwartet, braucht man nur den Zustand
> von Data (0/1) aufzunehmen und hat das Datenbit.
oder du nimmst einen Timerinterrupt mit der doppelten Zeit (also von 
einem ganzen Bit) den du nach dem halben Startbit (steigende Flanke) 
scharf schaltest. Dann reicht der Zustand beim Timer-Ereignis.

bingo schrieb:
> Der Modus mit ein/aus geht also wesentlich schneller
> als der frei laufende Modus
das stimmt so nicht. Das hängt davon ab, wann die Auswertung startet. 
Das kurz vor dem Satartbit sein, dann geht es schneller, oder während 
oder kurz nach der Datenübertraguzng, dann dauert es länger.

bingo schrieb:
> dass als erstes ein Startbit kommt, kann man die Daten einfach per Polling
> aufnehmen
buhhh!

bingo schrieb:
> Zudem fällt die Erwärmung des Dies im TSIC weg, die angesichts des
> geringen Stromverbrauch nur gering, aber messbar ist.
da wäre ich an Daten interessiert.

von Knetling (Gast)


Lesenswert?

BINGO! bingo!  ;-)

Mit deinen Bildern schnall ich auch wie das 2. Byte kommt. In der 
Zackwire-PDF sieht man das nichtso richtig.

0000001011010111 = 727
727 * 1000 / 1024 = 709
709 - 500 = 209
Braucht man keine Fließkommas, super. Hast du 20,9°C in der Bude? ;-)

Denk mal, ich geh so vor:
Ich werf alle Sekunde den TSIC an, und lass ab kurz danach den PCINT mit 
dem Timer von fallende bis steigende Flanke die Bits bestimmen. Erster 
Wert ist Refferenz, die andern vergleich ich auf länger-kürzer. Lässt 
sich bestimmt leichter programmiern, als die Portabfrage nach 
Refferenzzeit.

Wenn ich die Bilder seh, geht es 20 mal runter und 20 mal rauf. Sind 20 
Bits. Kann ich ja nach 20 Bits im PCINT den TSIC wieder ausschalten.

Nächste Sekunde hol ich mir die Bits und lass das Ganze wieder losgehen.

Wirklich easy!


So schnell soll die Tempraturmessung ja nicht sein, wenn MSP da recht 
hat. Kann mans bestimmt sein lassen, das zweite Startbit als Reffernz zu 
nemen. Schmeiß ich hinterher raus.

Noch paar Fragen.
Komisch daß in der Zackwire-PDF 65-85ms nach einschalten steht. Auf den 
Bildern sinds ja nur schlappe 40 ms. Na Egal, ist viel kleiner als meine 
Sekunde und der PCINT hat solange nichts zu tun.

Sind die 125µs so ungenau? 8MHZ /64 = 125kHz = 8µs beim Timer0 müssten 
doch reichen.

Muss ich 40 mal im PCINT kurz kucken welche Flanke war und Timer0 
auslesen und vergleichen oder reseten. Was für 30 Berechnungen im PCINT 
machen MSP Angst?
Das berechnen mach ich im Hauptprogramm, hat noch Reserve dafür.


Schönen Dank schon mal,
meld mich morgen wies klappt.
Sonnabens ist ja Zeit zum probiern.

von Bernie (Gast)


Lesenswert?

Oha, die 39 ms Reaktionszeit bei "bingo (Gast)" sind überraschend, das 
Bild sieht aber überzeugend aus. Habe dem Datenblatt mit 65...85 ms 
vertraut.

Die Bilder sind wirklich gut fürs Verständnis! - Danke, "bingo (Gast)"!
Wie "Knetling" schreibt, liest man das ZacWire.PDF und ist danach noch 
etwas unsicher...

Die Reaktionszeit hat bei mir aber keinen Einfluss, da der Interrupt vom 
TSIC solange nicht stört. Nur doof, wenn eine Warteschleife zu lange 
wartet...

Was macht ihr da für umständliche Sachen mit Polling, oder 
Timerinterrupt nach jeder Flanke starten und nach irgendwelchen Zeiten 
scharfschalten?

Ich gehe da etwas praktischer ran:

Der Interrupt fürs TSIC-Signal und der freilaufende Timer sind sowieso 
im Geschäft, also:

TSIC-negative Flanke: Timer-Reset,
TSIC-positive Flanke: Timer auslesen.

Startbit erkennen: Nach Einschalten kein Problem, sonst lange Pause 
(Timer-Overflow) auswerten.
Zur Not auch noch 3..4 Befehle für Plausibilität in den Interrupt 
einbauen (1/2 µs @ 8 MHz).

Startbit - positive Flanke: Zeit als T-Referenz erfassen.
Weitere Bits - positive Flanke: Vergleich mit T-Referenz und als logisch 
0, oder logisch 1 in 2..3 reservierte Bytes reinschieben.

(Hab ich alles schon vor einer Woche geschrieben...)

GENAU SO ist es "voll easy"!


"Knetling" hat offenbar einen ähnlichen Weg eingeschlagen.
Schaun wir mal...


Für die, die keine DÜNNBRETTBOHRER sind:

Man könnte sich noch überlegen, ob überhaupt 3 Bytes für die erfassten 
Bits nötig sind.

Nur 2 Bytes: Es würden nur 5 Bit vom Hi-Byte, Hi-Parity, das 2. 
Startbit, 8 Bits vom Lo-Byte und Lo-Parity erfasst. Vom Hi-Byte sind 
aber nur 3 Bit aktiv, davor sind 5 konstante NULL-Bits.

Wenn ich die Parity-Bits auswerte, muss ich einfach die 3 fehlenden 
oberen Hi-Bits als empfangene NULLen annehmen. Die Parity-Auswertung 
wird da leicht "beschummelt". Das Parity-Bit geht aber auch davon aus, 
dass diese Bits NULL sind - und kann sowieso nie sagen, welches Bit 
fehlerhaft ist. Nach meiner Einschätzung hat diese Einsparung keinen 
Einfluss auf die Datensicherheit. Weiß es jemand besser?

von bingo (Gast)


Lesenswert?

> Oha, die 39 ms Reaktionszeit bei "bingo (Gast)" sind überraschend, das
> Bild sieht aber überzeugend aus. Habe dem Datenblatt mit 65...85 ms
> vertraut.

Die Zeit zwischen Einschalten und dem ersten Startbit ist tatsächlich 
nur etwa 30-45 ms. Ich habe in den letzten Jahren mehr als 20 TSIC 
verbaut (Hygrosens), alle getestet, alle lagen zwischen 30 und 42 msec. 
Zeiten wie in den Datenblättern habe ich nie erlebt, ich habe allerdings 
nicht unter -10°C oder über +50°C gemessen.

von Knetling (Gast)


Lesenswert?

Jau, läuft schon!

Tausend Dank schon mal!

Hab es nach Bernies Tip mit nur zwei Emfänger-Bytes gemacht.
Und noch die Tempratur auf die Anzeige, passt gut zum Thermometer.
Die Steuerung klappt wie vorher - naja die muss auch nur alle 8ms 
reagiren und der PCINT braucht immer viel weniger wie 10µs.

Benützt ihr die Paritys? Ich schieb die und das 2. Startbit einfach 
raus.

Läuft superstabil, hab aber noch die Woche bißchen Zeit. Hey MSP, wie 
brauchst du nur ein Interupt für jedes Bit? Mein PCINT kommt aber immer 
auf jede Flanke. Und den USART brauch ich aber schon um für die 
PC-Verbindung.

von Bernie (Gast)


Lesenswert?

Freut mich, dass du fast von allein, zu einer ähnlichen recht effektiven 
Lösung gekommen bist. Bin aber nicht sehr überrascht: Es bietet sich 
doch einfach an, wenn man das TSIC-Protokoll verstanden hat und es 
resourcensparend umsetzen will.

Wir verschwenden allerdings zwei Interrupts pro Bit!

Wenn ich den einen kryptischen Beitrag von "MSP (Gast)" richtig 
entschlüsselt habe, macht er es recht umständlich, um nur einen 
Interrupt pro Bit zu brauchen:

1) Irgendwie das Startbit abwarten,
2) dessen Low-Zeit (halbe Bitdauer) mit Timer messen,
3) schnell den Timer umdefinieren, dass er mit der doppelten Zeit einen 
Interrupt liefert, der dann hoffentlich (!) immer die Bitmitte trifft.

Wäre mir zu unsicher: Die Erfassung (mit 1 MHz) kann leicht 1,5 µs 
danebenliegen. Macht 3 µs pro Bit, die sich aufsummieren. Nach 19 
weiteren Bit sind das 57 µs = fast eine halbe Bitzeit. Totaler Murks!

Also den ganzen Aufwand für das zweite Byte nochmal! Selbst dann ist es 
für die erforderliche Genauigkeit von 1/4 Bitdauer HOFFENTLICH nur nah 
der Grenze. Aufwändig, aber unsicher!

Vielleicht komm ich nicht drauf und er hat das Ei des Kolumbus gefunden? 
- Aber "MSP (Gast)" bevorzugt leider die Kritik an anderen, gegenüber 
eigenen nachprüfbaren Vorschlägen.


Zum Temperatureinfluss habe ich mir ein paar überschlägige Gedanken 
gemacht:

Mit der Einschaltung 1 mal pro Sekunde sieht es gut aus!

Typische TO92-Transistoren haben einen R_th von 0,2 K/mW zu ruhender 
Luft.
Wird beim TSIC mit dieser Bauform ähnlich sein.

Datenblatt TSIC: < 0,1 mA bei 3,3 V  ->  < 0,33 mW.
Macht < 0,33 mW * 0,2 K/mW = < 0,07 K

Also knapp 0,1 °C zu viel bei Dauerbetrieb an 3,3 V.
Bei 1/10 Einschaltzeit < 0,01 °C. (Das sieht der TSIC nicht.)

Eine pessimistische Schätzung für 5 V sei 0,2 mA = 1 mW.
Ich messe 15 mV an den vorgeschlagenen 220 Ohm (+ 100 nF nach Masse), 
etwa 0,07 mA...

Also wahrscheinlich unter 0,2 °C Fehler bei Dauerbetrieb an 5 V.
Bei 1/10 Einschaltzeit unter 0,02 °C. (Jenseits der Auflösung des TSIC.)

von Spess53 (Gast)


Lesenswert?

Hi

>Wäre mir zu unsicher: Die Erfassung (mit 1 MHz) kann leicht 1,5 µs
>danebenliegen. Macht 3 µs pro Bit, die sich aufsummieren. Nach 19
>weiteren Bit sind das 57 µs = fast eine halbe Bitzeit. Totaler Murks!

Unsinn. Mit jeder fallenden Flanke beginnt das Spiel vorn. Da summiert 
sich nichts.

MfG Spess

von Bernie (Gast)


Lesenswert?

Hey Spess, von dir bin ich aber klügere Beiträge gewohnt!

Hier geht es um die benötigten Interrupts pro Bit bei ZacWire.
"MSP (Gast)"  behauptet, dass man es mit EINEM Interrupt pro Bit 
schafft.

Um die fallende Flanke zu erfassen, brauch ich einen Interrupt, oder
zeitintensives Polling.

Um den Bitwert (den duty cycle) zu erfassen, brauche ich einen 
Timer-Interrupt, oder einen Interrupt von der positiven Flanke, oder 
zeitintensives Polling.

Wenn Polling nicht in Frage kommt, weil der µC noch anderes zu erledigen 
hat, verbleiben ohne Hexerei immer ZWEI Interrupts pro Bit.

von Knetling (Gast)


Lesenswert?

Hab noch gegrübelt, ob es mit Timer-Capture oder so besser wird. Wird 
nur mehr Aufwand.

Die Software UART so ungenau laufengelassen, ist mir zu 
fehlerfreundlich. Und die wie Spess53 auf jedes Bit zu synchronisieren, 
kostet gleichen Aufwand wie die Messung von fallende auf steigende 
Flanke.

was meckert MSP nur immer rum? Und die eigene Idee müssen ihm noch 
andere hinschreiben.

Was solls! Es funktoniert sauber, jetzt auch mit Paritys.

Danke Jungs!

von Andreas S. (igel1)


Lesenswert?

Hallo Matthias,

beim groben Code-Scan fiel mir folgendes auf:

----------------------------------------
void zac_off() {
  ZACPORT &= 1 & ZACPIN;
}
----------------------------------------

Diese Funktion kann den TSIC ausschalten,
muß es aber nicht: wenn ZACPIN == 1 ist,
dann wird die Funktion nichts bewirken.
Außerdem setzt die Funktion bei ZACPIN != 1
den ganzen ZACPORT auf 0 - und das ist
evtl. gar nicht gewollt.

Vermutlich meintest Du eher folgendes:
----------------------------------------
void zac_off() {
  ZACPORT &= ~(1 < ZACPIN);
}
----------------------------------------

Trotzdem wird das vermutlich dein Problem noch nicht lösen.

Ich denke, man muß sich sehr genau Gedanken darüber machen,
wie man die TSIC-Abfrage zwischen den USB-Interruptaufrufen
platziert (oder gar beides verschachtelt).

Dann bislang kannst Du nie sicher sein, ob Du eine fallende
oder steigende Flanke rechtzeitig mit Deinem INT1 detektierst -
der Prozessor könnte sich ja zum Zeitpunkt eines Flanken-
wechsels in der USB-Interruptroutine aufhalten (ist alles
noch ein bißchen Mutmaßung - ich muß Deinen Code erst noch
"richtig" sichten).

Bevor ich tiefer einsteige, hätte ich ein paar Fragen:

- Was genau soll Deine Schaltung+Auswerteprogramm tun?
- Welchen ATmega verwendest Du?
- Welche Quarzfrequenz verwendest Du?
- Wie sieht die Software auf dem PC aus?

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

Hallo Matthias,

mir fielen noch ein paar Fragen ein:

- Welche Entwicklungsumgebung benutzt Du?
- Welches Entwicklungsboard benutzt Du?
- Welches Betriebssystem hat Dein PC, der per USB die Daten empfangen 
soll?
- Könntest Du netterweise kurz skizzieren, wie Du über das
  USB/HID-Protokoll die Daten auf dem PC auswertest?
- Könntest Du netterweise nochmals beide Codeversionen hochladen:
  1.) das vollständige Programm mitsamt den USB Codeanteilen
  2.) die um die USB-Codeanteile "gestrippten" Programmversion

Sodann fiel mir auf, daß in Deinem Code die Angabe der
CPU-Taktfrequenzangabe fehlt:

#define F_CPU 12000000UL

Das könnte ggf. bei Verwendung der delay-Funktionen in Deinem Code
Probleme generieren, siehe:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

Hallo Matthias,

hier noch eine Anmerkung zur Motivation:

Ich bin mir sehr sicher, daß wir zumindest den USB-freien Code
ans Laufen bekommen werden.

Was den Code mit USB-Anteilen angeht, so wird das sicher etwas
anspruchsvoller. Aber die Angaben beim Herstellen machen Hoffnung
http://vusb.wikidot.com/troubleshooting
(siehe Punkt 6. Interrupts and Timing)

Dort ist zu lesen, daß man offenbar den USB-Interrupt auch mal ein
paar Millisekunden ausschalten darf (was für ein TSIC-Dateneinlesen
ggf. ausreichen könnte)

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

Hallo Matthias,

ich habe soeben Deinen Code in meiner Entwicklungsumgebung übersetzt 
(AVRStudio Version 4.19 Build 716) und siehe da, die einzige 
Compiler-Warnung lautet:

c:/programme/winavr-20100110/lib/gcc/../../avr/include/util/delay.h:85:3 
:  warning: #warning "F_CPU not defined for <util/delay.h>"
Build succeeded with 1 Warnings...

Will sagen: das solltest Du unbedingt noch fixen.

Viele Grüße

Igel1

von Matze0001 (Gast)


Lesenswert?

Evtl. hilft es ja jemanden weiter oder kann es wer gebrauchen..
Hab mir mal ein mit Thermometer zusammen gebaut, funktioniert immernoch 
einwandfrei... Habe für die Anzeige Schieberegister mit 7 Segmentanzeige 
verwendet. Der Mega lief glaub ich mit meinen Code auf 4Mhz. Wenn man 
die Frequenz verändert muss man glaub ich ein wenig an der Stelle zum 
messen der Strobe-Zeit herumbasteln...
Nicht schimpfen, war mein erstes Lernprojekt. Hatte vorher 0 Ahnung vom 
Programmieren und hatt das in 3 Wochen fertig...
Evtl. ist das eine oder andere nicht so optimal, aber fürs erste mal...
Und es funktioniert :)

Hier mal der Code:


1
.include "m8def.inc"
2
 
3
4
.def  temp1      = r16
5
.def  temp2      = r17
6
.def  temp3      = r18
7
.def  tsicwert_low  = r19
8
.def  tsicwert_high  = r20
9
.def   Digit1        = r21
10
.def   Digit2        = r22
11
.def   Digit3        = r23
12
.def  null      = r24
13
.def  portstatus    = r25
14
15
16
.equ SCHIEBE_DDR  = DDRD
17
.equ SCHIEBE_PORT = PORTD
18
.equ Rclock       = 2
19
.equ Clock        = 1
20
.equ Data         = 0
21
22
 
23
 
24
25
; Die Port Pins auf Ausgang konfigurieren
26
27
    ldi   temp1, (1<<Rclock) | (1<<Clock) | (1<<Data) ; Anm.1
28
    out   SCHIEBE_DDR, temp1
29
30
31
    ldi   temp1, HIGH(RAMEND)
32
    out   SPH, temp1
33
  ldi   temp1, LOW(RAMEND)    ; Stackpointer initialisieren
34
    out   SPL, temp1
35
36
 
37
38
  ldi  temp1,0b00000000    
39
  out  ddrc,temp1          ; Port C als Eingang Initialisieren
40
  
41
42
;___________________________________________________________________________
43
;___________________________________________________________________________
44
45
main:
46
47
;  rcall read_tsic
48
;  rcall calc
49
;  rcall convert
50
;  rcall write_data
51
;  rjmp  main
52
53
54
;___________________________________________________________________________
55
;___________________________________________________________________________
56
57
read_tsic:
58
59
daten:
60
61
  clr digit1
62
  clr digit2
63
  clr digit3
64
  clr tsicwert_high
65
  clr tsicwert_low
66
67
  ldi    temp2, 8        ;Wieviele Bits müssen gelesen werden?
68
  ldi    temp3, 0b00000010
69
  ldi    null,    0b00000000
70
  rjmp   warten
71
72
73
74
75
76
77
;____________________________________________________
78
79
;                     warten
80
81
82
warten:
83
    ldi    temp1, 100
84
    sbis   pinC, 0
85
    rjmp   warten
86
    
87
count:
88
    
89
    dec   temp1
90
    brne   control
91
    rjmp  start
92
93
control:    
94
95
    sbic  pinC, 0
96
    rjmp   count
97
    rjmp   warten
98
99
;____________________________________________________
100
101
;             start Byte1
102
103
start:
104
105
    sbic   pinC, 0      ;warten bis fallende Flanke bei Startbit kommt
106
    rjmp   start
107
108
strobe:
109
    
110
    out   tccr0, temp3    ;timer bei fallender Flanke von Startbit einschalten
111
112
strobe2:
113
    sbis  pinC, 0
114
    rjmp  strobe2      ;Strobe-Zeit messen
115
    in    temp1, tcnt0
116
    push  temp1
117
    out   tccr0, null
118
    out    tcnt0, null
119
120
strobe3:
121
122
    sbic  pinC, 0
123
    rjmp  strobe3      ;warten bis fallende Flanke kommt
124
125
strobecount:
126
    dec    temp1
127
    nop
128
    nop
129
    nop
130
    brne  strobecount
131
132
    in    portstatus, PinC    ;Portstatus nach abgelaufener Strobezeit abfragen
133
    ror    portstatus
134
    brcs  bitcount1
135
    rjmp  bitcount0
136
137
138
setbit:
139
140
    sbr   tsicwert_high, 0b00000001  ;Bit in tsicwert_high schreiben
141
    lsl    tsicwert_high
142
    rjmp   strobe3
143
144
bitcount1:
145
146
       ;springt hier her wenn Bit 1 war
147
148
    pop    temp1        ;gemessene Strobezeit wieder ins Register r16 laden
149
    push  temp1        ;und wieder sichern
150
    dec    temp2
151
    brne  setbit
152
    sbr    tsicwert_high, 0b00000001
153
    rjmp  next
154
155
bitcount0:
156
157
    ;springt hier her wenn Bit 0 war
158
159
    pop    temp1
160
    push  temp1
161
    dec    temp2
162
    brne  links
163
    rjmp   warten3
164
165
links:
166
    lsl    tsicwert_high
167
168
warten2:
169
    sbis  pinC, 0      ;warten bis steigende Flanke kommt, danach wieder
170
    rjmp  warten2      ;bei strobe3 warten bis fallende Flanke vom nächsten Bit kommt
171
    rjmp  strobe3
172
173
174
warten3:
175
    sbis  pinC, 0
176
    rjmp  warten3
177
    rjmp  next
178
        
179
next:
180
    sbic  pinC, 0
181
    rjmp  next
182
next2:    
183
    sbis  pinC, 0
184
    rjmp  next2
185
    ldi   temp2, 8
186
    ;pop    temp1
187
;______________________________________Byte2____________________________________________
188
189
start2:
190
191
    sbic   pinC, 0      ;warten bis fallende Flanke bei Startbit kommt
192
    rjmp   start2
193
194
strobe4:
195
    
196
    out tccr0, temp3      ;timer bei fallender Flanke von Startbit einschalten
197
198
strobe22:
199
    sbis  pinC, 0
200
    rjmp  strobe22      ;Strobe-Zeit messen
201
    in    temp1, tcnt0
202
    push  temp1
203
    out   tccr0, null
204
    out    tcnt0, null
205
206
strobe32:
207
208
    sbic  pinC, 0
209
    rjmp  strobe32      ;warten bis fallende Flanke kommt
210
211
strobecount2:
212
    dec    temp1
213
    nop
214
    nop
215
    nop
216
    brne  strobecount2
217
218
    in    portstatus, Pinc    ;Portstatus nach abgelaufener Strobezeit abfragen
219
    ror    portstatus
220
    brcs  bitcount12
221
    rjmp  bitcount02
222
223
224
setbit2:
225
226
    sbr   tsicwert_low, 0b00000001  ;Bit in r18 schreiben
227
    lsl    tsicwert_low
228
    rjmp   strobe32
229
230
bitcount12:
231
232
       ;springt hier her wenn Bit 1 war
233
234
    pop    temp1        ;gemessene Strobezeit wieder ins Register r16 laden
235
    push  temp1        ;und wieder sichern
236
    dec    temp2
237
    brne  setbit2
238
    sbr    tsicwert_low, 0b00000001
239
    rjmp  back1
240
241
bitcount02:
242
243
    ;springt hier her wenn Bit 0 war
244
245
    pop    temp1
246
    push  temp1
247
    dec    temp2
248
    brne  links2
249
    rjmp  back1
250
251
links2:
252
    lsl    tsicwert_low
253
254
warten22:
255
256
    sbis  pinC, 0        ;warten bis steigende Flanke kommt, danach wieder
257
    rjmp  warten22      ;bei strobe3 warten bis fallende Flanke vom nächsten Bit kommt
258
    rjmp  strobe32
259
260
back1:
261
    ;pop    temp1
262
  ;  ret
263
;__________________________________________________________________________________
264
;__________________________________________________________________________________
265
266
calc:
267
268
        ;ldi tsicwert_low,  0b00000000
269
    ;ldi tsicwert_high, 0b00000000
270
271
;11Bit Rohwert des TSic in Temperatur umrechnen
272
;Originalformel aus dem Datenblatt T= (Digital_signal/2047*(HT-LT)+LT) [°C]
273
;LT = -50,  HT = 150 als Standardwert für die Temperatur-Berechnung
274
;
275
;Umgestellt für ASM
276
;t=TSic_Wert*200 /2048-50   (die 2047 um 1 erhöht um besser rechnen zu können)
277
;t=TSic_Wert*25/256-50  (gekürzt /8)
278
;Da eine Stelle hinter dem Komma angezeigt wird, rechne ich die Temperatur*10 und setze
279
;vor die letzte Stelle den Dezimalpunkt (Festkomma).
280
;Temperatur*10=(TSic_Wert*250)/256-500 
281
  clr    temp1
282
  mov    temp2,tsicwert_low
283
  mov    temp3,tsicwert_high  
284
  lsl    tsicwert_low      ;*2
285
  rol    tsicwert_high
286
  add    tsicwert_low,temp2  ;*3
287
  adc    tsicwert_high,temp3
288
  lsl    tsicwert_low      ;*6
289
  rol    tsicwert_high
290
  sub    temp1,tsicwert_low  ;*256 - *6 = *250
291
  sbc    temp2,tsicwert_high  ;TSic_Wert*250 steht jetzt in temp1/temp2/temp3
292
  sbci  temp3,0      ;Durch verwerfen des LSB (temp1) wird durch 256 geteilt  
293
  subi  temp2,low(500)  ;500 (50.0) abziehen
294
  sbci  temp3,high(500)
295
296
; Nach ASCii wandeln
297
298
  tst temp3        ;prüfen ob negativ
299
  brmi neg16        ;wenn negativ dann weiter zu neg16
300
  rjmp calc100a      
301
302
neg16:       
303
304
  com temp2
305
    com temp3
306
    subi temp2,Low(-1)    ;umrechnen für negativ
307
    sbci temp3,High(-1)
308
309
;_______________________________Plusbereich___________________________
310
calc100a:
311
  subi  temp2,low(100)  ;100 abziehen
312
  sbci  temp3,high(100)
313
  brcs  calc100b
314
  inc   digit1
315
  rjmp  calc100a
316
317
calc100b:
318
  ldi temp1, 100
319
  add temp2, temp1
320
321
calc10a:
322
  subi temp2, 10
323
  brcs calc10b
324
  inc  digit2
325
  rjmp calc10a
326
327
calc10b:
328
  ldi temp1, 10
329
  add  temp2, temp1
330
331
calc1:
332
  mov digit3, temp2
333
  ;ret
334
335
;___________________________________________________________________
336
;___________________________________________________________________
337
;Ergebnis umwandeln für Ausgabe auf Digit
338
339
convert:
340
341
  clr    r1
342
343
digit100er:
344
  ldi     ZL, LOW(Codes2*2)  ; die Startadresse der Tabelle in den
345
    ldi     ZH, HIGH(Codes2*2) ; Z-Pointer laden
346
 
347
  mov     temp1, Digit1         ; die wortweise Adressierung der Tabelle
348
    add     temp1, Digit1        ; berücksichtigen
349
 
350
    add     ZL, temp1         ; und ausgehend vom Tabellenanfang
351
    adc     ZH, r1            ; die Adresse des Code Bytes berechnen
352
 
353
    lpm                       ; dieses Code Byte in das Register r0 laden
354
 
355
    mov     digit1, r0           ; und ins Register für 100er laden
356
357
digit10er:
358
  ldi     ZL, LOW(Codes*2)  ; die Startadresse der Tabelle in den
359
    ldi     ZH, HIGH(Codes*2) ; Z-Pointer laden
360
361
   mov     temp1, Digit2         ; die wortweise Adressierung der Tabelle
362
    add     temp1, Digit2     ; berücksichtigen
363
 
364
    add     ZL, temp1         ; und ausgehend vom Tabellenanfang
365
    adc     ZH, r1            ; die Adresse des Code Bytes berechnen
366
 
367
    lpm                       ; dieses Code Byte in das Register r0 laden
368
 
369
    mov     digit2, r0           ; und ins Register für 10er laden
370
371
digit1er:
372
  ldi     ZL, LOW(Codes*2)  ; die Startadresse der Tabelle in den
373
    ldi     ZH, HIGH(Codes*2) ; Z-Pointer laden
374
375
   mov     temp1, Digit3     ; die wortweise Adressierung der Tabelle
376
    add     temp1, Digit3     ; berücksichtigen
377
 
378
    add     ZL, temp1         ; und ausgehend vom Tabellenanfang
379
    adc     ZH, r1            ; die Adresse des Code Bytes berechnen
380
 
381
    lpm                       ; dieses Code Byte in das Register r0 laden
382
 
383
    mov     digit3, r0        ; und ins Register für 1er laden
384
  
385
  sbr    digit2, 0b01000000
386
387
back2:
388
    ;ret
389
    rjmp starte
390
391
Codes:                               ; Die Codetabelle für die Ziffern 0 bis 9
392
                                     ; sie regelt, welche Segmente für eine bestimmte
393
                                     ; Ziffer eingeschaltet werden müssen
394
                                     ;
395
  .db     0b10111110        ; 0:
396
     .db     0b10001000        ; 1:
397
   .db     0b00111101        ; 2:
398
    .db     0b10101101        ; 3:
399
    .db     0b10001011        ; 4:  
400
  .db     0b10100111        ; 5:
401
    .db     0b10110111        ; 6:
402
    .db     0b10001100        ; 7:
403
    .db     0b10111111        ; 8:
404
    .db     0b10101111        ; 9:
405
406
407
408
Codes2:                               ; Die Codetabelle für die Ziffern 0 bis 9
409
                                     ; sie regelt, welche Segmente für eine bestimmte
410
                                     ; Ziffer eingeschaltet werden müssen
411
                                     ;
412
  .db     0b00000000        ; 0:
413
     .db     0b10001000        ; 1:
414
   .db     0b00111101        ; 2:
415
    .db     0b10101101        ; 3:
416
    .db     0b10001011        ; 4:  
417
  .db     0b10100111        ; 5:
418
    .db     0b10110111        ; 6:
419
    .db     0b10001100        ; 7:
420
    .db     0b10111111        ; 8:
421
    .db     0b10101111        ; 9:
422
423
424
425
426
;_________________________________________________________________________
427
;_________________________________________________________________________
428
429
430
write_data:
431
432
;________________________________Ausgabe_______________________________
433
434
435
Starte:
436
    rcall   Schiebe
437
    rcall   SchiebeOut
438
  rjmp  daten
439
440
441
    
442
 
443
;-----------------------------------------------------------------------------
444
;
445
; Die Ausgabe im Schieberegister in das Ausgaberegister übernehmen
446
;
447
; Dazu am RCK Eingang am Schieberegister einen 0-1-0 Puls erzeugen
448
;
449
SchiebeOut:
450
    sbi   SCHIEBE_PORT, Rclock
451
    cbi   SCHIEBE_PORT, Rclock
452
    ret
453
 
454
;-----------------------------------------------------------------------------
455
;
456
; 8 Bits aus Digit3 an das Schieberegister ausgeben
457
Schiebe:
458
    ldi   temp1,8              ; 8 Bits müssen ausgegeben werden
459
 
460
Schiebe_1:
461
     ;
462
     ; jeweils das höchstwertige Bit aus Digit1 ins Carry-Flag schieben
463
     ; Je nach Zustand des Carry-Flags wird die Datenleitung entsprechend
464
     ; gesetzt oder gelöscht
465
     ;
466
    rol  Digit3                  ; MSB -> Carry
467
    brcs Schiebe_One             ; Carry gesetzt? -> weiter bei Schiebe_One
468
    cbi  SCHIEBE_PORT, Data      ; Eine 0 ausgeben
469
    rjmp Schiebe_Clock           ; und Sprung zur Clock Puls Generierung
470
Schiebe_One:
471
    sbi  SCHIEBE_PORT, Data      ; Eine 1 ausgeben
472
 
473
     ;
474
     ; einen Impuls an Clock zur Übernahme des Bits nachschieben
475
     ;
476
Schiebe_Clock:
477
    sbi   SCHIEBE_PORT, Clock    ; Clock-Ausgang auf 1 ...
478
    cbi   SCHIEBE_PORT,Clock     ; und wieder zurück auf 0
479
 
480
    dec   temp1                  ; Anzahl der ausgegebenen Bits runterzählen
481
    brne  Schiebe_1              ; Wenn noch keine 8 Bits ausgegeben -> Schleife bilden
482
483
484
;-----------------------------------------------------------------------------
485
;
486
; 8 Bits aus Digit2 an das Schieberegister ausgeben
487
Schiebe2:
488
    ldi   temp1,8                ; 8 Bits müssen ausgegeben werden
489
 
490
Schiebe_12:
491
     ;
492
     ; jeweils das höchstwertige Bit aus Digit1 ins Carry-Flag schieben
493
     ; Je nach Zustand des Carry-Flags wird die Datenleitung entsprechend
494
     ; gesetzt oder gelöscht
495
     ;
496
    rol  Digit2                  ; MSB -> Carry
497
    brcs Schiebe_One2            ; Carry gesetzt? -> weiter bei Schiebe_One
498
    cbi  SCHIEBE_PORT, Data      ; Eine 0 ausgeben
499
    rjmp Schiebe_Clock2          ; und Sprung zur Clock Puls Generierung
500
Schiebe_One2:
501
    sbi  SCHIEBE_PORT, Data      ; Eine 1 ausgeben
502
 
503
     ;
504
     ; einen Impuls an Clock zur Übernahme des Bits nachschieben
505
     ;
506
Schiebe_Clock2:
507
    sbi   SCHIEBE_PORT, Clock    ; Clock-Ausgang auf 1 ...
508
    cbi   SCHIEBE_PORT, Clock    ; und wieder zurück auf 0
509
 
510
    dec   temp1                  ; Anzahl der ausgegebenen Bits runterzählen
511
    brne  Schiebe_12             ; Wenn noch keine 8 Bits ausgegeben -> Schleife bilden
512
 
513
 
514
515
;-----------------------------------------------------------------------------
516
;
517
; 8 Bits aus Digit1 an das Schieberegister ausgeben
518
Schiebe3:
519
    ldi   temp1,8                ; 8 Bits müssen ausgegeben werden
520
 
521
Schiebe_13:
522
     ;
523
     ; jeweils das höchstwertige Bit aus Digit1 ins Carry-Flag schieben
524
     ; Je nach Zustand des Carry-Flags wird die Datenleitung entsprechend
525
     ; gesetzt oder gelöscht
526
     ;
527
    rol  Digit1                  ; MSB -> Carry
528
    brcs Schiebe_One3            ; Carry gesetzt? -> weiter bei Schiebe_One
529
    cbi  SCHIEBE_PORT, Data      ; Eine 0 ausgeben
530
    rjmp Schiebe_Clock3          ; und Sprung zur Clock Puls Generierung
531
Schiebe_One3:
532
    sbi  SCHIEBE_PORT, Data      ; Eine 1 ausgeben
533
 
534
     ;
535
     ; einen Impuls an Clock zur Übernahme des Bits nachschieben
536
     ;
537
Schiebe_Clock3:
538
    sbi   SCHIEBE_PORT, Clock    ; Clock-Ausgang auf 1 ...
539
    cbi   SCHIEBE_PORT, Clock    ; und wieder zurück auf 0
540
 
541
    dec   temp1                  ; Anzahl der ausgegebenen Bits runterzählen
542
    brne  Schiebe_13             ; Wenn noch keine 8 Bits ausgegeben -> Schleife bilden
543
 
544
    ret
545
546
back3:
547
    ;ret

von Matthias P. (matthias_p65)


Lesenswert?

Alter Schwede, kaum habe ich hier ein paar Tage nicht hineingeschaut, 
geht hier voll die Post ab. ;) Schön, das das Thema von solchem 
Interesse ist.

Andreas S. schrieb:
> Vermutlich meintest Du eher folgendes:
> ----------------------------------------
> void zac_off() {
>   ZACPORT &= ~(1 < ZACPIN);
> }
> ----------------------------------------
>
> Trotzdem wird das vermutlich dein Problem noch nicht lösen.

Ja, das habe ich auch schon gesehen. Ist inzwischen behoben, da ich die 
Funktion aber eh nicht benutzt habe, gab es an der Stelle kein Problem.

Andreas S. schrieb:
> - Welche Entwicklungsumgebung benutzt Du?

Eclipse + CDT + winavr

> - Welches Entwicklungsboard benutzt Du?

Ein Steckbrett. ;)

> - Welches Betriebssystem hat Dein PC, der per USB die Daten empfangen
> soll?

Win7 x64 / Ubuntu

> - Könntest Du netterweise kurz skizzieren, wie Du über das
>   USB/HID-Protokoll die Daten auf dem PC auswertest?
> - Könntest Du netterweise nochmals beide Codeversionen hochladen:
>   1.) das vollständige Programm mitsamt den USB Codeanteilen
>   2.) die um die USB-Codeanteile "gestrippten" Programmversion

Ich werde heute oder morgen mal das vollständige Programm anhängen.

> Sodann fiel mir auf, daß in Deinem Code die Angabe der
> CPU-Taktfrequenzangabe fehlt:
>
> #define F_CPU 12000000UL

Keine Sorge, fehlt nicht, ich hatte nur den Code nicht vollständig 
hierhin kopiert.

Andreas S. schrieb:
> Bevor ich tiefer einsteige, hätte ich ein paar Fragen:
>
> - Was genau soll Deine Schaltung+Auswerteprogramm tun?

Zyklisch die Temperatur des TSIC auslesen und in einem Puffer speichern, 
der über die USB Schnittstelle ausgelesen werden kann.

> - Welchen ATmega verwendest Du?

ATMega8

> - Welche Quarzfrequenz verwendest Du?

16MHz

> - Wie sieht die Software auf dem PC aus?
>

Die entspricht im großen und ganzen dem Beispiel HID-Data aus der 
VUSB-Lib, aber ich werde auch das mal mit anhängen.

Ich werde dann jetzt mal den Rest des Threads studieren. ;)

Gruß
MP

von Andreas S. (igel1)


Lesenswert?

@Matze0001:

Danke für den Code. Ich hatte Matthias allerdings so verstanden,
daß die ganze Party in C starten soll.

@Matthias:

Dann sind wir schon mal gespannt auf Deinen Code.
Bitte sowohl den Code mit USB-Anteilen als auch den ohne USB-Anteile
hochladen. Bitte nur vollständig, sonst suchen wir möglicherweise
an der falschen Ecke.

Kleine Frage noch:
Läuft der USB-Host-Part Deines Programms unter Deinem Win7 x64 oder
unter Deinem Ubuntu oder unter beidem?

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

@Matthias:

- Habe Deinen Schaltplan nachgebaut:
  Dazu habe ich nur den linken Teil Deines Schaltplans nachgebaut und 
alles,
  was in Deinem Schaltplan Richtung ATmega geht, entsprechend mit meinem
  Pollin AVR-Funkboard verbunden - war gerade zur Hand (ATmega8L-8PU,
  12MHz Quarz - ein Wunder, dass der MC bei 3,5V noch mit 12MHz läuft 
...)
- Habe MinGW heruntergeladen und installiert
- Habe V-USB heruntergeladen
- Habe das HID-Data Beispiel compiliert und auf meinen MC geflashed
  (Fuses ebenfalls ...)
- Habe den PC-Part (hidtool) compiliert
- Habe alles zusammengesteckt
- Habe mit "hidtool write" und "hidtool read" Funktionskontrolle gemacht

=> 3x "Alaaf" gebrüllt, als alles funktionierte

Will sagen: Jetzt wäre es supergut, wenn Du Deinen Code hochladen 
könntest, denn jetzt könnte ich live und in Farbe testen ...

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Lesenswert?

Jo, ich mach heute Abend mal ein Paket fertig, bin gestern nicht dazu 
gekommen.

Gruß
MP

von Matthias P. (matthias_p65)


Lesenswert?

Also, ich hab mir die ganze Geschichte nochmal angesehen.

Es hat so absolut keinen Sinn mehr, ich hab' total den Faden verloren.
Ich werde alles wegwerfen und wenn ich Muße habe, nochmal ganz von vorne 
anfangen.

Trotzdem Dank an alle, die Ihre Hilfe angeboten haben.

von Andreas S. (igel1)


Lesenswert?

> Also, ich hab mir die ganze Geschichte nochmal angesehen.
>
> Es hat so absolut keinen Sinn mehr, ich hab' total den Faden verloren.
> Ich werde alles wegwerfen und wenn ich Muße habe, nochmal ganz von vorne
> anfangen.
>
> Trotzdem Dank an alle, die Ihre Hilfe angeboten haben.


Hmmm - schade.

Da hatte ich einige Stunden investiert, um eine gleichartige Umgebung 
zusammenzuschrauben und nu ist alles "umsonst" ...

Bisschen blöd komme ich mir da schon vor.

Ich tröste mich jetzt einfach mal damit, daß die Fehlersuche vermutlich 
kein Zuckerschlecken geworden wäre und uns Matthias bestimmt bald hier 
seinen neuen Code vorstellen wird.

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Lesenswert?

Andreas S. schrieb:
> Hmmm - schade.
>
> Da hatte ich einige Stunden investiert, um eine gleichartige Umgebung
> zusammenzuschrauben und nu ist alles "umsonst" ...
>
> Bisschen blöd komme ich mir da schon vor.

Tut mir leid, ich wollte auf keinen Fall den Eindruck erwecken, ich 
würde die Sache hinwerfen.

Ich hatte das Projekt nur so lange liegen lassen, daß ich mich selbst 
wieder reindenken musste. Ich habe mich nur dazu entschlossen den Code 
neu zu schreiben. Leider habe ich im Augenblick wenig Zeit. Vielleicht 
komme ich ja am Wochenende dazu.

Sieh's doch positiv, du hast doch Ruck-Zuck V-USB ans Laufen bekommen. 
;)

von Andreas S. (igel1)


Lesenswert?

Schon gut, schon gut - ich war wohl ein wenig übermotiviert.

Sorry, daß ich Dich so gedrängelt habe - schließlich geht's hier ja nur 
um Spaß an der Freude.

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Angehängte Dateien:

Lesenswert?

So, ich habe das ganze nochmals in Angriff genommen, den Code aufgeräumt 
und in lesbare Form gebracht. Ich habe immer noch das selbe Problem, 
d.h. es kommt nur Müll bei der Messung raus.

Interessierte können sich die Projekte im Anhang mal ansehen.

hid_temperature.zip ist die Firmware (als Eclipse Projekt mit Makefile)
QTHIDControl ist die PC-Applikation dazu. Dies ist ein QT Projekt.

Wenn man auf den "Read Data" Button klickt, wird der USB Buffer vom 
Controller auf den PC übertragen, die ersten beiden Bytes sind die vom 
TSIC gelesenen Daten, die wie gesagt nicht stimmen.

@igel1: Falls noch Interesse besteht, kannst Du ja mal reinschauen.

Gruß
MP

von Matthias P. (matthias_p65)


Lesenswert?

Also ich kann inzwischen ausschließen, daß es am USB-Teil liegt, ich 
habe jetzt ein LCD Display angeschlossen und bekomme die selben 
abstrusen Werte geliefert.

von tt2t (Gast)


Lesenswert?

Schliess doch mal ein LCD direkt an den µC an und schau, ob Deine 
TSIC-Routine überhaupt stimmt. Nach dem obigen Bildern von bingo (Gast) 
ist es doch sonnenklar, wie das Protokoll ist.

Ich hab auch schon 2x oder 3x TSIC verbaut und es gab nie Probleme, ich 
arbeite aber mit PICs und nehme die Routine von uxdx 
Beitrag "Re: digitaler Thermosensor"

von Matthias P. (matthias_p65)


Lesenswert?

Matthias P. schrieb:
> ich habe jetzt ein LCD Display angeschlossen

;)

von Matthias P. (matthias_p65)


Lesenswert?

So ich habe jetzt mal eine Variante nur mit LCD gebaut:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
#include <util/delay.h>
4
#include <avr/pgmspace.h>
5
#include "zacwire.h"
6
#include "lcd.h"
7
8
zacstate state = ZAC_READING_START_BIT_LOW;
9
10
unsigned int buffer[2];
11
12
// half the value of tstrobe
13
volatile uint16_t strobe = 0;
14
// high byte of a zacwire paxcket
15
volatile uint8_t zac_high_byte = 0;
16
// low byte of a zacwire paxcket
17
volatile uint8_t zac_low_byte = 0;
18
// the bit of the byte currently being read including parity
19
volatile uint8_t zac_current_bit = 8;
20
21
volatile uint16_t timer_start = 0;
22
volatile uint16_t timer_end = 0;
23
24
/**
25
 * Start timer1 in normal mode
26
 */
27
void timer1_start_normal();
28
/**
29
 * Stop timer1
30
 */
31
void timer1_stop();
32
/**
33
 * Stop timer1 from ctc mode
34
 */
35
void timer1_stop_ctc();
36
/**
37
 * Start timer1 in ctc mode and load the output compare register (OCR1A)
38
 */
39
void timer1_start_ctc(uint16_t cmp);
40
/**
41
 * Select falling edge for INT1 pin
42
 */
43
void int1_falling_edge(void);
44
/**
45
 * Select rising edge for INT1 pin
46
 */
47
void int1_rising_edge(void);
48
/**
49
 * Turn INT1 off
50
 */
51
void int1_enable(void);
52
/**
53
 * Turn INT1 on
54
 */
55
void int1_disable(void);
56
57
ISR (TIMER1_COMPA_vect) {
58
59
  // stop the counter and reset timer count
60
  timer1_stop_ctc();
61
  TCNT1 = 0;
62
63
  // reading the HIGH byte of a zacwire packet
64
  if (state == ZAC_READING_HIGH_BYTE) {
65
    // read bits ignoring the last bit (parity)
66
    // actually reading 9 bits in total
67
    if (PIND & (1<<PIND3)) {
68
      if (zac_current_bit > 1) {
69
        zac_high_byte |= (1 << (zac_current_bit - 1));
70
      }
71
    }
72
    // decrement current bit every trigger
73
    if (zac_current_bit > 0) {
74
      zac_current_bit--;
75
    }
76
    // have read all bits from HIGH byte, switch
77
    // to LOW byte
78
    else {
79
      state = ZAC_READING_LOW_BYTE;
80
      // now we have one bit more since there comes another stop bit
81
      zac_current_bit = 9;
82
    }
83
84
  }
85
86
  // after reading the parity bit of the first byte, we can omit
87
  // the stop bit, since the next falling edge does not
88
  // happen until the next start bit (LOW byte)
89
90
  // reading the LOW byte of a zacwire packet
91
  else if (state == ZAC_READING_LOW_BYTE) {
92
    // read bits ignoring the first bit (start bit) and the last bit (parity)
93
    // actually reading 10 bits in total
94
    if (PIND & (1<<PIND3)) {
95
      if (zac_current_bit > 1 && zac_current_bit < 9) {
96
        zac_low_byte |= (1 << (zac_current_bit - 2));
97
        // PORTC |= ( 1 << PC3 );
98
      }
99
    }
100
    // decrement current bit every trigger
101
    if (zac_current_bit > 0) {
102
      zac_current_bit--;
103
    }
104
    // all bits read => back to the beginning
105
    else {
106
      if (zac_high_byte > 0 && zac_high_byte != buffer[0]) {
107
        buffer[0] = zac_high_byte;
108
      }
109
      if (zac_low_byte > 0 && zac_low_byte != buffer[1]) {
110
        buffer[1] = zac_low_byte;
111
      }
112
113
      zac_high_byte = 0;
114
      zac_low_byte = 0;
115
      strobe = 0;
116
      zac_current_bit = 8;
117
      state = ZAC_READING_START_BIT_LOW;
118
    }
119
  }
120
121
}
122
123
ISR (INT1_vect) {
124
125
  /**
126
   * Beginning of zacwire packet, first falling edge
127
   * start measuring tstrobe time
128
   */
129
  if (state == ZAC_READING_START_BIT_LOW) {
130
    // start timer1 and use rising edge for int1
131
    // if the next rising edge happens this is the second half of the start bit
132
    timer1_start_normal();
133
    int1_rising_edge();
134
    state = ZAC_READING_START_BIT_HIGH;
135
136
  }
137
  else if (state == ZAC_READING_START_BIT_HIGH) {
138
    // rising edge of the start bit
139
    // at this stage the first rising edge of the start bit happened
140
    // we now have the strobe time acquired
141
    timer1_stop();
142
    strobe = TCNT1;
143
    // select falling edge, thus the startbit is complete
144
    // on next falling edge
145
    int1_falling_edge();
146
    // next stage
147
    state = ZAC_ACQUIRING_TSTROBE;
148
  }
149
  else if (state == ZAC_ACQUIRING_TSTROBE) {
150
151
    // falling edge of start bit occured, reset timer count
152
    TCNT1 = 0;
153
154
    // start timer 1 in ctc mode, timer interrupt then occurs
155
    // each tstrobe/2 (50% duty cycle)
156
    timer1_start_ctc(strobe);
157
158
    // we now start reading the first byte
159
    state = ZAC_READING_HIGH_BYTE;
160
161
  }
162
  else if (state == ZAC_READING_HIGH_BYTE || state == ZAC_READING_LOW_BYTE) {
163
    TCNT1 = 0;
164
    timer1_start_ctc(strobe);
165
  }
166
167
168
}
169
170
void timer1_start_normal() {
171
  TCCR1B = (0 << CS12) | (0 << CS11) | (1 << CS10); // no prescaler
172
  // TIMSK |= (1 << TOIE1); // timer overflow interrupt enable
173
}
174
void timer1_stop() {
175
  TCCR1B = (0 << CS12) | (0 << CS11) | (0 << CS10); // stop timer
176
  // TIMSK &= ~(1 << TOIE1); // stop overflow interrupt
177
  // TIFR |= (1 << TOV1); // clear timer overflow flag
178
}
179
180
void timer1_stop_ctc() {
181
  TIMSK &= ~(1 << OCIE1A); // stop ctc interrupt
182
  // TIFR |= (1 << TOV1); // clear timer overflow flag
183
}
184
185
void timer1_start_ctc(uint16_t cmp) {
186
  OCR1A = cmp; // set value to output compare register
187
  TCCR1B = (1 << WGM12) | (0 << CS12) | (0 << CS11) | (1 << CS10); // ctc, no prescaler
188
  //Enable the Output Compare A interrupt
189
  TIMSK |= (1 << OCIE1A);
190
}
191
192
void int1_falling_edge(void) {
193
  MCUCR &= ~(1 << ISC10);
194
  GIFR |= (1 << INTF1); // clear external interrupt flag
195
}
196
197
void int1_rising_edge(void) {
198
  MCUCR |= (1 << ISC10);
199
  GIFR |= (1 << INTF1); // clear external interrupt flag
200
}
201
202
void int1_enable(void) {
203
  GICR |= 1 << INT1;
204
}
205
206
void int1_disable(void) {
207
  GICR &= ~(1 << INT1);
208
}
209
210
void int0_enable(void) {
211
  GICR |= 1 << INT0;
212
}
213
214
void int0_disable(void) {
215
  GICR &= ~(1 << INT0);
216
}
217
218
219
int main(void) {
220
221
  lcd_init(LCD_DISP_ON);
222
  lcd_clrscr();
223
224
  DDRC = 0xFF;
225
226
  // enable INT0 and INT1
227
  GICR =  1 << INT1;
228
  // VUSB defaults for INT0 and trigger on falling edge for INT1
229
  MCUCR =  (1 << ISC11) | (0 << ISC10);
230
231
  char disp[16];
232
233
  // turn TSIC on
234
  zac_on();
235
  // wait for the tsic to settle
236
  _delay_us(125);
237
  // enable interrupts
238
  sei();
239
240
  for(;;) {
241
     _delay_ms(200);
242
    sprintf(disp,"%u %u  ",buffer[0],buffer[1]);
243
    // sprintf(disp,"%u ",strobe);
244
    lcd_gotoxy(0,0);
245
    lcd_puts(disp);
246
  }
247
248
  return 0;
249
250
}

Weiterhin kein vernünftiges Ergebnis. Der Strobe Wert spielt verrückt 
sobald ich die Temperatur am Sensor signifikant ändere.

Die Werte stimmen natürlich vorne und hinten nicht.

von Matthias P. (matthias_p65)


Lesenswert?

Die ISR enthielt noch Fehler, bringt aber auch keine Verbesserung:
1
ISR (TIMER1_COMPA_vect) {
2
3
  PORTC ^= (1 << PC0);
4
5
  // stop the counter and reset timer count
6
  timer1_stop_ctc();
7
  TCNT1 = 0;
8
9
  // reading the HIGH byte of a zacwire packet
10
  if (state == ZAC_READING_HIGH_BYTE) {
11
    // read bits ignoring the last bit (parity)
12
    // actually reading 9 bits in total
13
    if (PIND & (1<<PIND3)) {
14
      if (zac_current_bit > 0) {
15
        zac_high_byte |= (1 << (zac_current_bit - 1));
16
      }
17
    }
18
    // decrement current bit every trigger
19
    if (zac_current_bit > 0) {
20
      zac_current_bit--;
21
    }
22
    // have read all bits from HIGH byte, switch
23
    // to LOW byte
24
    else {
25
      state = ZAC_READING_LOW_BYTE;
26
      // now we have one bit more since there comes another stop bit
27
      zac_current_bit = 9;
28
    }
29
30
  }
31
32
  // after reading the parity bit of the first byte, we can omit
33
  // the stop bit, since the next falling edge does not
34
  // happen until the next start bit (LOW byte)
35
36
  // reading the LOW byte of a zacwire packet
37
  else if (state == ZAC_READING_LOW_BYTE) {
38
    // read bits ignoring the first bit (start bit) and the last bit (parity)
39
    // actually reading 10 bits in total
40
    if (PIND & (1<<PIND3)) {
41
      if (zac_current_bit > 0 && zac_current_bit < 9) {
42
        zac_low_byte |= (1 << (zac_current_bit - 1));
43
      }
44
    }
45
    // decrement current bit every trigger
46
    if (zac_current_bit > 0) {
47
      zac_current_bit--;
48
    }
49
    // all bits read => back to the beginning
50
    else {
51
      if (zac_high_byte > 0 && zac_high_byte != buffer[0]) {
52
        buffer[0] = zac_high_byte;
53
      }
54
      if (zac_low_byte > 0 && zac_low_byte != buffer[1]) {
55
        buffer[1] = zac_low_byte;
56
      }
57
58
      zac_high_byte = 0;
59
      zac_low_byte = 0;
60
      strobe = 0;
61
      zac_current_bit = 8;
62
      state = ZAC_READING_START_BIT_LOW;
63
    }
64
  }
65
66
}

von da1l6 (Gast)


Angehängte Dateien:

Lesenswert?

Hallo

Ohne alles gelesen zu haben, interpretiere ich den letzten Beitrag mal 
als "geht immer noch nicht"

Daher hier mein Code zum auslesen eines TSic306.
Konfiguriert wird mit den #defines am Anfang. Momentan ist es für Timer0 
und INT0 Eingang eines attiny2313 eingestellt.

Zur Benutzung einmal TSic_Init aufrufen, sei() und dann mit 
TSic_getValue() lesen.
TSic_isResultAvailable() gibt 1 zurück sobald ein noch nicht gelesener 
Wert vorliegt.

da1l6

von Matthias P. (matthias_p65)


Lesenswert?

Die nicht Interrupt getriebene Variante von hier:

http://www.mikrocontroller.net/attachment/49058/tsic_zacwire_avr.c

läuft überdies einwandfrei, so daß ich Probleme in der Schaltung auch 
vollständig ausschließen kann.

von Matthias P. (matthias_p65)


Lesenswert?

@da1l6 : Dein Posting kam während ich noch am Schreiben war. Danke, ich 
werde mir das mal ansehen.

Wie es immer so ist, richtig befriedigend ist das alles nicht, wenn man 
nicht selbst auf die Lösung gekommen ist. :(

Die Sache mit dem freilaufenden Timer, wie sie weiter oben auch schon 
einmal vorgeschlagen wurde, verstehe ich allerdings nicht wirklich.

von da1l6 (Gast)


Lesenswert?

Hallo

Das mit dem freilaufenden Timer ist eigentlich ganz einfach:
Du merkst dir den Timer Wert bei der fallenden Flanke (bit Start) und 
dann nochmal bei der steigenden (Bit Ende). Die Differenz der Werte 
ergibt die Bit Bauer.

Vergleicht man die Bit-Dauer des ersten "Bits" (sync, "50%" Low Dauer) 
mit den der folgenden (25% oder 75%) kann man den Bit Wert ermitteln.

da1l6

von Andreas S. (igel1)


Lesenswert?

Ah prima - es geht weiter ...

@Matthias:

Dummerweise habe ich diese Woche 0,0% Zeit.
Sehr ärgerlich - ich würde Dir doch so gerne helfen ...

Solltest Du keinen LogicAnalyzer besitzen, so kannst du gerne den Code 
mal etwas mit Debug-Statement aufpoppen (=Debug-Ports setzen/rücksetzen) 
und ich schließe die Maschinerie dann an meinen LA an - das öffnet 
oftmals die Augen, weil man neben dem TSIC-Signal genau sehen kann, 
welcher Code-Bereich wann durchlaufen wird.

Geht bei mir aber leider alles nicht sofort, sondern vermutlich erst in 
ein paar Tagen.

Viele Grüße

Igel1

von Bernie (Gast)


Lesenswert?

GRAUENHAFT, was hier gefrickelt wird!

Riesige Statusmaschine,
Interrupts und Timer im Betrieb umprogrammieren,
... und und und

- Alles unnötig!

Das macht man mit (z.B.) INT1 auf beiden Flanken und
einem freilaufenden Zähler, der nach jeder negativen
Flanke resettet wird. Dazu noch ca. 6 globale
UINT8-Varablen.

OHNE UMPROGRAMMIEREN!

Erfassung der Bits in der INT1-IRQ-Routine,
Auswertung durch das Hauptprogramm.

Ihr treibt den TO noch in's Irrenhaus!

von Matthias P. (matthias_p65)


Lesenswert?

> GRAUENHAFT, was hier gefrickelt wird!

Tja, was soll ich sagen, der Thread geht ja schon eine Weile, meistens 
kommen Vorschläge wie man es nicht tun soll. Du hast ja sicher auch mal 
irgendwo angefangen.;)

Bernie schrieb:
> Das macht man mit (z.B.) INT1 auf beiden Flanken und
> einem freilaufenden Zähler, der nach jeder negativen
> Flanke resettet wird. Dazu noch ca. 6 globale
> UINT8-Varablen.

Deine Vorschläge sind sicher gut gemeint, aber viel anfangen kann ich 
damit nicht, weil mir hier einfach die Erklärung fehlt, das warum und 
wie wird mir nicht klar.

Ich hatte bei der Diskussion zwischen Dir und MSP eh schon lange den 
Faden verloren.

Es scheint ja alles ganz simpel zu sein, allerdings habe ich das 
Internet lange nach einer funktionierenden Interrupt getriebenen Lösung 
abgesucht und nichts gefunden. Der erste brauchbare Vorschlag kam erst 
von da1l6, leider muss ich mich auch hier erst durchwühlen und 
Anpassungen für den ATMega8 vornehmen.

Aber wirklich etwas gelernt oder verstanden habe ich bis hierin nicht.

Ausser vielleicht daß mein Code scheisse ist, aber das wusste ich vorher 
schon...

von Andreas S. (igel1)


Lesenswert?

Hallo Matthias,

bitte nicht verzweifeln.
Sobald ich 2 Stündchen Zeit habe, werde ich Dir helfen!

Wir kriegen das hin - und zwar nicht mit irgendeinem Fremdcode (das 
ginge schnell, wäre aber sicherlich wenig befriedigend für Dich), 
sondern mit Deinem Code.

Also: Kopf hoch - laß Dich nicht unterkriegen.

Wenn die 2 Heißluftproduzenten im Thread keine für Dich verständlichen, 
auf Deinen Code zugeschnittenen Tipps geben wollen oder können, so werde 
ich es in ein paar Tagen, wenn ich wieder Zeit habe, versuchen - ist 
versprochen!

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Lesenswert?

Andreas S. schrieb:
> bitte nicht verzweifeln.

Keine Sorge, ich hab schon ganz andere Projekte gestemmt. ;)

Ich werde mir heute Abend mal die Lösung von da1l6 zu Gemüte führen.

von usuru (Gast)


Lesenswert?

Ich kenne mich mit AVRs nicht sehr gut aus, komme von den Kollegen, die 
PICs einsetzen.

M.W. hat auch ein AVR eine Capture-Einheit, mit der man ganz easy Zeiten 
messen kann. Z.B. wird bei fallender Flanke ein Interrupt generiert, der 
den Timer resetted und startet und bei steigender Flanke übernimmt die 
Capture-Einheit die Timerdaten automatisch in einen Zwischenspeicher und 
löst wieder einen Interrupt aus. Dort kannst Du dann die Werte weiter 
bearbeiten.

von Andreas S. (igel1)


Lesenswert?

Hallo Matthias,

bitte sende mir noch Deine Datei "lcd.h" sowie die zugehörige
Source-Datei ("lcd.c" ?), damit ich Deinen Code in meiner
AVR-Studio-Umgebung kompilieren kann.

Bitte ebenfalls den LCD-Typ sowie die Verkabelung angeben.
Vielleicht kann ich ja auch das noch nachbauen - habe hier
nämlich wenige LCD-Displays rumfliegen.

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Lesenswert?

Das ist die LCD Lib von Peter Fleury

http://homepage.hispeed.ch/peterfleury/lcdlibrary.zip

Das Display muss dann noch in der lcd.h angepasst werden. Ich habe ein 
HD44780 Kompatibles im 4-Modus verwendet. Du musst die Pins RS,RW und E 
sowie DB4-DB7 angeben. Die Zeilen- und Spaltenanzahl muss auch angepasst 
werden. Ggf. musst du noch die Adressen der Zeilen ändern, das passt 
aber meistens.

Weiter oben hatte ich alles in ein zip-Archiv gepackt.

Gruß
MP

von da1l6 (Gast)


Lesenswert?

Hallo

@Bernie

Wie willst du das Interrupt getrieben machen ohne eine State-Machine zu 
benutzen?

Das umprogrammieren des Timers ist übrigens optional, dient aber dazu 
die Pause zwischen den Messwerten zu erkennen um den Anfang eine 
Übertragung zu finden.
Da der TSic ungefragt sendet wird AVR eventuell mitten in einer 
Transmission eingeschaltet/resettet, dann gibt es nur noch Datenmüll.

Alternativ kann man:
 - Die Stromversorgung des TSic schalten.
 - Einen zweiten Timer verwenden. Wenn man etwa für periodische Aufgaben 
sowieso schon einen laufen hat, ist das günstig.

da1l6

von Bernie (Gast)


Lesenswert?

Natürlich braucht man etwas, um die erfolgreiche Datenerfassung zu
signalisieren! Aber ich halte es für übertrieben, eine Variable, die
mit dem Wert 20 bekannt gibt, dass der Wert erfasst ist,
"State-Machine" zu nennen.

Das TSIC Telegramm besteht aus 20 positiven Flanken.
Zu Beginn setze ich die Variable ABLAUF auf 21, mit jeder positiven
Flanke wird sie um 1 erhöht.
Bei jeder negativen Flanke wird ein Timer geNULLT.

Nur beim Telegrammstart (Negative Flanke nach einer Pause von >= 500 µs)
wird ABLAUF auf NULL gesetzt. Die nächste positive Flanke liefert die
Referenzzeit, alle weiteren positiven Flanken ergeben durch
Zählerstands-Vergleich die Datenbits.

Wurde der Telegrammstart mitten im Telegramm falsch erkannt, kommt
ABLAUF nicht auf den Wert 20, der nächste "echte" Telegrammstart fängt
wieder bei NULL an.

Nach ordentlicher Erfassung des Telegramms ist ABLAUF = 20 und wenn nun
die Erfassung der Flanken abgeschaltet wird, bleibt dieser Wert stehen.

Sieht das Hauptprogramm den Zustand ABLAUF = 20 kann es die Bits
dekodieren und irgendwann wieder eine neue Messung starten.

---------------------------

Wenn gewünscht, kann ich ein "Papierprogramm" liefern,
da ich beim AVR nur mit ASM arbeite.

von Andreas S. (igel1)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

ich habe den Code in Deiner Routine "ISR (INT1_vect)" mit einem
Debug-Signal für meinen LogicAnalyzer angereichert (siehe Code
unten). Danach habe ich das erste Bildchen im Anhang "geschnappt".

Im zweiten Bild sind alle Debug-Makros deaktiviert/auskommentiert.
Es zeigt eine andere Situation.

Erläuterungen zum 1. Bildchen im Anhang:

TSIC306:       Signal des TSIC306
TSIC_PROBE:    Debug-Pulse, die das DSPIKE()-Makro erzeugt - siehe
               unten im Code. Als Argument wird die Puls-Länge in us an
               das Makro übergeben.
DTOGGLE:       Toggelt bei Eintritt in "ISR (TIMER1_COMPA_vect)"
Andere Signale:   Wildes Gemüse - einfach ignorieren

Ich hoffe, das hilft ein Stückchen weiter ...
Alles weitere in Bälde.

Viele Grüße

Igel1


------------------------------------------

1
#define DEBUG_DDR  DDRC
2
#define  DEBUG_DDB  DDC1
3
#define DEBUG_PORT  PORTC
4
#define  DEBUG_PIN  PINC1
5
#define DSPIKE(us)  {DEBUG_PORT |=  (1 << DEBUG_PIN); _delay_us(us); DEBUG_PORT &= ~(1 << DEBUG_PIN);}  // generate high-spike of length "us" on debug pin
6
7
8
ISR (INT1_vect) {
9
10
  /**
11
   * Beginning of zacwire packet, first falling edge
12
   * start measuring tstrobe time
13
   */
14
  if (state == ZAC_READING_START_BIT_LOW) {
15
    // start timer1 and use rising edge for int1
16
    // if the next rising edge happens this is the second half of the start bit
17
    timer1_start_normal();
18
    int1_rising_edge();
19
    state = ZAC_READING_START_BIT_HIGH;
20
  DSPIKE(5);
21
  }
22
  else if (state == ZAC_READING_START_BIT_HIGH) {
23
    // rising edge of the start bit
24
    // at this stage the first rising edge of the start bit happened
25
    // we now have the strobe time acquired
26
    timer1_stop();
27
    strobe = TCNT1;
28
    // select falling edge, thus the startbit is complete
29
    // on next falling edge
30
    int1_falling_edge();
31
    // next stage
32
    state = ZAC_ACQUIRING_TSTROBE;
33
  DSPIKE(15);
34
  }
35
  else if (state == ZAC_ACQUIRING_TSTROBE) {
36
37
    // falling edge of start bit occured, reset timer count
38
    TCNT1 = 0;
39
40
    // start timer 1 in ctc mode, timer interrupt then occurs
41
    // each tstrobe/2 (50% duty cycle)
42
    timer1_start_ctc(strobe);
43
44
    // we now start reading the first byte
45
    state = ZAC_READING_HIGH_BYTE;
46
  DSPIKE(30);
47
48
  }
49
  else if (state == ZAC_READING_HIGH_BYTE || state == ZAC_READING_LOW_BYTE) {
50
    TCNT1 = 0;
51
    timer1_start_ctc(strobe);
52
  DSPIKE(45);
53
  }
54
55
56
}

von Matthias P. (matthias_p65)


Lesenswert?

Sieht ziemlich chaotisch aus, was da im  zweiten Byte passiert. Das 
erste Byte sieht ja noch wunderbar aus, warum dann allerdings im 
zweiten Byte nochmals alle 4 States aus der ISR durchlaufen werden  ist 
mir schleierhaft.

Auf jeden Fall hast Du mich dazu gebracht, mir mal einen Logic Analyzer 
zu bestellen. ;)

von Andreas S. (igel1)


Angehängte Dateien:

Lesenswert?

Hallo Matthias,

so - funktioniert.

Im Anhang siehst Du die Signale, die Dein Code vor der Massage und nach 
der Massage produziert ...

Vor der Massage hatte ich gemessene -25,99 Grad in meinem Zimmer, nun 
sind's immerhin schon 17,90 Grad :-)

Kleiner Wermutstropfen: ich weiß nicht mehr ganz, an welchen Stellen ich 
gedreht habe - ich meine es waren 2 Stellen.

Ich muß daher die nächsten Tage nochmals Deinen originalen mit meinem 
modifizierten Code vergleichen.

Bis dahin der Tipp: schau mal in die timer1_stop_ctc - Routine!

Du hörst von mir.

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Lesenswert?

Andreas S. schrieb:
> Bis dahin der Tipp: schau mal in die timer1_stop_ctc - Routine!

Alter Schwede, das war der entscheidene Hinweis!!!

es muss natürlich heissen:
1
void timer1_stop_ctc() {
2
    TCCR1B = (0 << CS12) | (0 << CS11) | (0 << CS10); // stop timer
3
    TIMSK &= ~(1 << OCIE1A); // disable ctc interrupt
4
}

Ein paar kleine Änderungen in der ISR (TIMER1_COMPA_vect) waren auch 
noch notwendig, da ich das letzte Bit nicht mitgezählt hatte:
1
ISR (TIMER1_COMPA_vect) {
2
3
  // stop the counter and reset timer count
4
  timer1_stop_ctc();
5
  TCNT1 = 0;
6
7
  // reading the HIGH byte of a zacwire packet
8
  if (state == ZAC_READING_HIGH_BYTE) {
9
    // read bits ignoring the last bit (parity)
10
    // actually reading 9 bits in total
11
    if (PIND & (1<<PIND3)) {
12
      if (zac_current_bit > 0) {
13
        zac_high_byte |= (1 << (zac_current_bit - 1));
14
      }
15
    }
16
    // decrement current bit every trigger
17
    if (zac_current_bit > 0) {
18
      zac_current_bit--;
19
    }
20
    // have read all bits from HIGH byte, switch
21
    // to LOW byte
22
    else {
23
      state = ZAC_READING_LOW_BYTE;
24
      // now we have one bit more since there comes another stop bit
25
      zac_current_bit = 9;
26
    }
27
  }
28
29
  // after reading the parity bit of the first byte, we can omit
30
  // the stop bit, since the next falling edge does not
31
  // happen until the next start bit (LOW byte)
32
33
  // reading the LOW byte of a zacwire packet
34
  else if (state == ZAC_READING_LOW_BYTE) {
35
    // read bits ignoring the first bit (start bit) and the last bit (parity)
36
    // actually reading 10 bits in total
37
    if (PIND & (1<<PIND3)) {
38
      if (zac_current_bit > 0 && zac_current_bit < 9) {
39
        zac_low_byte |= (1 << (zac_current_bit - 1));
40
        // PORTC |= ( 1 << PC3 );
41
      }
42
    }
43
    // decrement current bit every trigger
44
    if (zac_current_bit > 0) {
45
      zac_current_bit--;
46
    }
47
    // all bits read => back to the beginning
48
    else {
49
      zac_current_bit = 8;
50
      state = ZAC_READING_START_BIT_LOW;
51
    }
52
  }
53
}

Es läuft endlich!

Danke für Deine Hilfe! Die Diagramme vom Logic Analyzer waren auch sehr 
hilfreich an der Stelle.

Jetzt mach ich mich endlich an die USB-Anbindung.

Gruß
MP

von Matthias P. (matthias_p65)


Angehängte Dateien:

Lesenswert?

So hier nochmals das komplette Projekt ohne USB Anbindung für ATMega8 
mit HD44780 LC-Display.

Ein Wermutstropfen bleibt,denn es gibt noch einen Bug:

Ab -25°C wird die Temperatur wieder von 0°C an runtergezählt.

Ich konnte bisher nicht ausmachen, woran es liegt. Wer Lust hat, kann ja 
mal reinschauen. Ich mach mich jedenfalls mal an die Fehlersuche.

von Matthias P. (matthias_p65)


Angehängte Dateien:

Lesenswert?

Sodale, der Fehler war schneller gefunden als gedacht:

statt
1
  if (state == ZAC_READING_START_BIT_LOW) {
2
    // if there was on measurement cycle, store data
3
    // from zacwire in usb transfer buffer
4
    if (zac_high_byte > 0)
5
      buffer[0] = zac_high_byte;
6
    if (zac_low_byte > 0)
7
      buffer[1] = zac_low_byte;

muss es nur heissen:
1
  if (state == ZAC_READING_START_BIT_LOW) {
2
    // if there was on measurement cycle, store data
3
    // from zacwire in usb transfer buffer
4
    buffer[0] = zac_high_byte;
5
    buffer[1] = zac_low_byte;

beide Bytes müssen natürlich auch den Wert 0 annehmen dürfen.

Dank an alle, insbesondere an igel1.

@Admin : macht es Sinn für die USB Anbindung bei Bedarf einen neuen 
Thread zu eröffnen?

AKtualisierte Version im Anhang, kompletter Schaltplan folgt.

Gruß
MP

von Andreas S. (igel1)


Lesenswert?

Hallo Matthias,

freut mich, daß ich Dir helfen konnte.
Ich gebe zu: ich mußte auch ein ganzes Weilchen suchen ...

Bei der Analyse fielen mir noch folgende Dinge auf:

- Der Code ist vermutlich gegen Leitungsstörungen sehr anfällig:
  Eine fallende Flanke zu viel (oder eine verpaßt) und alles
  gerät aus dem Takt. Wenn in Kürze bei Dir noch USB-Interrupts
  dazukommen, befürchte ich, daß genau dies eintreten könnte:
  Du wirst Flanken verpassen, weil sich Dein Code gerade in
  USB-ISR's befindet.

- Das Parity-Bit würde ich ebenfalls auswerten - das bringt
  zusätzliche Sicherheit.

- Der Code ist (noch) nicht nicht ganz interrupt-sicher.
  Wenn Du z.B. in Deiner for-Schleife eine Operation, die
  sich über mehrere Maschinen-Zyklen erstreckt, wie z.B.
  "temp = buffer[0]*256 + buffer[1]", ausführst, so könnte
  Dein eigener Interrupt dazwischenfunken und mittendrin
  die buffer-Werte austauschen - das Ergebnis wäre dann
  ziemlich willkürlich. Du könntest also ggf. ein atomar
  beschreibbares Flag bzw. eine Variable verwenden, die Du
  vor Operationen buffer[x] setzt. Das Flag wird dann in
  Deiner ISR ausgelesen und verhindert, das buffer[x]
  aktualisiert wird. Ich meine einen ähnlichen Ansatz bei
  Alternativ-Codevorschlägen in diesem Thread gesehen
  zu haben.

Wie dem auch sei:

Ich bin gespannt, wie's nun mit Deinem Projekt Richtung USB
weitergeht. Schreib doch mal, was Du nun vorhast!

Evtl. kann ich ein bisschen Zeitscheibe abschnibbeln und
noch etwas beitragen.

Hast Du tatsächlich schon einen LA bestellt?
Wenn ja - welchen?

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

... und noch eine kleine Frage an Dich, Matthias:

Wie - um alles in der Welt - ist Dir der -25 Grad-Bug aufgefallen:
Hast Du die Schaltung in die Tiefkühltruhe gelegt?

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Lesenswert?

Andreas S. schrieb:
> Bei der Analyse fielen mir noch folgende Dinge auf:
>
> - Der Code ist vermutlich gegen Leitungsstörungen sehr anfällig:
>   Eine fallende Flanke zu viel (oder eine verpaßt) und alles
>   gerät aus dem Takt. Wenn in Kürze bei Dir noch USB-Interrupts
>   dazukommen, befürchte ich, daß genau dies eintreten könnte:
>

Ja, daß der Code noch nicht 100% robust ist, ist mir klar, mir ging es 
jetzt in der ersten Iteration nur ums Verstehen.

> Ich bin gespannt, wie's nun mit Deinem Projekt Richtung USB
> weitergeht. Schreib doch mal, was Du nun vorhast!
>

Ich will jetzt in erster Linie mal die gemessene Temperatur über die USB 
Schnittstelle auslesen.

> Hast Du tatsächlich schon einen LA bestellt?
> Wenn ja - welchen?

Diesen hier:

http://www.batronix.com/versand/logik-analyser/logic-analyser-pla1016.html

> Wie - um alles in der Welt - ist Dir der -25 Grad-Bug aufgefallen:
> Hast Du die Schaltung in die Tiefkühltruhe gelegt?

Mit Kältespray geht das ganz hervorragend ;)

http://www.reichelt.de/Kaelte-und-Druckluftsprays/KONTAKT-316/3//index.html?ACTION=3&GROUPID=4068&ARTICLE=9518&SHOW=1&START=0&OFFSET=16&;

von soika@gmx.de (Gast)


Lesenswert?

Hallo Matthias, hallo liebe Mitleser,

>> Hast Du tatsächlich schon einen LA bestellt?
>> Wenn ja - welchen?

>Diesen hier:
>http://www.batronix.com/versand/logik-analyser/log...

Den kleinen oder den großen LA? (PLA1016 oder PLA2532)
Interessantes Teil - vor allem schön viel Speicher.
Du wirst sehen: mit einem LA macht das Basteln 10x mehr Spaß.

>Mit Kältespray geht das ganz hervorragend ;)
Och - das war so naheliegend, daß ich niemals darauf gekommen wäre...


> Ich will jetzt in erster Linie mal die gemessene Temperatur
> über die USB Schnittstelle auslesen.

Ich habe Deinen Code einfach einmal in das hid-data Beispiel aus der 
V-USB Lib eingefrickelt. Und siehe da - ab und an konnte ich die 
korrekte Temperatur übertragen. Aber leider eben nicht zuverlässig.

Ich befürchte, daß dies prinzipbedingt auch nicht zuverlässig 
funktionieren kann und daß Du für zuverlässige Messungen Deinen Code 
ziemlich heftig umstricken mußt.

Damit der Atmega8 das USB-Protokoll sprechen kann, müssen nämlich sehr 
strenge Timings eingehalten werden.

Die Kommentare im File "usbdrv.h" von V-USB sind hierzu recht 
interessant:

-------------------- schnipp ------------------------------------
Interrupt latency:
The application must ensure that the USB interrupt is not disabled for 
more than 25 cycles (this is for 12 MHz, faster clocks allow longer 
latency). This implies that all interrupt routines must either have the 
"ISR_NOBLOCK" attribute set (see "avr/interrupt.h") or be written in 
assembler with "sei" as the first instruction.

Maximum interrupt duration / CPU cycle consumption:
The driver handles all USB communication during the interrupt service
routine. The routine will not return before an entire USB message is 
received and the reply is sent. This may be up to ca. 1200 cycles @ 12 
MHz (= 100us) if the host conforms to the standard. The driver will 
consume CPU cycles for all USB messages, even if they address another 
(low-speed) device on the same bus.
--------------------- schnapp -----------------------------------

Nach dieser Lektüre bin ich selber etwas ratlos:

Wie soll man mit hinreichender Genauigkeit die TSIC Impulslängen messen 
...

- wenn man ISR's nicht länger als 25 CPU-Zyklen abklemmen darf und
- wenn man während solcher Messungen bis zu 100us von USB-ISR's 
unterbrochen werden kann?

Kleine Ansätze habe ich zwar schon, aber nichts wirklich Geniales.

Will sagen: Foren-Leser mit guten Ideen, bitte vortreten !

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Lesenswert?

soika@gmx.de schrieb:
> Den kleinen oder den großen LA? (PLA1016 oder PLA2532)
> Interessantes Teil - vor allem schön viel Speicher.
> Du wirst sehen: mit einem LA macht das Basteln 10x mehr Spaß.

Den kleinen, ich fange immer erst klein an. ;)

> Ich befürchte, daß dies prinzipbedingt auch nicht zuverlässig
> funktionieren kann und daß Du für zuverlässige Messungen Deinen Code
> ziemlich heftig umstricken mußt.
>
> Damit der Atmega8 das USB-Protokoll sprechen kann, müssen nämlich sehr
> strenge Timings eingehalten werden.
>

Ja, das befürchte ich auch. Ich habe schon darüber nachgedacht, einen 
zweiten ATMega
nur für den TSIC zu nehmen und die Daten dann über I2C an den USB ATMega 
zu übertragen.

> Will sagen: Foren-Leser mit guten Ideen, bitte vortreten !

Genau!

von soika@gmx.de (Gast)


Lesenswert?

>> Ich befürchte, daß dies prinzipbedingt auch nicht zuverlässig
>> funktionieren kann und daß Du für zuverlässige Messungen Deinen Code
>> ziemlich heftig umstricken mußt.
>>
>> Damit der Atmega8 das USB-Protokoll sprechen kann, müssen nämlich sehr
>> strenge Timings eingehalten werden.

> Ja, das befürchte ich auch. Ich habe schon darüber nachgedacht,
> einen zweiten ATMega nur für den TSIC zu nehmen und die Daten
> dann über I2C an den USB ATMega zu übertragen.

Hmmm... Das wäre der brute force Ansatz.
Dann könnte man auch direkt einen Atmega mit integrierter USB-Einheit 
nehmen. Aber da wäre die Herausforderung dann ganz futsch.

Ich bin dafür, daß wir es trotzdem mit nur einem Atmega probieren.
@Matthias: Ziehst Du mit?

>> Will sagen: Foren-Leser mit guten Ideen, bitte vortreten !

> Genau!

Die Jungs rennen uns nicht gerade die Bude ein ...
Ich befürchte fast, wir sind auf uns allein gestellt.
Im Umfeld USB legen sowieso die meisten die Ohren an.


Hier also meine noch ganz unreifen Ideen:

- Zunächst nochmals das Problem:

  Wenn Du bislang mit Deinem INT1 auf fallende Flanken des TSIC reagiert
  hast und Deine INT1-ISR aufgerufen hast, so wußtest Du, daß die Flanke
  maximal 1-2 Taktzyklen vorher passiert ist. Damit waren also prima
  Zeitmessungen möglich.

  In dem Moment, wo die INT0-Aufrufe von V-USB dazukommen, weißt Du
  das leider nicht mehr: Die fallende Flanke des TSIC könnte nämlich
  genau zu einem Zeitpunkt passieren, in dem sich Dein Atmega gerade in
  der INT0-ISR zur Abarbeitung der USB-Routinen befindet. Erst wenn
  diese Abarbeitung beendet ist, kommt dein INT1-Aufruf dran. Der
  ist dann aber quasi nutzlos, denn Du kannst den exakten Zeitpunkt
  der fallenden Flanke nicht mehr rekonstruieren.

- Der 1. Lösungsansatz:

  Man könnte ggf. die INT0-ISR von V-USB mit einem Flag ausstatten:
  Immer wenn ein INT0-Interrupt erfolgt, weil gerade mal wieder ein
  USB-Paketchen vorbeiflog, wird in der INT0-ISR dieses Flag gesetzt.

  Wenn dann Deine INT1-ISR aufgerufen wird, kannst Du an Hand des
  Flags erkennen, ob zwischen dem letzten Aufruf und dem jetzigen
  Aufruf ein INT0-Aufruf stattgefunden hat. In diesem Fall verwirfst
  Du einfach das Ergebnis - in ca. 100ms kommt wirft der TSIC erneut
  die Temperatur raus und dann gilt:
  Neue Chance, neues Glück.

  Ganz Hartgesottene könnten sogar die Längen von LOW- UND HI-Impuls-
  flanken messen, denn aus beiden Messungen kann man ableiten, ob ein
  TSIC-Bit nun 0 oder 1 war. Damit würde man die Chancen einer erfolg-
  reichen Messung vermutlich dramatisch erhöhen.

- Allgemein:

  Allgemein würde ich vermutlich von der etwas komplizierten Status-
  Maschine ein wenig abrücken und mir bei INT1-Aufrufen (auf fallende 
UND
  steigende Flanken) nur noch den Zählstand eines durchlaufenden
  Zählers sowie den oben erwähnten Flag-Status merken.

  Nach X Flankenwechseln kann dann die Auswertung beginnen.


Soweit mein erster Ansatz.
Was meinst Du/meint Ihr dazu?

Gibt's einfachere Ansätze?

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Lesenswert?

>
> - Der 1. Lösungsansatz:
>
>   Man könnte ggf. die INT0-ISR von V-USB mit einem Flag ausstatten:
>   Immer wenn ein INT0-Interrupt erfolgt, weil gerade mal wieder ein
>   USB-Paketchen vorbeiflog, wird in der INT0-ISR dieses Flag gesetzt.
>
>   Wenn dann Deine INT1-ISR aufgerufen wird, kannst Du an Hand des
>   Flags erkennen, ob zwischen dem letzten Aufruf und dem jetzigen
>   Aufruf ein INT0-Aufruf stattgefunden hat. In diesem Fall verwirfst
>   Du einfach das Ergebnis - in ca. 100ms kommt wirft der TSIC erneut
>   die Temperatur raus und dann gilt:
>   Neue Chance, neues Glück.
>

Ich finde das klingt erstmal plausibel. Das könnten wir versuchen. Es 
muss sich dann in der Praxis zeigen, wie oft der Fall eintritt, daß die 
Temperaturmessung von einem USB Interrupt unterbrochen wird. Es würde ja 
völlig ausreichen, wenn einmal die Sekunde eine brauchbare Messung 
herauskommt.

>   Allgemein würde ich vermutlich von der etwas komplizierten Status-
>   Maschine ein wenig abrücken und mir bei INT1-Aufrufen (auf fallende
> UND
>   steigende Flanken) nur noch den Zählstand eines durchlaufenden
>   Zählers sowie den oben erwähnten Flag-Status merken.
>
>   Nach X Flankenwechseln kann dann die Auswertung beginnen.

Das werde ich jetzt sowieso mal in Angriff nehmen, nachdem die ganze 
Sache ja jetzt endlich läuft, sollte das kein großes Problem darstellen.

Sofern ich Zeit finde, werde ich das am Wochenende mal in Angriff 
nehmen.

von soika@gmx.de (Gast)


Lesenswert?

Oh ha - 7:53 Uhr - der frühe Vogel fängt den Wurm ...

Ich habe derweil das Interrupt-Verhalten des Atmega mal etwas näher 
untersucht, weil ich wissen wollte:

- ob ein INT1, der während der Abarbeitung einer INT0-ISR auftritt,
  dazu führt, dass nahtlos nach der INT0-ISR die INT1-ISR aufgerufen
  wird oder

- ob erst noch wieder kurz zum Hauptprogramm zurückgesprungen wird,
  bevor in die INT1-ISR verzweigt wird.

Leider scheint Letzteres der Fall zu sein, was eine meiner schönsten 
Ideen pulverisiert: ich wollte nämlich in der main-loop einfach das im 
letzten Posting erwähnte Flag immer wieder zurücksetzen.

Dadurch könnte man INT0-ISR's ohne "INT1-Störung" in der main-loop per 
Flag-Rücksetzung als harmlos kennzeichnen - quasi "neutralisieren - 
während eine INT0-ISR mit "INT1-Störung" von der anschließend nahtlos 
aufgerufenen INT1-ISR an Hand des Flags als "schlimmer" Vorfall entlarvt 
werden könnte.

Letzterer Fall nur müßte zum Verwerfen der aktuellen TSIC-Meßreihe 
führen während der erste Fall eben nicht dazu führen müßte.

Nun gut - genug spekuliert: dadurch, daß zwischen INT0-ISR und INT1-ISR 
stets ein Mini-Abstecher ins Hauptprogramm gemacht wird, ist obiges 
Konzept erst einmal nicht so einfach umzusetzen.

Aaaaber, ich habe eine sehr wichtige Stelle im V-USB-Code gefunden -
Auszug aus usbconfig-prototype.h:

-----------------------schnipp--------------------------
/* #define USB_RX_USER_HOOK(data, len)     if(usbRxToken == 
(uchar)USBPID_SETUP) blinkLED(); */
/* This macro is a hook if you want to do unconventional things. If it 
is
 * defined, it's inserted at the beginning of received message 
processing.
 * If you eat the received message and don't want default processing to
 * proceed, do a return after doing your things. One possible 
application
 * (besides debugging) is to flash a status LED on each packet.
 */
-----------------------schnapp--------------------------

Damit kann man beliebigen Code (der vermutlich nicht länger als X Zyklen 
dauern darf) in die V-USB ISR's einschleusen und das dürfte Gold wert 
sein!

Mal sehen, was sich damit machen läßt ...

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

Hallo Matthias, liebe Forenteilnehmer,

ich habe inzwischen einige Experimente gemacht und die Vermutungen 
erhärten sich, daß alle oben gemachten Annahmen und Befürchtungen wahr 
sind:

- Es ist scheinbar korrekt, daß man USB_RX_USER_HOOK verwenden kann,
  um im Falle von eingehenden USB-Paketen eigenen Code einzuschleusen.
  Das ist ja schon mal gut!

  Ich verwende diesen Hook um per Flag zu kennzeichnen, wenn INT0
  aufgerufen wurde und somit ggf. die Feststellung des Flanken-
  zeitpunktes durch INT1 verworfen werden muss.

- Es ist leider auch korrekt, daß man den INT0 nur gaaaanz kurz von
  seiner Arbeit abhalten darf. Will sagen: INT1-Routinen dürfen wirklich
  nur Mini-Mini sein. Die oben erwähnten 25 Taktzyklen scheinen auch
  Aufruf und Return der Interruptroutine zu beinhalten.
  Bislang ist es mir daher nicht gelungen, mehr als ca. 4 C-Befehle in
  INT1 unterzubringen, ohne die Stabilität des USB-Protokolls zu
  gefährden.

Zwischenfazit:

- Ich tue mich ziemlich schwer damit, den Sensor zuverlässig auszulesen
  und gleichzeitig die Timings des USB-Protokolls nicht zu verletzen.

  Das Szenario, von dem auszugehen ist, macht dabei Kummer:
  Ich gehe davon aus, daß jederzeit ein USB-Paket vorbeifliegen und
  INT0 auslösen kann, um anschließend die INT0-Routine unbekannte
  Zeit zu beschäftigen.

Ich bin wirklich gespannt, ob Du, Matthias oder andere Forenteilnehmer
eine gute Lösung finden. Mir ist der Heldenstreich bislang jedenfalls
noch nicht gelungen.

Es funktioniert bei mir bislang nur in 9 von 10 Fällen, aber eben
nicht immer.

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

Hallo Matthias, hi Leute,

bin fast am Ziel:

TSIC wird vom Atmega ausgelesen und die Daten brav per USB an den 
Computer übertragen.

USB Kommunikationsfehler beim Auslesen treten inzwischen bei härtester 
Abfragefrequenz "nur" noch in ca. 1 von 10.000 Fällen auf:
"error reading data: Communication error with device"

Will sagen: ein Atomkraftwerk sollte man damit nicht steuern ..

Für eine Haus-/Wohnungs-Heizungssteuerung sollte es aber vielleicht 
ausreichen. @Matthias: was ist eigentlich Dein Anwendungszweck?

Viele Grüße

Igel1

von Matthias P. (matthias_p65)


Lesenswert?

Andreas S. schrieb:
> Für eine Haus-/Wohnungs-Heizungssteuerung sollte es aber vielleicht
> ausreichen. @Matthias: was ist eigentlich Dein Anwendungszweck?

Ich wollte ein Atomkraftwerk damit steuern. ;)

Scherz beiseite, es soll ein Thermometer mit USB-Schnittstelle zum 
Datenaufzeichnen werden. Da soll noch ein zweiter TSIC dran, mittels 
Umschalter zu schalten, damit ich eine zweite Temperatur 
(aussen)erfassen kann.

Ich bin jetzt aber sehr gespannt, wie Du das gelöst hast.

von Andreas S. (igel1)


Lesenswert?

Hallo zusammen,

hier die derzeitige Lage:

- der Code ist noch nicht dokumentiert und nicht aufgehübscht
- ohne Doku/Hübschung ist der Code absolut unverständlich
- ich bin diese Woche leider wieder knapp bei Zeit
- ich möchte Dich/Euch trotzdem nicht ewig warten lassen.

Die Lösung:

- ich poste den Code hier häppchenweise, sobald er kommentiert ist
- ist zwar ein bißchen blöd, aber besser als gar nichts
- außerdem kann man sich so häppchenweise eindenken :-)
- bitte seht's mir nach ...

Viele Grüße

Igel1


PS:  Hier schon mal die ersten ca. 50% des Gesamtcodes:
1
// Store timer1 value in global variable "timer" when TSIC-signal flips
2
// This ISR has to be as short as possible in order to avoid USB-timing violations
3
// V-USB website tells us, that INT0-interrupts shall not be disabled longer than 25 CPU cycles !.
4
5
ISR (INT1_vect) 
6
{
7
  //PORTC ^= (1 << PC0);  // uncomment this for debugging purposes
8
  //store timer value and reset timer
9
  timer = TCNT1;          // store content of timer counter 1 (TCNT1) in global variable "timer"
10
                          // which will be stored in pulselength array later on
11
  TCNT1 = 0;
12
  int1visits = 1;         // this flag tells the main loop to process the above stored timer-value
13
                          // since processing doesn't fit into this ISR due to USB timing restrictions
14
}
1
// Calculate tsic temperature data using pulse length information (which is stored in array "pulselength")
2
3
uint16_t calctempdata(){
4
5
6
  uint8_t   i;            // internal helper variable 
7
  uint8_t    parity;      // tsic's parity information goes in here
8
  uint16_t  tstrobe;      // pulselength to compare with (equals 1st "reference".pulse, called "tstrobe")
9
  uint16_t  result;       // tsic bits will be assembled here to form the 2 byte temperature data
10
11
  result  = 0;            // initialize variable
12
  tstrobe = pulselength[1];  // pulselength of one single TSIC pulse sequence is stored in array pulselength[1 ... 39]
13
                             // the tstrobe value resides in pulselength[1]
14
15
16
  // process pulselength information for the first byte (including parity) 
17
  parity = 0;
18
  for(i=3; i<19; i=i+2){        // examine only length of LO-edge of of every TSIC bit
19
    //uart_putc('x');           // uncomment this if you want the see the computation's result
20
    result = (result << 1);     // shift left by one - later insert new bit at the LSB position
21
    if(pulselength[i] < tstrobe){  // if LO-edge of pulse < tstrobe => TSCI bit: "1"
22
      //uart_putc('1');         // uncomment this if you want the see the computation's result
23
      result++;                 // inserting "1" at LSB can be easily done by incrementing result by one
24
      parity++;                 // count parity bits
25
    } else {                    // if LO-edge of pulse > tstrobe => TSCI bit: "0"
26
      //uart_putc('0');         // uncomment this if you want the see the computation's result
27
    }
28
  }
29
  if((pulselength[19] < tstrobe) != (parity & 1)) {    // check for parity-errors
30
    //uart_puts("ParityError1");
31
    return(PARITYERROR);
32
  }
33
34
35
  //uart_putc('.');             // uncomment this if you want the see the computation's result
36
  wdt_reset();                  // keep the V-USB's watchdog timer happy
37
    usbPoll();                  // intervall between two consecutive usbPoll() calls must be <50ms !
38
39
  // process pulselength information for the 2nd byte (including parity)
40
  parity=0;
41
  for(i=23; i<39; i=i+2){
42
    //uart_putc('y');           // uncomment this if you want the see the computation's result
43
    result = (result << 1);
44
    if(pulselength[i] < tstrobe){
45
      //uart_putc('1');         // uncomment this if you want the see the computation's result
46
      result++;
47
      parity++;
48
    } else {
49
      //uart_putc('0');         // uncomment this if you want the see the computation's result
50
    }
51
  }
52
  if((pulselength[39] < tstrobe) != (parity & 1)) {
53
    //uart_puts("ParityError2");
54
    return(PARITYERROR);
55
  }
56
57
58
  return(result); 
59
    
60
}

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.