PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : INSTR() nur bis 254?



Majuz
11.06.2009, 12:11
Hallo zusammen,

Ich schreibe momentan ein Programm mit dem ich eine SD-Karte mit einem Mega32 auslese. Das ganze mache ich OHNE dateisystem. Ich lege in Windows auf einer mit FAT16 Formatierten SD-Karte eine Textdatei an die folgenden Inhalt hat: "<START>Datenbereich<ENDE>". Mit dem µC lese ich dann immer einen Sektor (512Bytes) in einen String ein und durchsuche den anschließend nach meinem Startstring. In meinem Fall "<START>". Anschließend suche ich nach dem Endstring; in meinem Falll "<ENDE>".
Dann kann ich genau sagen, wo die Daten auf der SD-Karte liegen.
Das ganze funktioniert bisher FAST einwandfrei. "<START>" findet er immer in Sektor 771 an Stelle 1. Solange "<ENDE>" vor 254 ist funktioniert es auch, aber sobald das Ende nach 254 ist fehlt ihm 255 :-( Aus Stelle 400 wird dann also z.b. 145. Die Variable ist vom Word-typ, aber er speichert wohl nur das untere byte ab :-(

Hier mal mein Quellcode (Falls wer verbesserungen weiß, immer her damit!):


$regfile = "m32def.dat"
$crystal = 16000000
$lib "mmc.lib"
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.7 , Rs = Portc.6
Config Lcd = 16 * 4
$include "config_mmc.bas"
Dim Berror As Byte
Dim Abuffer As String * 512 ' Hold Sector to and from CF-Card
Dim Wsrampointer As Word ' Address-Pointer for write
Dim Lsectornumber As Long ' Sector Number
Dim Ende As Byte
Dim S_position As Word
Dim E_position As Word
Dim S_sektor As Long
Dim E_sektor As Long
Dim Datenmenge As Word
Dim Temp As Word

Ende = 0
Lsectornumber = 0
Cls
If Gbdriveerror = 0 Then
Cls
Do
Wsrampointer = Varptr(abuffer)
Berror = Drivereadsector(wsrampointer , Lsectornumber)
If S_position = 0 Then
S_position = Instr(1 , Abuffer , "<START>")
S_sektor = Lsectornumber
End If
If S_position <> 0 Then
E_position = Instr(1 , Abuffer , "<ENDE>")
If E_position <> 0 Then
E_sektor = Lsectornumber
Ende = 1
End If
End If
Incr Lsectornumber
Loop Until Ende = 1
Else
Lcd "Fehler :-("
End If
If S_sektor = E_sektor Then
Datenmenge = S_position + 7
Datenmenge = E_position - Datenmenge
Else
Datenmenge = S_position + 6
Datenmenge = 512 - Datenmenge
Temp = E_sektor - S_sektor
Temp = Temp - 1
Temp = Temp * 512
Datenmenge = Datenmenge + Temp
Datenmenge = Datenmenge + E_position
Datenmenge = Datenmenge - 1
End If
If Gbdriveerror = 0 Then
Cls
Lcd S_sektor ; ":" ; S_position ; "-" ; E_sektor ; ":" ; E_position
Locate 2 , 1
Lcd "Menge: " ; Datenmenge ; " Bytes"
End If
End 'end program


Falls das wirklich nur bis 254 geht muss ich wohl selbst was stricken. Dann lese ich es in ein Array anstatt einen String ein und muss mir dann selbst eine Suchfunktion basteln. Falls dafür jemand einen Tipp hat wäre ich sehr verbunden! :-)

Gruß
Marius

kolisson
11.06.2009, 12:33
hi

hier ein auszug aus der bascom hilfe für die definition von STRING:

· String (up to 254 bytes). Strings are stored as bytes and are terminated with a 0-byte. A string dimensioned with a length of 10 bytes will occupy 11 bytes.


das erklärt es dir dann.

klaus

Majuz
11.06.2009, 19:45
Hm... stimmt, das erklärt einiges.

Was ich dann aber nicht verstehe:
Wieso funktioniert es dann überhaupt? Er liest ja die kompletten 512Bytes ein und er findet auch die Endposition, also muss er auch alles durchsuchen. Einzig und allein der Rückgabewert müsste als Word anstatt Byte sein, dann würde es funktionieren. Andernfalls käme er ja nie zu der Zeile "Ende = 1" und käme nie aus der Schleife raus. Daher bin ich auch nicht auf diese Lösung gekommen...

Also muss ich jetzt mal versuchen mir selbst eine Suchfunktion zu basteln.

Majuz
12.06.2009, 09:54
Hi

Ich hab das jetzt etwas anders gelöst.



$regfile = "m32def.dat"
$crystal = 16000000
$lib "mmc.lib"
Config Lcdpin = Pin , Db4 = Portc.5 , Db5 = Portc.4 , Db6 = Portc.3 , Db7 = Portc.2 , E = Portc.7 , Rs = Portc.6
Config Lcd = 16 * 4
Dim Abuffer As String * 512 At $60 ' Hold Sector to and from CF-Card
Dim String1 As String * 254 At $60 Overlay
Dim String2 As String * 254 At $10a Overlay
Dim String3 As String * 171 At $1b4 Overlay
$include "config_mmc.bas"
Dim Berror As Byte
Dim Wsrampointer As Word ' Address-Pointer for write
Dim Lsectornumber As Long ' Sector Number
Dim Ende As Byte
Dim S_position As Word
Dim S_position2 As Byte
Dim S_position3 As Byte
Dim E_position As Word
Dim E_position2 As Byte
Dim E_position3 As Byte
Dim S_sektor As Long
Dim E_sektor As Long
Dim Datenmenge As Word
Dim Temp As Word

Ende = 0
Lsectornumber = 0
Cls
If Gbdriveerror = 0 Then
Cls
Do
Wsrampointer = Varptr(abuffer)
Berror = Drivereadsector(wsrampointer , Lsectornumber)
If S_position = 0 Then
S_position = Instr(1 , String1 , "<START>")
S_position2 = Instr(1 , String2 , "<START>")
S_position3 = Instr(1 , String3 , "<START>")
If S_position2 <> 0 Then
S_position = S_position2 + 170
End If
If S_position3 <> 0 Then
S_position = S_position3 + 340
End If
S_sektor = Lsectornumber
End If
If S_position <> 0 Then
E_position = Instr(1 , String1 , "<ENDE>")
E_position2 = Instr(1 , String2 , "<ENDE>")
E_position3 = Instr(1 , String3 , "<ENDE>")
If E_position2 <> 0 Then
E_position = E_position2 + 170
End If
If E_position3 <> 0 Then
E_position = E_position3 + 340
End If
If E_position <> 0 Then
E_sektor = Lsectornumber
Ende = 1
End If
End If
Incr Lsectornumber
Loop Until Ende = 1
Else
Lcd "Fehler :-("
End If
If S_sektor = E_sektor Then
Datenmenge = S_position + 7
Datenmenge = E_position - Datenmenge
Else
Datenmenge = S_position + 6
Datenmenge = 512 - Datenmenge
Temp = E_sektor - S_sektor
Temp = Temp - 1
Temp = Temp * 512
Datenmenge = Datenmenge + Temp
Datenmenge = Datenmenge + E_position
Datenmenge = Datenmenge - 1
End If
If Gbdriveerror = 0 Then
Cls
Lcd S_sektor ; ":" ; S_position ; "-" ; E_sektor ; ":" ; E_position
'Lcd E_position ; "--" ; E_position2
Locate 2 , 1
Lcd "Menge: " ; Datenmenge ; " Bytes"
Thirdline
Lcd "Text"
End If
End 'end program 'end program


So funktioniert es jetzt einwandfrei. ich hab extra 3 Strings genommen, die sich überschneiden, damit er auch was findet, wenn z.B. "<ENDE>" genau an der Grenze der Strings steht.

Nun muss ich aber noch den Sonderfall einbauen, dass der String "<START>" bzw. "<ENDE>" über eine Sektorgrenze geht. Das wird noch aufwändig :-)

