Forum: Mikrocontroller und Digitale Elektronik USB-Stringdeskriptor ohne Abzählen


von Henrik H. (Firma: TU Chemnitz) (heha)


Lesenswert?

Hallo,
keine Frage sondern eine gefundene Lösung. Betrifft avr-gcc ab Version 
9, mit -std==c++17, C++-Programmierung, meist mit ATmega32U4 (Arduino 
Pro Micro oder Leonardo) oder auch v-usb.

Will man USB-Stringdeskriptoren erstellen bereitet das Abzählen der 
UTF-16-Buchstaben einige Mühe. Hier helfen Templates weiter. Dazu die 
folgende Lösung, die auch mit allen Zeichen außerhalb 7-bit-ASCII 
funktioniert:
1
typedef unsigned char byte;
2
3
// strlen geht nur mit char*, hier wird's für char16_t gebraucht
4
template<typename T>constexpr size_t length(const T*s) {
5
  return *s ? 1 + length(s+1) : 0;
6
}
7
8
// 黑 (hēi) = schwarz; geheim: Funktionen verstecken; -std=c++17
9
namespace {
10
// Der USB-Deskriptor als maulgerechtes Byte-Array
11
template<size_t N>struct desc_t{
12
 byte a[N];
13
 constexpr operator byte*() {return a;}
14
 constexpr operator const byte*() const {return a;}
15
};
16
17
// Mache String-Deskriptor <Zeichenzahl N> mit Längenbyte davor
18
// Blöd dass das nicht mit einem Konstruktor lösbar ist.
19
template<size_t N>constexpr auto strdesc(const char16_t*s) {
20
  constexpr auto L=2+N+N;
21
  static_assert(L<256,"String zu lang!");
22
  desc_t<L> A = {};
23
  A[0]=L;
24
  A[1]=3;
25
  for (size_t i=0; i<N; i++) {A[2+i+i] = s[i]&0xFF; A[3+i+i] = s[i]>>8;}
26
  return A;
27
}
28
}
29
// Nutzt den String 2× und lässt den Compiler die UTF8-zu-UTF16-Konvertierung machen
30
#define StrDesc(s) (黑::strdesc<length(u##s)>(u##s))
31
32
constexpr auto str=StrDesc("Hallo");    // geht (nur) mit der unschönen #define-Hintertür
33
// wie das mit __attribute__((section(.text))) im Flash landet muss noch untersucht werden.
34
35
bool onSetup(
36
 ...
37
 switch(bIndexL) {
38
  case 0: SetupDat=StrDesc("\u0407"); return true;  //deutsch
39
  case 1: SetupDat=str; return true;
40
  case 2: SetupDat=StrDesc(" Welt"); return true;
41
  ...
42
 }
43
}
Da mit C++17 auch Umlaute und (wie hier dargestellt) chinesische Zeichen 
in Bezeichnern unterstützt werden, kann man davon Gebrauch machen.
Andere Lösungen, die von <code>std::array</code> Gebrauch machen 
funktionieren mangels geeigneter Include-Dateien nicht mit avr-gcc.

von Thomas Z. (usbman)


Lesenswert?

mm ich weis ja nicht...
Für mich sieht das so aus als wenn du dich unbedingt in c++ austoben 
willst.
Ich kann jedenfalls keine Vorteile gegenüber einem herkömmlichen Array 
erkennen. Zumal man das auch ganz bequem ins Flash legen kann. Was dein 
code ja noch nicht kann.
Ob das alles so richtig ist kann ich schlecht beurteilen, da reichen 
meine c++ Kenntnisse nicht. Ich sehe aber eine schliesende Klammer 
zuviel.
Einen Vorteil deiner Lösung ist wohl der direkte unicode support. Ich 
für meinen Teil arbeite nur mit 0x0409 für Englisch.

von Henrik H. (Firma: TU Chemnitz) (heha)


Lesenswert?

Das mit dem Flash funktioniert so, hier für den 
Langing-Page-Url-Deskriptor, wie man ihn für WebUsb braucht:
1
template<size_t N>struct landingpageurldesc_t{
2
 byte l,t,h,s[N];  // length, type, http/https switch, url-string
3
 operator const byte*() const {return &l;}
4
};
5
template<int N>constexpr auto landingpageurldesc_f(byte h,const char*s) {
6
 landingpageurldesc_t<N>A={};
7
 static_assert(sizeof A<256,"URL too long!");
8
 A.l=sizeof A;
9
 A.t=3;  // Descriptor type: string (really!)
10
 A.h=h;  // protocol, 0=http, 1=https
11
 for (int i=0;i<N;i++) A.s[i]=s[i];
12
 return A;
13
}
14
#define LP(h,s) (landingpageurldesc_f<length(u8##s)>(h,u8##s))
15
16
static PROGMEM constexpr auto LandingPageUrlDesc
17
  = LP(1,"www.tu-chemnitz.de/~heha/app.htm");
18
  // "1" means "https://", "0" means "http://" prefix
19
#undef LP
Das funktioniert hier mit dem älteren gcc 5.30 und -std=C++14. Und, 
nein, es geht nicht darum sich in C++ auszutoben sondern les- und 
wartbaren Kode zu schreiben, der zudem kürzestmöglichen Maschinenkode 
generiert; hier: gar keinen, nur Daten. Schade, dass das (zurzeit) nicht 
kürzer und kompakter geht: IMHO gehört das Initialisieren der struct in 
seinen Konstruktor, aber genau das geht nicht zusammen mit constexpr.

