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/

February 07, 2016

Output Stage Design and Analysis in LTSpice

I've used the past weeks to come up with an output stage for the e-stim unit. I want to go for a high-voltage, low-current pulsed output. For this output, I want to be able to control pulse level and width/frequency. An LTSpice model of my basic design for one channel of the output stage can be seen below.

Starting on the top left and right, a digital PWM signal at half the pulse width and with the desired duty cycle is expected on Vdir and its inverse on Vdirinv. The Arduino Due has four PWM channels each with an output pin and an inverted output pin, called PWMH4, PWML4, ..., PWMH7, PWML7, which may be used here.

The 2N7002 transistors open and close with the PWM signal and are used in a Darlington configuration to drive the IRF9Z power MOSFETs, for which a gate voltage of 3.3V would be too small. The two MOSFETs in turn operate the two halves of the secondary side of a transformer, with center tap connected to the stim supply voltage V12. This way, the transformer is used to step up voltage and step down current. Using only one half on the secondary side at a time, and driving currents towards the center tap guarantees a zero net current flow over one PWM period. Diode D2 protects from overvoltages due to reverse currents that occur when the inductors discharge.

On the lower right, the standard test load resembling the human body, see [3], can be seen connected to the transformer's primary side. LTSpice also demands that the load be grounded. After some reading on [1], I decided to use Xicon 42TU003 transformers [2] which offer a 1:12 step-up ratio and are stable at frequencies of up to 3.4 kHz. Both should be more than sufficient for the purpose. The inductances in the LTSpice model are rough estimates computed from the transformer's winding numbers, dimensions, and using a standard material constant.

On the lower left, an input voltage controlling the pulse level is expected at Vref, and may be generated by RC-smoothing a 3.3V PWM signal. This result should be in the range of 0.3V to 1.3V, and is fed to an op-amp, which serves as impedance decoupler and doubles the voltage on the way.
Transistor Q1, a TIP41, to the right of the op-amp is operated in its linear regime and serves as a current controller. The higher the input level voltage Vref (green), the higher the base voltage (red) of the transistor, and in turn the higher the collector-emitter current (blue): at a base voltage of 0.6V it starts to open, and at around 2.6V it sinks the maximum current, see [3] and the figure to the left. Later, the collector-emitter stim current passing through the TIP41 may be measured before resistor R9 using the Arduino's analog-digital converter.

Here are examples of the stage's behavior on full level and for 50, 100, 200, and 500 microsecond PWM pulses (frequencies of 1kHz, 2.5kHz, 5kHz, and 10KHz) on Vdir (grey) and Vdirinv (magenta). On the primary side, pulses with levels of 28V to 65V to either side are generated at the same frequency, with higher voltages occurring at lower frequencies because of the inductors' latency. The input current drawn (blue) is around 1.8A, with less than 100mA in alternating directions on the output stage (red). Note that the net output current over a period is zero.

Two alternating 50 microsecond pulses generate 100 microsecond pulse with zero average current on the output. A voltage level of only 28V (56V delta) is reached because of the inductors' latency.

Two alternating 100 microsecond pulses generate 200 microsecond pulse with zero average current on the output. A voltage level of only 36V (72V delta) is reached because of the inductors' latency.

Two alternating 200 microsecond pulses generate 400 microsecond pulse with zero average current on the output. A voltage level of only 50V (100V delta) is reached because of the inductors' latency.

Two alternating 500 microsecond pulses generate 1 millisecond pulse with zero average current on the output. A voltage level of 65V (130V delta) is reached because of the inductors' latency. This is close enough to the theoretical maximum voltage of 72V (144V delta) we expect at a secondary side voltage of 12V and using half the transformer's secondary side at a 1:6 ratio.

On the software side, clusters of these pulses will later be generated at frequencies of below 300 Hz and with a duty cycle of up to 10% after reading on [1]. This may be achieved by manipulating the PWM signals on Vdir and Vdirinv. Then, the stage draws a maximum of 180mA even if continuously operated and exposes the user to only 18mA.

References

[1] http://www.smartstim.com/forum/index.php
[2] http://eu.mouser.com/ProductDetail/Xicon/42TU003-RC/
[3] http://pdf.datasheetcatalog.com/datasheet2/e/0ldok1yf92ap6kg5ei3lsiyjfuyy.pdf

January 02, 2016

Digital-Analog Conversion, or Voltage Control by PWM

For volume (amplitude) control of the unit's output channels, I will need a way of converting a digital volume value (say, a percentage) to an analog voltage level.