kolisson
12.06.2009, 11:17
hallo majuz,

als ich gestern abend nochmal so drüber nachdachte, kam mir auch so ein ähnliches konstruct mit hintereinanderliegenden strings und overlays in den sinn.

so hast du es ja nun auch gelöst .

alternativ dachte ich jedoch darüber nach, ob die string-lösung überhaupt so ideal ist.

hast du mal drüber nachgedacht, statt string ein array of byte zu nehmen?
du könntest zwei byte am anfang jeden blockes als längenangabe des datensatzes reservieren. ob du diese längenangabe nun alle 612 oder alle 256 byte setzt bleibt ja dir überlassen.

so ein system scheint mir wesentlich einfacher zu verarbeiten. es erzeugt dann zwar ungenutze bereiche innerhalb der datenblöcke aber bei den grossen speicherkarten kann man sich das ja leisten.

gruss klaus

Majuz
12.06.2009, 12:27
Es müsste eigentlich kein String sein.
Was es am Ende geben soll:
Ich Bastle derzeit an der Steuerung einer Eigenbau-CNC-Fräse. Ich möchte die Daten (Wie genau die letztendlich aussehen weiß ich noch gar nicht :-) ) mit dem PC auf eine SD-Karte schreiben und dann mit dem AVR auslesen. Dieses Teilprogramm um das es hier geht soll quasi "nur" faststellen, wo auf der SD-Karte die Daten liegen und wie viele Daten es sind. Wenn ich dann direkt hinter "<START>" 2 Bytes reserviere für die Menge der Daten kann ich mir immerhin das "<ENDE>" und die Rechnungen am Programmende sparen.
Ein anderes Teilprogramm liest die Daten dann aus und verarbeitet sie (Schrittmotoren ansteuern).

Am einfachsten wäre es natürlich, wenn das Programm am PC (schreibe ich in VB6) in einem vordefinierten Sektor anfängt zu schreiben. Dann müsste ich nur diesen Sektor auslesen und hätte in den ersten Bytes die Datenmenge stehen. Wie ich allerdings mit VB auf die Sektoren der SD-Karte schreibe konnte ich noch nicht finden :-(