March 13, 2016

Arduino Due Flash Memory Access

The Arduino Due does not have an EEPROM like the earlier Arduinos had. Persistent memory locations, i.e. memory that can cold variable values which will be available even after power loss, watchdog reset, or after switching the box off and on again, are however available in the form of the Due's flash memory that also holds the program code and static data.

In my box, I want to store persistent data that holds the user's most recent configuration. This includes favorite channel settings (but not amplitude, which will be set to zero initially for safety), preferences, etc. I will also store certain indicators that can be read during start() and will tell whether the box was re-booted due to an intermittent power loss or a watchdog reset.

The Due has 512 kBytes of flash memory, of which usually only a part is occupied by the program code. The remainder should be usable to store and restore persistent data. Flash memory starts at address 0x00080000 (IFLASH0_ADDR) in the Due's 32-bit flat address space, and at 512k size (IFLASH0_SIZE+IFLASH1_SIZE) ranges until address 0x000fffff. The lower addresses will likely contain boot loader and program code.

The entire flash memory address space can usually only be read, and doing so is no different that ready any other (SRAM) memory location. The following function translates a flash memory address (from 0x00000 to 0x7ffff) to a C++ pointer pointing into the Due's 32-bit flat address space: To write it, the Due needs to be told explicitly that write access is permitted: After this, writing a buffer of given length to a flash memory address is possible using the following instruction sequence: boolean The above code snippets are taken and modified from a very nice library called DueFlashStorage to be found at [1]. It includes Atmel's header and source files (efc.h, efc.cpp, flash_efc.h, flash_efc.cpp) required to access the SAM 3x8E's embedded flash service component (EFC), and it builds a little C++ class for flash memory write access on top.

References

[1] https://github.com/sebnil/DueFlashStorage

Output Stage Circuit Board

I've completed soldering of the output stage. The circuit board to the right holds two channels laid out next to each other, labelled A and B, and I'm going to solder another copy of that one for channels C and D.

The op-amp stage seen on the lower left of the Spice sketch will be soldered separately, and jointly for all four channels. I'll be replacing the LT6088 by an MCP604 op-amp, which houses 4 op-amps in one chip. I could also be using a two op-amp MCP602 chip on the two-channel circuit board. The single-channel variant MCP601 unfortunately isn't available for through-hole soldering from my supplier.

The board holds two copies of the red part of the output stage design. I've had to substitute the IRF9Z24 MOSFETs for IRF9Z34 ones that my supplier had in store; the difference is just that a higher maximum current is allowed. Diode D2 became an 1.5KE 15A overvoltage protection diode.

The five pin connectors on the upper end of the board are Vdir and Vdirinv (pulse direction), VB at the TIP41 base (channel pull), and Vcur at the TIP41 emitter before R9 (current sensing). They will all be connected to the respective Arduino Due pins by jumper wire. I've also added an extra diode (BAT48) with n-side at the TIP41 collector, and the fifth pin Vcurinv is the p-side of that diode that allows to measure the reverse current when the transformer discharges.

On the lower end of the board is a stereo audio connector that exposes the three taps of the transformer's primary side; this is the low-current, high-voltage area of the board.

The reverse side became a bit more busy than I wanted. The Eagle layout looked so nice ...

Like at the end of all my other hardware posts, Eagle layouts for this board are available from the project's github repository. They differ a bit from the prototypical soldering job above and come in several variants:
  • I've added a one-channel variant with a seven-pin connector (the five above, plus V+ and GND) and without the op-amp stage. This one can easily be duplicated into a two- or four-channel variant and conforms to my soldering job above.
  • I've also added two-channel variant of this board that includes an op-amp stage of appropriate size. The pin connector then replaces Vcur (TIP41 base) by the level PWM voltage Vref (RC smoothed before op-amp Vin).

Parts List

Output stage per channel:
2x IRF9Z24 or 9Z34 P-channel power MOSFET
1x TIP41A/B/C power transistor
2x 2N7002 N-channel MOSFET
1x BAT48 diode or similar
1x SMA6J diode or similar
4x 10k resistor
2x 1k resistor
1x 1 Ohms resistor, 1% tolerance, >2A current
1x mono (2-tap) or stereo (3-tap) audio jack
1x transformer Xicon 42MT003 or similar (1200:8 windings, 120V, ~3kHz)
1x 5-pin connector
1x 2-pin power supply connector

