From f662cee896c2d37b67b56be930f496010a9cec81 Mon Sep 17 00:00:00 2001 From: Xevion Date: Sun, 17 Jul 2022 14:31:50 -0500 Subject: [PATCH] New Post: Restricted memory and Data Framing Tricks --- ...stricted-memory-and-data-framing-tricks.md | 200 ++++++++++++++++++ assets/img/iso15693_diagram.png | Bin 0 -> 32853 bytes assets/img/iso15693_diagram.svg | 1 + assets/iso15693_diagram.edn | 35 +++ 4 files changed, 236 insertions(+) create mode 100644 _posts/2022-07-16-restricted-memory-and-data-framing-tricks.md create mode 100644 assets/img/iso15693_diagram.png create mode 100644 assets/img/iso15693_diagram.svg create mode 100644 assets/iso15693_diagram.edn diff --git a/_posts/2022-07-16-restricted-memory-and-data-framing-tricks.md b/_posts/2022-07-16-restricted-memory-and-data-framing-tricks.md new file mode 100644 index 0000000..eb580e1 --- /dev/null +++ b/_posts/2022-07-16-restricted-memory-and-data-framing-tricks.md @@ -0,0 +1,200 @@ +--- +layout: default +title: Restricted Memory & Data Framing Tricks +date: 2022-07-16 13:51:00 -0500 +tags: c memory embedded ti msp430 union +excerpt_separator: +_preview_description: +--- + +Working on microcontrollers is a far cry from the essentially unlimited memory and floating point operations available +in _Python_ and other high level languages. Here's what I have been learning at my _first_ internship... + + + +For the past 7 weeks I've been working at **Black Pearl Technology** in a Software Development Internship. +To be honest, I really wasn't expecting to be getting an internship this summer, and while I was sad about it, I +was loaded up on summer courses such that I'd have a leisurely fun summer doing what I wanted. + + +Well, fortunately (for my career, not so much for my stress levels), I managed to snag this internship at the last second, +and I've been learning _so_ much. + +## Restricted Memory + +While working there, I was assigned to a project that required usage of a Texas Instruments MSP430 microcontroller. +While there were many quirks with working on MCUs like this (like having to ditch JetBrains & VSCode altogether!), the +biggest quirk isn't working with C: it's working without `malloc` altogether. + +On low memory devices like this, memory is extremely limited and there is no certainty that your code will not leak memory. +Usage of `malloc` and other dynamic memory allocation methods are considered innately dangerous - while there is a chance +you will write perfect code that will properly allocate/deallocate, you can't be certain that your complex program +won't run out of memory on such a small footprint to work with. + +Instead, variables are assigned either inside methods for short periods (and passed around), or they are assigned statically and globally. +It appears that the libraries I use personally prefer globally accessible variables, which 99% of the time, is very wrong - but in +Microcontroller land, global variables are your friend. + +```c +#include + +uint8_t uid[4]; // A unique identifier + +int main(void) { + UID_generate(uid, 4); // Pass the pointer, the function writes to it (and does not return it) + UART_putUid(uid, 4); // Write the UID to UART +} +``` +```c +void UID_generate(uint8_t uid, int length) { + uint8_t i = 0; + while(i < length) + uid[i++] = RANDOM_char(); +} + +void UART_putUid(uint8_t* uid, int length) { + uint8_t i = 0; + while(i < length) + UART_putChar(uid[i++]); +} + +void UART_putChar(uint8_t value) { + while(!(UCB0IFG & 0x1)); + UCB0TXBUF = value; +} +``` + +
+ + + UART is a serial communication technology we use to send characters & text to the COM terminal. + For more information, click here. + + +
+ +There's not much more to this - don't use `malloc`, stick to the _stack_ for actively executing methods and use _global variables_ +when you need to go into low power mode while maintaining state. + +Overall, this doesn't hinder ones ability to write working code - the features are still there, but the way you access +methods, store data & manipulate is re-organized - sometimes at the detriment to quality & refactoring efforts. + +## Data Framing Tricks + +While at my internship, I used my MSP430 microcontroller to communicate with various devices over UART and SPI. I also sent +commands to a ISO15693 NFC wafers. All of these interfaces are extremely low level and the best documentation I have +is often just a PDF and some random code scattered across the internet. There is no library to speak of, usually. + +Communicating at a low level like this requires reading and writing individual bytes of data into _frames_, or arrays +of bytes with a well-defined structure. + +
+ ISO15693 Write Single Block (Addressed) source +
+ +[![ISO15693 Write Single Block Diagram][iso15693-diagram]][iso15693-diagram-edn] + +Traditionally, commands are built statically all at once in a mostly hardcoded manner: + +```c +uint8_t offset = 0; +ui8TRFBuffer[offset++] = 0x61; +ui8TRFBuffer[offset++] = 0x21; +ui8TRFBuffer[offset++] = 0xA7; +ui8TRFBuffer[offset++] = 0x3E; +ui8TRFBuffer[offset++] = 0xFF; +ui8TRFBuffer[offset++] = 0x58; +// ... You get the idea +``` + +Instead, what if we could format this into a `struct` that we could pass around on the stack with a pointer? + +```c +struct AddressedSingleBlockWrite { + uint8_t Flag; + uint8_t Command; + uint8_t Address[8]; + uint8_t Block; + uint8_t Data[4]; +}; + +int main() { + struct AddressedSingleBlockWrite command; + command.Flag = 0x20 | 0x40; + command.Command = 0x21; + uint8_t address[8] = {0xA7, 0x3E, 0xFF, 0x58, 0x21, 0x32, 0x10, 0xFE}; + memcpy(&command.Address, &address, sizeof(command.Address)); + command.Block = 0x05; + uint8_t data[4] = {0x11, 0x22, 0x33, 0x44}; + memcpy(&command.Data, &data, sizeof(command.Data)); +} +``` + + +Now we have a defined structure in our source code and we can move and manipulate various parts of our command +structures without having to deal with hardcoded offsets. Still though, if we want to copy this command structure into +the buffer, we have to individually copy each part of the command - which will break the second we modify its structure. + +There's a fantastic solution for it: **Unions**. + +```c +union ASBWUnion { + uint8_t data[15]; + struct AddressedSingleBlockWrite marshalled; +}; +``` + +```c +union ASBWUnion demarshalled; +demarshalled.marshalled = command; + +for (int i = 0; i < 15; i++) + printf("%x ", demarshalled.data[i]); +``` + +```sass +60 21 a7 3e ff 58 21 32 10 fe 5 11 22 33 44 +``` + +`union`s are special datatypes that share a single memory footprint (equal to it's largest member) starting at the exact same point memory. +They combine neatly with `struct`s to allow us to represent the `AddressedSingleBlockWrite` as a single byte array. + +
+Note: When implementing this, I do recommend that you create macro definitions for the length of the final command structure. +This will help greatly when it comes to refactoring or making adjustments to your command structure. +
+ +#### Reversing Endianness + +If you check out TI's [sloa141][sloa141] PDF on ISO 15693 commands, you'll notice that many of the examples have +sections of their bytes reversed - sections like the _Address_ and _Data_ sections, but not the entire compiled command. + +One such example, `60 21 9080C2E5D2C407E0 06 55443322` (spaces inserted to split it apart), has the _Flag_, _Command_, +_Address_, _Block_ and _Data_ stored in that order. But for this particular example, how could the address `E007C4D2E5C28090`? +How could the data be `0x22334455`? It's the Endianness - the order of bytes as the underlying architecture understands it. + +While for my particular usage, reversing endianness was not needed, it's an interesting problem that can be solved +quite easily with our new data structure. + +```c +void ReverseEndianness(struct AddressedSingleBlockWrite asbw) { + uint8_t temp; + int i = 0; + + for (; i < 4; i++) { + temp = command.Address[i]; + command.Address[i] = command.Address[8 - i - 1]; + command.Address[8 - i - 1] = temp; + } + + for (i = 0; i < 2; i++) { + uint8_t temp = command.Data[4 - i - 1]; + command.Data[4 - i - 1] = command.Data[i]; + command.Data[i] = temp; + } +} +``` + +[iso15693-diagram]: /assets/img/iso15693_diagram.png +[iso15693-diagram-edn]: /assets/iso15693_diagram.edn +[sloa141]: http://www.ti.com/lit/an/sloa141/sloa141.pdf \ No newline at end of file diff --git a/assets/img/iso15693_diagram.png b/assets/img/iso15693_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..2071dfb8e905cc4a9ed6f9a2281d90f120dae901 GIT binary patch literal 32853 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 zx!_d?=kYax`o<(}lmizbn8kB~#ZC0123456789abcdeFlagCommandAddressBlockData000f6121a73eff58213210fe0511223344 \ No newline at end of file diff --git a/assets/iso15693_diagram.edn b/assets/iso15693_diagram.edn new file mode 100644 index 0000000..e10603d --- /dev/null +++ b/assets/iso15693_diagram.edn @@ -0,0 +1,35 @@ +(defattrs :bg-green {:fill "#a0ffa0"}) +(defattrs :bg-yellow {:fill "#ffffa0"}) +(defattrs :bg-pink {:fill "#ffb0a0"}) +(defattrs :bg-cyan {:fill "#a0fafa"}) +(defattrs :bg-purple {:fill "#e4b5f7"}) + +(defattrs :vertical [:plain {:writing-mode "vertical-rl"}]) + +(def row-height 70) +(def boxes-per-row 15) + +(draw-column-headers {:stroke "#ffffff"}) +(draw-box (text "Flag" :vertical) :bg-purple) +(draw-box (text "Command" [:vertical {:font-size 11}]) :bg-cyan) +(draw-box (text "Address" :math) [{:span 8} :bg-yellow]) +(draw-box (text "Block" :vertical) :bg-pink) +(draw-box "Data" [{:span 4} :bg-green]) + +(draw-box 0x61 [:bg-purple {:next-row-height 30}]) +(draw-box 0x21 :bg-cyan) + +(draw-box 0xA7 :bg-yellow) +(draw-box 0x3E :bg-yellow) +(draw-box 0xFF :bg-yellow) +(draw-box 0x58 :bg-yellow) +(draw-box 0x21 :bg-yellow) +(draw-box 0x32 :bg-yellow) +(draw-box 0x10 :bg-yellow) +(draw-box 0xFE :bg-yellow) + +(draw-box 0x05 :bg-pink) +(draw-box 0x11 :bg-green) +(draw-box 0x22 :bg-green) +(draw-box 0x33 :bg-green) +(draw-box 0x44 :bg-green) \ No newline at end of file