Essentially, a digital-analog converter (ADC) is required. I will, however, not use the ADC channels of the Due: it's only got two of them, and using them would be overkill, too. The common way to digitally control a voltage is by pulse-width modulation and a low-pass filter. Basically, a high-frequency PWM waveform gets smoothened by the capacitor in the low-pass filter. Then, the duty cycle of the PWM waveform is varied to control the voltage. The output voltage equals the integral of the PWM voltage (3.3V for the Due) over one PWM period. Hence, varying the duty cycle between 0% and 100% will yield an output voltage varying linearly between 0V and 3.3V. The website [1] nicely explains the concept in easy words.

A small rippling may remain in the output voltage depending on the design of the low-pass filter. For this, the capacitor is first chosen according to the current load that the voltage line needs to drive. Then, the resistor value determines the low-pass filter's properties, such as cutoff frequency, ripple voltage, and settling time. The applet [2] allows to compute output voltage, ripple voltage, and settling time for a duty cycle step change if given a PWM frequency and the resistor and capacitor values of the low-pass filter.

References

[1] http://provideyourown.com/2011/analogwrite-convert-pwm-to-voltage/
[2] http://sim.okawa-denshi.jp/en/PWMtool.php

Audio-In from Line-Out, Arduino Due DAC

Audio tracks can be a great source of widely varying and entertaining beat patterns for e-stim. All powerful boxes can generate e-stim pulses from a line-in port. I want to support this, too, and think about computing volume (VU) and frequency analysis (FFT) information as a source of time-varying information for e-stim programs.

The common way to interface a line-in port with an Arduino is the LM386 low-end audio amplifier chip. After all, we're not interested in high fidelity over a wide range of frequencies, but just in volume and beat pattern information. Sketches and circuits for LM386 are a dime a dozen on the interwebs, and most are based on the 20-gain minimum-parts circuit from the chip's datasheet [1]. Some examples and circuits can be found on [2,3,4].

To the left is my realization of that circuit, including a line-in connector with an audio cable coming from my notebook plugged in. Because the LM386 runs on 5V and the Due's ADC runs on 3.3V, I've added a simple 5V to 3.3V voltage divider using a 2.7k and a 5.6k resistor in series. You'll quickly fry the ADC channel if you don't. A number of alternative voltage conversion strategies can be found here [5]. The 10uF capacitor for a gain of 200 can still be seen soldered in, but is disconnected.
Two things didn't go too well in the first place. For one, it's paramount that the 5V supply is a good one. I had to realize that, when powering my Due from my notebook's USB, my power supply does not do too well because the LM2596 chip requires an input voltage of at least 8.5V. Powering from a 9V block fixed this. Second, the yellow cable runs to channel 0 of the Due's ADC, which is PIO A.2 pin number 85 [6]. It's stupidly labelled "A7" on the Due board, which helped a lot to eat into my spare time.

I'm running the ADC in free-running mode, meaning that it continuously converts incoming voltage in the 0V to 3.3V range to 12-bit integers in the range 0 to 4095. I've set up the Due DAC's direct memory access (DMA) to transfer the readings into a double-buffer in memory: An interrupt is triggered whenever a buffer is full, which happens every 2000/128 ~ 15 ms. The interrupt service routine (ISR) then switches buffers. Information on the Due's ADC and DMA controllers can be found in the SAM3X8E data sheet [7].

Now there's always a buffer being filled by DMA, while a full buffer holding ~15 ms worth of amplitude data could be processed inside loop(). I could be computing VU or FFT values from the amplitude information stored therein. But that's for another post.

Parts List

1x LM386 audio amplifier
1x 220uF capacitor, electrolytic
2x 10uF capacitor, electrolytic (3 for 200-gain)
1x 47nF capacitor
1x 10k resistor
1x 5.6k resistor (or similar)
1x 2.7k resistor (or similar at half the value)
1x 10 Ohms resistor
1x 3 pin header
1x mono audio socket

The total cost is about USD 5. Eagle files with scheme and board layout can be found in the project's GitHub repository.

References

[1] http://www.ti.com/lit/ds/symlink/lm386.pdf
[2] http://www.instructables.com/id/LM386-Audio-Amplifier/
[3] http://www.arduino-hacks.com/arduino-vu-meter-lm386electret-microphone-condenser/
[4] https://www.arduino.cc/en/Tutorial/SimpleAudioPlayer
[5] http://jamesreubenknowles.com/level-shifting-stragety-experments-1741
[6] http://forum.arduino.cc/index.php?topic=132130.0
[7] http://www.atmel.com/images/atmel-11057-32-bit-cortex-m3-microcontroller-sam3x-sam3a_datasheet.pdf