Electronics, Embedded Systems, and Software are my breakfast, lunch, and dinner.
Several years ago I wrote a post which introduced my method of declaring XML comments in my source code and scanning them with a Python script to produce a generated byte array. I've used this several times over the years and as tends to happen, I now hate it. My biggest pet peeve has turned out to be its lack of flexibility. Every time I want to do something crazy, like create HID reports or add extensive audio descriptors (with their relatively complicated cross-referencing scheme), I end up having to make big changes to my Python. It just isn't simple enough! The other thing is that it's not very portable either. If have some hardware that, for example, locks endpoint addresses to specific endpoint instances (a restriction that the STM32 USB peripheral doesn't have, but the SAMD21 does), it'll be yet another modification to the script.
I'd like to introduce in this post a fluent API written entirely using C++ constexpr which enables a syntax like this:
1constexpr auto kHidEndpointIn = usb::EndpointDescriptor()
2 .EndpointAddress(0x81)
3 .Attributes(0x03)
4 .MaxPacketSize(64)
5 .Interval(1);
6constexpr auto kHidEndpointOut = usb::EndpointDescriptor()
7 .EndpointAddress(0x01)
8 .EndpointAddress(0x01)
9 .Attributes(0x03)
10 .MaxPacketSize(64)
11 .Interval(1);
12
13constexpr auto kConfigDescriptor =
14 usb::ConfigurationDescriptor(0)
15 .ConfigurationValue(1)
16 .Attributes(0x80)
17 .WithInterface(
18 usb::InterfaceDescriptor()
19 .InterfaceClass(0x03)
20 .InterfaceSubClass(0x00)
21 .WithEndpoint(kHidEndpointIn)
22 .WithEndpoint(kHidEndpointOut));
To produce something like this in the .rodata section of my executable:
1000014e1 <_ZL17kConfigDescriptor>:
2 14e1: 00290209 eoreq r0, r9, r9, lsl #4
3 14e5: 80000101 andhi r0, r0, r1, lsl #2
4 14e9: 00040900 andeq r0, r4, r0, lsl #18
5 14ed: 00030200 andeq r0, r3, r0, lsl #4
6 14f1: 21090000 mrscs r0, (UNDEF: 9)
7 14f5: 01000111 tsteq r0, r1, lsl r1
8 14f9: 07001922 streq r1, [r0, -r2, lsr #18]
9 14fd: 40038105 andmi r8, r3, r5, lsl #2
10 1501: 05070100 streq r0, [r7, #-256] @ 0xffffff00
11 1505: 00400301 subeq r0, r0, r1, lsl #6
12 1509: 00000001 andeq r0, r0, r1
Now, I'm not a C++ expert by any means. I'm almost certain I did things in a harder way than necessary. But my hope is that by telling my journey in getting to this point someone might find some benefit.
Continue on to read more!