Hi there!
Welcome to the fourth part of the tutorial
how to play a video on Arduino uno!
We finally arrived to our first milestone
of this project! In fact, in this episode
we will show how to play an unlimited length
animation stored on the sd card!
Let’s go for the next hack!
Last time we analyzed the SPI and USART in
SPI mode connections, and we showed that the
USART allows for a much higher transfer rate.
This was a crucial point, In fact, when dealing
with videos, one must find a trade-off between
computational power and bandwidth or memory
usage. However an Arduino Uno has almost no
computational power to perform even the simplest
on-the-fly decoding at 20 fps.
Therefore the video must be stored in the
raw format, i.e. without any compression.
Let’s do the math: a 160x128 frame will
take 40kbytes at 16 bits per pixel.
At 20 fps, this means more than 800kB/s.
That’s huge, however, the price per GB is
nowadays very small, and a 4GB sd card can
store about 80 minutes.
There is still another problem: the bandwidth!
In fact, 800kB/s is much larger than what
we achieved last time, with the USART. We
already said in the previous video, that the
throughput can be improved by reading more
than one sector at once
However, other two problems arise:
First, the Arduino Uno has just 2kB of RAM,
and the FATfs library alone already takes
roughly 600 bytes, leaving space only for
less than 3 sectors.
Second, we must then send the data we read
from the SD card to the display, using the
same USART.
Therefore, our total system bandwidth would
be 1.6 MB/s and not just 800kB/s.
However, since we do not need to perform any
computation on the video data, such data doesn’t
even have to enter the CPU! And this what
we are going to do: instead of transferring
the data from the SD to the CPU and from the
CPU back to the display, we can just connect
the SD output line to the display input line.
In this way, by enabling the display, when
the SD card is actually outputting video data,
we can directly transfer the video data from
the SD card to the display.
This resembles the Direct memory access technique,
(as known as DMA).
The only problem of this solution, is that,
with such a connection, the CPU can never
access the display, to properly initialize
it.
A quick workaround is to use a multiplexer,
and have another GPIO pin to select where
the display data input line is connected to:
the CPU or the SD card.
But wait… another IC? Another GPIO? We’re
from next-hack, aren’t we?  We must find
a much clever solution… a resistor!
Let’s explain how does it work! Let’s
suppose for a moment that we are working on
3.3V system,. In this way no level translation
is necessary!
When only the SD card is selected, the SD
is directly connected to the CPU. The resistor
does not interfere, because the logic level
on MISO is forced by the SD card, and the
logic level on MOSI is forced by the CPU.
If these two levels are different nothing
will occur: a current will flow from one line
to the other, but if the resistor value is
large enough, that current will be small enough,
not to create any problem.
When only the display is selected, the SD
card releases the bus, therefore its data
outpu signal, DOUT, will be in high impedance.
In other word, it will act as if it were disconnected!
Data from the CPU will pass through the resistor
to the display DIN line.
If we select both the display and the SD card,
magic occurs! In fact, the SD card is still
directly connected to the CPU as in the first
case, however, the display is also listening
to the SD card, and not to the CPU! In fact,
the external resistor will have a much larger
value, with respect to the output of the SD
card. In this way, the logic level read by
the display will be determined by the SD card,
and not by the CPU!
Perfect! We just need a resistor!
Well… not just one! in fact, we have another
problem: the Arduino Uno works at 5V, therefore
we need 5V to 3.3V level translators, as we
did in the second video of this series. The
values of all the resistors must be carefully
chosen to meet bandwidth and voltage level
specifications.
In our website, you’ll find all the detailed
calculation of all the resistor values!
Here is the final schematics! Since our circuit
gets now quite messy, we preferred to build
it using a prototyping board, instead of a
breadboard.
Remember: you must use the hacked version
of the display, as we did in the second video!
We used kicad to layout the connections, in
order to simplifying the soldering process!
Soldering the circuit is very easy. The only
trick is to press the headers as shown here!
We also suggest to first solder the resistors
and the connectors. Then, trim the resistor’s
wires, and use them as the connections!
This completes the hardware: let’s now deal
with the software.
First of all, we need to create some functions,
that select the display, when the SD card
is outputting data. We created a copy of the
function f_read on the FATfs library, and
we modified it.
In such function, we see that it calls another
function named disk_read(). Therefore we copy
and modify this function too. disk_read(),
in turn, calls rcvr_datablock(). So let’s
copy and modify this function. Yes, again
another function is called: rcvr_spi_multi()!
We must copy it too! Finally, this is the
function we were looking for! We modify it
in order to select the display before reading
the sector, and t deselect it after completion.
The main program now gets simple:
In the setup function we put everything we
need to prepare the display for drawing data.
In the first part of the loop function, we
open a file named “video.raw”.
Then we create another loop, in which we read
large chunks of data, using the new function
we created. We read 40 kB at once, but we
do not use any buffer, as we do not need to
store the data. In this way we amortize all
the overheads we discussed about in the previous
video! The loop exits when there are no data
bytes available.
The only limit of the video length, is the
SD card size.
Now everything is ready, and we just need
a video, in the raw format.
For this purpose, we need to perform 2 steps.
As a first step, export your video as a collection
of bitmap pictures. The resolution of each
picture must be 160 x 128 at 24 bpp. The original
video must have a frame rate of 20 frames
per second! If your video has a different
frame rate, you can change it during the export
process!
In the website we explain everything to do
this!
Then, use the command line tool available
on our website to create a single big file,
from all the frames. In next-hack.com you’ll
find both the program and the full source
code. The code can be also compiled for L
inux or MAC.
Now, put the created file on in the SD card.
Upload the sketch to the Arduino, and put
a jumper in the position we are now showing.
This will prevent the programming IC from
interfering with the SD card and the display.
Remember to remove the jumper and disconnect
the shield when you want to upload again your
sketch.
After this, turn off the Arduino, insert the
shield you have created, and connect the Arduino
again.
After some while, you’ll see the video playing!
However, for sure it won’t play at 20fps.
It will likely play faster, as we did not
put any synchronization. To do this, simply
uncomment the line “synchronizeTo20Hz();”.
The frame rate can be measured using a scope,
or a frequency meter, on the chip select line
of the display.
You will measure a frequency, which is 80
times the frame rate, as one frame is composed
of 80 sectors!
We got 1.71 kHz without synchronization, which
corresponds to about 21.4 fps. That is 855
kB/s, which is very close to the theoretical
maximum value one could achieve using an 8MHz
USART!
The code without synchronization is an useful
tool to assess how much headroom we have left
for audio!
For best performance we recommend using large
cluster sizes. For instance we used 16kB clusters.
The card access time will also play a major
role on the maximum speed.
What speed did you get? Write a comment!
If you uncomment the line “synchronizeTo20Hz();”,
you’ll achieve exactly 1.6 kHz, that is
your video is playing at 20 fps.
That’s all for today…. No wait! Where
is the magic box?!? We promised we would have
shown what’s inside, but there is no magic
box!
Well, if you look closer, actually there is
a magic box! The 680 Ohm resistor IS the magic
box! It acts like a DMA controller, allowing
for the data to go in the right place!
Now, really, that’s all for today! In the
next episode we will show you how to play
a 16-bit audio file! This will be required
for the last step: integrating audio and video!
This won’t be trivial, and further optimization
will be needed!
As always youl’’ find everything you need
for this project in our website: next-hack.com!
Please, rate, comment, share and subscribe!
Thanks for watching!
What’s your next hack?