March 06, 2016

Arduino Pin Assignment Overview

I'm posting here an overview of the pin assignment and cable colors for my box. It's based on the very nice pinout diagram from [1].

ChannelPulse levelPulse directionLEDChannel pullCurrent sensing
yelloworange, greenbluepink
APWML4 (pin 9)PWML0 (pin 34), PWMH0 (pin 35)PIOD9 (pin 30), redPIOC12 (pin 51)AD7 (pin A0)
BPWML5 (pin 8)PWML1 (pin 36), PWMH1 (pin 37)PIOA7 (pin 31), yellowPIOC13 (pin 50)AD6 (pin A1)
CPWML6 (pin 7)PWML2 (pin 38), PWMH2 (pin 39)PIOD10 (pin 32), greenPIOC14 (pin 49)AD5 (pin A2)
DPWML7 (pin 6)PWML3 (pin 40), PWMH3 (pin 41)PIOC1 (pin 33), bluePIOC15 (pin 48)AD4 (pin A3)

As you can see, I've made provisions for four output channels. I've also added the rotary encoder pins (PIOD0-2, pins 25-27), the ST7735 display (pins 2-4 and SPI header pins), the line-in audio on AD0 (pin A7), and an ESP8266 WiFi chip on Serial3 as a future extension for communication with the outside world.

References

  • [1] http://forum.arduino.cc/index.php?topic=132130.0

Arduino Watchdog

For my e-stim box project, safety is important. One issue are unexpected hangups of the Arduino CPU due to undetected coding errors. This may potentially leave the Arduino's outputs in a dangerous state. A convenient way of preventing this is the built-in watchdog. The watchdog can be programmed to automatically reset the CPU after a specified timeout period if the loop() function hangs up and fails to "tame" the watchdog within the timeout period. In the start() function that is executed immediately after a reset, the Arduino's outputs can then be brought to a safe state.

One kludge is the Arduino's core library, which disables the watchdog, after which it cannot be reenabled. To overcome this, edit hardware/arduino/sam/variants/arduino_due_x/variants.cpp and remove (comment) the line reading WDT_Disable(). This is my tiny watchdog class. The watchdog can be configures once by specifying a timeout period in milliseconds, which has to be between 4 ms and 16 seconds in a granularity of 4 ms. The reset function must be called inside loop() and at a rate faster than the timeout period, or the watchdog will automatically reset the CPU.

The implementation is simple: Update: The Arduino folks seem to have reacted to the kludge, and have made the watchdog accessible in the newest core library revision 1.6.5. [1]

References

  • http://forum.arduino.cc/index.php?topic=350058.msg2416416#msg2416416

DMA text buffer driver for an ST7735 1.8" color TFT display with SPI interface

As I said in the project objectives, I want to equip the box with a color display driven user interface that can be operated using a rotary encoder with push button. An 1.8 inch TFT display based on the ST7735 chip with a resolution of 160x128 pixels and an SPI interface can be had for USD 20 from Adafruit [1] (or for less from your favorite Hong Kong supplier, but consider your karma). Adafruit has also developed and maintains an Arduino library to interface it [2]. They also ship and support larger and higher resolution displays, such as 2.8 inch or 3.5 inch ones, but I want to keep cost and computational load as low as possible.

Interfacing the ST7735 display is dead easy and was an instant success for me: On the reverse side, VCC and LED pins connect to +3.3V, GND connects to ground, CS, RST, and DC are assigned to Arduino PIO pins (e.g. pins 2 to 4), SCL connects to the SPI header's SCK pin, and SDA connects to the SPI header's MOSI pin. Like for most things, YouTube has more than one tutorial, e.g. [3].

