#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include "usb.h"
/* Realisiert serielle Schnittstelle über USB, so einfach wie möglich
*/
char query[64]; // Stringpuffer für Anfrage (":MAIN:FREQ:0?\r\n" u.ä.)
byte querylen; // Länge, ab dem 64. Zeichen wird abgeschnitten
char answer[64];
byte answerlen;
static byte usbCfg;
static const char E0_SSH=3; // 8 Byte CONTROL I/O
static const char E1_SSH=6; // 64 Byte BULK OUT (Maximum für Bulk)
static const char E2_SSH=6; // 64 Byte BULK IN
static const char E3_SSH=3; // 8 Byte INTERR IN
#define W(x) byte(x),byte((x)>>8)
static const PROGMEM byte DeviceDesc[]={
18, // bLength
1, // bDescType Device
W(0x0200), // USB version supported
2, // bDeviceClass Communications
0, // bDeviceSubclass
0, // bDeviceProtocol
1<<E0_SSH, // bMaxPacketSize0 8
W(0x03EB), // Atmel
W(0x2045), // CDC Demo
W(0x0100), //
0, // iVendor
0, // iProduct
0, // iSerialNo
1 // bNumConfig
};
static const PROGMEM byte ConfigDesc[67]={
9, // bLength
2, // bDescType Config
W(sizeof ConfigDesc), // wTotalLength
2, // bNumInterfaces
1, // bConfigValue
0, // iConfig
0xC0, // bmAttributes
0, // bMaxPower
// interface descriptor
9, // bLength
4, // bDescType Iface
0, // bInterfaceNumber
0, // bAlternateSetting (only one)
1, // bNumEndpoints
2, // bIfaceClass communication
2, // bIfaceSubclass abstract control model
1, // bIfaceProtocol V.25ter, common AT commands
0, // iIface
// HEADER_FUNCTIONAL_DESCRIPTOR
5, // bLength
0x24, // bDescType CS_INTERFACE
0, // bDescSubtype header
W(0x0110), // bcdCDC specification version 1.1
// ABSTRACT_CONTROL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR
4, // bLength
0x24, // bDescType CS_INTERFACE
2, // bDescSubtype abstract control management
0x02, // bmCapabilities
// UNION_FUNCTIONAL_DESCRIPTOR
5, // bLength
0x24, // bDescType CS_INTERFACE
6, // bDescSubtype union
0, // bMasterIface
1, // bSlaveIface0
// CALL_MANAGEMENT_FUNCTIONAL_DESCRIPTOR
5, // bLength
0x24, // bDescType CS_INTERFACE
1, // bDescSubtype call management
3, // bmCapabilities
1, // dDataIface
// ENDPOINT_DESCRIPTOR_3_IN
7, // bLength
5, // bDescType ENDPOINT
0x83, // bEndpointAddress 3 IN
0x03, // bmAttributes interrupt
W(1<<E3_SSH), // wMaxPacketSize 8
2, // bInterval 2 ms
// INTERFACE_DESCRIPTOR_1
9, // bLength
4, // bDescType IFACE
1, // bIfaceNumber
0, // bAlternateSetting
2, // bNumEndpoints
0x0A, // bIfaceClass data
0, // bIfaceSubclass
0, // bIfaceProtocol
0, // iIface
// ENDPOINT_DESCRIPTOR_1_OUT
7, // bLength
5, // bDescType ENDPOINT
0x01, // bEndpointAddress 1 OUT
0x02, // bmAttributes bulk
W(1<<E1_SSH), // wMaxPacketSize 64 (Maximum)
0, // bInterval
// ENDPOINT_DESCRIPTOR_2_IN
7, // bLength
5, // bDescType ENDPOINT
0x82, // bEndpointAddress 2 IN
0x02, // bmAttributes bulk
W(1<<E2_SSH), // wMaxPacketSize 64 (Maximum)
0, // bInterval
};
static const PROGMEM byte LineCoding[] = {
W(9600),W(0), // Baudrate
0,
0,
8 // Datenbits
};
// Interrut-Daten
static const PROGMEM byte IntData[]= { // 8 Byte
0xA1,0x20,0,0,0,0,2,0
};
#undef W
void usbInit() {
UHWCON=1;
USBCON=0xB0; // Ubus aktivieren, aber ohne Takt
USBINT=1; // beim nächsten usbPoll USB aktivieren oder deaktivieren lassen
} // Dokumentationsfehler: USBCON.7 muss 1 sein damit UDCON.0 0 bleibt!
static void usbPollEp0() {
UENUM=0; // Endpoint 0 betreffend
if (UEINTX&4) { // OUT angekommen?
UEINTX=~4; // Daten ignorieren
}
if (UEINTX&8) { // SETUP angekommen?
byte len;
const byte*addr;
byte bmRequestType=UEDATX;
byte bRequest=UEDATX;
byte wValueL=UEDATX;
byte wValueH=UEDATX;
byte wIndexL __attribute__((unused)) =UEDATX;
byte wIndexH __attribute__((unused)) =UEDATX;
byte wLengthL=UEDATX;
byte wLengthH=UEDATX;
UEINTX=~8; // Jetzt erst(!) alles löschen außer TXINI
// Der Einfachheit halber (Platzmangel!!) ohne bmRequestType auszuwerten,
// ohne String-Deskriptoren
// wLength=wLengthH<<8|wLengthL;
switch (bRequest) {
// Standard-Requests
case 0: { // Get Status
UEDATX=bmRequestType?0:1; // Self Powered (bei Device Request)
UEDATX=0;
}goto okay;
case 1: // Clear Feature
case 3: goto okay; // Set Feature
case 5: { // Set Address (wValueL)
UEINTX=0; // mit 0-Byte-IN-Paket beantworten
UDADDR=wValueL; // laut Datenblatt, praktisch nicht erforderlich
while (!(UEINTX&1)); // warten bis abgeschickt
UDADDR=wValueL|0x80;
}return;
case 6: switch (wValueH) { // Get Descriptor
case 1: addr=DeviceDesc; break;
case 2: addr=ConfigDesc; len=sizeof ConfigDesc; goto gd2;
default: goto stall;
}
len=pgm_read_byte(addr);
gd2:
if (!wLengthH && len>wLengthL) len=wLengthL;
while (len) {
byte n;
while (!((n=UEINTX)&0x0D));// warten bis Interrupt
if (n&0x0C) return; // Ende bei (unerwartetem) OUT- oder SETUP-Paket (15h kommt vorbei!)
n=1<<E0_SSH; if (n>len) n=len;
len-=n;
asm volatile(
"1: lpm r0,Z+ \n"
" sts %1,r0 \n"
" dec %2 \n"
" brne 1b \n"
:"+z"(addr):"m"(UEDATX),"r"(n));
// do UEDATX=pgm_read_byte(addr++); while (--n);
UEINTX&=~1;
}
return;
case 8: { // Get Configuration
UEDATX=usbCfg&1;
}goto okay;
case 9: if (wValueL<2) { // Set Configuration
if (usbCfg=wValueL) { // alles einfach gepuffert (reicht hier)
UENUM=1; UECONX=1; UECFG0X=0x80; UECFG1X=2|E1_SSH-3<<4; // EP1OUT 64 Byte
UENUM=2; UECONX=1; UECFG0X=0x81; UECFG1X=2|E2_SSH-3<<4; // EP2IN 64 Byte
UENUM=3; UECONX=1; UECFG0X=0xC1; UECFG1X=2|E3_SSH-3<<4; // EP3IN 16 Byte
UENUM=0;
}else{
UERST=0x0E;
UERST=0; // alle 3 Endpoints rücksetzen
}
querylen=0;
goto okay;
}break;
case 10: { // Get Interface (= Alternate Setting)
UEDATX=0;
}/*nobreak*/;
case 11: goto okay; // Set Interface (ignorieren)
// Klassenspezifische Requests
case 0x20: { // Set Line Coding (wLength==7)
//outlen=wLengthL;
while (!(UEINTX&4));
UEINTX=~4;
/*
byte n;
while (!(n=UEINTX)&0x4C); // warte bis OUT-Daten gekommen sind
if (n&0x40) goto stall; // NAKIN: Host will Status zu früh
if (n&0x08) return; // Ende bei unerwartetem SETUP (kommt nie)
beep(60,10);
byte*addr=modeBuffer;
n=sizeof modeBuffer;
do *addr++=UEDATX; while (--n); // Daten einlesen (schlimmstenfalls ungültige)
UEINTX&=~4;
*/
}goto okay; // 0-Byte-IN-Paket absenden
case 0x21: { // Get Line Coding (wLength==7)
#if 1
addr=LineCoding;
len=sizeof LineCoding;
}goto gd2;
#else
byte n=7;
if (!wLengthH&&n>wLengthL) n=wLengthL;
addr=modeBuffer;
if (n) do UEDATX=*addr++; while (--n);
}goto okay; // Daten absenden, OUT-Quittung ignorieren
#endif
case 0x22: usbCfg=0x21; // Set Control Line State (wLength==0)
case 0x23: // Send Break (wLength==0?)
okay:UEINTX=~1; return;
}
stall:UECONX=0x21; // Alles andere: STALL aktivieren
}
}
// Abfrage von E1
static void usbPollEp1() {
UENUM=1;
if (UEINTX&0x80) { // Daten angekommen?
// Anfrage darf zerhackt ankommen und muss mit '\n' enden
while (UEINTX&0x20) {
char c=UEDATX; // Zeichen einsammeln
if (querylen<sizeof query) query[querylen++]=c; // in Stringpuffer ablegen
if (c=='\r' && !(UEINTX&0x20) // Manuelle Eingabe von ENTER (= '\r') in PuTTY (kein Zeichen folgt)
|| c=='\n') { // maschinelle Eingabe von "\r\n" (en bloc) oder '\n' (solo)
handleQuery();
UENUM=1;
querylen=0;
}
}
UEINTX=0; // Datenblock zurück zur SIE
}
}
static void usbPollEp3() {
UENUM=3;
if (usbCfg&0x30 && UEINTX&1) { // Puffer frei?
UEINTX=~1; // Interrupt quittieren (extra erforderlich?)
if (usbCfg&0x20) { // Erster Interrupt: 8 Bytes
asm volatile(
"1: lpm r0,Z+ \n"
" sts %1,r0 \n"
" dec %2 \n"
" brne 1b \n"
::"z"(IntData),"m"(UEDATX),"r"(sizeof IntData));
}else{ // Zweiter Interrupt: 2 Bytes
UEDATX=3;
UEDATX=0;
}
UEINTX=0; // Interruptdaten abschicken
usbCfg-=0x10; // erst 2x → 1x, dann 1x → 0x0x
}
}
void usbPoll() {
if (USBINT&1) { // Ab- oder Anstecken des Hosts?
USBINT=0;
if (USBSTA&1) { // Angesteckt
UDCON=0; // Pullup aktivieren
}else{ // Abgesteckt (Dazu muss am Arduino der Transistor T1 ausgelötet werden!)
UDCON=1; // Pullup deaktivieren
USBCON=0xB0; // Takt deaktivieren
usbCfg=0; // Meldung für Geigel-Menü
}
}
byte udint=UDINT;
UDINT=0;
if (udint&0x10 && USBCON&0x20) { // Aktivität && Takt eingefroren?
PLLCSR=0x12; // PLL aktivieren (für 16-MHz-Quarz)
while(!(PLLCSR&1)); // warten bis eingerastet
USBCON=0x90; // Takt aktivieren
}
if (udint&1) { // USB-Suspend
USBCON=0xB0; // Takt deaktivieren
PLLCSR=0; // PLL deaktivieren
}
if (udint&8) { // USB-Reset-Ende?
usbCfg=0; // Nicht konfiguriert (Adresse wird von Hardware zurückgesetzt)
UENUM=0;
UECONX=1; // Endpoint 0 aktivieren
UECFG1X=2|E0_SSH-3<<4;// 8 Bytes für Endpoint 0 im DPRAM reservieren
}
usbPollEp0();
if (!(usbCfg&0x20)) usbPollEp1();
usbPollEp3();
}
void usbSend() {
do{
usbPoll(); // verändert UENUM
UENUM=2;
}while (!(UEINTX&0x80)); // Warten bis Puffer frei
const char*p=answer;
for (byte i=0; i<answerlen; i++) UEDATX=*p++; // Puffer füllen
UEINTX=0; // Puffer abschicken
}
void usbPurge() {
UENUM=1; // OUT-Puffer leeren
while (UEINTX&0x80) UEINTX=0;
UENUM=2;
while (UESTA0X&0x03) {
UEINTX=0x05; // Kill Bank IN
while (UEINTX&0x04); // warte bis fertig
}
UERST=0x04;
UERST=0; // Keine Daten, die auf Abholung warten
}
Detected encoding: UTF-8 | 0
|