von Noch ein Kommentar (Gast)


Lesenswert?

> les- und wartbaren Kode zu schreiben

Tja... Problem ist halt: damit jemand den Programmcode lesen kann, muss 
er wissen, was LP() macht.

Auch wenn du eine gute Doku schreibst - es ist halt ein weiterer Punkt, 
in den man sich einarbeiten muss. Bei so einer einfachen Aufgabe lohnt 
sich der Einarbeitungsaufwand nicht. Bytes zählen und 
Flüchtigkeitsfehler korrigieren geht schneller.

von Oliver S. (oliverso)


Lesenswert?

Henrik H. schrieb:
> Andere Lösungen, die von <code>std::array</code> Gebrauch machen
> funktionieren mangels geeigneter Include-Dateien nicht mit avr-gcc.

Hier werden sie geholfen:

https://github.com/modm-io/avr-libstdcpp

Oliver

von PittyJ (Gast)


Lesenswert?

Henrik H. schrieb:
> Das mit dem Flash funktioniert so, hier für den
> Langing-Page-Url-Deskriptor, wie man ihn für WebUsb
> braucht:template<size_t N>struct landingpageurldesc_t{
>  byte l,t,h,s[N];  // length, type, http/https switch, url-string
>  operator const byte*() const {return &l;}
> };
> template<int N>constexpr auto landingpageurldesc_f(byte h,const char*s)
> {
>  landingpageurldesc_t<N>A={};
>  static_assert(sizeof A<256,"URL too long!");
>  A.l=sizeof A;
>  A.t=3;  // Descriptor type: string (really!)
>  A.h=h;  // protocol, 0=http, 1=https
>  for (int i=0;i<N;i++) A.s[i]=s[i];
>  return A;
> }
> #define LP(h,s) (landingpageurldesc_f<length(u8##s)>(h,u8##s))
> static PROGMEM constexpr auto LandingPageUrlDesc
>   = LP(1,"www.tu-chemnitz.de/~heha/app.htm");
>   // "1" means "https://";, "0" means "http://"; prefix
> #undef LP
> Das funktioniert hier mit dem älteren gcc 5.30 und -std=C++14. Und,
> nein, es geht nicht darum sich in C++ auszutoben sondern les- und
> wartbaren Kode zu schreiben, der zudem kürzestmöglichen Maschinenkode
> generiert; hier: gar keinen, nur Daten. Schade, dass das (zurzeit) nicht
> kürzer und kompakter geht: IMHO gehört das Initialisieren der struct in
> seinen Konstruktor, aber genau das geht nicht zusammen mit constexpr.

Les und wartbar finde ich das nicht. Warum man z.B. das Element l und 
nicht Length nennen konnte, verstehe ich nicht.
Kurzer Code im Eeprom, das ist Ok. Aber das hat nichts mit 
Variablennamen im Sourcecode zu tun. Im Sourcecode hat man genug Platz 
für viele Kommentare und aussagekräftige Variablennamen

Genauso mit
  A.t=3;  // Descriptor type: string (really!)
Warum kann man man t nicht als DescriptorType schreiben? Schlimmer aber 
noch diese 3. Was macht die 3? Klarer wäre ein Enum:
enum
{
  Type_Device   = 1,
  Type_Configuration = 2,
  Type_String   = 3,
};

usw.
Aber so ist das kryptisch und unwartbar.

Ich arbeite mit den USB-Descriptoren, die von STM bereitgestellt wurden.
Die sind im Sourcecode einiges länger. Man kann sie aber verstehen und 
ändern.

von Noch ein Kommentar (Gast)


Lesenswert?

> Les und wartbar finde ich das nicht.

> Warum man z.B. das Element l und nicht Length nennen konnte...

Nehmen wir mal einen ganz anderen Bereich - statische Berechnung im 
Bauwesen.

Dort sind diese ganzen Formalitäten in den Normen festgelegt. "g" steht 
immer für konstante Lasten. "q" steht immer für veränderliche Lasten. 
Auch die Schritte der Berechnungen sind in den Normen festgelegt. Alle 
statischen Berechnungen sind gleich aufgebaut.

Wird ein Gebäude umgebaut, kann jeder Statiker die Berechnungen seines 
Vorgängers lesen und abändern.

Aber der Preis für die Verständlichkeit und Wartbarkeit ist halt: Henrik 
H. muss seine Ideen bei der ISO einreichen und es dauert Jahre, bis wir 
sein Konzept benutzen können.

Wie man es dreht und wendet - es ist immer verkehrt. Legt jeder selbst 
fest, was er als verständlich und wartbar bezeichnet, versteht niemand 
die Programme des anderen. Einigen wir uns, braucht jeder Fortschritt 
Jahrzehnte.

von Stefan F. (Gast)


Lesenswert?

Noch ein Kommentar schrieb:
> Legt jeder selbst fest, was er als verständlich und wartbar bezeichnet

Nein! Einzelkämpfer will keine Firma haben, jedenfalls keine die ich 
kenne.

Jedes Team legt das gemeinsam fest. Und vernünftigerweise diskutiert 
man nur die Details, die wirklich weh tun oder Zeit/Geld kosten. Wenn 
Leute z.B. über Leerzeichen hinterm Komma oder Zeilenumbrüche vor 
geschweiften Klammern diskutieren, verlasse ich den Raum und arbeite an 
etwas das Geld einbringt (weil es mir Scheißegal ist).

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.