Once the Adafruit library is told the pin numbers of CS, RST, and DC, it takes care of the initialization procedure. The library itself can then be used to draw a graphics primitives such as pixels, lines, and rectangles. In this post, I would like to talk about two things:
  1. Design and implementation of a text buffer class for the ST7735 built on top of Adafruit's class. This is done so I can easily design a user interface based on color characters instead of graphics primitives.
  2. Replacing Adafruits bit-banging SPI interface by a DMA transfer from memory to SPI for speed and to parallelize the rendering of the text buffer (by the Arduino CPU) and transferral of the rendered buffer to the display (by the Arduino's DMA controller)

Text framebuffer

My class TextFrameBuffer realizes a text mode frame buffer that contains a two-dimensional array of character-attribute pairs. Old-schoolers among you will remember VGA's mode 3 frame buffer at B800:0000 - this is what this class recreates. The frame buffer's size ST7735_SCRWIDTH x ST7735_SCRHEIGHT depends on the size of the font's characters. I'm currently using a 6x8 pixel font and hence get 26x16 characters. Each frame buffer cell in m_buf stores an ASCII character byte in [0], and an attribute byte in [1]. The attribute is composed from a 4-bit foreground and a 4-bit background palette index, enabling 16 colors that are predefined in s_palette.
The list of text functions should be self-explanatory and will be extended as the need arises. The job of render is to convert the current text frame buffer to a pixel frame buffer and transfer that one via SPI to the display. In order to keep the data transfer minimal, a dirty_update keeps track of a dirty rectangle, the frame buffer region that needs to be converted and transferred. If the dirty rectangle is non-empty, the function prepares the ST7735 address window, initiates the SPI data transfer, and then loops over all character rows (y), all vertical scanlines of a character (jj) and all horizontal pixels of a scanline (x). Then, the attribute at (y,x) is converted into a pair of ST7735 foreground and background colors using s_palette. A horizontal pixel line is fetched from the font, depending on the ASCII character code in the text frame buffer at (y,x). Then, the bits in that line are read and either foreground or background color are written to the scanline buffer. We'll discuss what to do in place of the TODO in a second.

Faster bit-banging transfer to SPI

The first thing to notice is that Adafruit's display and library are somewhat sluggish, to the point of being clearly noticeable on the display, for two reasons: The SPI clock is set to 4MHz (CLK divider 21) only, and a bit-banging SPI access is used and transfers only one pixel color at a time.

As for the first point, my display tolerates an SPI clock rate of up to 42 MHz (CLK divider 2), which is a huge step forward. This apparently depends on the particular batch your display comes from, so your mileage may vary.

As for the second point, a lot can be gained by extending the SPI class used by Adafruit's ST7735 class by an additional function that transfers an entire buffer of bytes via SPI, instead of repeatedly calling transfer() for each byte in the buffer.

DMA transfer to SPI

Even better, however, is the idea to offload the job of transferring a scanline buffer via SPI. This has recently been demonstrated for displays based on the IL9341 chip, e.g. [4,5]. We're going to do the same for the ST7735 (something I couldn't find out there) and free the Arduino's CPU of the entire SPI communication burden, which will taken on by the Arduino's DMA controller. In doing so, the CPU can assemble the next scanline already while the current one is being transferred.

In order to do so, the SPI initialization code needs to be changed significantly: First, the DMA controller needs to be enabled. Then, we'll fix the SPI pin used, and we'll also fix the SPI chip select (CS) number to that particular pin such that DMA buffer transfers end up at the proper device. I'm sure I'm doing some things twice here by calling SPI_Configure first. 84/SPI_CLK_DIVIDER is the SPI clock in MHz; I'm using SPI_CLK_DIVIDER=3 for 28MHz without any issues.

Below is the code for initiating a DMA buffer transfer. The function sendBufferDMA accepts a data pointer and a buffer length in bytes. Internally, SPIClass has a new member dma that indicates the number of an 8-bit DMA channel to be used; I'm using channel 0. After sendBufferDMA returns, the CPU can proceed doing other things (e.g. assemble the next scanline) while the buffer is being transferred. Before initiating the next DMA buffer transfer, we need to see the current one out: In place of the TODO in the code above, I can now call: I use two scanline[] buffers indexed by a variable nbuf that flips between 0 and 1, such that there's always a buffer being assembled and a buffer being transferred. Using waitForDMA and sendBufferDMA, the burden of text buffer rendering is barely noticeable on my Arduino Due.

As always, the modified SPIClass, Adafruit_ST7735 class, and TextFrameBuffer class are available for download from the project's github repository.

References

  • [1] https://www.adafruit.com/products/358
  • [2] https://github.com/adafruit/Adafruit-ST7735-Library
  • [3] https://www.youtube.com/watch?v=boagCpb6DgY
  • [4] https://www.youtube.com/watch?v=vnEwzN14BsU
  • [5] http://marekburiak.github.io/ILI9341_due/