19 Februari 2013

POV Globe - Part IV - Software & Data

This is in continuation to: POV Globe - Part III - Electronics

The data layout on the SD card and the software are rather technical and trivial, but still worth a mention.

I'm using the SD as a raw block device (i.e. no file-system). This is in order to reduce read overhead, as well as to make sure that all image data is in consecutive blocks (as mentioned in the previous post, jumping between non-consecutive blocks takes an eternity of 380us). Since reads are always in blocks, the format is block aligned. Some numbers:
One block of data = 512B = 4096bit = 16 columns.One revolution (or "frame") = 512 columns = 32 blocks = 16KB.One second of animation = 50 frames = 800KB.A quick calculation shows that 1GB of data can hold a little over 20 minutes of 50FPS animation. My card has 2GB, and it is easy to find cards with more.
The format is as follows:
First 4 bytes are the total number of blocks in the image, followed by 508B of padding.Then the image itself follows, column-by-column, where the bytes in each column are ordered from top 8 LEDs to bottom 8 LEDs, and the bits in each byte are ordered so that the MSB is the top LED and LSB is the bottom LED.
In order to write the raw data to the SD card, I'm using a USB card reader and the Linux dd command, directly to the device.
I will not get into all the details - most of them are just technicalities.
The only part worth mentioning is how the main loop synchronizes the drawing of the columns to the physical location of the LED row, designated by a pulse from the IR sensor, once per revolution. It also needs to take into account possible out-of-sync situations, and "catch-up" if we're late.
A timer keeps the time elapsed since the beginning of the column.The variable col_dur holds the duration of a single column (1/512 the duration of a revolution) - it is updated every time we get an IR pulse.The variable pos keeps the current (estimated) physical position of the LEDs, between 0..511.The variable cur_col keeps the last column that has been drawn, between 0..511.And the main loop looks something like this:while (true) { // draw all the columns until the current position while (cur_col != pos) { draw_column(); if (++cur_col == 512) col = 0; } while (TIMER < col_dur); // wait until the end-of row time // advance position according to how much time elapsed do { TIMER -= col_dur; if (++pos == 512) pos = 0; } while (TIMER >= col_dur); // check position sensor and make adjustments if (pos == 0) { ... wait for rising edge of the sensor ... ... recalculate col_dur (make it longer or equal), and reset TIMER ... } else if (sensor_rising_edge()) { // got a premature signal - we're falling behind ... force pos = 0, recalculate col_dur (make it shorter), and reset TIMER ... }}The draw_column() function just streams 32B of data from the SD to the LED board, pulses the latch line, and then prepares the next column (i.e. issues a SD command to get the next block and waits for it to become ready, if needed).
In practice, because of the SD latency-related problems described in my previous post, this function currently takes care of skipping the first block, while the second is fetched, and in the future it will also take care of streaming the first block from flash.
The last video in the previous post demonstrates this algorithm in action, taking button presses in lieu of IR switch signals.

View the original article here

Ikuti Blog ini


Mau dapet Update-an Blog ini lewat e-mail? Masukkin aja alamat Email kamu disini:

Dikirim Oleh FeedBurner