Hi Thorsten,
Quote
ich dachte der Große USB-Treiber braucht das nicht mit dem "USB_ControlJob" ?
Erzeugt der Treiber einen eigenen Task/Process für die Aufrechterhaltung der Verbindung?
das habe ich ja nicht anders behauptet.
Nur der kleine und der USBboot Treiber
Der große USB läuft im Interrupt und erledigt alles automatisch im Hintergrund.
Mal ein Beispiel.
Code
type
tCmdTyp = (CTRLcmd, ISP3cmd, UPPcmd, DEBcmd);
tCMDrec = record
typ : tCmdTyp;
cmd : char;
end;
var
RXbuf : tbuf512, Align4; // USB incomming Buffer
TXbuf : tbuf512, Align4; // USB outgoing Buffer
cnt[@TXbuf] : word;
CMDrec1[@RXbuf] : tCMDrec;
CMDrec : tCMDrec;
HostCmd : tCMDrec;
USBdataAvail : boolean;
HostFailed : boolean;
usbRXCount : word;
_Rx : word;
_usbRxTimeOut : boolean;
procedure USB_Rx_Receive(Status : tUSBep_Status; nb_transfered : word);
var
ch : char;
begin
if Status = USBEP_TRANSFER_OK then
usbRXCount:= nb_transfered;
USBdataAvail:= true;
else
usbRXCount:= 0;
USBdataAvail:= true;
endif;
end;
{--------------------------------------------------------------}
function usbRxBuf(p : pointer; c : word) : boolean;
var
i : word;
begin
if HostFailed or (not UsbDriverConnect) then
c:= 0;
return(false);
endif;
SetSysTimer(TimerU, 2000 div SysTick);
repeat
until isSysTimerZero(TimerU) or USBdataAvail;
if c > usbRXCount then
c:= usbRXCount;
endif;
_usbRxTimeOut:= isSysTimerZero(TimerU);
_Rx:= usbRXCount;
CopyBlock(@RXbuf[2], p, c); // die ersten beiden sind die Länge
USBdataAvail:= false; // oder nach RXCallback ??
usbRXCount:= 0;
xUSB_RXsetBuffer(1, @USB_Rx_Receive, @RXbuf, 512);
//RestoreInts;
return(_Rx > 0);
end;
function RxStat : boolean;
begin
if HostFailed then
return(false);
endif;
return(USBdataAvail);
end;
function RxByte : byte;
var
b : byte;
begin
if HostFailed then
return(0);
endif;
w:= 1; // 1bytes data
if usbRxBuf(@b, w) then
return(b);
else
return(0);
endif;
end;
procedure TxByte(bb : byte);
begin
if not UsbDriverConnect then
return;
endif;
usbTxBuf(@bb, 1);
end;
procedure TxChar(ch : char);
begin
TxByte(byte(ch));
end;
procedure TxBlock(p : pointer; cnt : word);
begin
if HostFailed then
return;
endif;
usbTxBuf(p, cnt);
end;
function RxCmd : tCMDrec;
var
w : word;
CMD[@w] : tCMDrec;
begin
w:= RxWord;
return(CMD);
end;
// receive a block of bytes
function RxBlock(p : pointer; w : word) : word;
begin
if HostFailed then
return(0);
endif;
usbRxBuf(p, w);
return(w);
end;
//--------------------------------------------------------------
function usbTxBuf(p : pointer; Count : word) : boolean;
begin
if HostFailed or (not UsbDriverConnect) then
return(false);
endif;
TxJob:= USB_Ep_GetJob($81);
SetSysTimer(TimerU, 2000 div SysTick);
repeat
if not TxJob^.busy then
cnt:= Count;
CopyBlock(p, @TXbuf[2], Count);
if USB_EpRun($81, true, @TXbuf, Count + 2, nil) then // Job noch belegt ?
return(true);
else
return(false);
endif;
endif;
until isSysTimerZero(TimerU);
return(false);
end;
// usw ...
Procedure MainLoop;
var ch : char;
begin
repeat
xUSB_GetUSBbusState;
if USBdataAvail then
HostCmd:= RxCmd;
ch:= HostCmd.cmd;
case HostCMD.typ of
ISP3cmd : ISP3mode(ch);
|
UPPcmd : UPPmode(ch);
|
else
TXchar('?');
endcase;
endif;
until (not UsbDriverConnect) or (not USBvBUS.USBvBUSpin);
end;
// Main --------------------
xUSB_UserVendorRequest(@USB_VendorRequest);
xUSB_Enable; // USB aktivieren
while (not UsbDriverConnect) or (not USBvBUS.USBvBUSpin) do
BeepClick();
endwhile;
xUSB_RXsetBuffer(1, @USB_Rx_Receive, @RXbuf, 512); // Link RX Event
// wait for Windows Setconfiguration
repeat
mDelay(700);
BeepClick;
until USBDEV_State = UsbDev_STATE_Configured;
USB_RxSetBuf(@RxBuf);
while UsbDriverConnect and USBvBUS.USBvBUSpin do
MainLoop;
endwhile;
xUSB_Detach; // am Win abmelden
end ISP3_X.
Das wäre ein Beispiel:
Der AVrco sendet immer [Typ,cmd, data] oder Daten [CountW, Daten....]
Der AVRProg sendet hier ein Overlay CMD auf den RXBuf, welcher im usbRxBuf zerlegt wird.
So könnte man ein Protokoll aufbauen, da der USB ja auch nur eine Serielle ist.
Das ist simple gelöst hat aber auch einen Nachteil.
CMDs schön easy aber wenn du später Daten pur senden willst, musst du ja z.B. 2+ 512 Bytes senden. Länge+Daten.
Aber das ist halt kein Vielfaches von 64 Bytes und muss in zwei Blöcken gesendet werden.
Diese Einzelbytes kosten Zeit, da der USB ja nur bei Langeweile vom WIn dran kommt. Da kann ein Paket schon mal ne Sekunde im Buffer warten.
Deshalb für sowas dann:
Komando Anzahl z.B 512 Blöcke und Feuer frei.
Nach jeden Paket läßt du deinen Xmega ein ACK senden, das er empfangen hat und fertig ist.
Ist zwar langsam aber sicher.
Je nach Anwendung kann man auch darauf verzichten und alle Blöcke so durchschieben. Das hängt ja immer von der Menge ab. Ich schiebe immer 4MB Blöcke rüber, da machen solche Kleinigkeiten viel aus.
Ich habe mein Board 3x aufgebaut. Einmal mit Xmega und mit jeweils SAMD21 und SAM51.Beim Xmega muss ich ein ACK senden, weil er sonst überannt wird aber bei den ARMs kann ich die Blöcke in eins so durchschiebeen. Ohne das langesame Einzelbyte.
Es kommt immer darauf, was du machen willst.
Sind es nur ein paar kB dann ist das ober Frage/Antwort Protokoll genau richtig, so wird es auch in den Programmern gemacht.
Quote
Im Delphi Senden....
function usbTxBlock(p: pointer; count: word): boolean;
var
Buf: TBuf;
begin
if count = 0 then begin
Result := true;
Exit;
end
else
Result := false;
try
if usbHandle <> Invalid_Handle_Value then begin
Buf[0] := count;
Buf[1] := count shr 8; // [Anzahl bytes die kommen]
move(p^, Buf[2], count);
inc(count, 2);
repeat
repeat
Result := usbWriteFile(Buf, count, nWritten);
if Result and (nWritten = count) then
Exit;
if not Result then begin
Result:= false;
Exit;
end;
until Result;
Dec(count, nWritten);
until count = 0;
end;
except
Result := false;
end;
end;
Dies ist eigentlich nur nötig , wenn man Devices hat die nur 64 bytes Buffer haben.
Der große Treiber kann aber Multipaket und packt dir die automatisch zu einen RX Buf zusammnen.
D.h du bekommst auch 512 bytes in einen Block.
So könnte man sich das Word vor dem Buffe sparen.
Das geht bei den kleinen Treibern nicht , da muss man es über den obrigen Umweg machen.
so ...
miparo