mirror of
https://github.com/Xevion/v2.xevion.dev.git
synced 2025-12-10 16:09:07 -06:00
New Post: Restricted memory and Data Framing Tricks
This commit is contained in:
200
_posts/2022-07-16-restricted-memory-and-data-framing-tricks.md
Normal file
200
_posts/2022-07-16-restricted-memory-and-data-framing-tricks.md
Normal file
@@ -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: <!--more-->
|
||||
_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...
|
||||
|
||||
<!--more-->
|
||||
|
||||
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 <stdint.h>
|
||||
|
||||
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;
|
||||
}
|
||||
```
|
||||
|
||||
<center>
|
||||
<i>
|
||||
<small>
|
||||
UART is a serial communication technology we use to send characters & text to the COM terminal.
|
||||
For more information, click <a href="https://www.youtube.com/watch?v=VBRUyLcqXV4">here</a>.
|
||||
</small>
|
||||
</i>
|
||||
</center>
|
||||
|
||||
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.
|
||||
|
||||
<center>
|
||||
<b>ISO15693 Write Single Block (Addressed)</b> <a href="http://www.ti.com/lit/an/sloa141/sloa141.pdf" style="color: #90bcff">source</a>
|
||||
</center>
|
||||
|
||||
[![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.
|
||||
|
||||
<center>
|
||||
<b>Note</b>: 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.
|
||||
</center>
|
||||
|
||||
#### 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
|
||||
BIN
assets/img/iso15693_diagram.png
Normal file
BIN
assets/img/iso15693_diagram.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
1
assets/img/iso15693_diagram.svg
Normal file
1
assets/img/iso15693_diagram.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
35
assets/iso15693_diagram.edn
Normal file
35
assets/iso15693_diagram.edn
Normal file
@@ -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)
|
||||
Reference in New Issue
Block a user