Quantcast
Channel: Andys Workshop
Viewing all 38 articles
Browse latest View live

Nokia QVGA TFT LCD for the Arduino Mega. Design and build (part 1 of 2)

$
0
0

In two of my previous articles I showed you how to reverse engineer the Nokia 2730 LCD for connecting to a device with 3.3V I/O’s and then I showed you how to build a 16-channel level converter for connecting devices together that have differing I/O level requirements.

This article brings together the knowledge we have gained in the previous two articles and puts it to use by creating a project that will allow a Nokia QVGA 24-bit colour TFT LCD to be indirectly connected to an Arduino Mega via a level converter, all on one small 50mm PCB.

All quite straightforward so far. The real innovation will be in the graphics library that I present in part two of this article set. The graphics library will use the external memory interface built in to the Arduino Mega to transfer data to the LCD in a single assembly instruction.

There is no faster way to transfer data out of the Arduino Mega to a peripheral. Doing it like this opens up the possibility of full colour bitmap graphics at a respectable refresh speed.

So now you know the endgame, let’s take the time to explain it all in detail.

The Nokia 6300 24-bit QVGA LCD

Perhaps the first surprise of this article is my choice of LCD. Given that the previous article showed how to reverse engineer the Nokia 2730 LCD you could have been forgiven for assuming that this was the one I’d use.


The 6300 panel with protective film attached

Well, since that project I noticed that a great many of Nokia’s QVGA displays seem to vary only slightly in their pinouts and as such the likelihood of them using the same controller would be high. I picked the Nokia 6300 for this project for the following reasons:

  • It’s readily available (on ebay) and costs very little.
  • It claims to support 16M colours (24 bit). You would be forgiven for not noticing the difference between 16M and 262K colours though.
  • It’s pin compatible with a large number of Nokia models: 6300, 6301, 5310, 7500, 8600, 6120C, E90 and E51.
  • The connector is readily available from online sources.

The connector

The connector is the same 24-pin board-to-board connector used in the Nokia 2730. An eagle-eyed reader has identified the manufacturer and the part number. It is made by JST and the part number is 24R-JANK-GSAN-TF. Here’s the datasheet.

It is easy to obtain them either direct from JST’s online shop or in small quantities from online sources. Here are some direct links to the ones that I know of:

gsmserver.com

cellnetos.com

stellatech.com

The pinout for the connector is shown here:

Here’s a photograph of the LCD side of the connector. If you look closely you can see where the ground pins connect directly into the ‘ground pour’ inside the ribbon cable. This helps to identify where pin 1 is located because the big “1″ silkscreen’d on to the FPC is in the wrong place.


The connector on the LCD FPC cable

The backlight

Like the 2730 before it, the 6300 backlight consists of 4 white LEDs in series requiring around 13V to power them. Just like before, I will use the OnSemi NCP5007 constant current backlight driver to do the heavy lifting here.


The NCP5007 in SOT23-5 package

The NCP5007 will be configured to supply a constant 20mA through the backlight circuit and we will use a PWM signal on the ENABLE pin to vary the brightness.

Power supply design

We need both 5V and 3.3V inputs for this design. 5V will be used to power the backlight driver as well as set the reference level for the Arduino side of the level converter. 3.3V will be used to set the reference for the LCD side of the regulator.

The backlight draws the most power from this design so I will optionally allow 5V to be supplied externally from a supply that shares a common ground with the Arduino itself.

TFTs like these draw a very small amount of current, typically less than 10mA so I will supply it indirectly from a GPIO pin through the level converter. This allows me to control the order in which power is applied. Many TFTs prefer their I/O supply to come up before the panel supply and for safety I’m going to assume that this is the case with this device. Had the device required significant amounts of current I would have had to use a couple of transistors to switch the current on and off.

Arduino interface design

The Nokia 6300, like the Nokia 2730, uses an 8-bit 8080 protocol to communicate with the LCD. The 8080 protocol consists of a chip select signal, 8 bits of data, read and write lines and another line that is used to indicate whether you want to transfer data or set a command register value.


The 8080 protocol write cycle

The above image summarises the state of the 8080 bus during a write cycle. The key point to note is that data is transferred to the controller on the rising edge of the WR line. Can we get this line from the Arduino Mega’s external memory interface? Well yes, we can. The following diagram from the datasheet shows the timing of the external memory bus.


The ATMega1280 external memory timing

Note the behaviour of the address latch enable (ALE) line. We need an active-low chip select (/CS) signal that is low during the data setup and transfer phase. ALE behaves perfectly in this respect and we will use it for chip-select. This is a critical part of the design. If ALE was not present or did not obey the above timings then this design would fail.

The register/data select line

This is the line used to select whether you’re transferring data or writing to a register. It is somewhat confusing in that it is variously referred to as RS or D/CX depending on which datasheet you’re reading. I’m going to call it RS from now on.

The trick we will use to select or deselect RS is one that we’ve been using on more powerful ARM MCUs for a long time. We will attach RS to external address line A8. That means that if we write to a memory location that has bit 8 set in its address then we will transfer 8 bits with RS high. Conversely if we write to a memory location that has bit 8 reset in its address then we will transfer 8 bits with RS low. A neat trick!

The only precaution that we must take is that we select an address that is not actually in the internal 8Kb SRAM, the address must be up above that range to avoid a clash. We will use address 0×8000 for writing to a register (RS=low) and address 0×8100 for writing data (RS=high).

Selecting a low address line (A8) means that we can free up address lines 10 to 15 for GPIO, saving 6 pins. It doesn’t matter that our selected address locations 0×8000 and 0×8100 are high up in the address range who’s address lines are free’d for GPIO. The ATMega will still correctly control A8. Not only is this design fast, it’s frugal with pins too. Here is the mapping of Arduino pins to their LCD function.

Arduino ATMega Port Function
22 PA0 D0
23 PA1 D1
24 PA2 D2
25 PA3 D3
26 PA4 D4
27 PA5 D5
28 PA6 D6
29 PA7 D7
34 PC3 VIO
35 PC2 VDD
36 PC1 A9 (1)
37 PC0 RS
38 PD7 /RESET
39 PG2 /CS
40 PG1 /RD (2)
41 PG0 /WR

(1) A9 is not used but cannot be released for GPIO.
(2) /RD is not used but cannot be released for GPIO and is pulled up to VIO.

Our design is write-only to the LCD. We cannot read data back from it, and would never want to.

The schematic

Now that we have a design we can create the schematic in the Eagle designer. All the 5V signals from the Arduino that are destined for the LCD are routed through the level converter and will come out the other side at 3.3V.




The schematic (click for larger PDF)

Parts list

Here’s a list of the parts that I used for this build.

Part Value Device Package
C-6300 NOKIA12X2 24R-JANK-GSAN-TF 12X2
C1 4.7u ceramic 0805
C2 1u ceramic 50V 0805
C3,C4,C6,C7,C8,C9 100n ceramic 0603
C5 4.7u ceramic 0805
D1 CD214A-B140LF schottky DO214AC
L1 22uH LQH3NP_J0 1212
MEGA pin header MA12-2 MA12-2
R1 10R resistor R0805
R3 33K resistor R0805
SV2 pin header MA03-2 MA03-2
U1 NCP5007 NCP5007 SOT23-5
U2 74ALVC164245DLSTD 74ALVC164245DLSTD SSOP48DL

The board

After creating the schematic the next stage is to switch to the CAD designer and lay out the board. I placed the components and routed the traces manually. The connector is placed so that the FPC will wrap around the board edge and allow me to mount the LCD on the other side using double-sided sticky tabs.

With the LCD facing up, the interface pins face down and press directly into the sockets on the Arduino. The pin header is placed as close to the edge of the board as possible so that adjacent Arduino pins are not obscured.


The PCB layout and routing

After staring at the layout until I’m square-eyed (sound familiar to anyone?) I’m feeling confident that the header pins are all where they should be, the connector positioning will result in the LCD ending up in the right place and the silk-screening will end up on the correct side of the board.

The build

With the board design complete I uploaded it to ITead Studio for manufacturing. About two weeks later the boards arrived in the mail. As usual all were perfectly manufactured with no visible flaws.


The component side of the board


The LCD side of the board

I construct the boards by tinning the pads and then reflowing the larger components such as the level converter, LCD connector socket and the NCP5007 using a hot-plate. I then reflow the smaller discrete components using my Aoyue hot-air gun.

After the completed PCB is cleaned and dried the design is completed by pressing the LCD connector into its socket and mounting the panel on double-sided sticky pads. That it fits comfortably on to the pads was a bit of a relief because the metal back of the panel must stand clear of the traces and particularly the vias on that side of the board.


The component side of the board

The open holes in between the groups of header pins allow the unused Arduino pins to be accessed for general purpose use. The designers of the Arduino clearly knew what they were doing when they grouped together the external memory pins in the same place.

Unfortunately as you free up unused address pins from A15 downwards they free up from the middle of the external memory pin group. It would have been a nice design touch if they’d free’d from the end of the group instead but you can’t have everything.


The LCD side of the board

It fits perfectly, and it should do because I did carefully measure everything first. However you never really know if you’ve got it right until the hardware arrives and you put it all together.

It is required to connect the 3.3V and GND pins to the Arduino. With the blue jumper connected 5V will be taken directly from the Arduino board and used to power the backlight. With the jumper disconnected I must supply a regulated 5V myself to the 5V (in) pin.

If I want to control the backlight brightness using a PWM signal then I can connect that signal to the EN pin. Alternatively I can jumper the EN pin across and that will tie it high meaning that the backlight will always be at 100% brightness.

The software library

Now that the build is complete we need a software graphics library to exploit the capabilities of our new hardware. Continue reading part two of this two part series of articles where I present an advanced, high performance graphics library with lots of examples to try out and videos to watch.

Temporarily sold out! Some more boards are supposedly on the way to me so if you’re interested then please use my contact page to let me know and I’ll drop you a no-obligation email when they’re ready.

Update: 2 July 2012

I have noted that not all boards obtained on ebay are the same. To my surprise there are slight differences in the behaviour, nothing radical but enough for me to justify a new release of the software driver to deal with this ‘type B’ model. I will refer to the original model as ‘type A’.

The differences found so far are:

  • Type B will not initialise using my 16M driver. They require the 262K driver and then they will look and behave the same as before.
  • The hardware scrolling offset is reversed. Type ‘A’ boards scroll up one line with an offset of 319. These type ‘B’ boards will scroll up one line with an offset of 1.
  • Type ‘B’ boards support the faster 64K driver. Type ‘A’ boards do not. The raw fill rate for the 64K colour mode is 1.32 megapixels/second. It is 1.06 megapixels/second for the 262K and 16M modes on both boards.
  • These type ‘B’ boards seem to have a brighter backlight and could probably run optimally on a 90% PWM duty cycle.

There will be a software update in the next few days (now released, version 1.1.0) to support the difference in hardware scrolling behaviour.

Update: 23 September 2012

Another controller variant has turned up and we’re going to call this one ‘Type C’. It’s showing up in both 6300 and N82 screens. It behaves the same as Type A screens except for the following differences:

  • It won’t go into BGR colour transfer mode, only RGB is supported. Since RGB is supported by all variants I’ve seen RGB will become the default mode in my driver code from 2.2.0 onwards.
  • Page and column addresses are not reversed in landscape mode.

Type C panels are supported from version 2.2.0 of my driver code using a _TypeC suffix to the driver name. e.g. Nokia6300_Landscape_262K_TypeC.

Schematics, CAD and gerbers available

I have open-sourced my PCB design and CAD files so if you’re interested in building your own boards then head on over to my downloads page and grab yourself a copy. The archive includes gerbers for both the SSOP-48 and TSOP-48 level converter footprints.

Some boards for sale

I have some fully constructed boards as well as some bare PCBs for sale while stocks last.

Fully constructed boards

These boards are all fully built, tested and include the TFT panel. The price is £16.90 for UK buyers and £18.49 for worldwide buyers.


Location




Bare PCBs

These PCBs are the same as featured in my article with one important difference. The footprint for the level converter is TSOP48 (DGG suffix) and not the SSOP48 (DL suffix) pictured in the article.

The price including postage is £4 for UK buyers and £5 for worldwide delivery. I can also include one of the 24-pin JST 0.4mm connectors for a small amount extra. Contact me if you want to take me up on that.


Location





stm32plus: ILI9327 TFT driver

$
0
0

The code presented in this article requires a minimum of version 2.0.0 of my stm32plus library.


The TFT panel

The ILI9327 is a driver IC for 432×240 (WQVGA) panels. The panels are typically found in mobile phones; LG went through a phase of producing lots of phones with resolutions close to this as did several other manufacturers.

I got one of these panels on ebay. It came attached to a handy breakout board, though I have seen others that come with just the FPC tail if you’re feeling adventurous. This particular board seems to have the tail soldered directly to it somewhere underneath the panel.


The front of the board


The back of the board

There’s also an ADS7843-compatible touch screen driver and an SD card cage. This is a configuration we often see on development boards sourced from China.

Pinout

The seller included the pinout for the display. It’s a familiar 16-bit 8080 interface that is easily connected to the FSMC of the STM32 microcontroller. There’s no sign of a step-up DC-DC converter on the board so the white LED’s that make up the backlight must be in a parallel configuration.


The pinout

The touch screen is compatible with the ADS7843 controller and that can be hooked up to my stm32plus driver. I’ve had varying luck with the touch screens attached to these cheap boards. The ADS7843 is an A-D converter which means that the board should be carefully designed to minimise noise and it seems that not all of them are that well thought out.

Panel details

This panel is slightly unusual in that its resolution is 400×240 which is less than the full 432×240 supported by the ILI9327. How does that manifest itself? Well, the co-ordinates from 0-31 are not visible. That is, you can write to them but nothing will appear on the screen. Therefore we have to make allowances for that in the stm32plus driver and you will see how in the demo code.

My demo sequence exercises some of the common functions used in graphics operations such as rectangle, line and ellipse drawing as well as text rendering and hardware scrolling when the panel supports it (the ILI9327 does support hardware scrolling in portrait and landscape modes).

The datasheet for the ILI9327 is readily available on the internet. It’s written to the high standard that I’ve come to expect from ILITek making the job of writing a driver really straightforward.

The panel supports 16 bit colour (5-6-5 format) and 18-bit colour (6-6-6 format). The STM32F103 can easily drive either colour format at a fast pace. I’ve come to realise that the observable difference between 64K and 262K colours is pretty low and generally I’ll use 64K for the extra speed that it offers.




Data transfer protocol for the 16-bit interface (click for larger)

Using a 16-bit interface we can transfer a 64K colour pixel in one operation. A 262K colour pixel costs an extra transfer per-pixel and therefore halves the speed just for those extra 2 bits.

STM32 wiring

Here’s the wiring mapping table that you’re going to need if you intend to hook one of these up to an STM32 MCU. You’re free to change the address line that you use for RS, the bank selector that you use for /CS and the GPIO pin for RESET.

In my example code for this panel I’m using SRAM bank 1 and A16 for RS (register select). This configuration is compatible with the 100 and 144 pin STM32F103 devices.

LCD signal STM32 port port function
D0 PD14 FSMC_D0
D1 PD15 FSMC_D1
D2 PD0 FSMC_D2
D3 PD1 FSMC_D3
D4 PE7 FSMC_D4
D5 PE8 FSMC_D5
D6 PE9 FSMC_D6
D7 PE10 FSMC_D7
D8 PE11 FSMC_D8
D9 PE12 FSMC_D9
D10 PE13 FSMC_D10
D11 PE14 FSMC_D11
D12 PE15 FSMC_D12
D13 PD8 FSMC_D13
D14 PD9 FSMC_D14
D15 PD10 FSMC_D15
/WR PD5 FSMC_nWE
/RD PD4 FSMC_nOE
/CS PD7 FSMC_nE1
/RESET PE1 GPIO
RS (D/CX) PD11 FSMC_A16


stm32plus driver

stm32plus 2.0.0 comes with an updated ILI9327 demo application. Here’s an extract from the source code that shows how to set it up.

#include "config/stm32plus.h"
#include "config/display/tft.h"

using namespace stm32plus;
using namespace stm32plus::display;

class ILI9327Test {

	protected:
		typedef Fsmc16BitAccessMode<FsmcBank1NorSram1> LcdAccessMode;
		typedef ILI9327_400x240_Portrait_262K<LcdAccessMode> LcdPanel;

		LcdAccessMode *_accessMode;
		LcdPanel *_gl;
		Font *_font;

	public:
		void run() {

			// reset is on PE1 and RS (D/CX) is on PD11

			GpioE<DefaultDigitalOutputFeature<1> > pe;
			GpioD<DefaultAlternateFunctionFeature<GPIO_AF_FSMC,11> > pd;

			// set up the FSMC with RS=A16 (PD11)

#if defined(STM32PLUS_F1)
			Fsmc8080LcdTiming fsmcTiming(0,2);
#elif defined(STM32PLUS_F4)
			Fsmc8080LcdTiming fsmcTiming(4,10);
#endif
			_accessMode=new LcdAccessMode(fsmcTiming,16,pe[1]);

			// declare a panel

			_gl=new LcdPanel(*_accessMode);

			// apply gamma settings

			ILI9327Gamma gamma(0,0x10,0,0,3,0,0,1,7,5,5,0x25,0,0,0);
			_gl->applyGamma(gamma);

			// clear to black while the lights are out

			_gl->setBackground(0);
			_gl->clearScreen();

			// create the backlight on timer4, channel2 (PD13)

			DefaultBacklight backlight;

			// fade up to 100% in 4ms steps

			backlight.fadeTo(100,4);

			// create a font

			_font=new Font_VOLTER__28GOLDFISH_299;
			*_gl << *_font;

Let’s look at the code in a little more detail.

Includes and namespaces

#include "config/stm32plus.h"
#include "config/display/tft.h"

using namespace stm32plus;
using namespace stm32plus::display;

Firstly we need to include the headers that define the classes we’re going to use. Secondly, since all of stm32plus lives either in the stm32plus namespace or a sub-namespace (in this case stm32plus::display) we will import them into the global namespace to make the declarations of the objects less clumsy looking.

FSMC access mode and reset

typedef Fsmc16BitAccessMode<FsmcBank1NorSram1> LcdAccessMode;
typedef ILI9327_400x240_Portrait_262K<LcdAccessMode> LcdPanel;

LcdAccessMode *_accessMode;
LcdPanel *_gl;
Font *_font;

  public:
    void run() {

      // reset is on PE1 and RS (D/CX) is on PD11

      GpioE<DefaultDigitalOutputFeature<1> > pe;
      GpioD<DefaultAlternateFunctionFeature<GPIO_AF_FSMC,11> > pd;

      // set up the FSMC with RS=A16 (PD11)

#if defined(STM32PLUS_F1)
      Fsmc8080LcdTiming fsmcTiming(0,2);
#elif defined(STM32PLUS_F4)
      Fsmc8080LcdTiming fsmcTiming(4,10);
#endif
      _accessMode=new LcdAccessMode(fsmcTiming,16,pe[1]);

We use an ‘access-mode’ class to control how we communicate with the panel. Here we initialise it to use the FSMC in 16-bit mode with A16 as the RS line (PD11). We will also be wiring up the panel’s RESET line to PE1.

We declare an Fsmc8080Lcdtiming object that takes care of the timing details. The two parameters are the address setup and data setup times in HCLK cycles. At full speed the STM32F1 has a 36MHz FSMC bus and the STM32F4 has a 60MHz bus. Therefore the timings may be different for each MCU if the bus is faster than the panel.

To determine the value of these parameters you need your panel’s data sheet and the equations in the ST Microelectronics application note AN2790 (google it). Alternatively you can just start with some large-ish numbers and decrease until the timings get too tight and the panel doesn’t respond!

Declare a panel

// declare a panel

_gl=new LcdPanel(*_accessMode);

Now that we have an access mode we can declare the panel. The constructor will ask the access mode class to reset the panel and then it will do the initialisation sequence.

Our example initialises it in portrait mode, 18 bit colour (262K). If you take a look at TftInterfaces.h you will see that following modes are available:

ILI9327_400x240_Portrait_64K
ILI9327_400x240_Landscape_64K
ILI9327_400x240_Portrait_262K
ILI9327_400x240_Landscape_262K

The predefined drivers are just C++ typedefs that bring together the necessary combination of template instantiations to create a coherent graphics library. If you have an ILI9327 panel that is not 400×240 then you can support it by supplying your own ILI9327400x240PanelTraits class. See the code for details.

Gamma correction

      // apply gamma settings

      ILI9327Gamma gamma(0,0x10,0,0,3,0,0,1,7,5,5,0x25,0,0,0);
      _gl->applyGamma(gamma);

Gamma correction involves the manual adjustment of the panel’s response to different shades of grey to compensate for differences in the manufacturing process. Every panel will be slightly different.

Setting up the tweaks to the gamma curve are optional, a reasonable display will be obtained by using the default linear curve but if true-to-life colours are a requirement then a custom gamma curve is essential.

stm32plus includes an interactive gamma adjustment application, and that will be introduced in a future blog post. For now, you can just use the default settings and maybe come back to it later.

Backlight

// create the backlight on timer4, channel2 (PD13)

DefaultBacklight backlight;

// fade up to 100% in 4ms steps

backlight.fadeTo(100,4);

stm32plus comes with a PWM backlight controller template class, and a subclass of that called DefaultBacklight that assumes you can connect the backlight regulator to PD13.

This PWM output should be used to drive the base of suitable transistor, or the ‘enable’ pin of a constant current backlight driver. It should not be used to directly power the backlight because it’s likely to draw too much current from the MCU pin.

The fadeTo method allows smooth transitioning of the backlight between levels to give a pleasing user experience.

Font selection

// create a font

_font=new Font_VOLTER__28GOLDFISH_299;
*_gl << *_font;

stm32plus (as of 1.2.0) now supports the very convenient stream ‘<<' operator to output text and numbers to the display. To do this the operator needs to know which font to use, and we do that by using the << operator with the font object as the operand.

Get the source code

Updated source code is in stm32plus 2.0.0 (or later) available from my downloads page.

Watch the videos

I’ve uploaded a pair of short videos that shows the demo code in action. Firstly, here it is on the STM32F103.

Click here to view the video on YouTube.


Secondly, here it is on the STM32F4 Discovery board.

Click here to view the video on YouTube.

Interfacing the Nokia 6300 QVGA TFT to the standard Arduino

$
0
0

In two of my previous articles (here and here) I explained how we could connect the 8-bit 8080 interface presented by the TFT panel to the XMEM interface of the Arduino Mega to achieve a high performance full-colour graphical interface.

I went on to present a high-performance open-source template library for performing common graphical operations on the TFT panel. The result was a complete, ready-to-use graphical subsystem for users of the Arduino Mega.

In this follow-up article I will show you how to connect the same Nokia 6300 QVGA TFT panel to the standard Arduino Duemilanove or Uno. These limited devices present several challenges that we will need to overcome before we can put hand on heart and honestly say that we have a usable system.

The developer-in-a-hurry guide

Firstly, download at least version 2.0.0 of my library from my downloads page.

You can use nearly all my example code supplied with the library and presented in the previous article. All you need to do is append _Gpio to the name of the LCD driver and recompile:

//typedef Nokia6300_Landscape_262K LcdAccess;
typedef Nokia6300_Landscape_262K_Gpio LcdAccess;

Customising ports and pins

The default setup is for port D (which is pins 0..7) and pins 8 and 9 to be used for WR and RS respectively. If you want to change this then you need to edit GpioAccessMode.h which is located in the library installation directory. This is the section that you can customise:

				/*
				 * Here's where you choose the pins for the GPIO interface.
				 * Note that on the ATMega328P the data port has to be PORTD
				 */

				DATA_PORT = IO_PORTD,

				RS_PORT   = IO_PORTB,
				RS_PIN    = 1,
				WR_PORT   = IO_PORTB,
				WR_PIN    = 0

The port and pin definitions that you can select from are shown just above in the header file. The pin numbers (0..7) are indexes into the associated port. I have included comments that illustrate how the pin indexes map to Arduino pin numbers. For example:

				IO_PORTB = 0x05,			// 8,9,10,11,12,13,x,x

All the features are included, even the compressed LZG bitmaps. However, do recall that LZG bitmap support requires 2K of SRAM while working and that rules out its use on the Duemilanove and Uno devices.

Now, let’s continue with write-up that explains how we got here.

The ATMega328P

At the heart of the Arduino Uno (and Duemilanove, but I’m going to save myself some keystrokes by referring to both as the Uno from here on) is the ATMega328P MCU.


ATMega328P pinout. Arduino digital pins in red, analogue pins blue.

Not exactly brimming with GPIO pins is it? Worse still, they’re all heavily overloaded with on-chip peripherals so we’re really going to have to work to keep the pin usage down.

It runs at the same 16Mhz clock speed as the Mega1280 and Mega2560 but is significantly limited in terms of memory (32Kb flash/2Kb SRAM) and GPIO pins. We will need to overcome both of these limitations if we’re going to come with something that’s actually usable in your projects.

TFT to Arduino pin mapping

Here’s how we’re going to map the pins of the TFT to the pins on the Uno.
td>

Function Uno pin ATMega Port Port pin
D0 0 D 0
D1 1 D 1
D2 2 D 2
D3 3 D 3
D4 4 D 4
D5 5 D 5
D6 6 D 6
D7 7 D 7
WR 8 B 0
RS 9 B 1
CS GND n/a n/a
VIO 5V n/a n/a
VDD 5V n/a n/a
RESET RESET n/a n/a

The use of pins 0-7 (PORTD) is fixed and cannot be changed. The reason for this is that we can only get usable performance by writing 8 bits to an entire port in one operation and PORTD (Uno pins 0..7) is the only port exposed in its entirety on the Uno.

The two power inputs, VDD and VIO, can be connected directly to the Ardunio’s 5V output. On my board VDD and VIO go through the level converter so they actually end up at the LCD as 3.3V which is what I consider to be the maximum safe level for the panel. On the Mega board I use GPIO pins for these power inputs so that I can control the order in which they come up. From experience I now know that this is not necessary and it’s safe to apply power in any order.

Our first optimisation saves us a GPIO pin. We chain the panel’s RESET input to the RESET output on the Uno, taking advantage of its compatible active-low operation.

The MC2PA8201 datasheet that we’re working from shows clearly that CS can be low (active) across multiple bus transactions and so we opt to save a pin and gain a little in performance by grounding it. A different design could use CS to multiplex the pins that we’ve taken for the LCD for other purposes whilst the LCD is not being written to.


Connected to my Duemilanove clone

The two remaining control pins WR and RS could be moved to any other unused Uno pins. In this article we arbitrarily choose to use Arduino pins 8 and 9 which happen to be located on PORTB, pins 0 and 1.

Optimising the driver

I wrote a simple test program to help me to evaluate the results of my optimisations. It tests the raw pixel fill rate and measures a speed at which we can output text characters to the display. We’re using the 262K colour mode that requires three 8-bit transfers per pixel.

Here it is, all of it:

#include "Nokia6300.h"
#include "Font_volter_goldfish_9.h"
#include "ColourNames.h"

using namespace lcd;

/*
 * The orientation and colour depth that we will use
 */

typedef Nokia6300_Landscape_262K_Gpio LcdAccess;
LcdAccess *tft;
Font *font;

/*
 * Fill the screen and show pixels/sec
 */

void fillTest() {

	int32_t before,elapsed,persec;

	tft->setBackground(ColourNames::RED);

	before=millis();
	tft->clearScreen();
	elapsed=millis()-before;
	persec=76800000L/elapsed;

	tft->setBackground(ColourNames::BLACK);
	tft->setForeground(ColourNames::WHITE);

	*tft << Point(0,0)
			 << persec
			 << " pixels per second";

	delay(5000);
}


/*
 * Show random text and measure chars/sec
 */

void textTest() {

  int i;
  const char *str="The quick brown fox";
  Size size;
  Point p;
  LcdAccess::tCOLOUR randomColour;
  int32_t before,elapsed,persec;

  tft->setBackground(ColourNames::BLACK);
  tft->clearScreen();

  size=tft->measureString(*font,str);

  before=millis();
  for(i=0;i<500;i++) {

  	p.X=rand() % (tft->getXmax()-size.Width);
    p.Y=rand() % (tft->getYmax()-size.Height);

    randomColour=(((uint32_t)rand() << 16) | rand()) & 0xffffff;

    tft->setForeground(randomColour);
    *tft << p << str;
  }
  elapsed=millis()-before;
  persec=9500000L/elapsed;

  tft->setBackground(ColourNames::BLACK);
  tft->setForeground(ColourNames::WHITE);
  tft->clearScreen();

  *tft << Point(0,0)
  		 << persec
  		 << " characters per second";

  delay(5000);
}


void setup() {

	// create and initialise the panel and font

	tft=new LcdAccess;
	font=new Font_VOLTER__28GOLDFISH_299;

	// clear to black

	tft->setBackground(ColourNames::BLACK);
	tft->clearScreen();

	// select the font used througout

	*tft << *font;

	for(;;) {
		fillTest();
		textTest();
	}
}


void loop() {}

Basic implementation

Our basic implementation will focus on writing data and commands to the panel. I know this is going to perform poorly, but we need a straw man up and running so we can start the optimisation.


	inline void GpioAccessMode::writeData(uint8_t value) {

		digitalWrite(WR,LOW);
		digitalWrite(RS,HIGH);

		PORTD=value;

		digitalWrite(WR,HIGH);
	}

So there it is, the basic implementation of the 8080 protocol albeit with no respect for the timing requirements of the target panel because we don’t know for sure what they are! Let’s hope this works, your mileage may vary.

We initialise the control lines, write the data to the port and then pull WR high (data is transferred on the rising edge of WR). CS has been grounded so there’s no need to control it. A corresponding function for writing to a register exists but you don’t need to see that because the only difference is that RS is set LOW for the transaction.

As expected, the performance is poor, spectacularly poor in fact. The fill rate and character output rate are just 1.0% and 3.2% respectively of the figures achieved by the XMEM interface. Let’s examine why by taking a look at the assembly language emitted by the compiler.

        inline void GpioAccessMode::writeData(uint8_t value) {
     616:       cf 93           push    r28
     618:       c8 2f           mov     r28, r24

                digitalWrite(WR,LOW);
     61a:       60 e0           ldi     r22, 0x00       ; 0
     61c:       88 e0           ldi     r24, 0x08       ; 8
     61e:       0e 94 ba 07     call    0xf74   ; 0xf74 <digitalWrite>
                digitalWrite(RS,HIGH);
     622:       61 e0           ldi     r22, 0x01       ; 1
     624:       89 e0           ldi     r24, 0x09       ; 9
     626:       0e 94 ba 07     call    0xf74   ; 0xf74 <digitalWrite>

                PORTD=value;
     62a:       cb b9           out     0x0b, r28       ; 11

                digitalWrite(WR,HIGH);
     62c:       61 e0           ldi     r22, 0x01       ; 1
     62e:       88 e0           ldi     r24, 0x08       ; 8
     630:       0e 94 ba 07     call    0xf74   ; 0xf74 <digitalWrite>

We can see that the compiler makes the expected calls to digitalWrite() and, on the highlighted line, uses the OUT instruction to write data to the port. This part is good. OUT is the fastest possible way to write all bits to a port.

So we can confirm what we suspected all along. digitalWrite() performs very poorly, but direct port access generates optimal assembly instructions. Our optimisations must not interfere with that.

First round of optimisations

Now, there are faster versions of the digitalWrite() function around on the internet and we could use that but it would not be optimal. Let’s go straight for the prize, get our hands dirty and write some embedded assembly language.


	inline void GpioAccessMode::writeData(uint8_t value) {

		// RS=high, WR = low
		asm volatile( "sbi %0, %1" :: "I" (RS_PORT), "I" (RS_PIN) );
		asm volatile( "cbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) );

		// write the data
		asm volatile( "out %0, %1" :: "I" (DATA_PORT), "r" (value) );

		// WR = high
		asm volatile( "sbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) );
	}

Here we’ve completely ditched the slow ‘C’ calls and hand-crafted the assembly language necessary to set and reset bits in the port registers. I have chosen to use individual sbi and cbi instructions so that the user has flexibility in choosing which ports and pins to use. Each one of these instructions costs 2 clock cycles.

I have also abstracted out the hardcoded port and pin numbers into enumerations in a header file so that they can be easily changed to suit the requirements of the project.

The results of this optimisation are dramatic. We are now up to 22.5% and 40.9% of the pixel fill-rate and character output rate of the XMEM interface, respectively.

Is this enough? No of course not, we can now widen the scope of our optimisation to include the calls to these core functions. Is there even more scope for optimisation?

The second round of optimisation

In this round we will make a further optimisation by noting that in use we generally do hundreds or even thousands of data writes for every one instruction write. Using this knowledge we will maintain the RS line in a HIGH state (date write) and only pull it low for an instruction write, after which we will put it back to HIGH. This allows us to ignore it completely for data writes (the common case).


	inline void GpioAccessMode::writeCommand(uint8_t cmd) {

		// RS,WR = low
		asm volatile( "cbi %0, %1" :: "I" (RS_PORT), "I" (RS_PIN) );
		asm volatile( "cbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) );

		// write the command
		asm volatile( "out %0, %1" :: "I" (DATA_PORT), "r" (cmd) );

		// WR,RS = high
		asm volatile( "sbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) );
		asm volatile( "sbi %0, %1" :: "I" (RS_PORT), "I" (RS_PIN) );
	}

	inline void GpioAccessMode::writeData(uint8_t value) {

		// WR = low
		asm volatile( "cbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) );

		// write the data
		asm volatile( "out %0, %1" :: "I" (DATA_PORT), "r" (value) );

		// WR = high
		asm volatile( "sbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) );
	}

The result of this is a useful uptick in speed. We are now up to 30.6% and 48.2% of the pixel and character fill rates of the XMEM interface, respectively.

Final optimisations

In this final round of optimisation we consider how the Arduino IDE will compile our code and tune accordingly. At the time of writing and for reasons I cannot fathom, the authors have decided that size optimisations (-Os) are all you’re ever going to want.

The impact of this arbitrary decision is that the compiler will refuse any optimisation that would increase the size of the output image. That means only the most basic of inlining will ever be considered. Aggressive inlining is out of the question and will never happen under a -Os compilation flag.

To work around this we will replace the writeData() and writeCommand() functions that are at the core of our driver with some rather fugly pre-processor macros.

#define GPIO_WRITE_REGISTER_ADDRESS(cmd) \
	asm volatile( "cbi %0, %1" :: "I" (RS_PORT), "I" (RS_PIN) ); \
	asm volatile( "cbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) ); \
	asm volatile( "out %0, %1" :: "I" (DATA_PORT), "r" (cmd) );  \
	asm volatile( "sbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) ); \
	asm volatile( "sbi %0, %1" :: "I" (RS_PORT), "I" (RS_PIN) );

#define GPIO_WRITE_DATA_ADDRESS(value)	\
	asm volatile( "cbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) );  \
	asm volatile( "out %0, %1" :: "I" (DATA_PORT), "r" (value) ); \
	asm volatile( "sbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) );

#define GPIO_WRITE_COMMAND(cmd,parameter)  \
	GPIO_WRITE_REGISTER_ADDRESS(cmd);  \
	GPIO_WRITE_DATA_ADDRESS(parameter);

The impact of this is nothing short of spectacular. The overall speed more than doubles and we are now running at 72.2% and 67.8% of the pixel and character fill rates of the XMEM interface, respectively. We would not have to do this if the IDE would allow us to choose -O2 or -O3: the compiler would do it for us.

Performance summary

Final thoughts

If you want to really squeeze the last drop of performance from the driver then there is further scope for some assembly optimisation if you are prepared to accept the (slight) limitation that the WR and RS pins must be on the same IO port.

In the writeCommand() function we begin and end the sequence with a pair of cbi and sbi instructions, like this:

asm volatile( "cbi %0, %1" :: "I" (RS_PORT), "I" (RS_PIN) );
asm volatile( "cbi %0, %1" :: "I" (WR_PORT), "I" (WR_PIN) );

These instructions cost us two clock cycles each, giving an overhead of eight cycles for each call to the writeCommand() function.

If we assume that RS and WR are on the same port then it is faster to read the port state with the in instruction, set or reset both bits simultaneously with an or or an and instruction and then write back the port state with an out instruction.

Each of those instructions costs just one CPU cycle meaning that we could reduce the overall cost to six cycles, down from eight in the generic implementation.

I don’t want to impose the single-port limit on everyone so I’ve left my implementation as-is. This final optimisation suggestion is left as an exercise for the reader.

Watch the video

I’ve hacked together a short video that visually illustrates the difference in performance between the XMEM implementation on the Mega and the optimised inline ASM GPIO implementation on my Seeeduino (Duemilanove clone) board. I think you’ll agree that the GPIO performance is very good.

Click here to view the video on YouTube.


Some boards for sale

I have some fully constructed boards as well as some bare PCBs for sale while stocks last.

Fully constructed boards

These boards are all fully built, tested and include the TFT panel. The price is £16.90 for UK buyers and £18.49 for worldwide buyers.


Location




Bare PCBs

These PCBs are the same as featured in my article with one important difference. The footprint for the level converter is TSOP48 (DGG suffix) and not the SSOP48 (DL suffix) pictured in the article.

The price including postage is £4 for UK buyers and £5 for worldwide delivery. I can also include one of the 24-pin JST 0.4mm connectors for a small amount extra. Contact me if you want to take me up on that.


Location




Nokia N82 2.4 inch QVGA TFT on the Arduino

$
0
0

It’s been a few months now since I released the original two articles that detailed the design, build and optimised software library for the 2.0″ Nokia 6300 QVGA TFT connected to the Arduino Mega XMEM interface. Judging by the responses I’ve had, there’s a lot of you out there that are interested in connecting these mobile phone TFTs to your Arduinos.

Today we move on to reverse-engineer another Nokia QVGA TFT. This time we’re going to tackle the Nokia N82. Read on to find out how I got on.

The N82 TFT

The N82 TFT is a 2.4″ panel that’s also found in the N77, N78, N79, E66, E52, E75 and 6210S. It’s certainly clear that back in the not-so-distant past when Nokia was the clear market leader in mobile phones they certainly knew how, as a company, to take a successful design and keep re-releasing it over and over again with small tweaks to keep the income rolling in. Replacement N82 panels are available at the time of writing for as low as £3 on ebay.


Two TFTs. The N82 next to the 6300.

You can get an idea of the physical size difference between the 2.4″ and 2.0″ panels from the above photograph. The resolution is identical, the pixels are simply larger on the N82 panel. Pixel densities are around 200ppi for the 6300 and 167ppi for the N82. Having a higher pixel density means that your graphics appear smoother. Having a lower pixel density makes your text larger, and perhaps more readable.

The schematic and the connector

It must be our lucky day because the pinout to the N82 is identical to the 6300 and it uses the same 24-pin board-to-board connector too! The 0.4mm pitch connector is made by JST and the part number is 24R-JANK-GSAN-TF. Here’s the datasheet.

It is easy to obtain them either direct from JST’s online shop or in small quantities from online sources. Here are some direct links to the ones that I know of:

gsmserver.com
cellnetos.com
stellatech.com

The pinout for the connector is shown here:

Here’s a photograph of the connector plug. If you look closely you can see where the ground pins connect directly into the ‘ground pour’ inside the ribbon cable. This helps to identify where pin 1 is located when doing the reverse engineering.


The connector on the LCD FPC cable

The backlight

The backlight shares the same design as the 6300 in that it consists of four white LEDs in series. Therefore we will use the same NCP5007 constant current LED driver from OnSemi that we’ve been using in all our Nokia TFT designs so far.


The NCP5007 in SOT23-5 package

The NCP5007 will be configured to supply a constant 20mA through the backlight circuit and we will use a PWM signal on the ENABLE pin to vary the brightness. The NCP5007 really is a wonderful little device – truly one of those things that ‘just works’ and I’ll use it wherever I need to drive up to 5 LEDs.

The development board schematic

We’ll gloss over the explanation of how we drive the TFT controller using the Arduino Mega XMEM interface because that’s all been explained in this article and there are no changes to the interface in this design.




The Eagle schematic, click to download a PDF.

Just as before, we choose to use the 74ALVC164245 16-channel level converter from NXP to handle the conversion of the 5V signals from the Arduino down to a safe level of 3.3V where they won’t damage the panel.




The Eagle PCB design, click for larger.

In this design I’m using the TSOP-48 (0.5mm) pitch version of the level converter rather than the SSOP-48 (0.635mm) pitch that I used in the 6300 design. The reason for the change is that the TSOP-48 package is more readily available from the suppliers that I use.

Another slight change, again due to parts availability, is the inductor. I’ve switched to a slightly different model that has a larger footprint but is otherwise functionally identical to the one I was using before.

Bill of Materials

Here’s a full list of parts used in this development board.

Part Value Device Package
C-N82 NOKIA12X2 24R-JANK-GSAN-TF 12X2
C1,C5 4.7µF ceramic 0805
C2 1µF ceramic 50V 0805
C3,C4,C6,C7,C8,C9 100nF ceramic 0603
D1 Diodes Inc. BO530W-7 schottky SOD-123
L1 Sumida 22µH CDRH5D28NP-220NC 6x6mm
MEGA pin header MA12-2 MA12-2
R1 10Ω 1% resistor 0805
R3 33KΩ resistor 0805
SV2 pin header MA03-2 MA03-2
U1 NCP5007 NCP5007 SOT23-5
U2 74ALVC164245DGG level converter TSOP48


The development board

I generated the Gerber’s from Eagle and sent them off to ITead Studio to be printed using their very good value prototype PCB service. A few weeks later they arrived and all were perfect, just as I’ve come to expect.


The front of the PCB. The pads are not gold, the tungsten lighting used in the photograph just makes them look like they are.


The back of the PCB

Building the board is a familiar process for me by now. In case you’re interested, this is the procedure that I follow.

  1. Flux the pads then tin them with an iron so they all have little solder bumps on them. For the level converter and the connector this means loading up the tip of the iron with solder and lightly dragging it over the pads.
  2. Use a very small amount of paste flux as a glue to hold down the level converter, connector and inductor in place on the board. Overdoing the flux can cause bubbling during the reflow which will cause the component to move out of position (very bad).
  3. Heat the board with the three parts on a hot-plate until the solder reflows and the parts sit down into position.
  4. Inspect the joints under a microscope and use a fine-tip iron to touch each one that shows any movement when prodded with a pin. There will be at least one…
  5. Use tweezers and a hot-air gun to reflow the other components into place.
  6. Clean the board with soapy water and a toothbrush then leave for at least 24 hours in the airing cupboard to dry out.
  7. Solder the 2.54mm headers and test.

So it’s not a short process but the rewards are worth the effort. Here’s the fully built development board.


The board, fully built and awaiting the LCD

And here’s a shot with the LCD fitted. The 2.4″ display is a little oversized for the PCB but fits firmly and saves the additional cost of the larger PCB just to mount the screen.


The board with the LCD attached.

Driver source code

My XMEM driver code has been updated to include support for the N82. You will need to download at least version 2.1.0 of the driver to get that support.

Because the panel is driven identically to the Nokia 6300, all the examples will work as-is with no changes. However, for completeness I have included “N82″ driver names just in case I need to create specific customisations for this panel in the future.


#include "NokiaN82.h"

using namespace lcd;

typedef NokiaN82_Portrait_262K TftPanel;

The example snippet shows the driver name (NokiaN82_Portrait_262K ) and the header file (NokiaN82.h). Similar driver names for the Landscape orientation and 16M and 64K display modes (if supported by the panel) are included.

Another example

I created a full example that exercises the compressed graphics capability of the driver in a simulation of the output from a weather station on the Arduino. The source code is included in version 2.1.0 and above of the software driver.

This demo packs in 100Kb of compressed graphics in to the Atmega1280 and the controller code takes up a further 10Kb or so. The graphics are decompressed on-the-fly from flash by my LZG decoder routine included with the driver.

Here’s a video of the demo in action. Watch it here or click to watch in HD at YouTube.

Click here to view the video on YouTube.



Copyright Notices

The graphical weather images were downloaded from the Wikimedia Commons library. Please respect the rights of the original authors:

Snow image
Partly cloudy image
Rain image
Sun image

The numeric ‘temperature readout’ was created by typing the numbers 0..9 and the point and degree symbols into Wordpad in a nice font at 48 point size. Then I took a screenshot and used a paint program to crop out each character into its own bitmap and saved each one as a separate PNG.

I then ran the bm2rgbi utility included with the graphics library to convert the PNGs to LZG format and imported those binaries directly into the compiled flash image. You can see how that’s done by reading the example source code.

JPEG support now available

I’m pleased to announce driver support for displaying JPEG images. JPEG images may be compiled into flash and displayed from there or they may be streamed over an Arduino serial connection.

The driver documentation has been updated to include sample code and a demo video that shows you how to do it.

Here’s a link to the video that shows the JPEG decoder operating on images stored in flash. If you’d like to see it operating on JPEG images received over the serial link then please refer to the main driver documentation. The JPEG decoder is completely compatible with the Nokia 6300 as well as the N82.

Click here to view the video on YouTube.



Can I use it with the standard Arduino?

Yes you can, see this article for a write-up, including performance analysis and optimisations. The article is focused on the 6300 but the N82 works in exactly the same way.

Update: 23 September 2012

Another controller variant has turned up and we’re going to call this one ‘Type C’. It’s showing up in both 6300 and N82 screens. It behaves the same as Type A screens except for the following differences:

  • It won’t go into BGR colour transfer mode, only RGB is supported. Since RGB is supported by all variants I’ve seen RGB will become the default mode in my driver code from 2.2.0 onwards.
  • Page and column addresses are not reversed in landscape mode.

Type C panels are supported from version 2.2.0 of my driver code using a _TypeC suffix to the driver name. e.g. Nokia6300_Landscape_262K_TypeC.

Some boards for sale

I’ve constructed the remaining PCBs that I have and am selling them on a first come first served basis. The price including delivery is £16.90 for UK and £18.49 for anywhere else in the world. They come with the LCD fitted and are ready to plug in and play.

Temporarily out of stock! I’m building a new batch and expect to be completed on December 15th, 2012. Please check back here later.


Location




An Ethernet PHY for the STM32F107

$
0
0

A what? If you’ve never crossed paths with ethernet technologies before then you may not know what an ethernet PHY is. Well, it’s the physical transceiver that converts a well-known data-bus protocol implemented by your MCU into the physical signals that go down the wire.

MCU ethernet support

MCUs such as the STM32F107 come with a degree of ethernet support built-in. The MCU provides a 10/100Mbs MAC and can talk the standard Media Independent Interface (MII) protocol to the outside world.

This is where the PHY comes in. The PHY receives the 4-bit wide MII protocol and synthesises the differential signals necessary to drive the ethernet RJ45 socket. It also does the reverse, decoding incoming signals from the RJ45 connector and talking MII back to the MCU.


The MII protocol connections

This separation of concerns reduces overall cost by limiting the complexity of the components outside the MCU.

Other options

Only a minority of MCUs have ethernet support. The STM32F107 is one, the STM32F2 and STM32F4 series are others. Ethernet tends to feature in special series MCUs or newer and more expensive devices.


There’s an STM32F107 modestly hiding behind the faded print

If you don’t happen to have one of those then all is not lost. Companies such as Wiznet with their W5100 device have taken the logical step of integrating the MAC, PHY and the entire TCP stack into one IC. If you don’t have ethernet support on your MCU then this is what you could use, but it will cost a little more due to the increased feature set and the interface is likely to be serial which may limit performance.

Another option is to buy the ST official PHY add-on board for their F107 development board but it’s pricey, and it’s much more fun to build your own.

This article will focus on building a PHY targeted at the STM32F107 but compatible with any MCU that can talk the MII protocol.

The PHY IC

There’s a few of them to choose from, and they’re all much of a muchness, I mean how exciting can you make an ethernet 10/100 transceiver? ‘Not very’ is the answer to that question. They all implement the basic protocol and then add various extras such as indicator LEDs, error detection and various types of auto-detection of your configuration.

The one I selected is the Micrel KSZ8051MLL, available at the time of writing from Farnell in single units for 86p. Yes, 86 pence is all you need to fork out for the core IC in a PHY.


The KSZ8051MLL in LQFP48 7x7mm package, 0.5mm pitch.

Features of the KSZ8051MLL

Here’s a functional diagram of the KSZ8051MLL taken from the datasheet that shows how it fits into the ethernet physical layer.


Functional diagram.

As well as providing the core PHY support the KSZ8051MLL has some useful extra features:

  • Interrupt support so we don’t have to continually poll the device to find out when something has happened.
  • LED outputs for link, speed and activity status.
  • Auto detection of straight-through and crossover cables so you can use either type. (I like this one!).
  • Power saving modes – ethernet is quite power-hungry when it’s active.
  • Diagnostics for detecting where a cable fault lies, and diagnostics for detecting pin-to-board faults.
  • Single 3.3V (or 2.5V or 1.8V) power input – the 1.2V regulator is built in.
  • On-chip termination resistors for the differential pairs.

Bootstrapping

Upon power-up or hard reset the PHY has to configure its internal registers with some default values. Rather than hardcode some defaults, the device uses the rather ingenious method of momentarily sampling the levels (high or low) of a selection of its pins and using those levels to configure the registers.

That means that a low-cost board could configure the PHY by setting appropriate pull-ups and pull-downs on the pins and thereby not have to write microcontroller code to configure the device through software. Code not written is flash memory not used is money saved.

Well, we have a micrcontroller and rather a good one at that, so we’ll be configuring the registers to known values through software after a hard reset.

A development board schematic

After reading the datasheet, and the application notes on Micrel’s website, I decided that a development board should not be too hard to design. The challenges would lie in creating a reliable, interference free design that could cope with the high-frequency ethernet signals.

I took Micrel’s reference development board schematic as a starting point and modified it to remove features that I wasn’t going to need, and to adapt it so that it would work with the external components that I was going to select.




The schematic. It’s too large to read on this page so click the image to open it in a PDF

Basic design features

The design follows the recommendations made in the Micrel application note for the PHY with some modifications made by myself to tailor it for use by a powerful MCU such as the STM32F107.

There are no jumpers for setting the hardware strapping options. We will accept the defaults provided by the internal pull-ups and pull-downs on the PHY and make any adjustments via register access after reset. In practice the only limitation of this approach is that the PHY ID number will be fixed at ’1′ because there is no writeable register available to reconfigure this after reset.

All the PHY lines except the differential pairs are exposed in 2.54mm pin headers arranged around the board.

Decoupling of the power lines follows the advice in the application note and the datasheet, and we add 47µF of bulk capacitance to the board. Ferrite beads are used to suppress interference.
33Ω resistors are used on the MII signal lines to limit reflections. This design feature is also present in the Micrel reference design. The termination resistors that are commonly found between the differential TX/RX pairs and the PHY are not required in this design because they are integrated into the PHY itself.

The RJ45 connector

Ethernet design guidelines state that there must be a 1:1 isolation transformer between the cable and the PHY. These are known as the magnetics.


The TE Mag45 connector

Ethernet transformers are surprisingly expensive in small quantities so in my design I’ve chosen the TE 6605424-1 connector that integrates the magnetics and an ESD protection circuit into the connector.


The TE Mag45 schematic

RJ45 connectors that integrate the magnetics are commonly known as magjacks and are better value than buying the transformer and connector separately. Even so, this is the most expensive part in the design.

The clock

10/100 ethernet systems require a 25Mhz clock that runs at full speed for 100Mb/s operation and is internally divided down to 2.5Mhz when the link is set to 10Mb/s.


The 25Mhz crystal

The design allows for the 25Mhz clock to be sourced from an onboard crystal or from an external clock. In the former case R8 and R9 are solder bridges (or 0Ω resistors). In the latter case the clock should be applied to the XI pin and R8 and R9 are not connected.

I have chosen an Abracon ABLS-25.000MHZ-B2F-T crystal with an 18pF load capacitance requirement. The formula for choosing the values of the two load capacitors is well documented on the internet. It is:

C1 = C2 = 2 * CL - (CP + CI)

Where CP is the parasitic capacitance of the board and CI is the input capacitance of the PHY. For my Abracon crystal with its CL of 18pF this works out at C1 = C2 = 30pF, assuming the commonly quoted CP + CI = 6pF.

I’m a little concerned at the additional capacitance introduced by the presence of the XO and XI header pins. If the capacitance is too far from the ideal level then the crystal frequency will be off target or it may not even start oscillating.

I’ll be using my 1Ghz Ant18e logic analyser to check that the crystal’s output frequency is correct. I can measure the frequency at one of the PHY clock pins. Any attempt to measure it at the XI and XO pins will fail due to the the capacitance of the probes being added to the load capacitance seen by the crystal.

Bill of Materials

Here’s the full list of parts that I selected for this project. All of them are available from Farnell Electronics and I have included direct links to each part’s page at Farnell.

Identifiers Description Mftr. & Part No.
C13 CAPACITOR, CASE D, 47µF, 16V PANASONIC – EEEFC1C470P
C2,C3,C6,C8,C9,C11,C12 MLCC, 0603, Y5V, 50V, 100NF MULTICOMP – MCCA000256
C4,C5 CAPACITOR, NP0, 0603, 50V, 30PF KEMET – C0603C300J5GACTU
C10 MLCC, 0603, X5R, 6.3V, 2.2µF MULTICOMP – MCCA000516
FB1,FB2 FERRITE BEAD, 0603 CASE, 220Ω MURATA BLM18PG221SN1D
P1,P2,P3 HEADER, VERTICAL, 1ROW, 36WAY TE CONNECTIVITY / AMP – 8-146274-6
R3 RESISTOR, ANTI SULPHUR, 0603, 10K WELWYN – ASC0603-10KFT5
R4 RESISTOR, 0603, 6K49, 1% VISHAY DRALORIC – CRCW06036K49FKEA
R5 RESISTOR, 0603, 4K7 5%, 0.1W PANASONIC – ERJ3GEYJ472V
R6 RESISTOR, 0603, 1K 5%, 0.1W PANASONIC – ERJ3GEYJ102V
U1 TXRX, PHY, 10/100, MII, 3.3V, 48LQFP MICREL SEMICONDUCTOR – KSZ8051MLL
R8,R9 RESISTOR, 0Ω, 0R 0.1W PANASONIC – ERJ3GEY0R00V
RJ45 JACK, MAG45, THRU HOLE TE CONNECTIVITY – 6605424-1
Y1 CRYSTAL, 25M, 18PF CL, HC49/4HSMX ABRACON – ABLS-25.000MHZ-B2F-T
D1 LED, YELLOW, 0603, SMD KINGBRIGHT – KPT-1608YC
D2 LED, GREEN, 0603, SMD KINGBRIGHT – KPT-1608SGC
C1,C7 CAPACITOR, X5R, 0805, 6.3V, 22µF AVX – 08056D226MAT2A
R1,R2,R7 RESISTOR, 0603, 220R 5%, 0.1W PANASONIC – ERJ3GEYJ221V
D5 LED, RED, 0603, SMD KINGBRIGHT – KPT-1608SURCK
R10..R19 RESISTOR, 0603, 33R 5%, 0.1W PANASONIC – ERJ3GEYJ330V


PCB layout

The CAD for the board didn’t take too long. A few component footprints had to be created from scratch and I always route by hand these days.


The board CAD layout with layers merged

I only wish I’d had space on the top layer where the header pins are to label each pin with its function. Doing so would have exceeded the 50mm square limit that I was working to.

After designing the CAD I exported the Gerber CAM files and uploaded them to ITead Studio for manufacturing. A couple of weeks later they arrived.


The front of the board, looks great in red!

The differential TX/RX pairs are clearly visible snaking from the PHY to the RJ45 connector. The +/- lines are kept close to each other and have their lengths equalised as much as possible. Other signals, including the top ground pour are kept well away from these traces to help minimise interference.

I don’t do ground pours as a matter of course, only if the design calls for it and in this case the Micrel notes recommend it. I have seen reports on the internet of issues with prototype board manufacturing and ground pours that come into close proximity with other signals so I set a conservative 15 mil pour-to-trace clearance and removed ‘islands’ (areas of the pour that are not connected to a signal).


The back of the board

The back of the board contains the chassis and signal ground planes. Chassis ground from the RJ45 socket is isolated from the large central signal ground plane by an isolation moat. The break in the moat over on the right is to prevent a loop antenna being created.


The fully built PCB

The build process was fairly straightforward. I followed my usual procedure of reflowing on a hot-plate for the IC and any parts that have concealed pins. That meant the 47µF capacitor and the crystal in this case. Both of those do have enough of their pins protruding to hand-solder them but the majority of the pins are underneath so reflow is preferred. Also, their bases are plastic and would easily melt if they came into contact with an iron or an over-enthusiastic hot-air gun.

After reflow the remaining discrete components were simply hot-air’d into place with a pair of fine tweezers. The majority are 0603 size which is the smallest size I can work with both quickly and accurately. I can (and do) work accurately with 0402, but not quickly! The last steps are to use an iron to solder the headers and finally the RJ45.

Testing

I decided that the first stage of testing would be to simply verify that the thing is alive. By alive, that means that the clock is active and the PHY is responding to commands. So, I powered it up and wrote a small STM32 program that exercised the built-in NAND tree self-test mode.

This test involves first setting all pins high then pulling each one low in sequence and checking another pin to ensure it toggles from low to high or high to low. The result of the test was…

Fail!

Nothing happened. The PHY appeared to be completely ignoring me. After much head-scratching I decided that the clock cannot have started so I got my multimeter out, set it to continuity testing mode and meticulously tested the pads on a blank board for continuity where it should be and none where it should not be.

Found it! On close inspection there is an unwanted connection between R9 and the ground pour. Furthermore as bad luck would have it the connection is very hard to see because it’s under the silkscreen label. It’s visible under the microscope though.


Bad Gerber!

At first I thought it was a manufacturing defect until, that is I checked everything in the chain and found that the Gerbers had been badly generated by the Camtastic Gerber generator. This is rather annoying since I followed the online guide for producing the Gerbers and it had all seemed fine.

I spent some time investigating the issue and it turned out to be an option in the Camtastic ‘Export Gerber’ dialog that was selected by default and should be deselected to avoid the bug:


Deselect this checkbox

Deselecting the highlighted option results in Gerbers that appear to be correct, at least as far as I can tell by inspecting them in the Viewplot preview program.

Anyway, the fix for the boards that have already been manufactured involves the surgical application of a sharp knife to break the unwanted trace and I’m back in testing mode.

That did the trick. This time the NAND test passed with flying colours and I can move on to measure the clocks.



Clock testing with a logic analyser. Click for larger

The PHY outputs a clock on TXC and RXC when in MII mode and the line is disconnected. At 100Mb/s (the default) this clock will be the full 25Mhz. I set my logic analyser to its maximum asynchronous sampling rate of 1GHz and measured both pins. As you can see from the image above they are both spot on 25Mhz. Perfect.

Software driver

ST publish sample code that is designed to operate with their ST802RT1 PHY. It demonstrates a point-to-point web-server using the lwIP TCP stack.

The project is laid out in the same structural form as every other ST sample that I’ve seen. As much as I dislike flat, procedural ‘C’ code, there’s a lot to be said for the predictable format that ST uses. Once you’ve seen one then you’ll know exactly where to look in all the others.

The key changes that I needed to make to the sample were to change some preprocessor definitions for the status register and speed and duplex masks. Around about line 354 of stm32_eth.h I added:

//#define PHY_SR 31         /*!< Tranceiver Status Register */
/** 
  * @brief  For DP83848  
  */ 
//#define PHY_SR 16     /*!< Tranceiver Status Register */
/**
  * @brief  For Micrel KSZ8051MLL
  */
#define PHY_SR 0x1e     /*!< Tranceiver Status Register */

And slightly further on I added:

/** 
  * @brief  For DP83848  
  */ 
//#define PHY_Speed_Status  ((u16)0x0002)    /*!< Configured information of Speed: 10Mbps */
//#define PHY_Duplex_Status ((u16)0x0004)    /*!< Configured information of Duplex: Full-duplex */

/**
  * @brief  For Micrel KSZ8051MLL
  */
#define PHY_Speed_Status    ((u16)0x0001)    /*!< Configured information of Speed: 10Mbps */
#define PHY_Duplex_Status   ((u16)0x0004)    /*!< Configured information of Duplex: Full-duplex */

With these in place all I needed to do was comment out the stuff related to flashing LEDs via the HTTP server because I’m only interested in testing the link and the data transfer. The sample code compiled OK and I uploaded it to the MCU.



Embedded web server. Click for larger (600kB) image

It works! The link with the netbook is auto-negotiated at 100Mb/s full duplex. The embedded lwIP protocol stack, hardcoded to IPv4 address 192.168.0.8, is configured to respond to ICMP ping requests which you can see in the command prompt window. More impressively you can see Google Chrome successfully downloading a web page from the embedded http server.

Final testing

I decided to do a final round of testing to ensure that the design is robust. The aim was to verify all the possible link modes and ensure that there were no errors received on the line – too many framing errors could indicate a signal integrity problem due to interference.



RX signal lines

I hooked up my logic analyser to the RXDV, RXER, RXC and RXD[0..3] lines and took some samples triggered on the rising edge of RXDV (data valid) line. The results are shown in the image above. I also left it running for about 10 minutes with a trigger on the RXER line (receive error) while flooding the local network with broadcast UDP packets. It was never triggered, indicating that the signal integrity was perfect.

I tested the following configurations and all performed as expected:

  • Auto-negotiation of speed and duplex
  • 100Mb/s full and half-duplex
  • 10Mb/s full and half-duplex
  • Straight-through and crossover cables (auto MDI/MDI-X).

An stm32plus driver?

That’s the logical next step. I’d like to get to the stage where I can have the STM32 get an IP address via DHCP and perform both as an HTTP client and server. Stay tuned…

Reverse engineering the Nokia N95 8Gb QVGA LCD

$
0
0

In this, the latest instalment of my Nokia QVGA TFT reverse engineering series, I will take on the 2.8 inch 24-bit TFT that is designed to work with the Nokia N95 8Gb mobile phone. Read on to see how it worked out.

Background

In the first of my reverse engineering articles I tackled the Nokia 2730 display. I successfully discovered enough of the command set to write a driver for the Arduino and the STM32.

Flush with that success I moved on to tackle the Nokia 6300 and the Nokia N82. I was lucky in that these two shared the same pinout and command set as the 2730 so I could produce a development board with little difficulty.

The N95 8Gb


The Nokia N95 8Gb handset

Whilst on one of my ebay fishing expeditions I noticed that the N95 8Gb display was widely available and very cheap at about £4.50 for a clone panel from one of the multitude of HK-based sellers.

Furthermore, it shared the same JST connector as my previous reverse-engineering efforts and a little googling yielded the repair manuals that contained the schematic.


The phone connector schematic from RM320/321

Pretty quickly it became obvious that this was not an identical pinout to the 6300/N82. For a start, where’s the LED backlight in and out connection? It’s just not there. The other pins are familiar though, so I’ll get started and see if I can work out the differences as I go along.

Note that the N95 and the N95 8Gb are different phones with different LCD connectors. The N95 has a flat FPC connection that I have not yet been able to identify. The 8Gb model has the familiar JST 24R-JANK-GSAN-TF 24-pin board-to-board connector.


The two N95 variants as shown in ebay ads

Here’s a close-up of the connector fitted to the screen that I bought. The manufacturer has helpfully labelled the pin numbers on the FPC. In this case the numbering is correct but it’s never safe to rely on that and you should always find the correct pin numbering by locating the GND pins that connect directly into the FPC ground pour.


A close up of the JST connector

At 2.8 inches across the diagonal the N95 8Gb is the largest of the three sizes that I’ve reverse-engineered so far. The N82 was 2.4 inches and the 6300 the smallest at only 2.0 inches.


The 6300, N82 and N95 8Gb screens

All of the screens share the same 320×240 QVGA resolution. The pixel size and density are the variables that change along with the physical dimensions of the panel.

Reverse engineering

To get started I hooked it up to an STM32 development board as-per the schematic and tried it using the MC2PA8201 driver that I developed for the Nokia 6300 and N82.

It didn’t work.

It wasn’t a total failure though. I did notice some interesting behaviour.

  • When power was applied, and before RESET was asserted, the backlight came on for the briefest of instants. This could only mean that the backlight LED driving circuit is internal to the panel. This is good news as it will save me a number of components on a development board.
  • A panel with no backlight is so dark that it looks like it is off. By shining a bright light at the panel I could see that it was on and displaying some graphics although they were garbled and not showing correctly. More good news, the post-reset initialisation sequence must be more or less correct.

The next step was to run my test program that issues each of the possible 255 register-write operations and then reads some values back in the hope that the register I just wrote was a command to the panel to give me back some information.

Most of the commands gave only zeros but some of them yielded values. Here’s what I got.

04 : 29 29 91 5B 29 29 91 5B 29 29 
09 : 06 06 61 00 00 06 06 61 00 00 
0A : 08 08 08 08 08 08 08 08 08 08 
0B : 0C 0C 0C 0C 0C 0C 0C 0C 0C 0C 
0C : 66 66 66 66 66 66 66 66 66 66 
0F : F0 F0 F0 F0 F0 F0 F0 F0 F0 F0 
20 : 20 20 20 20 20 20 20 20 20 20 
2E : 00 A0 0C A0 A0 58 48 B0 80 40 
D0 : 40 40 01 40 40 01 40 40 01 40 
D1 : 41 41 01 41 41 01 41 41 01 41 
D2 : 40 40 01 40 40 01 40 40 01 40 
D3 : 41 41 01 41 41 41 01 41 41 41 
DA : 29 29 29 29 29 29 29 29 29 29 
DB : 91 91 91 91 91 91 91 91 91 91 
DC : 5B 5B 5B 5B 5B 5B 5B 5B 5B 5B 
DD : 00 29 91 5B 29 29 91 5B 29 29 
FC : 09 09 00 09 09 00 09 09 00 09 

Most of those mean nothing to me, but registers 0×04 and 0xDA to 0xDD are interesting. They match the behaviour of the device code readout from the MC2PA8201 used in the Nokia 6300 and N82.

Now I know that I’m looking for a controller that has a similar register set to the MC2PA8201 but has additional support for a firmware controlled backlight.

It’s an LDS285

I found a few controllers from Ilitek and one from Himax that supported firmware backlight control. None of their registers worked. Finally I tried the LDS285. This one worked. I was able to switch on the backlight and control its brightness via the registers.

I have no way of telling whether the controller is a genuine LDS285 or whether it’s a similar one that has a compatible command set, but I do know that enough of the commands work for me to be able to create a complete software driver.

You can download the 2.8Mb LDS285 datasheet as a PDF file from here.


The LDS285 brightness block diagram

Now I knew the identity of the compatible controller it was a simple matter to create a driver to support the LDS285. All features are working and I’m pleased to report that the screen I got from ebay supports 16M, 262K and 64K colour modes. The 64K mode only requires 2 transfers per pixel and so it is up to 33% faster than the 18 and 24-bit modes.

An Arduino development board

Designing a development board for the Arduino Mega would complete the family of three so I decided to do it. I will be using the XMEM interface just like in my 6300 and N82 boards.

The same NXP 74ALVC164245 level converter will be used to convert between the 5V arduino signal levels and the 3.3V signals required by the panel. There are many ways to convert levels and I prefer the NXP IC because it has a very fast 2.9ns propagation time that is guaranteed equal across all 16 of its ports, something that’s very important when you’re dealing with a data bus.




The Eagle schematic, click for a PDF

The schematic is simplified by the elimination of the NCP5007 backlight driver circuit that we used in the 6300 and N82 designs. I have chosen to enhance the stability a little by including a 22pF decoupling capacitor on the RESET line, just as Nokia have done in their schematic.




The Eagle CAD design, click for a PDF

The CAD layout was easier than the other boards because the backlight driver circuit and the EN pin header have been eliminated. Working within the 50mm x 50mm constraints means that the panel will be longer than the board by about 20mm on the long side.

Bill of materials

Here’s a complete list of parts used in this project.

Identifier Type Value Footprint
C1 Ceramic capacitor 4.7µF 0805
C2,C3,C4,C5,C7 Ceramic capacitor 100nF 0603
C6 Ceramic capacitor 22pF 0603
R1 Resistor approx 33K&ohm; 0805
U1 Level converter 74ALVC164245DGG TSSOP-48
MEGA Pin header 2×11 2.54mm
SV2 Pin header 2×2 2.54mm
NOKIA12x2 B2B connector 24R-JANK-GSAN-TF 0.4mm x 24


Building the PCB

Once the CAD designs were completed I exported the Gerber CAM files and sent them off to ITead Studio for production.

Three weeks pass by…

Ordering prototypes from China is great value but you do need to have patience! I used the time to write the Arduino LDS285 driver software that I’d need to use to test the new design. In fact I had so much time I also ported the driver software across to stm32plus in preparation for the next release of that library.

Finally they arrived and I’m happy to say that they appear to be flawless.


The front of the PCB

There’s more space on the component-side of the board because we don’t need the LED backlight driving circuit that we have on the N82 and 6300 boards.


The back of the PCB, which will actually be the front when the LCD is mounted on it

Assembling the components on to the board is a procedure that I’m now well-practised at.

Firstly the two fine-pitch components, the 0.5mm pitch level converter and the 0.4mm pitch socket, are reflowed into place using a hot-plate and touched up afterwards under a binocular microscope.

Secondly the remaining passive components, the capacitors and the resistor, are mounted using my Aoyue 852A hot-air gun. The final step is to hand-solder the pin headers into position with a normal soldering iron.

When it’s all done I wash the boards with soapy water and a toothbrush to remove the flux residues and then I leave them to dry in the airing cupboard overnight.


All built and ready to accept a screen

The next day when the board had dried out I attached the LCD. My measurements of where the socket would need to be positioned on the PCB were correct and the LCD sat just where I had envisioned it.


The screen is somewhat larger than the PCB

The screen is fixed to the PCB with an array of double-sided sticky pads that serve both to lift the metal back away from the PCB as well as to hold it fast to the board. Still, the significant overhang necessitates careful handling to ensure that the board is inserted and removed while gripping the PCB edges and not the screen.

Fire it up and run the demos

The software suite that I wrote for the N82 and 6300 is designed to be modular and so it was a trivial matter to write a driver for the LDS285 that would necessitate only a one-line change in the example programs to work with this new display. You will require at least version 2.3.0 of the driver.


Raining again? It must be summer in England.

To use the new driver we simply replace the include file that makes the driver definitions available to us:

//#include "Nokia6300.h"
#include "NokiaN95_8Gb.h"

And we change the typedef that gives us access to the panel driver:

//typedef Nokia6300_Landscape_262K TftPanel;
typedef NokiaN95_8Gb_Landscape_262K TftPanel;

I’m happy to say that all the driver functionality is available and the panels that I’ve tested so far all support 64K, 262K and 16M colour modes in landscape and portrait orientations.

Watch the videos

I’ve put together a quick YouTube video that shows the graphics library in action on the N95 8Gb. That should give you a sense of the size of this panel relative to the other ones I’ve reverse engineered.

This first video shows the panel attached to a 128K Arduino Mega:

Click here to view the video on YouTube.



This second video shows the panel attached to an STM32F4 Discovery board using the LDS285 driver found in my stm32plus library. When attached to the 3.3V STM32F4 device the level converter IC is just a pass-through with 3.3V on each side.

Click here to view the video on YouTube.



So that’s it, another Nokia QVGA panel gives up its secrets to the hobby engineering community. I hope that the information I’ve published here will be of some use to you and your projects.

Update: 1st December 2012

I’m not at all surprised to announce that, just like with the Nokia 6300 display, there is more than one variant of the display out there. This second variant that I’ve seen flips the column and row ‘cursor’ position commands in portrait mode. Other than that, it’s just the same.

I’ll be calling this variant ‘Type B’ and it will be supported in version 2.3.1 of my Arduino drivers that I hope to release tomorrow, the 2nd of December. Usage is just the same pattern as the 6300, all that is required is the suffix _TypeB on the driver name. e.g:

typedef NokiaN95_8Gb_Portrait_262K_TypeB TftPanel;

TftPanel *tft;
tft=new TftPanel;

Schematics, CAD and gerbers available

I have open-sourced my PCB design and CAD files so if you’re interested in building your own boards then head on over to my downloads page and grab yourself a copy.

Some boards for sale

I’ve constructed the remaining PCBs that I have and am selling them on a first come first served basis. The price including delivery is £16.90 for UK and £18.49 for anywhere else in the world. They come with the LCD fitted and are ready to plug in and play.

Temporarily out of stock! I’m building a new batch and expect to be completed on December 15th, 2012. Please check back here later.


Location




Reverse engineering the Nokia N93 QVGA LCD

$
0
0

Welcome to what will probably be the last in the series of articles in which I reverse engineer one of the Nokia QVGA cellphone displays from the pre-smartphone era. I think that by now I’ve covered every possible aspect of these incredibly cost effective little displays and hopefully opened up new avenues for hobbyists needing a richer display experience than they can get with a simple character LCD.

If you’ve been following these articles then you’ll know that I’ve successfully reverse engineered the 2730, 6300, N82 and N95 8Gb displays. Each one is readily and cheaply available on ebay making them ideal for incorporation into your microcontroller projects. All of my reverse engineering efforts have been accompanied by free, open-source libraries for the Arduino and the STM32.

In this final article I will tackle the N93 2.4″ display, which at the time of writing is available for just a few pounds, dollars or euros on ebay. Let’s see how I get on.

The physical display

The physical LCD is the same layout as all the previous Nokia LCDs that I’ve tackled. A short FPC cable runs from the LCD and is terminated by a board-to-board connector at the end.

The connector

The type of connector is usually a primary source of concern when reverse-engineering a cellphone LCD. Fortunately for me I discovered early on that the 24-pin 0.4mm pitch connector is made by a company called JST. The part number is 24R-JANK-GSAN-TF. Here’s the datasheet.

The connector is readily available in quantity direct from the JST web-store. It’s also available from cellphone repair stores and at the time of writing an enterprising reader is even selling them in single units on ebay.

Determining the connector pinout was not difficult. A little googling quickly unearthed the cellphone repair manual and schematic. Once the LCD connector was located in the schematic it was a simple matter of matching up pins 1..24 with the physical device.

If you look at the photograph of the connector you will see that a number of the pins run directly into a solid plane on the FPC cable. These are the ground connections. With that knowledge it’s trivial to determine where the connector pin 1 is located.

The backlight

I can see from the schematic that the backlight is the same as the 6300 and N82. It consists of 4 white LEDs in series that we must drive ourselves. By far the best way to do this is to use an IC designed to boost voltage and to supply a constant current to the LED string.

We will use the same NCP5007 constant current LED driver from OnSemi that we’ve been using in all our Nokia TFT designs so far. It’s cheap, simple to configure and requires few external components to make it work.

The NCP5007 will be configured to supply a constant 20mA through the backlight circuit and we will use a PWM signal on the ENABLE pin to vary the brightness.

An Arduino development board

It’s become something of a tradition for me to prove the reverse engineering by designing a development board for the Arduino Mega and I’ll continue that tradition here.

Level Conversion

All my previous boards have featured level conversion using the NXP 74ALVC164245DL device. We need level conversion because the Arduino Mega has 5V GPIO levels and I have to assume that these screens are designed for a maximum of around 3.3V. I’ve lost count of the number of TFT controller datasheets that I’ve read and I’ve never yet seen one that is designed for 5V I/O.

For this board I decided to use a different method of level conversion using a pair of logic buffer ICs. The device I use is the MC74VHC50 hex buffer from OnSemi, and I need two of them to handle all the signals on the board.

These ICs have 5V-tolerant inputs and a variable output determined by its VCC pin. The reason for this change in design is that these logic ICs are easier to get hold of, cheaper to buy and slightly easier to work with due to the wider pin pitch.

Before producing the LCD development board I verified that the level conversion strategy would work by hacking up some wires to the leads of the IC and verifying that an input level of 5V would be correctly converted down to 3.3V.

The image from my little pocket oscilloscope shows that the test square wave is perfectly reproduced on the IC output pin. You’ll have to take my word for it that the level is 3.3V in that image.

Schematic

The schematic image shows the display connector, level converter, backlight circuit and arduino connector all linked together. I decided to add a small amount of capacitance to the active-low RESET line to enhance its stability. I’ve never seen any unexpected resets in any of my boards but I note that all the official Nokia schematics add capacitance here so I’ve followed suit this time.



Click on this thumbnail for the schematic PDF

Here’s the complete bill of materials for this schematic.

Part Value Device Package
P1 NOKIA12X2 24R-JANK-GSAN-TF 12X2
P2 pin header MA12-2 MA12-2
P3 pin header MA3-2 MA3-2
C1 4.7µF ceramic 0805
C2 1µF ceramic 50V 0805
C3,C4,C5,C6 100nF ceramic 0603
C7 22pF ceramic 0603
D1 Diodes Inc. BO530W-7 schottky SOD-123
L1 Sumida 22µH CDRH5D28NP-220NC 6x6mm
R1 10Ω 1% resistor 0805
R2 33kΩ resistor 0805
U1 NCP5007 LED driver SOT23-5
U2,U3 MC74VHC50DR2G hex buffer SOIC-14


PCB layout

After designing the schematic the next step is to lay out the printed circuit board and route the traces between the components. My target board size is 50x50mm square so that I can use the cost-effective manufacturing service provided by ITead studios in China.



Click for a large image

The most critical part of this layout is the positioning of the LCD connector. It has to be accurately placed so that the LCD, when connected, wraps around the board and sits perfectly on the other side mounted on double-sided sticky pads to lift it clear of the board traces. In the above image pin 1 of the LCD connector is down the bottom right.

As usual these days I don’t even consider the auto-router built in to the design package. Manual routing is time consuming but the results are always better than an auto-router.

When the design was completed to my satisfaction I generated the Gerber files and sent them off to ITead for manufacturing. About two weeks later they arrived.


As usual the boards from ITead are flawless and I can get started with the build. My process is essentially unchanged from what I usually do. After tinning the contacts on the board I place the difficult components (the ICs, the connector and the inductor) with the aid of some flux and bake them on a hot plate. The solder on the tinned pads melts and the components drop into place.

The remaining discrete components are much easier to handle and I simply reflow them into place using my hot air gun and a pair of fine tweezers. When all is complete I examine the results under my binocular microscope and finally wash the boards to remove excess flux.


Testing the board

Only now, several weeks after starting do I get to see if the whole thing works. I’m hopeful though, given the success of the previous reverse-engineering efforts.

I quickly determined that the LCD controller is much the same as the 2730, 6300 and N82 with only minor differences around the commands that control the orientation. We don’t really know the official identity of this controller but it’s close enough to the MagnaChip MC2PA8201 that we can work from its datasheet with a high degree of confidence.

Even better, this batch of screens that I got on ebay appears to be of a very high quality. They’re bright, the colours are accurate and the viewing angle is very wide. They were advertised as ‘genuine Nokia’ and certainly appear to perform as if that’s what they are.

It’s not all plain sailing though. Despite my best efforts I’ve not managed to discover the correct sequence to do vertical scrolling. If any Nokia engineers are out there reading this and want to anonymously drop me a tip then my contact form awaits your keystrokes. Go on…

I have provided full support for the N93 in my Arduino library, version 2.4.0 and above available from my downloads page. To use the N93 panel you simply need to include its header file and change the name of the declared driver to match the N93.

#include "NokiaN93.h"
 
using namespace lcd;
 
typedef NokiaN93_Portrait_262K TftPanel;

All the examples except those that feature hardware scrolling such as the terminal demo are supported. The panels that I have obtained on ebay all support 16M and 262K colour modes in portrait and landscape orientation. Driver support is provided for the 64K colour mode but the panels that I have obtained don’t support it.

Here’s a short demo video on YouTube that shows the LCD board in action. Click on the ‘YouTube’ logo at the bottom right to watch it in a larger format at the YouTube site.

Click here to view the video on YouTube.



Some boards for sale

I’ve constructed the remaining PCBs that I have and am selling them on a first come first served basis. The price including delivery is £16.90 for UK and £18.49 for anywhere else in the world. They come with the LCD fitted and are ready to plug in and play. The LCD is one of the high quality ‘original Nokia’ models described in the above article.


Location




stm32plus 2.0.0: a major release

$
0
0

The latest release is now 2.1.0. Be sure to check out the announcement here.


I’m excited to announce that version 2.0.0 of stm32plus, the C++ library for STM32
devices has been released including lots of new features most significant of which is full support for the STM32F4 series of devices. Download it now from my downloads page.

Version 2.0.0 represents a major milestone in the development of stm32plus and this article will help you to understand what’s changed, what’s new and how you can get started.

Requirements

You will need an STM32F103 HD series or STM32F4 series development board. I use boards based on the STM32F103ZET6, STM32F103VET6 and STM32F407VGT6 running at 72MHz for the F103 and 168MHz for the F4, respectively.

The library makes no assumptions regarding your core clock speed and uses calculations based on the peripheral bus speeds and multipliers in the few cases where it needs to know a frequency.

You will need a toolchain for building ARM projects. The only toolchain that I can support is the ‘Sourcery G++ Lite’ edition from CodeSourcery, who have been bought by Mentor Graphics. I support Linux builds (my own distro is Debian) and Windows builds through a Unix-alike system such as Cygwin or mingw. At the time of writing I use the 2012-09 release on both Windows and Linux. The library is known to build on Mac OS/X but since I don’t have a Mac I cannot offer support for that platform.

I strongly recommend a hardware debugger. If you’re using an F1 board then you’ll need an external debugger such as the ARM-USB-TINY-H from Olimex. Users of the STM32F4DISCOVERY board are somewhat more fortunate in that there is an onboard STLINK debugger that is now fully compatible with OpenOCD 0.6.1. An external debugger is not required.

If you’re considering proceeding without a debugger then I urge you to reconsider. The ARM devices are complex MCUs far in advance of the 8 bit AVRs that people typically graduate from. The ability to single step your code in the Eclipse IDE is invaluable and saves hours of frustration.

Some library features

  • STM32F103 and STM32F4 support with no code change required by you. This of course assumes that you stick to using peripherals present on both the devices.
  • FSMC-based drivers for many TFT LCD controllers, including ILI9325, ILI9327, ILI9481, HX8347A, MC2PA8201 and LDS285. The latter two are designed to support the Nokia QVGA LCDs that you can find on this site.
  • Graphics library supports drawing primitives, bitmaps, jpegs and multiple fonts (font converter included). TrueType fonts and compressed PNG-style graphics are supported.
  • Drivers for HD44780 character based LCD displays.
  • Full featured FAT16 and FAT32 drivers for any block device, including SDIO SD cards. Supported features include long filename read/write, directory create/delete and device formatting.
  • USART polling, interrupt and DMA modes.
  • SPI polling, interrupt and DMA modes.
  • I2C polling and DMA modes.
  • I2S audio support.
  • External SRAM support via the FSMC peripheral.
  • All timers supported including direct support for many of the common usage patters. Timing utilities for millisecond and microsecond level delays and timings.
  • DAC support including the on-chip peripheral and the CS43L22 device on the STM32F4DISCOVERY board.
  • Real-time clock support.
  • CRC peripheral support.
  • Stream IO, observer patterns for interrupt handling.
  • The standard template library (STL) is included.
  • Full error-handing is supported throughout the library.
  • More than 45 examples illustrating every aspect of the library.

Installation

You can download the zip file containing the library from my downloads page. The README.txt file in the package provides a brief introduction and the INSTALL.txt file gives you detailed instructions on how to build and install the library.

Library design policies

Here’s a list of a few of the core stm32plus policies.

  • Simplified #include rules

    Previous versions of stm32plus required you to find and include all the headers that you needed. This was a pain. I have made significant improvements this area. Now you just need to include stm32plus.h and one header for each major feature that you’re using.

    For example, suppose that you’re writing a program that uses the USART peripheral. Your includes would look like this:

    #include "config/stm32plus"
    #include "config/usart.h"
    

    See the examples for the peripherals that you want to use for more information.

  • Object and memory ownership.

    As a general rule if an stm32plus object receives an object by reference then you own that object and you are responsible for not letting it go out of scope until you’re done using the stm32plus object that receives it.

    If an stm32plus object gives you back a pointer then you are responsible for releasing the memory allocated for that object. In all cases it’s assumed that the delete operator can be used to de-allocate objects.

    Where there are unavoidable exceptions to the above rules they will be noted in the documentation.

    File *file;
    FileSystem *fileSystem;
    
    // create filesystem omitted for brevity
    if(!fileSystem->openFile("/320x240_landscape.64",file))
      handleError();
    

    The file pointer returned by openFile is owned by you and must be deleted when you’re done with it.

  • Object construction

    The development world is split over the use of getters and setters versus constructor arguments for initialising objects. stm32plus uses constructor arguments. The advantage of this approach is that you can’t fail to set something because the constructor won’t let you.

  • Error handing

    Where it’s possible for something to go wrong then the stm32plus member function signature will be declared to return a bool type and it will return false if there is a problem. A nested failure will be propagated up the stack to you with the original cause of the problem intact.

    There is a class called ErrorProvider that is used to encapsulate the source and cause of the error. There is one global instance of ErrorProvider called errorProvider that can be examined to find problem causes.

    #include "config/stm32plus.h"
    
    void handleError() {
      uint32_t errorCode=errorProvider.getLast();
    // [...] display or interpret error
    }
    

    Where a constructor can result in an error condition it will be noted in the documentation. After the constructor has returned the caller should call errorProvider.hasError() to determine if the constructor failed.

A first project

It seems to be traditional in the embedded world to create an example program that blinks an LED on the development board to prove that the basics are working and I’m going to follow that tradition here.

My example will go through the steps required to create a new Eclipse project, add the settings required to build with stm32plus and then build the sample project. If you’re not using an Eclipse/CodeSourcery toolchain then you will need to make the necessary changes to fit your environment.

This example will target the SM32F103 board with an 8MHz external oscillator (HSE). You will need to have installed the GNU ARM Eclipse plugin.

Create the project

In Eclipse, select File->New->C++ Project. This will get you the screen shown below.

Ensure that you’ve selected ARM Cross Target Application and that the toolchain you’re using is selected. Enter a name for the project and hit Finish. You can hit Next if you want to modify the default build configurations (Debug and Release) but you can do that later so I’m not going to bother.

Eclipse will have created the project for you, and now we have to modify the build settings so that it picks up stm32plus, sets the required macros and has appropriate optimisation settings.

Select Project->Properties from the main menu. A large form with sections down the left will appear. Make the following changes.

C/C++ build -> Settings -> ARM Sourcery Windows GCC C++ Compiler -> Preprocessor

Add the following two symbols.

STM32PLUS_F1_HD
HSE_VALUE=8000000

Modify the 8000000 (8Mhz) constant to fit whatever value your external oscilator is set to.

C/C++ build -> Settings -> ARM Sourcery Windows GCC C++ Compiler -> Directories

Two additional directories are required. The base stm32plus library directory and its ‘include’ subdirectory. Here’s an example, adjust to fit your installation location:

C:\src\stm32plus-2.0.0\stm32plus
C:\src\stm32plus-2.0.0\stm32plus\include

C/C++ build -> Settings -> ARM Sourcery Windows GCC C++ Compiler -> Optimization

Check the function sections and data sections boxes. These settings cause the compiler to create a uniquely named code or data section for every function and data item in your code. Then, when the linker comes to assemble the program we tell it to discard unused sections, thus making your output binary as small as it can be.

Now go back and repeat the above project settings steps for the ARM Sourcery Windows GCC C Compiler section. Heaven forbid I won’t be writing any C code but the ST firmware library is C and we must ensure that it compiles correctly.

C/C++ build -> Settings -> ARM Sourcery Windows GCC C++ Compiler -> Miscellaneous

Version 2.1.0 and above of stm32plus requires that you select ISO 2009 C++ with GNU Extensions (-std=gnu++0x) from the Language Standard dropdown.

C/C++ build -> Settings -> ARM Sourcery Windows GCC C++ Linker -> General

Check Remove unused sections and enter the file name of the linker script for your project.

Hang on a minute, what’s a linker script? A linker script tells the linker how to assemble the different parts of your program, which order they should go in and at what memory locations. If this is the first time that you’ve done embedded development then the chances are you’ve never seen one before. They’ve always existed it’s just that they’re usually hidden from you by your tools.

Unfortunately because devices like the STM32 come in such a myriad of memory configurations it’s necessary for us to supply our own script. The following example shows the linker script that I use for my 512Kb/64Kb STM32F103ZET6. You can customise the FLASH and RAM sections of you have a different memory configuration. All the examples supplied with the stm32plus distribution have F1 and F4 linker scripts that you can copy and modify for your own use.

ENTRY(Reset_Handler)

_estack = 0x2000FFFF;    	/* end of $(RAM_LENGTH) RAM */

_Min_Heap_Size = 0;      	/* required amount of heap  */
_Min_Stack_Size = 4K; 		/* required amount of stack */

MEMORY
{
  FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 512K
  RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 64K
  MEMORY_B1 (rx)  : ORIGIN = 0x60000000, LENGTH = 0K
}

SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector))
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
	*(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH


   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
    .ARM : {
    __exidx_start = .;
      *(.ARM.exidx*)
      __exidx_end = .;
    } >FLASH

  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(.fini_array*))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH

  /* used by the startup to initialize data */
  _sidata = .;

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data : AT ( _sidata )
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM

  /* Uninitialized data section */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM

  PROVIDE ( end = _ebss );
  PROVIDE ( _end = _ebss );

  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(4);
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(4);
  } >RAM

  /* MEMORY_bank1 section, code must be located here explicitly            */
  /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */
  .memory_b1_text :
  {
    *(.mb1text)        /* .mb1text sections (code) */
    *(.mb1text*)       /* .mb1text* sections (code)  */
    *(.mb1rodata)      /* read-only data (constants) */
    *(.mb1rodata*)
  } >MEMORY_B1

  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

  .ARM.attributes 0 : { *(.ARM.attributes) }
}

C/C++ build -> Settings -> ARM Sourcery Windows GCC C++ Linker -> Libraries

Add ‘stm32plus-{version}-f1hd-debug’ to the ‘Libraries (-l)’ box. Replace {version} with the version of stm32plus that you are using. Add the stm32plus installation directory to the ‘Library search path (-L)’ box. This is the directory in which you will find libstm32plus-2.0.0-f1hd-debug.a.

C++ tweaks

The default GNU C++ compiler that ships with CodeSourcery g++ is not optimised for embedded development. By default it will pull in 10′s of K of un-necessary code as well as not having default support for the essential new and delete operators. We solve all these problems with one source code file.

Add a new C++ file to your project and call it LibraryHacks.cpp. Here’s what to put in it:

#include <cstdlib>
#include <sys/types.h>


/*
 * The default pulls in 70K of garbage
 */

namespace __gnu_cxx {

	void __verbose_terminate_handler() {
		for(;;);
	}
}


/*
 * The default pulls in about 12K of garbage
 */

extern "C" void __cxa_pure_virtual() {
  for(;;);
}


/*
 * Implement C++ new/delete operators using the heap
 */

void *operator new(size_t size) {
	return malloc(size);
}

void *operator new[](size_t size) {
	return malloc(size);
}

void operator delete(void *p) {
	free(p);
}

void operator delete[](void *p) {
	free(p);
}


/*
 * sbrk function for getting space for malloc and friends
 */

extern int  _end;

extern "C" {
	caddr_t _sbrk ( int incr ) {

		static unsigned char *heap = NULL;
		unsigned char *prev_heap;

		if (heap == NULL) {
			heap = (unsigned char *)&_end;
		}
		prev_heap = heap;
		/* check removed to show basic approach */

		heap += incr;

		return (caddr_t) prev_heap;
	}
}

System startup files

Two more boilerplates and we’re ready to write some real code! The ST framework needs an assembly language file ‘Startup.S’ that contains the code that will be executed straight after reset that does some basic initialisation before calling the ‘SystemInit’ function in ‘System.c’. System.c contains the code that starts the stm32 core clocks.

Startup.S (note the upper-case .S, eclipse won’t recognise it as an assembly code file if the case is not correct). This file is taken straight from the ST standard peripheral library and is appropriate for the HD (high-density) line of MCUs

Click to show Startup.S
/**
 ******************************************************************************
 * @file      startup_stm32f10x_hd.s
 * @author    MCD Application Team
 * @version   V3.3.0
 * @date      04/16/2010
 * @brief     STM32F10x High Density Devices vector table for RIDE7 toolchain.
 *            This module performs:
 *                - Set the initial SP
 *                - Set the initial PC == Reset_Handler,
 *                - Set the vector table entries with the exceptions ISR address
 *                - Configure the clock system and the external SRAM mounted on
 *                  STM3210E-EVAL board to be used as data memory (optional,
 *                  to be enabled by user)
 *                - Branches to main in the C library (which eventually
 *                  calls main()).
 *            After Reset the Cortex-M3 processor is in Thread mode,
 *            priority is Privileged, and the Stack is set to Main.
 *******************************************************************************
 * @copy
 *
 * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
 * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
 * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY
 * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
 * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
 * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
 *
 * <h2><center>&copy; COPYRIGHT 2010 STMicroelectronics</center></h2>
 */

  .syntax unified
  .cpu cortex-m3
  .fpu softvfp
  .thumb

.global  g_pfnVectors
.global  Default_Handler

/* start address for the initialization values of the .data section.
defined in linker script */
.word  _sidata
/* start address for the .data section. defined in linker script */
.word  _sdata
/* end address for the .data section. defined in linker script */
.word  _edata
/* start address for the .bss section. defined in linker script */
.word  _sbss
/* end address for the .bss section. defined in linker script */
.word  _ebss
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */

.equ  BootRAM,        0xF1E0F85F
/**
 * @brief  This is the code that gets called when the processor first
 *          starts execution following a reset event. Only the absolutely
 *          necessary set is performed, after which the application
 *          supplied main() routine is called.
 * @param  None
 * @retval : None
*/

    .section  .text.Reset_Handler
  .weak  Reset_Handler
  .type  Reset_Handler, %function
Reset_Handler:

/* Copy the data segment initializers from flash to SRAM */
  movs  r1, #0
  b  LoopCopyDataInit

CopyDataInit:
  ldr  r3, =_sidata
  ldr  r3, [r3, r1]
  str  r3, [r0, r1]
  adds  r1, r1, #4

LoopCopyDataInit:
  ldr  r0, =_sdata
  ldr  r3, =_edata
  adds  r2, r0, r1
  cmp  r2, r3
  bcc  CopyDataInit
  ldr  r2, =_sbss
  b  LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
  movs  r3, #0
  str  r3, [r2], #4

LoopFillZerobss:
  ldr  r3, = _ebss
  cmp  r2, r3
  bcc  FillZerobss
/* Call the clock system intitialization function.*/
  bl  SystemInit
/* Call the application's entry point.*/
  bl  main
  bx  lr
.size  Reset_Handler, .-Reset_Handler

/**
 * @brief  This is the code that gets called when the processor receives an
 *         unexpected interrupt.  This simply enters an infinite loop, preserving
 *         the system state for examination by a debugger.
 * @param  None
 * @retval None
*/
    .section  .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
  b  Infinite_Loop
  .size  Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex M3. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
*******************************************************************************/
   .section  .isr_vector,"a",%progbits
  .type  g_pfnVectors, %object
  .size  g_pfnVectors, .-g_pfnVectors


g_pfnVectors:
  .word  _estack
  .word  Reset_Handler
  .word  NMI_Handler
  .word  HardFault_Handler
  .word  MemManage_Handler
  .word  BusFault_Handler
  .word  UsageFault_Handler
  .word  0
  .word  0
  .word  0
  .word  0
  .word  SVC_Handler
  .word  DebugMon_Handler
  .word  0
  .word  PendSV_Handler
  .word  SysTick_Handler
  .word  WWDG_IRQHandler
  .word  PVD_IRQHandler
  .word  TAMPER_IRQHandler
  .word  RTC_IRQHandler
  .word  FLASH_IRQHandler
  .word  RCC_IRQHandler
  .word  EXTI0_IRQHandler
  .word  EXTI1_IRQHandler
  .word  EXTI2_IRQHandler
  .word  EXTI3_IRQHandler
  .word  EXTI4_IRQHandler
  .word  DMA1_Channel1_IRQHandler
  .word  DMA1_Channel2_IRQHandler
  .word  DMA1_Channel3_IRQHandler
  .word  DMA1_Channel4_IRQHandler
  .word  DMA1_Channel5_IRQHandler
  .word  DMA1_Channel6_IRQHandler
  .word  DMA1_Channel7_IRQHandler
  .word  ADC1_2_IRQHandler
  .word  USB_HP_CAN1_TX_IRQHandler
  .word  USB_LP_CAN1_RX0_IRQHandler
  .word  CAN1_RX1_IRQHandler
  .word  CAN1_SCE_IRQHandler
  .word  EXTI9_5_IRQHandler
  .word  TIM1_BRK_IRQHandler
  .word  TIM1_UP_IRQHandler
  .word  TIM1_TRG_COM_IRQHandler
  .word  TIM1_CC_IRQHandler
  .word  TIM2_IRQHandler
  .word  TIM3_IRQHandler
  .word  TIM4_IRQHandler
  .word  I2C1_EV_IRQHandler
  .word  I2C1_ER_IRQHandler
  .word  I2C2_EV_IRQHandler
  .word  I2C2_ER_IRQHandler
  .word  SPI1_IRQHandler
  .word  SPI2_IRQHandler
  .word  USART1_IRQHandler
  .word  USART2_IRQHandler
  .word  USART3_IRQHandler
  .word  EXTI15_10_IRQHandler
  .word  RTCAlarm_IRQHandler
  .word  USBWakeUp_IRQHandler
  .word  TIM8_BRK_IRQHandler
  .word  TIM8_UP_IRQHandler
  .word  TIM8_TRG_COM_IRQHandler
  .word  TIM8_CC_IRQHandler
  .word  ADC3_IRQHandler
  .word  FSMC_IRQHandler
  .word  SDIO_IRQHandler
  .word  TIM5_IRQHandler
  .word  SPI3_IRQHandler
  .word  UART4_IRQHandler
  .word  UART5_IRQHandler
  .word  TIM6_IRQHandler
  .word  TIM7_IRQHandler
  .word  DMA2_Channel1_IRQHandler
  .word  DMA2_Channel2_IRQHandler
  .word  DMA2_Channel3_IRQHandler
  .word  DMA2_Channel4_5_IRQHandler
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  0
  .word  BootRAM       /* @0x1E0. This is for boot in RAM mode for
                         STM32F10x High Density devices. */
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/

  .weak  NMI_Handler
  .thumb_set NMI_Handler,Default_Handler

  .weak  HardFault_Handler
  .thumb_set HardFault_Handler,Default_Handler

  .weak  MemManage_Handler
  .thumb_set MemManage_Handler,Default_Handler

  .weak  BusFault_Handler
  .thumb_set BusFault_Handler,Default_Handler

  .weak  UsageFault_Handler
  .thumb_set UsageFault_Handler,Default_Handler

  .weak  SVC_Handler
  .thumb_set SVC_Handler,Default_Handler

  .weak  DebugMon_Handler
  .thumb_set DebugMon_Handler,Default_Handler

  .weak  PendSV_Handler
  .thumb_set PendSV_Handler,Default_Handler

  .weak  SysTick_Handler
  .thumb_set SysTick_Handler,Default_Handler

  .weak  WWDG_IRQHandler
  .thumb_set WWDG_IRQHandler,Default_Handler

  .weak  PVD_IRQHandler
  .thumb_set PVD_IRQHandler,Default_Handler

  .weak  TAMPER_IRQHandler
  .thumb_set TAMPER_IRQHandler,Default_Handler

  .weak  RTC_IRQHandler
  .thumb_set RTC_IRQHandler,Default_Handler

  .weak  FLASH_IRQHandler
  .thumb_set FLASH_IRQHandler,Default_Handler

  .weak  RCC_IRQHandler
  .thumb_set RCC_IRQHandler,Default_Handler

  .weak  EXTI0_IRQHandler
  .thumb_set EXTI0_IRQHandler,Default_Handler

  .weak  EXTI1_IRQHandler
  .thumb_set EXTI1_IRQHandler,Default_Handler

  .weak  EXTI2_IRQHandler
  .thumb_set EXTI2_IRQHandler,Default_Handler

  .weak  EXTI3_IRQHandler
  .thumb_set EXTI3_IRQHandler,Default_Handler

  .weak  EXTI4_IRQHandler
  .thumb_set EXTI4_IRQHandler,Default_Handler

  .weak  DMA1_Channel1_IRQHandler
  .thumb_set DMA1_Channel1_IRQHandler,Default_Handler

  .weak  DMA1_Channel2_IRQHandler
  .thumb_set DMA1_Channel2_IRQHandler,Default_Handler

  .weak  DMA1_Channel3_IRQHandler
  .thumb_set DMA1_Channel3_IRQHandler,Default_Handler

  .weak  DMA1_Channel4_IRQHandler
  .thumb_set DMA1_Channel4_IRQHandler,Default_Handler

  .weak  DMA1_Channel5_IRQHandler
  .thumb_set DMA1_Channel5_IRQHandler,Default_Handler

  .weak  DMA1_Channel6_IRQHandler
  .thumb_set DMA1_Channel6_IRQHandler,Default_Handler

  .weak  DMA1_Channel7_IRQHandler
  .thumb_set DMA1_Channel7_IRQHandler,Default_Handler

  .weak  ADC1_2_IRQHandler
  .thumb_set ADC1_2_IRQHandler,Default_Handler

  .weak  USB_HP_CAN1_TX_IRQHandler
  .thumb_set USB_HP_CAN1_TX_IRQHandler,Default_Handler

  .weak  USB_LP_CAN1_RX0_IRQHandler
  .thumb_set USB_LP_CAN1_RX0_IRQHandler,Default_Handler

  .weak  CAN1_RX1_IRQHandler
  .thumb_set CAN1_RX1_IRQHandler,Default_Handler

  .weak  CAN1_SCE_IRQHandler
  .thumb_set CAN1_SCE_IRQHandler,Default_Handler

  .weak  EXTI9_5_IRQHandler
  .thumb_set EXTI9_5_IRQHandler,Default_Handler

  .weak  TIM1_BRK_IRQHandler
  .thumb_set TIM1_BRK_IRQHandler,Default_Handler

  .weak  TIM1_UP_IRQHandler
  .thumb_set TIM1_UP_IRQHandler,Default_Handler

  .weak  TIM1_TRG_COM_IRQHandler
  .thumb_set TIM1_TRG_COM_IRQHandler,Default_Handler

  .weak  TIM1_CC_IRQHandler
  .thumb_set TIM1_CC_IRQHandler,Default_Handler

  .weak  TIM2_IRQHandler
  .thumb_set TIM2_IRQHandler,Default_Handler

  .weak  TIM3_IRQHandler
  .thumb_set TIM3_IRQHandler,Default_Handler

  .weak  TIM4_IRQHandler
  .thumb_set TIM4_IRQHandler,Default_Handler

  .weak  I2C1_EV_IRQHandler
  .thumb_set I2C1_EV_IRQHandler,Default_Handler

  .weak  I2C1_ER_IRQHandler
  .thumb_set I2C1_ER_IRQHandler,Default_Handler

  .weak  I2C2_EV_IRQHandler
  .thumb_set I2C2_EV_IRQHandler,Default_Handler

  .weak  I2C2_ER_IRQHandler
  .thumb_set I2C2_ER_IRQHandler,Default_Handler

  .weak  SPI1_IRQHandler
  .thumb_set SPI1_IRQHandler,Default_Handler

  .weak  SPI2_IRQHandler
  .thumb_set SPI2_IRQHandler,Default_Handler

  .weak  USART1_IRQHandler
  .thumb_set USART1_IRQHandler,Default_Handler

  .weak  USART2_IRQHandler
  .thumb_set USART2_IRQHandler,Default_Handler

  .weak  USART3_IRQHandler
  .thumb_set USART3_IRQHandler,Default_Handler

  .weak  EXTI15_10_IRQHandler
  .thumb_set EXTI15_10_IRQHandler,Default_Handler

  .weak  RTCAlarm_IRQHandler
  .thumb_set RTCAlarm_IRQHandler,Default_Handler

  .weak  USBWakeUp_IRQHandler
  .thumb_set USBWakeUp_IRQHandler,Default_Handler

  .weak  TIM8_BRK_IRQHandler
  .thumb_set TIM8_BRK_IRQHandler,Default_Handler

  .weak  TIM8_UP_IRQHandler
  .thumb_set TIM8_UP_IRQHandler,Default_Handler

  .weak  TIM8_TRG_COM_IRQHandler
  .thumb_set TIM8_TRG_COM_IRQHandler,Default_Handler

  .weak  TIM8_CC_IRQHandler
  .thumb_set TIM8_CC_IRQHandler,Default_Handler

  .weak  ADC3_IRQHandler
  .thumb_set ADC3_IRQHandler,Default_Handler

  .weak  FSMC_IRQHandler
  .thumb_set FSMC_IRQHandler,Default_Handler

  .weak  SDIO_IRQHandler
  .thumb_set SDIO_IRQHandler,Default_Handler

  .weak  TIM5_IRQHandler
  .thumb_set TIM5_IRQHandler,Default_Handler

  .weak  SPI3_IRQHandler
  .thumb_set SPI3_IRQHandler,Default_Handler

  .weak  UART4_IRQHandler
  .thumb_set UART4_IRQHandler,Default_Handler

  .weak  UART5_IRQHandler
  .thumb_set UART5_IRQHandler,Default_Handler

  .weak  TIM6_IRQHandler
  .thumb_set TIM6_IRQHandler,Default_Handler

  .weak  TIM7_IRQHandler
  .thumb_set TIM7_IRQHandler,Default_Handler

  .weak  DMA2_Channel1_IRQHandler
  .thumb_set DMA2_Channel1_IRQHandler,Default_Handler

  .weak  DMA2_Channel2_IRQHandler
  .thumb_set DMA2_Channel2_IRQHandler,Default_Handler

  .weak  DMA2_Channel3_IRQHandler
  .thumb_set DMA2_Channel3_IRQHandler,Default_Handler

  .weak  DMA2_Channel4_5_IRQHandler
  .thumb_set DMA2_Channel4_5_IRQHandler,Default_Handler

/******************* (C) COPYRIGHT 2010 STMicroelectronics *****END OF FILE****/

System.c. This contains the startup code that initialises the clocks. The following file is appropriate for 72MHz devices.

#include "config/stdperiph.h"

static volatile ErrorStatus HSEStartUpStatus=SUCCESS;

uint32_t SystemCoreClock=72000000;

void SystemInit() {
	// SYSCLK, HCLK, PCLK2 and PCLK1 configuration
	// RCC system reset(for debug purpose) */
	RCC_DeInit();

	/* Enable HSE */
	RCC_HSEConfig(RCC_HSE_ON);

	/* Wait till HSE is ready */
	HSEStartUpStatus=RCC_WaitForHSEStartUp();

	if(HSEStartUpStatus == SUCCESS) {
		/* Enable Prefetch Buffer */
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);

		/* Flash 2 wait state */
		FLASH_SetLatency(FLASH_Latency_2);

		/* HCLK = SYSCLK */
		RCC_HCLKConfig(RCC_SYSCLK_Div1);

		/* PCLK2 = HCLK */
		RCC_PCLK2Config(RCC_HCLK_Div1);

		/* PCLK1 = HCLK/2 */
		RCC_PCLK1Config(RCC_HCLK_Div2);

		/* PLLCLK = 8MHz * 9 = 72 MHz */
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);

		/* Enable PLL */
		RCC_PLLCmd(ENABLE);

		/* Wait till PLL is ready */
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) {
		}

		/* Select PLL as system clock source */
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

		/* Wait till PLL is used as system clock source */
		while(RCC_GetSYSCLKSource() != 0x08) {
		}
	}
}

The blink source file

Finally we’re ready to create the source file that will execute the blink test. Create a new C++ source file in eclipse, call it ‘blink.cpp. Here it is:

/*
 * This file is a part of the open source stm32plus library.
 * Copyright (c) 2011,2012 Andy Brown <www.andybrown.me.uk>
 * Please see website for licensing terms.
 */

#include "config/stm32plus.h"
#include "config/gpio.h"
#include "config/timing.h"

using namespace stm32plus;

/**
 * This is the most basic example that uses GPIO and the Systick timer
 * to blink a LED on PF6 at 1Hz.
 *
 * If the STM32F4DISCOVERY board is your target then change the GPIO declation to...
 *
 * 	GpioD<DefaultDigitalOutputFeature<13> > pd
 *
 * ... and change 2 of "pf[6]" to "pd[13]" to blink the orange led on the discovery board.
 *
 * Compatible MCU:
 *   STM32F1
 *   STM32F4
 *
 * Tested on devices:
 *   STM32F103ZET6
 *   STM32F407VGT6
 */

class Blink {

	public:

		void run() {

			// initialise the pin for output

			GpioF<DefaultDigitalOutputFeature<6> > pf;

			// loop forever switching it on and off with a 1 second
			// delay in between each cycle

			for(;;) {

				pf[6].set();
				MillisecondTimer::delay(1000);

				pf[6].reset();
				MillisecondTimer::delay(1000);
			}
		}
};


/*
 * Main entry point
 */

int main() {

	// set up SysTick at 1ms resolution
	MillisecondTimer::initialise();

	Blink blink;
	blink.run();

	// not reached
	return 0;
}

This sample introduces a few core stm32plus features:

The MillisecondTimer class. Use the static methods on this class to perform millisecond resolution timings. You must call the initialise static member function once so that the cortex SysTick timer is started.

GPIO port and pin manipulation through the GpioF and DefaultDigitalOutputFeature templates. Note how you can address the pins using the convenient [] array operator. The example configures GpioF with a digital output pin on #6. This is a very simple example that can be easily extended. For example you can configure DefaultDigitalOutputFeature with a list of pin numbers to configure many in one go.

You can also configure input pins and alternate function pins by adding more parameters to the template, for example:

GpioF<
  DefaultDigitalOutputFeature<0>,
  DigitalOutputFeature<GPIO_Speed_10MHz,Gpio::OPEN_DRAIN,Gpio::PUPD_NONE,1,2,3>,
  DefaultDigitalInputFeature<4,5,6>,
  DefaultAlternateFunctionFeature<GPIO_AF_FSMC,11>
  > pf;

This contrived example shows how you can initialise lots of pins in different configurations, all in one statement. What you get on port F from this configuration is:

  • Output pin 0 at 50MHz, push-pull, no pull-up/down.
  • Output pins 1,2,3 at 10MHz, open-drain, no pull-up/down.
  • Input pins 4,5,6 at 50MHz, no pull-up.
  • FSMC alternate function pin 11, push-pull, no pull-up/down.

The concept of feature templates being used to construct a mixin object is heavily re-used throughout stm32plus so that the only code that is generated is the code that is specific to your application. This is good for both efficiency and code size.

Reporting bugs

If you think you’ve found a bug then I’ll certainly fix it. Please use my bugzilla installation to submit a bug report. You can also use bugzilla to browse the status of other bugs that have been found and enhancements that I’m planning.

Browse the example code online

Click here to browse all the example code in one easy to search web page.

License

Copyright (c) 2011, 2012, 2013 Andrew Brown. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  • Neither the name of stm32plus nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

  • THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANDREW BROWN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Generic Nokia LCD hacking board

$
0
0

Over the course of the last few months I’ve been presenting schematics and PCBs that you can use to attach various Nokia LCDs to popular microcontrollers. Today I’m going to go one step further and present the generic board that I use for hacking any Nokia LCD that happens to have one of the correct connectors.

The schematic

The aim of this project is to do the following:

  • Break out the 24R-JANK-GSAN-TF connector used by all the QVGA LCDs that I’ve reverse engineered on this site such as the Nokia 2730, 6300, N82, N95 8Gb and N93.
  • Break out the Hirose DF23-10 connector used by the Nokia 6100 and 1600.
  • Break out the Hirose DF23-22 connector used by the Nokia 6101 and others.
  • Provide an optional OnSemi NCP5007 LED driver for the backlights that are powered by up to 4 LEDs in series.
  • With those goals in mind, let’s take a look at the schematic. This image is really small. Click on it to download the schematic as a PDF.




    Click for a PDF

    SV1 is the 0.1″ breakout header. It has up to 24 pins mapped to the Nokia connectors, V+ and V- that correspond to the output and feedback pins for the backlight driver, EN for the backlight driver enable pin, and several VCC and GND pins.

    SV2 is the 24R-JANK-GSAN-TF connector. All 24 pins are mapped to the breakout header.

    SV3 is the Hirose DF23-10 connector. This connector only has 10 pins and so they are mapped to pins P1..10 on the breakout connector.

    SV4 is the Hirose DF23-22 connector. This connector has 22 pins that get mapped to pins P1..22 on the breakout connector.

    Here’s the complete bill of materials for this schematic.

    Part Value Device Package
    SV1 pin header MA15-2 MA15-2
    SV2 Nokia 12×2 24R-JANK-GSAN-TF 12×2
    SV3 Nokia 5×2 Hirose DF23-10 5×2
    SV4 Nokia 11×2 Hirose DF23-22 11×2
    C1 4.7µF ceramic 0805
    C2 1µF 50V ceramic 0805
    D1 CD214A-B140LF Bourns schottky diode DO214AC
    L1 22µH Murata LQH3NP_J0 inductor 1212
    R1 10Ω 1% resistor 0805
    U1 NCP5007 OnSemi LED driver SOT23-5


    The NCP5007 backlight driver is a boost converter that works by continuously raising its output voltage until it overcomes the forward voltage of the LEDs and current starts to flow. It then continues to raise the voltage until the configured current level is detected (I set this to 20mA with a 10&ohm; resistor in all my designs).

    An important side effect of this design is that the V+ and V- pins must not be left as an open circuit when the device is powered because it will cause the NCP5007 to endlessly raise its voltage output until it either burns out or automatically shuts down. OnSemi, the manufacturer, claims that it has an automatic shutdown for this case but you really don’t want to test that claim.

    The PCB layout

    With the schematic design now finalised, I set about designing the PCB.



    Click for a PDF

    There wasn’t enough space within the target of 50x50mm to fit all the connectors on the top side so I placed the backlight, breakout, 24R-JANK-GSAN-TF and DF23-10 connectors on the top and the DF23-22 on the bottom.

    Having a connector on the bottom meant that I would need to provide mounting holes for some feet to lift it off my workspace and so there are four 3mm holes provided in the corners.

    Time for manufacturing!

    I use the PCB manufacturing service provided by ITead Studio. After uploading the board to their site it takes between two and three weeks for the finished items to arrive in the mail.

    In a nod to the illicit, underground nature of a reverse engineering board I decided to get them printed with a black solder mask. I know, I need to get out more often.

    They look great in black but are nigh on impossible to photograph. I had to hold it at just the right angle to the light to make the features stand out.

    Here’s an image that shows the front of the PCB with the backlight circuit, 24R-JANK-GSAN-TF and the DF23-10 connectors affixed.

    Pin mappings for popular TFTs

    During the time I spent reverse engineering the various displays I kept notes about the pin mappings. In this section I’ll present those mappings for some of the popular Nokia LCDs.

    In the tables below, ‘board pin’ is the P0..Pn label on my board and ‘schematic pin’ is the pin number on the Nokia schematic found in the repair manual for the corresponding phone.

    Note that these mappings are correct for an LCD connected in the orientation shown in the photograph below.

    6300

    Also known as Connector
    6300, 6301, 5310, 7500, 8600, 6120C, E90 (small), E51 SV2


    Board pin Function schematic pin Notes
    1 LED1- 1 connect to LED2+
    2 LED2- 2 connect to V-
    3 VDDI 3 connect to 3.3V
    4 GND 4
    5 WR 5
    6 D0 6
    7 GND 7
    8 D2 8
    9 D4 9
    10 D6 10
    11 CS 11
    12 RESET 12
    13 TE 13
    14 D7 14
    15 D5 15
    16 GND 16
    17 D3 17
    18 D1 18
    19 RS 19
    20 RD 20
    21 GND 21
    22 VDD 22 connect to 3.3V
    23 LED2+ 23 connect to LED1-
    24 LED1+ 24 connect to V+



    I used the pinout in the above table to connect the board to a Nokia 6300 LCD obtained from ebay. I connected them to an STM32F103VET6 board and ran the mcp2pa8201 demo supplied with my stm32plus library.

    The above image shows the LCD driven by the STM32 via the hacking board.

    N82

    Also known as Connector
    N79 N78 N77 E66 6210 SV2


    Board pin Function schematic pin Notes
    1 LED- 1 connect to V-
    2 GND 2
    3 VDDI 3 connect to 3.3V
    4 GND 4
    5 WR 5
    6 D0 6
    7 GND 7
    8 D2 8
    9 D4 9
    10 D6 10
    11 CS 11
    12 RESET 12
    13 TE 13
    14 D7 14
    15 D5 15
    16 GND 16
    17 D3 17
    18 D1 18
    19 RS 19
    20 RD 20
    21 GND 21
    22 VDD 22 connect to 3.3V
    23 GND 23
    24 LED+ 24 connect to V+


    N95 8GB

    Also known as Connector
    n/a SV2
    Board pin Function schematic pin Notes
    1 VDD 13 connect to 3.3V
    2 GND 14
    3 TE 15
    4 RESET 16
    5 D1 17
    6 D3 18
    7 D5 19
    8 D7 20
    9 RS 21
    10 RD 22
    11 GND 23
    12 VIO 24 connect to 3.3V
    13 GND 1 labelled KBBC on schematic
    14 GND 2
    15 WR 3
    16 CS 4
    17 D6 5
    18 D4 6
    19 D2 7
    20 D0 8
    21 GND 9
    22 VIO 10 connect to 3.3V
    23 GND 11
    24 VDD 12 connect to 3.3V


    Remaining boards for sale

    The remaining blank boards that I have are available for sale at £6.50 for UK delivery and £7.50 for worldwide delivery. Each board includes one 24R-JANK-GSAN-TF connector (unsoldered) to get you started. The boards have the black solder mask, just as pictured in this article.


    Location




stm32plus 2.1.0

$
0
0

Due to the use of c++0x features the minimum compiler requirement is now version 4.7.0 of gcc


stm32plus version 2.1.0 has now been released and is available from my downloads page. This article will present a brief overview of the following new features.

  • LGDP453x TFT driver
  • SSD1289 TFT driver
  • SSD1963 TFT driver
  • ST7783 TFT driver
  • AT24C32/64 serial EEPROM support

As mentioned in the banner at the top of this page you will need to ensure that you are using at least version 4.7.0 of gcc. I’m currently working on a driver for one of the big on-chip peripherals and it just couldn’t be done cleanly without real variadic templates so I took the opportunity to migrate all the template ‘feature’ mix-in classes to variadics. I also replaced lots of subclass types that were there as a workaround for the lack of template typedefs with much cleaner template aliases.

I use the free ‘arm-2012.09′ arm-none-eabi gcc release supplied by CodeSourcery (aka. Mentor Graphics) on Windows 7 x64 and Ubuntu Linux and I recommend that you do too. Other gcc toolchains may also work but are not tested. non-gcc compilers will certainly not work.

The installation and usage instructions have not changed since version 2.0.0. Documentation can be found in this previous article.

LGDP453x TFT driver

The LGDP4531/2 is a 320×240 (QVGA) TFT panel from LG. The stm32plus driver for this panel was contributed by Andy Franz and gratefully accepted by myself into this release.

64K and 262K colour modes are supported in landscape and portrait orientations. A full list of driver declarations are:

LGDP453x_Portrait_64K
LGDP453x_Landscape_64K
LGDP453x_Portrait_262K
LGDP453x_Landscape_262K

Andy created a corresponding example demo that you can find in the ‘examples/lgdp453x’ directory.

SSD1289 TFT driver

Experimental support is now provided for the Solomon Systech 1289 QVGA TFT driver with 64K and 262K colours and in landscape and portrait mode. The driver names are:

SSD1289_Portrait_64K
SSD1289_Landscape_64K
SSD1289_Portrait_262K
SSD1289_Landscape_262K

An example demo program is supplied in the ‘examples/ssd1289′ directory.

I have labelled this driver as experimental because I have not been able to verify that it works with the cheap ebay SSD1289 panel that I have because my cheap panel appears to be hardwired into an interlaced mode.

That is, if you set a display window to cover the whole screen and then fill it with pixels then the pixels will fill the rows in this order: 1,0,3,2,5,4… This makes it impossible to support with my graphics driver.

The offending driver input line is ‘GD’ and of course it’s the only one you can’t set in software using the ‘driver output control (r01h)’ register. Hopefully one of you will have a panel with ‘GD’ set to the non-interlaced mode!

SSD1963 TFT driver

The SSD1963 is another one from Solomon Systech. It’s slightly unusual in that it’s not hard-wired to any particular resolution. Instead it allows you to program it to support any resolution up to 800×480 as long as you know the timings for the panel that you’re going to use.

The stm32plus driver for the SSD1963 calls upon small ‘traits’ classes to supply the timing and size information for the panel being controlled. My test panel is a 4.3″ 480×272 device obtained on ebay and I have supplied traits classes for it. The driver names are:

SSD1963_480x272_Portrait_262K
SSD1963_480x272_Landscape_262K
SSD1963_480x272_Portrait_16M
SSD1963_480x272_Landscape_16M

I’ve put together a short video of this panel in action connected to the STM32F103.

Click here to view the video on YouTube.

ST7783 TFT driver

I’ve got a cool new docking expansion board for my STM32F4DISCOVERY that adds some common peripherals to the system including an ethernet PHY, an RS232 socket, an SD cage and of course a QVGA LCD with a touch panel.

The driver IC for this panel is the ST7783 and I am pleased to announce support for it with 64K and 262K colours in portrait and landscape modes. The driver names are:

ST7783_Portrait_64K
ST7783_Landscape_64K
ST7783_Portrait_262K
ST7783_Landscape_262K

I also put together a short video that shows it in action. The F4 drives this panel very quickly indeed.

Click here to view the video on YouTube.

AT24C32/64 serial EEPROM support

Unlike some other devices such as the 8-bit AVRs the STM32 doesn’t have any EEPROM memory included on-chip although it is possible to emulate it to some extent by reading and writing the internal flash memory at runtime.

If you need real EEPROM support then you have to purchase and wire up an external IC.

The Atmel AT24C32 and AT24C64 are 32/64Kbit serial EEPROM devices that are controllable via an I2C bus. The STM32 I2C on-chip peripheral is ideally suited to communicating with these memories.

stm32plus provides two drivers named ‘AT24C32′ and ‘AT24C64′ to manage communication with these devices. Both drivers inherit from InputStream and OutputStream so you can use all the methods that the stream interfaces provide.

The driver class is templated with the I2C interface that you are going to use to communicate with it. An example declaration might be:

#include "config/stm32plus.h"
#include "config/i2c.h"
#include "config/eeprom.h"

typedef AT24C32<
  I2C2_Default<I2CTwoByteMasterPollingFeature>
> MyEeprom;

I2C::Parameters params;
MyEeprom eeprom(params);

A full example is included in the ‘examples/i2c_at24c32′ directory.

Changelog

Here’s the changelog for version 2.1.0 in full.

Added “TypeB” Nokia N95 8Gb driver (LDS285 controller). stm32plus now supports both types of this cellphone display that I’ve found so far.

Accepted LGDP453x TFT driver and demo contributed by Andy Franz. Thanks Andy!

Added drivers for SSD1289, SSD1963 and ST7783 LCD interfaces. Demos are included.

Change compile flags to C++0x with GNU extensions. This will facilitate a migration to variadic templates for the feature classes. The motivation for the migration is cleaner and smaller code generation.

Ported all peripheral classes that allowed template features to variadic templates.

Ported all LCD interface declarations from subclasses to template typedef aliases. Removed the colour depth suffix from graphic terminal class names, i.e. remove _64K, _262K suffixes. This is because there’s no difference between the colour depths making the suffix irrelevant.

Replaced SmartArray.h with scoped_array.h and scoped_ptr.h lifted from the open source Chromium project with appropriate credits.

Added reset() method to MillisecondTimer to bring the clock back down to zero.

Fixed STL slist for modern, stricter compilers.

Increased reliability of ADS7843 touch screen by preventing the pen interrupt from occurring while sampling is taking place.

Replaced the 3 GraphicTerminal* classes with a single GraphicTerminal template. The terminal declarations for each LCD interface have been updated accordingly. Graphics terminal declarations have now lost the colour depth suffix. So for example:

ILI9325_Terminal_Portrait_262K<LcdAccess> 

would now be:

  
ILI9325_Terminal_Portrait<LcdAccess>

Also, the terminal class constructor is now a reference and not a pointer.

Implemented AT24C32/64 serial EEPROM classes with I2C interface. An example program is provided. Include “config/eeprom.h” to get access to the class.

Created StreamBase base class for InputStream and OutputStream and lifted up the error codes into the base as well as the common close() interface method.

Arduino Mega 512K SRAM in shield format

$
0
0

Some time ago now I presented the design, development board and software driver for an add-on to the Arduino Mega that gave it access to 448Kb of additional SRAM arranged in 8 banks of 56Kb.

My design was realised by a compact add-on board that plugged into the relevant block of pins on the end of the Arduino Mega.

Recently I have been in touch with a fellow hacker, Colin Irwin of langrangianpoint.net, who also has an interest in Arduino projects. Colin adapted my design into a full Arduino shield format and was kind enough to send me a kit of parts to evaluate.

Colin’s Design


The bare PCB follows the format for Arduino Mega shields. Colin has made it a little easier to solder straight connectors by adopting the slightly staggered holes popularised by the guys over at Sparkfun.

Building it

I did the build with what is now my usual technique. Firstly I tin the pads on the board with a soldering iron, then I reflow the fine-pitch components using a hot-plate and finally the discrete SMD components are reflowed into place using a hot-air gun. When it’s all done I inspect under a microscope and touch up any joints that look insecure.

Here’s the finished item plugged on top of my Mega 128. The shield completely covers the Arduino and you can only just see the USB port poking out from the side.

As I’m very familiar with the design I wasn’t expecting any problems and it proved just so with the board working first time.

Finally

Colin’s design represents a logical step forward from my original design. It feels very solid when built and plugs in firmly to the Arduino underneath. Anyone wanting to find our more or to get their hands on one of these shields should visit Colin’s website at langrangianpoint.net.

Reverse engineering the Nokia E73 QVGA LCD

$
0
0

Readers with a keen memory will no doubt recall that I said that the N93 would probably be the last of the Nokia QVGA LCDs that I attempt to reverse engineer.

However probably is not definitely and one day whilst browsing ebay my finger slipped and next thing I knew I’d got myself an LCD for a Nokia E73 that was just crying out to be attached to my MCUs. So here we go, another in the series of articles in which I present a low-cost Nokia QVGA LCD that you can use as a display in your own projects.

The E73 LCD


It’s not a blackberry, really!

The E73 LCD is a 2.4″ display that differs slighly from the others that I’ve presented so far in that it’s designed to be used in a landscape (horizontal) aspect whereas the others have been designed to be used in a portrait (vertical) aspect.

All the displays can be put into landscape or portrait mode, the difference is in which direction hardware scrolling is possible. On the E73 hardware scrolling in landscape mode will move up and down. On the others hardware scrolling in portrait mode will move up and down.

The above image will give you an idea of the relative size and aspect of the E73 LCD (left) compared to the 2.0″ Nokia 6300 LCD (right).

According to the wide variety of sellers on ebay the E73 LCD is also compatible with the E71, E71X, E72 and E63 MS,

The connector

The connector is the same 24-pin JST 24R-JANK-GSAN-TF board-to-board connector made by JST that is used in all the Nokia displays that I have reverse engineered so far.

In previous articles I have linked to JST’s web-shop as a good source for these connectors. However several people have recently written to me saying that JST are now rejecting direct orders for these connectors, giving a reason that they are ‘special connectors for mobile phones’ and directing people to their distributors.

I suspect that JST’s distributors have exclusive sales agreements that would be undermined by the manufacturer offering direct sales and that’s the reason why they are now directing people to them.

The connector is available at very low cost from many resellers, for example:

gsmserver.com
cellnetos.com
stellatech.com

Connector schematic

The schematic for the Nokia E73 is readily available on the internet if you google for it, though, like the others it was probably never intended for release to the general public. Here’s a screen grab of the relevant part that documents the LCD connector.

All the usual 8080 bus and control connections are there, as are the LCD backlight circuit in and out connections. As this is a more recent Nokia phone than some of the others there is evidence of refinement to the design such as the elimination of the unnecessary CS input and the single in and out connectors for the backlight array.

Given my previous experience with the other phone models I was not expecting any surprises from this one. So let’s design a schematic for connecting this display to an MCU.

Development board schematic




Click to download a PDF of the schematic

The development board is designed to operate with the Arduino Mega XMEM interface though it will work with any MCU that operates in the 2.8V to 5V voltage range.

There are one or two design improvements since the N93. Most noticeably I have dispensed with the need for a 5V input. It was only ever used to power the backlight boost converter and we can do that with the 3.3V input so in the interests of reducing complexity that’s what I’ve done.

Another improvement is that I’ve routed the TE (Tearing Effect or Vsync) signal out from the LCD to an Arduino pin where it can be used to synchronise writes to the LCD with the display refresh signal to avoid flickering. It’s a CMOS (3.3V) level but should still be enough to trigger an Arduino input high level.

Level conversion

Level conversion is provided by the same pair of OnSemi MC74VHC50 hex buffers that I used successfully in the N93 reverse engineering project.

The SOIC-14 footprint with its 1.27mm pin pitch is really easy to work with, positively huge compared to the LCD connector!

Backlight boost circuit

In common with most of the other Nokia LCDs that I’ve reverse engineered this one has a backlight circuit consisting of four white LEDs in series. Assuming a forward voltage of roughly 3.2v for a white LED that means you need around 12.8v to power it.

As in all the previous projects I’ll be using the ever reliable OnSemi constant current LED driver to do the job. It needs only the minimum of external components: an inductor, a schottky diode, a level setting resistor and two capacitors. In return it’ll provide me with a constant 20mA output and an ‘enable’ pin that can be used for PWM dimming.

Bill of Materials

Here’s the complete bill of materials for the schematic.

Part Value Device Package
P1 NOKIA12X2 24R-JANK-GSAN-TF 12X2
P2 pin header MA12-2 MA12-2
P3 pin header MA2-2 MA2-2
C1 4.7µF ceramic 0805
C2 1µF ceramic 50V 0805
C3,C4,C5,C6 100nF ceramic 0603
D1 Diodes Inc. BO530W-7 schottky SOD-123
L1 Sumida 22µH CDRH5D28NP-220NC 6x6mm
R1 10Ω 1% resistor 0805
R2 33kΩ resistor 0805
U1 NCP5007 LED driver SOT23-5
U2,U3 MC74VHC50DR2G hex buffer SOIC-14


D1, the schottky diode can be almost any schottky in SOD-123 format. I’ve used the Diodes Inc. BO530W and the ST Micro STPS0520Z interchangeably.

R2 is a pull-up and can take a wide variety of values, 10kΩ all the way up to 100kΩ would probably be fine. I’ve used 33kΩ and 43kΩ with equal success.

The CAD design

After creating the schematic and selecting the footprints for the components then it’s time to create the CAD for the board.

The board was manually routed, I haven’t used an auto-router for some time now because even when they do manage to route to 100% I find some of their routing decisions questionable to say the least.

With hindsight I should have paid more attention to the position of the in/out signals on the level converter IC. There are buffers available that have the in’s and the out’s on opposing sides of the chip which makes routing much easier on a board like this. The footprint that I chose has the in’s and out’s adjacent to each other which means more vias and slightly more complex routing than I could otherwise have got away with.

Building the board

Once I was happy with the CAD I exported the Gerber files and uploaded them to ITead Studio for manufacturing.

About three weeks later the package arrived from China and, as usual, they were perfectly made. I like to think that my designs are conservative and I try to stay well clear of the published limits and tolerances to ensure that I don’t get any nasty surprises when the boards arrive.

My assembly method is essentially unchanged from what I usually do. Firstly all the pads on the board are tinned using a highly active flux, then the board is cleaned of all the flux residue because the acid in the flux would be damaging to the board over the long term. After cleaning I only use water-soluble flux from that point onwards.

The second step is to use a hot plate to reflow the connector, the three ICs and the inductor into place. This step is finalised by touching up the connections with a fine iron under a microscope.

The third step is reflow the remaining SMD components into place with my hot air gun and finally the pin headers are soldered in using a normal iron.

When it’s all assembled I wash the board in hot soapy water, rinse it and leave it to dry for 24 hours.

Software and testing

Before starting any of this I knew it was going to work because I’d prototyped the connections using my generic Nokia hacking board.

With the panel connected to the generic board I was able to make the necessary changes to the Arduino driver software to cater for the differences between this panel and the others.

The differences were minimal and were mainly centered around the fact that this panel is normally landscape (320×240 pixels) instead of the portrait (240×320 pixels) mode of all the other screens. It was not hard to adjust my driver code to cope with this without breaking all the other drivers.

I have provided full support for the E73 in my Arduino library, version 2.5.0 and above available from my downloads page. To use the E73 panel you simply need to include its header file and change the name of the declared driver to match the E73.

#include "NokiaE73.h"
 
using namespace lcd;
 
typedef NokiaE73_Landscape_262K TftPanel;

Portrait and landscape modes are supported, as is hardware scrolling. I have provided support for 16M, 262K and 64K colour modes although the panels that I have seen so far only support the high-resolution colour modes: 16M and 262K.

Here’s a short, out of focus and poorly lit demo video on YouTube that shows the LCD board in action. Click on the ‘YouTube’ logo at the bottom right to watch it in a larger format at the YouTube site.

Click here to view the video on YouTube.



Some boards for sale

I’ve constructed the remaining PCBs that I have and am selling them on a first come first served basis. The price including delivery is £16.90 for UK and £18.49 for anywhere else in the world. They come with the LCD fitted and are ready to plug in and play.


Location




A generic optimised 16-bit LCD adaptor for the Arduino

$
0
0

There are many TFT modules available on the market that are designed to connect directly to an MCU to provide a full colour graphical display, just search ebay for “tft module” to see what I mean.

Unfortunately for Arduino users the majority of these modules expose a 16-bit interface and are designed to be connected to ARM devices. The way I see it there are two problems to be solved here in order to allow them to be connected to an Arduino. Firstly, they are always designed to be run at 3.3V and secondly the 16-bit data bus uses up a ton of GPIO pins.

I’m going to present a combination of hardware and software that solves both of those problems and also shows how you can achieve extremely high performance with a few driver optimisation tricks.

The hardware adaptor

I decided to design an adaptor that would feature 5V to 3.3V level conversion as well as a latch that would allow me to reduce the pins required by the data bus from 16 to 8 at the expense of an additional pin for controlling the latch. A total saving of 7 pins over the simplistic solution.

How does the latch work?

The latch works sort of like a small memory. In ‘transparent’ mode signals pass right through it and come straight out the other side. When you flip to ‘latched’ mode the latch ignores its inputs and continues to output the last values it received on those inputs.

I can use these properties of the latch to push out two 8-bit values in sequence and have the latch ‘expand’ them to 16-bits.

The diagram shows the two phases of the operation of the latch. In the first phase I push out the low 8-bits which gets routed through and around the latch to occupy both the low (which I want) and the high (which I don’t want) bytes of the output. In the second phase I lock the latch and then push out the high 8-bits. The latch ignores this new data and so the only route is around the latch into the correct position in the outputs.

Another advantage of the latched design is that it dovetails nicely with the external memory (XMEM) peripheral that comes built in to the Arduino Mega 1280 and 2560. With a little bit of clever code I should be able to use the XMEM interface to drive the LCDs.

The schematic




Click the image to download a PDF

The schematic pulls together the level conversion, latch, a connector designed to directly fit the Arduino Mega and an output connector for the 16-bit LCD. I’ll explain each sub-section of the schematic here.

The Arduino Mega Connector

This a 10×2 male pin header with pin assigments designed to match the Arduino Mega’s XMEM interface that is exposed in the block of pins starting at 23. The letter ‘H’ in the net designators indicates that these are high level 5V signals. I should probably have used ‘T’ for TTL but there you go.

The data and control signals are all there, including CS (chip-select) that I map to ALE (address latch enable) on the XMEM interface even though this signal is rarely required by the LCD panels and can usually be just tied to ground.

The level converters

The SN54LVTH2245 octal bus transceiver from Texas Instruments is an ideal choice for level conversion in this design. The propagation delay at 3.3V is around 2ns which is very fast. The inputs and outputs are on opposite sides of the package which simplifies routing and it’s easy to find the device on the market.

The latch

The SN74LV573APW transparent D-Type latch, again from Texas Instruments, satisfies the speed requirements imposed by the XMEM interface and is easy to find on the market. Latches, like bus transceivers are readily available in all shapes and sizes and there’s nothing to stop you using different parts as long as the specifications are similar to these.

The control header

This is a 2×2 pin male header that I’ll use to provide the vital power and ground supplies from the Arduino board. The two ‘user’ inputs U1 and U2 can be used to convert any arbitrary output signal from the Arduino from 5V down to 3.3V. I added these because I had some unused ports on the level converters and it did seem a shame to waste them. Later on you will see how I use one of these to provide a PWM backlight control to the LCD panels.

The output connector

This connector provides the output signals for the LCD. In my design I’ll be using a right-angled male header to simplify the rats nest of interconnecting wires that need to be connected here.

As well as the output signals there are pairs of power and ground outputs. You can’t have too many of those!

Bill of materials

Here’s a table showing the full bill of materials (BOM) for this design.

Description Designator Footprint
Ceramic capacitor, 100nF C1, C2, C3 0603
Ceramic capacitor, 10µF C4 0805
Header, 13×2, right angle P1 HDR2X13H
Header, 10×2 P2 HDR2X10
Header, 2×2 P3 HDR2X2
SN74LV573APW transparent D-Type latch U1 TSOP-20
SN54LVTH2245 octal bus transceiver U2, U3 SOIC-20


The PCB design







Click images for PDFs

Manually routing the board didn’t present any real challenges and I was able to fit the whole design into a 45mm by 35mm PCB. Thankfully this time I’d remembered to check that the level converters had inputs and outputs on opposing sides of the package instead of adjacent to each other.

Building the board

Once the PCB layout was finalised and the Gerber CAM files exported I then uploaded them to ITead Studio’s manufacturing service and waited the requisite 2-3 weeks for them to arrive in the post.

As usual the quality is spot-on with no defects evident at all. This time I went with a blue solder mask so that it would match the PCB colour of the Arduino Mega that it’s designed to slot into.

Now it’s time to build it.

My procedure for building a PCB with surface mount components remains unchanged. Firstly I tin all the pads with a soldering iron and some highly active flux that makes for easy drag soldering across the small IC pads. Then I thoroughly clean all the flux residue off the board because the acids can attack the board over the long term.

Now the pads are tinned I use a hot-plate to reflow the ICs into place and touch up the reflowed joints afterwards under a binocular microscope. The discrete components are then reflowed into place with my Aoyue 852A hot air gun and finally the pin headers are soldered into place with an ordinary soldering iron. The final touch is to wash the boards in hot soapy water and leave to dry for 24 hours.

Now I’ve got something that I can play with it’s time to see if I can come up with some driver software that will do it justice. Something tells me I can…

The software drivers

My previous Nokia QVGA reverse engineering efforts that you can find documented extensively on this site have left me with a flexible and expandable driver that will make a good base from which to add support for this new adaptor.

The driver code is separated into access modes and panel drivers that are bound together with a graphics library at compile-time by using C++ templates. To support this new adaptor I will need to write at least one new access mode and as many panel drivers as are required to support the panels that I own.

Let’s get started by taking a look at the 16-bit XMEM access mode.

The 16-bit xmem access mode

The XMEM interface built into the Arduino Mega MCU is designed to provide access to external memories with a 16-bit address bus and an 8-bit data bus. It just so happens that the protocol for accessing these memories is close enough to the 8080 protocol required by the LCDs that I can sometimes use it successfully. I say ‘sometimes’ because external factors such as the panel timing parameters do come into play.

So, when the Arduino Mega wants to write to an external memory it does the following:

  1. ALE is driven high.
  2. It drives the 16-bit address on to the bus where the lower 8 bits are multiplexed with the data lines.
  3. ALE is driven low, locking in the lower 8-bits of the address.
  4. It pules the WR line from high to low and to high again. Data is transferred on the rising edge of the pulse.

I can make use of this sequence to transfer 16-bits to the LCD in a single transaction that also includes the RS (register/data select) line. I will use the lower 8-bits of the 16-bit address to transfer the lower 8-bits of LCD data. I will use address line A8 to hold the RS signal and I’ll use the 8 data lines to transfer the upper 8-bits of LCD data. I will also set bit 15 of the address so that I’m guaranteed to be in the external memory range and will not collide with the internal 8Kb of SRAM.

Let’s take a look at the pinout for this access mode:

Arduino Port Function
22 PA0 D0/D8
23 PA1 D1/D9
24 PA2 D2/D10
25 PA3 D3/D11
26 PA4 D4/D12
27 PA5 D5/D13
28 PA6 D6/D14
29 PA7 D7/D15
35 PC2 /RESET
37 PC0 RS
39 PG2 /CS
41 PG0 /WR


Initialising the XMEM interface is a simple matter of writing the correct values to the two XMEM registers. The XMEM pins 30..34 (PC3..PC7) are free’d up for GPIO.

inline void Xmem16AccessMode::initialise() {

  // set up the reset pin
  pinMode(RESET_PIN,OUTPUT);
  digitalWrite(RESET_PIN,HIGH);

  // set up the xmem registers

  // free PC3..PC7 for user GPIO
  XMCRB=_BV(XMM1) | _BV(XMM2);    

  // enable xmem, no wait states
  XMCRA=_BV(SRE);
}

Because performance is absolutely paramount I wrote the data and command output functions in AVR assembly language.

inline void Xmem16AccessMode::writeData(uint8_t lo8,uint8_t hi8) {

  // this is equivalent to:
  // *reinterpret_cast<volatile uint8_t *>(0x8100 | lo8)=hi8;
  // this method costs 5 clock cycles

  __asm volatile("  ldi r27,0x81  \n\t"
                 "  mov r26,%0    \n\t"
                 "  st  X,%1      \n\t"
                 :: "d" (lo8), "d" (hi8)
                 : "r26", "r27");
}

In AVR assembly language the combination of r26 and r27 make up a 16-bit X register that we use in the above code to send the 8-bit data value to the external memory. It’s also helpful that r26 and r27 are free for use in inline assembly code without fear of colliding with code emitted by the C++ compiler. Nevertheless I do declare them in the ‘clobber’ list at the end of the __asm section.

The method used to write a command is almost the same, except bit A9 is set to zero:


inline void Xmem16AccessMode::writeCommand(uint8_t lo8,uint8_t hi8) {

  // this is equivalent to: 
  // *reinterpret_cast<volatile uint8_t *>(0x8000 | lo8)=hi8;
  // this method costs 5 clock cycles

  __asm volatile("  ldi r27,0x80  \n\t"
                 "  mov r26,%0    \n\t"
                 "  st  X,%1      \n\t"
                 :: "d" (lo8), "d" (hi8)
                 : "r26", "r27");
}

To see this access mode in action we need a logic analyser so I hooked up the output of the adaptor to my Ant18e and captured the lower 8 data lines, RS and WR.




Click for larger

I can see from the section between the two timing markers that the AVR core pulses WR for exactly one clock cycle.

Some of the panels that I have are not happy with the tight timings provided by the XMEM interface, even when I add some address and data hold cycles so for this reason I decided to branch out and build an optimised GPIO access mode. I’m glad that I did because in some areas the optimisations that I created resulted in a huge performance increase.

The 16-bit gpio access mode

The 16-bit GPIO access mode emulates the automatic operation of the XMEM interface by using ordinary GPIO. There are advantages and disadvantages to this approach. Single writes are going to be a few cycles slower than the XMEM interface but for multiple writes of the same value I can optimise heavily to create a routine that will outperform anything else as long as the panel supports 16-bit (64K colour) mode. The other advantage is that I’m not restricted to the XMEM pins and I could even get it to work on the ordinary 32Kb Arduino.

Gpio16LatchAccessMode is a template that takes a type containing my port and pin mappings as its parameter. For example, using the same ports and pins as Xmem16AccessMode I declare this type:

struct Gpio16LatchAccessModeXmemMapping {
    enum {

      // ports are the I/O index, not the physical address

      PORT_DATA  = 0x02,    // PORTA
      PORT_WR    = 0x14,    // PORTG
      PORT_RS    = 0x08,    // PORTC
      PORT_ALE   = 0x14,    // PORTG
      PORT_RESET = 0x08,    // PORTC

      // pins are the 0..7 port index, not the arduino numbers

      PIN_WR    = PIN0,
      PIN_RS    = PIN0,
      PIN_ALE   = PIN2,
      PIN_RESET = PIN2
    };
  };

The Gpio16LatchAccessMode template looks like this. You can see that I also provide a concrete instantiation of the template using a typedef for easy use.

template<typename TPinMappings>
class Gpio16LatchAccessMode {

  protected:
    static uint8_t _streamIndex;
    static void initOutputHigh(uint8_t port,uint8_t pin);

  public:
    static void initialise();
    static void hardReset();

    static void writeCommand(uint8_t lo8,uint8_t hi8=0);
    static void writeCommandData(uint8_t cmd,uint8_t data);
    static void writeData(uint8_t lo8,uint8_t hi8=0);
    static void writeMultiData(uint32_t howMuch,uint8_t lo8,uint8_t hi8=0);
    static void writeStreamedData(uint8_t data);
};

typedef Gpio16LatchAccessMode<Gpio16LatchAccessModeXmemMapping> DefaultMegaGpio16LatchAccessMode;

I’ll skip the initialise method because I’m sure you’re not particularly interested in code that just sets the direction and level of GPIO pins. Let’s get straight on to the writeData() and particularly the writeMultiData() methods. Firstly, writeData(), the method that writes out a single value:

template<class TPinMappings>
inline void Gpio16LatchAccessMode<TPinMappings>::writeData(uint8_t lo8,uint8_t hi8) {

  __asm volatile(
      "  sbi %1, %5   \n\t"     // ALE   = HIGH
      "  out %3, %7   \n\t"     // PORTA = lo8
      "  sbi %2, %6   \n\t"     // RS    = HIGH
      "  cbi %1, %5   \n\t"     // ALE   = LOW
      "  out %3, %8   \n\t"     // PORTA = hi8
      "  cbi %0, %6   \n\t"     // /WR   = LOW
      "  sbi %0, %6   \n\t"     // /WR   = HIGH

      :: "I" (TPinMappings::PORT_WR),     // %0
         "I" (TPinMappings::PORT_ALE),    // %1
         "I" (TPinMappings::PORT_RS),     // %2
         "I" (TPinMappings::PORT_DATA),   // %3
         "I" (TPinMappings::PIN_WR),      // %4
         "I" (TPinMappings::PIN_ALE),     // %5
         "I" (TPinMappings::PIN_RS),      // %6
         "d" (lo8),                       // %7
         "d" (hi8)                        // %8
        );
}

Straightforward stuff. We just bit-bang the 8080 protocol with the additional overhead of the latch programming.

Now, it’s very common in graphics libraries to write out solid blocks of colour. Whether you’re clearing the screen, drawing rectangles or just straight lines it all comes down to setting an ‘output window’ on the display and then pumping out pixels.

The writeMultiData() method takes advantage of the fact that you only need to set the data and RS lines once and then you can toggle the /WR line as often as you need to in order to write out your block of pixels. This technique was first suggested to me by Andrew, the author of this instructable. Here’s my take on the method.

template<class TPinMappings>
inline void Gpio16LatchAccessMode<TPinMappings>::writeMultiData(uint32_t howMuch,uint8_t lo8,uint8_t hi8) {

  __asm volatile(
    "    sbi  %9, %7       \n\t"      // ALE   = HIGH
    "    out  %1, %2       \n\t"      // PORTA = lo8
    "    sbi  %3, %8       \n\t"      // RS    = HIGH
    "    cbi  %9, %7       \n\t"      // ALE   = LOW
    "    out  %1, %4       \n\t"      // PORTA = hi8

    "    clr  r20          \n\t"      // r20 = global interrupt status
    "    brid intdis%=     \n\t"      // branch if global interrupts are off
    "    cli               \n\t"      // disable global interrupts (because we cannot let PORTG get modified by an IRQ)
    "    inc  r20          \n\t"      // r20 (global interrupts disabled) = 1
    "intdis%=:             \n\t"
    "    in   r18, %0      \n\t"      // get PORT_WR to r18 and r19
    "    mov  r19, r18     \n\t"
    "    cbr  r18, %6      \n\t"      // clear WR in r18
    "    sbr  r19, %6      \n\t"      // set WR in r19
    "    cpi  %A5, 40      \n\t"      // if howMuch<40 then jump to lastlot
    "    cpc  %B5, r1      \n\t"
    "    cpc  %C5, r1      \n\t"
    "    brsh batchloop%=  \n\t"
    "    rjmp lastlot%=    \n\t"
    "batchloop%=:          \n\t"
    "    out  %0,  r18     \n\t"      // toggle /WR 40 times
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    subi %A5, 40      \n\t"        // subtract 40 from howMuch
    "    sbci %B5, 0       \n\t"
    "    sbci %C5, 0       \n\t"
    "    cpi  %A5, 40      \n\t"        // if howMuch >= 40 then go back for another batch
    "    cpc  %B5, r1      \n\t"
    "    cpc  %C5, r1      \n\t"
    "    brlo lastlot%=    \n\t"
    "    rjmp batchloop%=  \n\t"
    "lastlot%=:            \n\t"        // load index Z with the address of the end
    "    ldi  r31, pm_hi8(finished%=)   \n\t"
    "    ldi  r30, pm_lo8(finished%=)   \n\t"
    "    lsl  %A5          \n\t"        // multiply remaining by 2
    "    sub  r30, %A5     \n\t"        // subtract remaining*2 from Z
    "    sbci r31, 0       \n\t"
    "    ijmp              \n\t"        // jump to Z to finish off the writing
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "    out  %0,  r18     \n\t"
    "    out  %0,  r19     \n\t"
    "finished%=:           \n\t"
    "    cpi  r20, 0       \n\t"          // if global interrupts were enabled when we came in, restore them now
    "    breq skipinten%=  \n\t"
    "    sei               \n\t"
    "skipinten%=:          \n\t"

    :: "I" (TPinMappings::PORT_WR),     // %0
       "I" (TPinMappings::PORT_DATA),   // %1
       "d" (lo8),                       // %2
       "I" (TPinMappings::PORT_RS),     // %3
       "d" (hi8),                       // %4
       "d" (howMuch),                   // %C5,%B5,%A5
       "I" (_BV(TPinMappings::PIN_WR)), // %6
       "I" (TPinMappings::PIN_ALE),     // %7
       "I" (TPinMappings::PIN_RS),      // %8
       "I" (TPinMappings::PORT_ALE)     // %9
    : "r18", "r19", "r20", "r30", "r31"
  );
}

This is how it works.

  1. We program RS and set up the 16 data lines.
  2. If interrupts were enabled when we came in then I disable them. As well as ensuring a consistent timing this ensures that an ISR cannot change the port values that I set up in (3).
  3. The port holding WR is read and I cache this value in registers with the WR bit set and reset.
  4. If there are fewer than 40 pixels to write then go to step 7.
  5. Write out 40 pixels using consecutive out commands. This achieves an 8 megapixel/second fill rate.
  6. Subtract 40 from the pixel counter and if more than 40 remain then go back to step 5.
  7. Calculate an indirect jump into the trailing 39 out commands that will fill the remaining pixels and use ijmp to go there.
  8. If we disabled interrupts then restore them.

The number 40 was chosen somewhat arbitrarily as a trade off between flash memory usage and performance. I tried values of 10, 20, 30, 40 and stopped there as the increase in speed was getting smaller and smaller to the point of irrelevance.

Once again, to see the effect of this code a logic analyser is required. Hooking up the trusty Ant18e yielded these captures.







Click for larger

The first capture shows the effect of the optimised /WR toggling. As expected we are achieving a fill rate of 8 megapixels/second.

The second capture shows how much overhead is added by the code that subtracts 40 from the total and jumps back to execute the next block of 40 pixels. It is 1 microsecond.

So is it worth this hand-optimisation? Yes it definitely is. In the following sections where I show the panel drivers that I’ve written, the accompanying videos are all running this driver. You can see how fast the screen clearing, rectangle drawing, gradient drawing and solid ellipse drawing operations are for yourself.

Optimising the line drawing algorithm

The first versions of my driver code featured the Extremely Fast Line Drawing Algorithm. It seemed to perform well and so I didn’t turn my attention to optimising it.

Recently however, I was tipped off that the EFLA was suboptimal on an MCU without an intrinsic divide instruction and that the classic Bresenham would probably be be better. Time for some profiling. I implemented the Bresenham algorithm using the pseudo-code in the Wikipedia article under the Simplification header.

My pseudo-random line drawing example code runs over a fixed 5 second period. These are the timings that I got on an ILI9325 panel in 64K colours and portrait mode:

Algorithm Lines drawn in 5 seconds
EFLA 1055
Bresenham 1110


Sure enough Bresenham is slightly faster. Not much, but enough to get me thinking about whether it could be further optimised for this library. In producing an optimised implementation we rely on some basic facts:

  1. All the pixels in the line are the same colour.
  2. The panels provide ‘windowed’ output with auto-increment in the X-direction.
  3. The panels allow X and Y positions to be changed independently.

Here’s the resulting code:

template<class TDevice,class TAccessMode>
inline void GraphicsLibrary<TDevice,TAccessMode>::drawLine(const Point& p1,const Point& p2) const {

  // optimisation for straight lines. filling rectangles is much more efficient than plotting points

  if(p1.X==p2.X)
    fillRectangle(Rectangle(p1.X,Min(p1.Y,p2.Y),1,Abs(p2.Y-p1.Y)+1));
  else if(p1.Y==p2.Y)
    fillRectangle(Rectangle(Min(p1.X,p2.X),p1.Y,Abs(p2.X-p1.X)+1,1));
  else {

    int16_t x0,x1,y0,y1;

    x0=p1.X;
    y0=p1.Y;
    x1=p2.X;
    y1=p2.Y;

    if(x0>x1) {
    
      // the optimiser does this swap method faster than
      // the xor-trick

      int16_t t;

      t=x0;
      x0=x1;
      x1=t;

      t=y0;
      y0=y1;
      y1=t;
    }

    // calculate constants up-front

    int16_t dx=x1-x0;
    int16_t dy=Abs(y1-y0);
    int16_t sy=y0<y1 ? 1 : -1;
    int16_t mdy=-dy;
    int16_t err=dx-dy;
    bool xinc;

    // set the drawing rectangle that we need and plot the first point

    this->moveTo(x0,y0,this->getXmax(),this->getYmax());
    this->beginWriting();
    this->writePixel(_foreground);

    while(x0!=x1 || y0!=y1) {

      int16_t e2=2*err;

      if(e2>mdy) {

        err-=dy;
        x0++;

        // make a note that X has incremented

        xinc=true;
      }
      else
        xinc=false;       // nothing happened to X

      if(x0==x1 && y0==y1) {

        if(xinc) {

          // plot the pending X increment before returning

          this->writePixelAgain(_foreground);
          break;
        }
      }

      if(e2<dx) {
        err+=dx;
        y0+=sy;

        // Y has changed. We're going to have to do a complete
        // pixel write after we've moved the bare minimum of
        // window pointers

        if(xinc)
          this->moveX(x0,this->getXmax());

        this->moveY(y0,this->getYmax());

        this->beginWriting();
        this->writePixel(_foreground);
      }
      else {

        // Y has not changed, if X has changed then all we need
        // to do is push out another pixel

        if(xinc)
          this->writePixelAgain(_foreground);
      }
    }
  }
}

The algorithm should be recognisable from the Wikipedia article. I initially set a window that extends from the first point to the far right and bottom of the panel. This means that in cases where two horizontal pixels have to be written out together I can omit the cursor movement entirely because the panel has done it automatically for me.

When I do have to move the cursor I am careful to only move it in the horizontal or vertical direction that’s required.

Running the line demo with this optimised implementation results in a score of 2060 lines, a solid 85% speed increase for very little effort. Here’s a chart showing the final scores:

The actual speed increase obtained is variable and the flatter the line the better the increase. My demo draws pseudo-random lines so the speed increase that I quote could be viewed as an average.

Now that I have a solid pair of access modes I can move on to the driver templates that call upon their services to perform primitive operations.

ILI9325

The ILI9325 is a very common QVGA controller found in many of the standalone panels that you see on the market as well as in panels that ship with MCU development boards. The ILI9325 panel that I have came packed with an STM32 development board, but there’s nothing to stop me detaching it and hooking it up to the Arduino as I’ve done here.

I’ve provided support for portrait and landscape orientations in the 64K (16-bit) colour mode. Hardware scrolling is supported in both orientations. To use the driver you need to include the header file.

#include "Generic16BitILI9325.h"

Typedefs are provided for all combinations of orientations and access modes. In all my examples I declare a panel using code similar to this:

typedef ILI9325_Landscape_64K_Gpio16Latch TftPanel;
//typedef ILI9325_Portrait_64K_Gpio16Latch TftPanel;
//typedef ILI9325_Landscape_64K_Xmem16 TftPanel;
//typedef ILI9325_Portrait_64K_Xmem16 TftPanel;
//TftPanel *tft=new TftPanel;

The commented out drivers are provided for reference. The first two are GPIO access modes and the last two are the XMEM modes.


My touch screen is looking a bit scuffed

Version 3.0.0 and above of the xmemtft library provides support for the ILI9325 and includes a suite of demos that you can use to get started. Here’s a video that shows the graphics library demo in action using the optimised GPIO driver.

Click here to view the video on YouTube.



ILI9327

The ILI9327 controller provides a resolution of up to 432×240 pixels. This controller is not quite as common as the QVGA devices that you see everywhere.

The panel that I have is 400×240, 32 pixels fewer than the maximum available to the driver. The driver implements this by making the first 32 pixels invisible. That is, you can write to them but nothing will appear on the screen. I cater for this quirk with a ‘traits’ class provided as a parameter to the driver template.

I’ve provided support for portrait and landscape orientations in the 64K (16-bit) colour mode. Hardware scrolling is supported in both orientations. To use the driver you need to include the header file.

#include "Generic16BitILI9327.h"

Typedefs are provided for all combinations of orientations and access modes. In all my examples I declare a panel using code similar to this:

typedef ILI9327_400x240_Landscape_64K_Gpio16Latch TftPanel;
//typedef ILI9327_400x240_Portrait_64K_Gpio16Latch TftPanel;
//typedef ILI9327_400x240_Landscape_64K_Xmem16 TftPanel;
//typedef ILI9327_400x240_Portrait_64K_Xmem16 TftPanel;
//TftPanel *tft=new TftPanel;

The commented out drivers are provided for reference. The first two are GPIO access modes and the last two are the XMEM modes.

Version 3.0.0 and above of the xmemtft library provides support for the ILI9327 and includes a suite of demos that you can use to get started. Here’s a video that shows the graphics library demo in action using the optimised GPIO driver.

Click here to view the video on YouTube.



ILI9481

The ILI9481 controller from Ilitek is a 480×320 (HVGA) controller that is less commonly found in panels that you can get on ebay. There are four times as many pixels on an HVGA panel as there are on a QVGA panel so it was interesting to see how the graphics driver performs on this panel.

I’ve provided support for portrait and landscape orientations in the 64K (16-bit) colour mode. Hardware scrolling is supported in both orientations. To use the driver you need to include the header file.

#include "Generic16BitILI9481.h"

Typedefs are provided for all combinations of orientations and access modes. In all my examples I declare a panel using code similar to this:

typedef ILI9481_Landscape_64K_Gpio16Latch TftPanel;
//typedef ILI9481_Portrait_64K_Gpio16Latch TftPanel;
//typedef ILI9481_Landscape_64K_Xmem16 TftPanel;
//typedef ILI9481_Portrait_64K_Xmem16 TftPanel;
//TftPanel *tft=new TftPanel;

The commented out drivers are provided for reference. The first two are GPIO access modes and the last two are the XMEM modes.

Version 3.0.0 and above of the xmemtft library provides support for the ILI9481 and includes a suite of demos that you can use to get started. Here’s a video that shows the graphics library demo in action using the optimised GPIO driver. I was pleased to see that the operations that make most use of optimised fill algorithm, for example the gradient and clear screen demos, perform extremely quickly.

Click here to view the video on YouTube.



HX8347A

The HX8347A controller from Himax is a 320×240 (QVGA) device commonly featured in the LCD boards that you can get on ebay.

I’ve provided support for portrait and landscape orientations in the 64K (16-bit) colour mode. Hardware scrolling is supported in both orientations. To use the driver you need to include the header file.

#include "Generic16BitHX8347A.h"

Typedefs are provided for all combinations of orientations and access modes. In all my examples I declare a panel using code similar to this:

typedef HX8347A_Landscape_64K_Gpio16Latch TftPanel;
//typedef HX8347A_Portrait_64K_Gpio16Latch TftPanel;
//typedef HX8347A_Landscape_64K_Xmem16 TftPanel;
//typedef HX8347A_Portrait_64K_Xmem16 TftPanel;
//TftPanel *tft=new TftPanel;

The commented out drivers are provided for reference. The first two are GPIO access modes and the last two are the XMEM modes.

Version 3.0.0 and above of the xmemtft library provides support for the HX8347A and includes a suite of demos that you can use to get started. Here’s a video that shows the graphics library demo in action using the optimised GPIO driver.

Click here to view the video on YouTube.



Get the source code from github

As of version 3.0.0 you can now find all the source code on github.com. If you’re interested in extending the library or just curious as to how it works then please feel free to get involved.

If you don’t have or want a github account then you can download the pre-packaged release from my downloads page.

License change

This new release is now licensed under the terms of the Apache License, version 2. Previous versions used the BSD license and the reason for the change is primarily the migration of the source code to Github. The Apache license preserves all the rights that the BSD license conveyed, formally recognises and protects the role of the contributor and includes protection against patent abuse.

Some boards for sale

As usual this project has left me with a small surplus of boards that I’m going to sell on a first come first served basis. All boards are fully constructed just like in the pictures featured in this article. All you need to do is supply a TFT board with a 16-bit interface to connect to it. If your TFT supports the 64K colour mode then you can use the optimised GPIO driver featured in this article.

If your TFT controller is one of those featured in this article then it should work ‘out-of-the-box’, otherwise you will need to write the driver code yourself, something I will provide help and advice with.


Location




stm32plus::net, a C++ TCP/IP stack for the STM32

$
0
0

Welcome to a landmark release, version 3.0.0, of my stm32plus C++ library for the STM32F1 and STM32F4 series of microcontrollers.

This release introduces support for the ethernet MAC peripheral in the form of an object-oriented TCP/IP stack as well as support for the STM32F107 connectivity line of MCUs. Furthermore, all the source code is now available on github.com to enable easy browsing and collaborative development.

Read on for all the details.

stm32plus::net

It’s been some time now since I published the designs for my ethernet PHY for the STM32F107 based on the Micrel KSZ8051MLL.

I was naturally very pleased with the success of that design and at the same time frustrated that the only usable TCP/IP stack was LwIP. Now there’s nothing functionally wrong with LwIP, it does exactly what it sets out to do and works on a wide range of processors. My problems were that it’s just a bad fit for a C++ design, doesn’t gel well with modern programming techniques, and as a general solution it can never take full advantage of everything the STM32 MAC has to offer.

So I decided to dig out my nineteen year old hardback copy of TCP/IP illustrated and put my twenty years of commercial experience of programming against the TCP/IP protocols to use and write a completely new TCP/IP stack consisting of all original code and designed to be object-oriented, very fast and very efficient.

Several months-worth of weekend and evening hacking later and it’s ready. Here’s a non-exhaustive list of some of features.

  • ARP, IPv4, UDP, TCP, ICMP, DHCP, DNS, LLIP support.
  • KSZ8051MLL and DP83848C PHY support included in MII/RMII mode.
  • IP large packet fragmentation/reassembly support.
  • IP/TCP/UDP hardware checksum offload support.
  • Hardware MAC address filtering.
  • FTP and HTTP application layer examples included.

The design

A TCP/IP stack is a many-layered affair. Each layer builds on the capabilities of the one below it in order to present new functionality.

The picture shows the network stack as implemented by stm32plus. I say, as implemented by… because purists may notice that I’ve included ICMP in the transport layer and not the network layer. That’s because it requires the services of the network layer, specifically IP, to work so I’ve lifted it up into the transport layer. Really it straddles both transport and network layers.

Declarative implementation

A design goal was to emulate the operation of the layers as closely as possible and to allow the user to pick and choose the protocols required at compile-time. If there are any incompatibilities or missing components then the program should fail to compile.

I decided to use the C++0x variadic template feature to model the stack. Each layer has its own variadic template that you can configure with the components for that layer and have it link automatically to the layers below.

For example, the Transport layer template looks like this:

template<class TNetworkLayer,template<class> class... Features>
class TransportLayer : public virtual TNetworkLayer,
                       public Features<TNetworkLayer>... {

Don’t worry if the template syntax makes your head spin, that’s a normal reaction! What’s achieved here is that we pull in all the transport layer components and give them access to the network layer as well as linking the transport layer directly to the network layer.

All the layers follow this pattern and when put together we achieve a complete stack modelled in code just like the theoretical diagram.

From the user’s point of view, configuring a stack is as simple as selecting the components that you want and then creating types for them. Here’s an example taken from one of the many sample applications.

typedef PhysicalLayer<DP83848C> MyPhysicalLayer;
typedef DatalinkLayer<MyPhysicalLayer,DefaultRmiiInterface,Mac> MyDatalinkLayer;
typedef NetworkLayer<MyDatalinkLayer,DefaultIp,Arp> MyNetworkLayer;
typedef TransportLayer<MyNetworkLayer,Udp,Tcp> MyTransportLayer;
typedef ApplicationLayer<MyTransportLayer,DhcpClient> MyApplicationLayer;
typedef NetworkStack<MyApplicationLayer> MyNetworkStack;

No code is generated here, we’re just creating types that will define the stack. Actually declaring the stack is really easy:

MyNetworkStack::Parameters params;
MyNetworkStack stack;

if(!stack.initialise(params))
  error();

// start the ethernet MAC Tx/Rx DMA channels
// this will trigger the DHCP transaction

if(!stack.startup())
  error();

Note the declaration of MyNetworkStack::Parameters. Every module in the stack has the option of exposing configuration parameters that you can change at runtime if the defaults are not to your requirements. The MyNetworkStack::Parameters structure is dynamically put together using inheritance at compile-time from the modules in your stack so you never see options that are not relevant to you and memory usage is kept to a minimum.

Inter-communication within the stack

Any stack module can expose protected or public methods and have them made available to higher layers. By convention I expose some such methods that can be considered well known and not subject to change.

Generally they will be prefixed with the name of the module. For example, the TCP module exposes some methods beginning with tcp that upper layers can use. If your application was to use the tcpConnect method but you didn’t configure in the TCP module then your application will fail to compile. That’s what we want, compile time errors are infinitely preferable to runtime errors.

That’s all very well for downward communication but what about upward and lateral communication? For example when an ethernet frame arrives at the MAC we need to pass it up the stack through the layers. We do that using a signal/slot implementation based on Don Clugston’s Fastest Possible C++ Delegates. I was so impressed by these delegates that they have now completely replaced the Observable/Observer pattern that used virtual functions throughout stm32plus.

Many modules expose events that you can subscribe to. Some of the events are internal but some of them are quite useful, particularly the error notification and frame reception events. For example, the asynchronous UDP receiver example subscribes to errors and UDP packet notifications.

// subscribe to error events from the network stack

_net->NetworkErrorEventSender.insertSubscriber(
    NetworkErrorEventSourceSlot::bind(this,&NetUdpReceiveAsyncTest::onError)
  );

// subscribe to incoming datagrams from the UDP module

_net->UdpReceiveEventSender.insertSubscriber(
    UdpReceiveEventSourceSlot::bind(this,&NetUdpReceiveAsyncTest::onReceive)
  );

Buffer handling within the stack

The stack goes to great lengths to avoid wasting cycles by copying memory buffers around. Outgoing data is transmitted in-place from the buffer that you supply and incoming data is passed up the stack with zero copying. In the UDP subscriber example above, the delegate will be called with a data pointer that points directly into the MAC’s receive buffer.

Observer out, slots and delegates in

Previous versions of stm32plus used the Observer pattern whenever the library had to call back to you. While this did work well it was not the most efficient design, did not have type-safe callback parameters and it forced your implementation class to have a vtable so you could implement the virtual onNotify callback method.

So, starting with version 3.0.0 of stm32plus the Observer pattern has been replaced by a type-safe, high-performance signal/slot implementation. As you can see in the above code sample you call the insertSubscriber method to add your callback method. There is a corresponding removeSubscriber call for de-registering your callback.

Error Handling

stm32plus::net follows the stm32plus convention of returning false from a method to indicate failure and sets the values in the global errorProvider instance of the ErrorProvider class to indicate the source and reason for failure.

In addition to this stm32plus::net will raise an error event that you can subscribe to in order to get asynchronous notification of failures. The main reason for this additional feature is that stm32plus::net does a considerable amount of work in the background for you and it needs a way to report a failure. For example, an automatically generated ICMP echo reply may fail to be sent and without the event reporting method this failure would be lost.

Here’s how to subscribe to failure events in the stack. Several of the examples do this:

// subscribe to error events from the network stack

_mystack->NetworkErrorEventSender.insertSubscriber(
  NetworkErrorEventSourceSlot::bind(
            this,
            &NetUdpSendTest::onError));

Your class method implementation of onError might look like this:

void onError(NetEventDescriptor& ned) {

  NetworkErrorEvent& errorEvent(
     static_cast<NetworkErrorEvent&>(ned)
   );

  // do something with the NetworkErrorEvent

You should be aware that, depending on the source of the error, your method may be called within the context of an IRQ. stm32plus provides a static method Nvic::isAnyIrqActive() that can be used to detect whether you are in an IRQ context.

Module documentation

Each of the modules in the stack has its own options and methods. This section details them all.

Application layer
HTTP FTP DNS DHCP LLIP Static IP
Transport layer
TCP UDP ICMP
Network layer
ARP IPv4
Datalink layer
MAC
Physical layer
PHY


Example applications

stm32plus::net ships with examples that cover all aspects of using the network stack. Here’s a list, along with a link to the source code in github.

Example Purpose
net_dhcp Demonstrates the use of the DHCP client to fetch your IP address, subnet mask, default gateway and DNS servers.
net_dns This examples demonstrates the use of the DNS client to look up a host name on the internet. In this example we will look up “www.google.co.uk”. After obtaining an IP address and our DNS servers via DHCP this example will perform the DNS lookup.
net_ftp_server This demo brings together a number of the stm32plus components, namely the network stack, the RTC, the SD card and the FAT16/32 filesystem to build a simple ftp server that listens on port 21.
net_llip This examples demonstrates the use of the Link Local IP client to automatically select an unused IP address from the “link local” class B network: 169.254/16. Link-local addresses can be used in a scenario where a DHCP server is not available, such as when a number of computers are directly connected to each other.
net_ping_client This example demonstrates the ICMP transport by sending echo requests (pings) to a hardcoded IP address (change it to suit your network).
net_tcp_client This example demonstrates a TCP ‘echo’ client. It will attempt to connect to a server on a remote computer and send it a line of text. The server will read that line of text and then send it back in reverse. An example server, written in perl, is included in this example code directory.
net_tcp_server This example demonstrates a TCP ‘echo’ server. Telnet to this server and type lines of text at it to see them echo’d back. Maximum 100 characters per line, please. Multiple simultaneous connections are supported up to the configured maximum per server.
net_udp_receive This example demonstrates how to receive UDP packets from a remote host. After obtaining an IP address via DHCP this example will wait for UDP datagrams to arrive on port 12345. When a datagram arrives it will print the first 10 bytes to USART #3.
net_udp_receive_async This example demonstrates how to receive UDP packets from a remote host. After obtaining an IP address via DHCP this example will wait for UDP datagrams to arrive on port 12345. When a datagram arrives it will print the first 10 bytes to USART #3. The reception is done asynchronously via a subscription to an event provided by the network stack’s UDP module.
net_udp_send This example demonstrates how to send UDP packets to a remote host. After obtaining an IP address via DHCP this example will send three 2Kb UDP packets to a remote host every 5 seconds. The target IP address is hardcoded into this example code and you can change it to fit your network configuration.
net_web_client This example shows how to use the HttpClientConnection to retrieve an HTTP resource. In this example we will connect to http://www.st.com and ask for the root document. We will write the response to the USART.
net_web_pframe This example demonstrates a cycling ‘picture frame’ of JPEG images downloaded from the internet and displayed on the attached LCD screen. The images are pre-sized to fit the QVGA screen and are located in a directory on my website.
net_web_server This demo brings together a number of the stm32plus components, namely the network stack, the RTC, the SD card and the FAT16/32 filesystem to build a simple web server that listens on port 80.


Preferred toolchain

The CodeSourcery EABI arm-2012.09 release is the minimum supported toolchain. Other toolchain providers may work but I cannot provide any support for them. At a bare minimum the following requirements must be met by a toolchain:

  1. C++11 support to a level compatible with gcc 4.7.x.
  2. Support for a ‘locking’ callback for the malloc() libc call. All toolchains built around ‘newlib’ support this through __malloc_lock() and __malloc_unlock(). See the LibraryHacks.cpp file in any of the network examples. This is very important.

Test systems

I use two test boards to verify the net code, each one is pictured above.

The first is the WaveShare Port107V which is an STM32F107VCT6-based board. The PHY is a Micrel KSZ8051MLL mounted on a development board that I designed myself. The KSZ8051MLL operates in MII mode.

The second board is a daughter-board for the STM32F4DISCOVERY that I picked up on ebay. You slot the discovery board into it and immediately gain access to a number of additional peripherals including an SDIO slot, a USART port, a TI DP83848C ethernet PHY, and a QVGA LCD screen attached to the FSMC. The DP83848C runs in RMII mode.

If you’re thinking of buying one of those daughterboards for yourself then generally I would recommend it as most of the peripherals such as the QVGA screen (ST7783 driver) and the ethernet PHY ‘just work’. However there are some design decisions that I consider to be poor that you should be aware of:

  • The SDIO data and control lines are not pulled up to 3V3. This means that using the SDIO interface is impossible unless you add external pullups (I use 10K). The pins that you need to pull up are PC8,PC9,PC10,PC11 and PD2.
  • The USART is hardwired to USART 3 using PC10/11 as TX/RX. These clash with SDIO D2 and D3 so you can have SDIO or USART but not both at the same time. A jumper is provided to choose. SDIO cannot be remapped, but there are four USARTs and two UARTs with a myriad of remapping possibilities so I find it frankly bizarre that the designers chose a pin-pair that clashes with an unremappable peripheral.
  • The ST7783 QVGA LCD has an SPI resistive touchscreen. The touchscreen inputs are mapped to exactly none of the three available SPI peripherals on the F4. Doh!

Get the source code from github

As of version 3.0.0 you can now find all the source code on github.com. If you’re interested in extending the library or just curious as to how it works then please feel free to get involved.

If you don’t have or want to use the git client then you can download one of the releases as a zip or tar.gz file from github.com.

Watch the video

Networks are not exactly the most photogenic of subjects, after all they’re just a bunch of wires and connectors. I’ll spare you the dubious pleasure of a presentation showing Wireshark captures and instead regale you with a short video showing the net_web_pframe example running on the STM32F4 Discovery board.

Click here to view the video on YouTube.

The example streams JPEG images from my website direct to the LCD panel and then pauses for 10 seconds before getting the next one.

License change

This new release is now licensed under the terms of the Apache License, version 2. Previous versions used the BSD license and the reason for the change is primarily the migration of the source code to Github. The Apache license preserves all the rights that the BSD license conveyed as well as formally recognising and protecting the role of the contributor and it includes protection against patent abuse.

Reverse engineering the LG KF700 480 x 240 widescreen cellphone LCD

$
0
0

Hello and welcome to my first published non-Nokia cellphone LCD reverse-engineering effort. All my articles in this series focus on bringing you all of the details that you would need in order to connect a low-cost cellphone LCD to an MCU for use in your own projects. This one is no different. I will explain the pinout and the signals. I will tell you about the connector and where you can buy it and I will tell you about the controller IC and of course I will give away the complete source code driver for that controller.

Let’s get started.

The LG KF700

The KF700 was released in 2008 and is now a discontinued model. It featured a 3.0 inch 480×240 LCD in an unusual wide-screen format with an aspect ratio of 2:1. I noticed that replacement LCDs were available cheaply for it on ebay and so I set about trying to find the schematics.

That part was easy, a simple google search for ‘KF700 schematic’ will yield the set of repair manuals including the all-important electrical information.

The schematic document is very informative and part of me is very thankful to LG for making this easier than it could have been but another part relishes the challenge and this one was less of a challenge than the others have been.

The composite photograph shows how a replacement screen looks when it arrives from ebay. There’s a protective film over the front and the back exposes the FPC cable and connector.

A small exposed square houses some unknown circuitry that, on the genuine LG part includes a green LED that lights up when power is applied.

Identifying the interface mode

If this LCD is going to stand any chance at all of being usable by a small microcontroller then it’s going to need to have an embedded controller IC. If it has a parallel or an MDDI interface then we are out of luck because those modes need an external framebuffer and a powerful microcontroller.

As luck would have it, LG published the following block diagram on page 58 of the service manual.

Those signals identify that there’s a controller IC present. Good. Next thing we need to do is identify the connector and its pinout. Again, LG are to be thanked for publishing the connector schematic on the very next page.

It’s a 40-pin board-to-board connector and all the expected signals are there plus a few that I’m going to have to take an educated guess about. I’m going to assume that EBI2_ADDR(11) is the register select (RS or D/CX) signal. At this stage I’ve no idea what LCD_IF_MODE is going to do but it only has two states so it won’t be much effort to try both.

One strong positive is that the presence of six backlight LED outputs tells us that the backlight is wired up in a parallel configuration, negating the need for a step-up DC/DC converter. A simple resistor could be used to regulate the current.

Where to buy the connector

Often this is a tricky one, even if one can identify the connector it can be impossible to find a supplier willing to sell it in small quantities. LG to the rescue again. In the appendix to the service manual they tell you the part number.

It is ENBY0045701 and it’s available from your local LG spare parts supplier. I used http://www.4ourhouse.co.uk/. Let’s take a close look at it.

It’s a 40-pin receptacle with a 0.4mm pin pitch making it difficult to hand-solder but no problem for reflow on a hot plate.

The connector orientation

With a spare LCD to hand it’s not too difficult to determine the correct connector orientation, i.e. locating pin number 1. The way I do that is to find the ground connections on the schematic and then examine the connector under a microscope.

Typically the ground connections will connect directly into the ground plane embedded in the FPC tail making them simple to identify. Once I think I’ve found them I’ll use the continuity checker on my multimeter to verify that all the pins that I think are ground are connected together.

I’ve mentioned before that one should not trust the pin numbers that are silkscreen’d on to the connector because they often do not match the schematic, and that’s the case this time. The pin labelled #1 on the connector photograph above is actually pin #40 on the schematic.

A connector footprint

I used an old copy of Protel to do the design for this project and I realise that many of you will be using CADSoft Eagle so I’m going to give you a little help with the connector footprint.

First, here’s the schematic and footprint view.

Each pad is 0.2mm x 1mm and I have specified a solder-mask expansion value of 0.051mm.

The pad at the top-left with the crosshair on it is centered at position (0,0). The pad to the right of it is at position (0.4mm,0).

The pad at the bottom left is at position (0,-3.38mm). The pad to the right of it is at position (0.4,-3.38mm).

I have not included pads for the four supporting tabs. If you do include them then connect them to your GND net.

The three yellow lines that make up the silkscreen on the left are 0.2mm thick and have the following (x,y) -> (x1,y1) values in mm:


(-1.45,0) => (-0.4,0)
(-1.45,-3.4) => (-1.45,0)
(-1.45,-3.4) => (-0.4,-3.4)

The three yellow lines that make up the silkscreen on the right are 0.2mm thick and have the following (x,y) -> (x1,y1) values in mm:


(8,0) => (9.1,0)
(9.1,-3.4 => (9.1,0)
(8,-3.35) => (9.1,-3.4)

Hopefully that’s all you need to create a compatible footprint in Eagle or any other package.

Identifying the controller

Traditionally this has always been the hardest part of any reverse engineering effort involving probing registers and trying to work out how they match up with known controllers. Thankfully though, LG have again been generous in the service manual. On page 58 it says this:

There it is, handed to us on a plate. The datasheet for the HX8352A is readily available on the internet and a quick scan through it tells me that it’s somewhat similar to the HX8347A that I already know quite well.

With the controller identified, it’s time to design a development board.

A development board schematic




Click thumbnail for PDF

The schematic contains the basics for the breakout of the connector and a few extras that I decided to include for fun. Let’s go through them in detail.

The 30-pin breakout header

The 2.54mm, 30-pin breakout header has the following pinout.

Pin Function
RS LCD register select.
RD LCD read control. Pulled up to VDD.
WR LCD write control.
CS LCD chip select. Can be grounded.
0..15 LCD 16-bit data bus.
VSO Vertical Sync output.
MO Colour mode selection. GND=64K, VDD=262K
RE LCD reset.
EN LCD backlight enable. Tie to VDD for full brightness or apply PWM signal to control it.
VDD 3.3V power input. 3.3V is the limit, do not exceed it.
GND Ground.
VLED Voltage source for constant current backlight generator. Normally tie to VDD unless using STM32 F4 Discovery board in which case should be connected to a 5V output from that board.


The LCD power supply

LCD controllers normally take two power supply inputs, one for the panel and one for the digital signals. In this case they appear to be bonded together on pins 40 and 39 and the phone supplies them with 2.7V.

A quick glance at the HX8352A datasheet reveals that the recommended ranges for the power supplies are Vcc = 2.4 ~ 3.3V and IOVcc = 1.65 ~ 3.3V. My target microcontroller is going to be the STM32 running at 3.3V so I will not be needing any level conversion as 3.3V is at the top end of both the recommended ranges.

The backlight power supply

The backlight is comprised of six white LEDs in parallel so we do not need a step-up converter and could in theory drive it with a single current-limiting resistor. The LEDs probably have a forward voltage of around 3.2V each and there are calculators out there on the web that will do Ohm’s law and tell you the value of the resistor that you should use.

However, for tasks like this I much prefer a constant current source over a simple resistor. There are integrated devices on the market that exactly match what I need, for example the CAT3649 by OnSemi. I evaluated that device and even bought a few to play with. In the end though, I decided to go with the circuit in this instructable.

The circuit has the advantage of requiring only a handful of very low-cost parts, two MOSFETs, a transistor and four resistors. The current-setting resistor is calculated for a current of 20mA. VLED is the input power supply, 3.3V in all my tests. If you plan to use this circuit on the STM32 F4 Discovery board then VLED should be connected to 5V. The 3V outputs from the board are not high enough to drive the backlight circuit and if you try then the display will be quite dimly lit.

My circuit differs slightly from the instructable in that I’ve added a second MOSFET, Q3, so that we can control the brightness of the backlight with a PWM signal from a microcontroller through the ‘EN’ signal. The gate of Q3 is pulled down to ensure it stays off until needed and the MCU pin is protected from unwanted current discharge from Q3′s gate capacitance by a 100&ohm; resistor, R5.

The RD line

RD is only used if you want to read data from the framebuffer. Read cycles are very much slower than write cycles and so this line is hardly used. Therefore it’s pulled up to VDD in my design.

The SPI flash

Since this is going to be quite a large breakout board I decided to include the necessary footprints for a SPI flash IC circuit that can be used to store such things as graphics and fonts that can then be utilised by the software driver.

The SOIC-8 SPI flash footprint pinout is a de-facto standard across manufacturers. I’ve included the Winbond W25Q16DW as an example but any device in the 209 mil SOIC-8 format is likely to be compatible.

Bill of materials

Here’s the complete BOM for this project:

Label Value Footprint Comment
C1,C2,C3 100nF 0603 Decoupling
P1 ENBY0045701 40×0.4mm FPC connector
P2 2×15 2.54mm LCD breakout
P3 2×3 2.54mm Flash breakout
Q1,Q3 TSM3442 SOT26A-6 Logic MOSFET (use any)
Q2 BC847 SOT23-3 Small signal transistor
R1 100k&ohm; 0805
R2 22&ohm; 1% 0805 Current setting
R3,R4,R6,R7,R8,R9 22k&ohm; 0805 Pull up/down (value not important)
R5 100&ohm; 0805 MCU protection
U1 SPI flash SOIC-8 209mil Use any compatible IC


The PCB CAD

The target maximum size for this design is 10x5cm so I’ve got quite a lot of leeway to layout the components and include M3 screw holes to use as mounting feet. Here’s the front.



Click for PDF

The only constraint is the slot required to accomodate the FPC tail so it can thread through the board and plug into the socket located on the component side of the board. Here’s the back layer.



Click for PDF

Getting the slot and connector in the correct location is the most tricky part. I measured where I thought the location would be and then tested it by printing out a 1:1 paper copy of the layout, cutting out the slot and offering up the LCD to ensure that it would fit.

The manufactured board

Regular readers will know that I usually use ITead Studio as the manufacturing service. Other service providers exist such as Seeed Studio and one I hadn’t heard of before, Elecrow.

I decided to give Elecrow a try since they were offering colour PCB printing at no extra cost. I was pleased with the result and suspect that they’re using the exact same fabrication plant as Seeed and ITead.

Building the board

My technique for building a board like this involves several stages of preparation, reflow and hand-soldering. Firstly I use a highly active flux (Fluxite) to tin the pads because I find that this flux is ideal for drag-soldering with a normal iron across the tiny 0.4mm pitch pads without creating any bridges or solder spikes. When this is done the board is thoroughly cleaned to remove all trace of the flux – it’s corrosive and should not be left on the boards. After cleaning only water-soluble flux designed for electronics is used.

With the board prepared I use a paste flux to lightly grease the IC pads and then place the connector and the ICs into position using a microscope to ensure accuracy. I then use a hot plate to reflow the board until I see the components ‘sit down’ into the little solder bumps. The next stage is to touch-up the reflowed joints with a normal iron under the microscope – there’s always one or two legs that didn’t quite drop into the solder while it was on the hot plate.

Finally the discrete 0603 and 0805 components are reflowed into place using a hot-air gun and any through-hole devices (e.g. pin headers) get soldered on with a regular iron.

Here’s the component-side of the board after assembly. The screen is designed to fit on the other side with the FPC cable wrapping through the large slot and mating with the connector. Although the 40-pin 0.4mm connector looks a nightmare to fit I did not find it any more difficult than the 24-pin Nokia connector that I’ve worked with in the past. I wouldn’t like to try it with just an iron though; reflow makes it so much easier.

Here’s the finished article with the LCD fitted and (thankfully) mating correctly with the socket on the component side. Double-sided sticky pads serve to both fix the LCD in place and lift it up above the heads of the screws that are used to raise the components on the underside of the board off the table.

Now the hardware looks great, on to the software.

Programming the LCD controller

As mentioned earlier in this article, LG were kind enough to fess up to the controller being an HX8352A in the service manual. The pinout looks familiar enough with just a few pins in there that are not part of the of 8080 bus.

Firstly there’s the LCD_VSYNC_OUT pin. This one looks like it’s going to be the pulse that’s emitted at the start of the frame’s vertical blanking period to enable the microcontroller to synchronise its drawing to avoid the ‘tearing effect’, which co-incidentally is the name that this pin is often given by other controllers. Let’s jump forward a bit and see the signal that we get from this pin with a logic analyser.



Click for larger

Here we can see the vsync pulse being emitted with a frequency of 62.72Hz which corresponds to the refresh rate of the display.

The other unusual pin is the LCD_IF_MODE pin. It took me a while to catch on to this one. If you drive it low then the controller runs in 64K colour mode. If you drive it high then it runs in 262K mode. Every other controller that I’ve seen does this with a register setting and it was only when I noticed the lack of such a setting in the HX8352A datasheet that I twigged what this pin was for.

Writing the driver was fairly straightforward, aided and abetted by Himax’s application note that can be found online. In it they give some sample initialisation sequences which I adapted to this screen’s characteristics by changing the oscillator frequency to an appropriate value.

An stm32plus driver

I built a number of drivers for the STM32 F1 and F4. The first one uses the FSMC to drive the LCD bus in the same way that I’ve always done in the past. This performs well, especially on the F4, and is really easy to program. You can see the example code here on github. The FSMC access mode supports 16 and 18 bit colours using the following driver names:

LG_KF700_Landscape_64K
LG_KF700_Landscape_262K
LG_KF700_Portrait_64K
LG_KF700_Portrait_262K

Here’s a photograph of the LCD connected to the STM32F4 Discovery board via the FSMC peripheral and running my graphics library demo.

The optimised stm32plus GPIO access mode

If you’re a regular reader of these articles then you’ll remember how I developed an optimised Arduino GPIO driver for 16-bit LCDs that achieved a very high fill-rate thanks to some assembly language trickery. Well, I’ve ported that technique to stm32plus for the F1 series of MCUs in the form of a Gpio16BitAccessMode template.

This GPIO driver means that not only can the F1 almost keep up with the F4 speed-wise, but it can also be used on devices that do not have the FSMC peripheral.

    /**
     * Forward declaration for the template specialisations. These drivers
     * are highly optimised assembly language implementations designed to
     * extract the maximum performance from a GPIO based design. Each one
     * has been hand-tested and timed with a logic analyser to ensure it
     * meets its timing requirements.
     */

    template<class TPinPackage,
             ColourDepth TColourDepth,
             uint16_t TClockFrequency,
             uint16_t TLow,
             uint16_t THigh>
    class Gpio16BitAccessMode;

The basic idea is the same as the Arduino. Of course the assembly language is different and I have to provide specialisations of the access mode to suit the core clock and the write-cycle timing requirements of the LCD controller because, unlike the Arduino, the STM32 can drive the write-cycle faster than the controller can handle so I have to slow it down by a degree suited to the core clock speed of the target MCU. Gpio16BitAccessMode only uses raw GPIO and so it will work on devices that do not have the FSMC peripheral.

The additional complexity of having an instruction pipeline makes timing computation difficult as the same instruction can perform differently depending on its context. Having a logic analyser is critical to getting this right. For example, the method used to perform a 100ns write-cycle for one 16-bit data value on a 72Mhz MCU is shown here. The write cycle is divided 50ns low and 50ns high.

template<class TPinPackage,ColourDepth TColourDepth>
inline void Gpio16BitAccessMode<TPinPackage,TColourDepth,72,50,50>::writeData(uint16_t value) const {

  __asm volatile(
    " str  %[value], [%[data]]          \n\t"         // port <= value
    " str  %[rs],    [%[cset], #0]    	\n\t"         // [rs] = 1
    " str  %[wr],    [%[creset], #0]  	\n\t"         // [wr] = 0
    " mov  r0,       r0                 \n\t"         // burn 2 cycles so we meet the timing requirements
    " mov  r0,       r0                 \n\t"
    " str  %[wr],    [%[cset], #0]    	\n\t"         // [wr] = 1
    :: [creset]    "r" (_controlResetAddress),        // the control reset address
        [cset]     "r" (_controlSetAddress),          // the control set address
        [data]     "r" (_portOutputRegister),         // the data port
        [wr]       "r" (TPinPackage::Pin_WR),         // WR pin bit
        [rs]       "r" (TPinPackage::Pin_RS),         // RS pin bit
        [value]    "r" (value)                        // input value
  );
}

Hooking up a logic analyser shows the signal is performing as expected:



Click for larger

As with the Arduino driver, the main performance boost is found in the unrolled writeMultiData() function. writeMultiData() takes advantage of the data value already being present on the bus lines and just toggles the WR line as fast as we are allowed in order to transfer blocks of data.

template<class TPinPackage>
inline void Gpio16BitAccessMode<TPinPackage,COLOURS_16BIT,72,50,50>::writeMultiData(uint32_t howMuch,uint16_t value) const {
  __asm volatile(
      "    str  %[value],   [%[data]]                 \n\t"     // port <= value
      "    str  %[rs],      [%[cset], #0]             \n\t"     // [rs] = 1
      "    cmp  %[howmuch], #40                       \n\t"     // if less than 40 then go straight
      "    blo  lastlot%=                             \n\t"     // to the finishing off stage

      // in the following unrolled loop each STR is duplicated for the sole purpose
      // of burning cycles so that we meet the timing requirements. The target is 50ns
      // low, 50ns high. We achieve 55ns/55ns which is close enough.

      "batchloop%=:                                   \n\t"
      "    str  %[wr], [%[creset], #0]                \n\t"     // [wr] = 0
      "    str  %[wr], [%[creset], #0]                \n\t"     // [wr] = 0
      "    str  %[wr], [%[cset], #0]                  \n\t"     // [wr] = 1
      "    str  %[wr], [%[cset], #0]                  \n\t"     // [wr] = 1
      "    str  %[wr], [%[creset], #0]                \n\t"     // [wr] = 0
      "    str  %[wr], [%[creset], #0]                \n\t"     // [wr] = 0
      "    str  %[wr], [%[cset], #0]                  \n\t"     // [wr] = 1
      "    str  %[wr], [%[cset], #0]                  \n\t"     // [wr] = 1

      // [snipped!] see source for the rest of this method

The code fragment shows how writeMultiData() starts off. Take a look at the source in github if you’re interested in the rest of the method.

Note how each str instruction is repeated. This apparently un-necessary duplication of instructions is there to burn enough cycles to keep WR low, then high for a minimum of 55ns respectively because the HX8352A has a minimum write cycle of 100ns. Let’s see how it looks with the logic analyser.



Click for larger

If you look closely you’ll see that the first cycle is slightly stretched compared to the others. I believe this extra clock cycle is being inserted by the MCU because the address for the str instruction is not yet set up on the bus but am not entirely sure (remember what I said about the fun involved with programming a pipelined architecture!).

Overclocking the controller

Yes you can. I did it several times by accident during the development of the Gpio16BitAccessMode template. My first attempt at the access mode resulted in a 42ns/42ns write cycle and it ran through my graphics library demo without any glitches or other artefacts. Try overclocking if you want, but be aware that even if it appears to be successful you could be storing up trouble for later on. It’s your risk to take.

Watch the video

Here’s a short video showing the FSMC access mode running on the F4 Discovery board. This demo is included with stm32plus, named hx8352a.

Click here to view the video on YouTube.



Using it on the Arduino Mega

With the Arduino Mega being a 5V device and this screen being 3.3V, some kind of adaptor is required. Luckily that’s exactly what I built in in this article. It was very simple for me to hook up this screen in 64K colour mode and port the stm32plus driver to the Arduino.

I’ve committed the driver code to the xmemtft github repository and created a release 3.0.1 that you can get from my downloads page. The highly optimised GPIO driver performs very well on this display. See for yourself in this youtube video.

Click here to view the video on YouTube.



Using the onboard SPI flash

The boards that I have built come with a Winbond W25Q16DW 16Mbit SPI flash IC and a breakout header to get at the functionality. The available pins on the breakout header are shown below.

Pin Function
SS Slave select (active low chip-select). Pulled up to VDD.
HOLD Hold input. Pulled up to VDD and can be left NC if not required.
WP Write protect. Pulled up to VDD and can be left NC if not required.
MISO MISO. Master in, slave out (data read from flash)
MISO MOSO. Master out, slave in (data write to flash)


stm32plus comes with two new example programs that exercise the SPI flash functionality. flash_spi_program is a general purpose programming utility that will program and verify a set of files that you supply on an SD card.

The above image shows the output of the flash_spi_program example when run against the sample files that I include with the example code.

The second example, flash_spi_reader, makes use of the example JPEG files programmed by the flash_spi_program example. It goes into a loop reading each image from the flash device and writing it to the KF700 LCD display.

Some boards for sale

As usual this project has left me with a small surplus of boards that I’m going to sell on a first come first served basis. All boards are fully constructed with a screen attached and are tested just like in the pictures featured in this article. The SPI flash IC on the board is a Winbond 16 Megabit W25Q16DWSSIG device. Worldwide postage is included (tracking is not available).


Postage included





Reverse engineering the Sony Ericsson Vivaz high resolution 640 x 360 cellphone LCD

$
0
0

Welcome to another in my series of cellphone LCD reverse-engineering articles. In this article I’m going to present everything you need to hook up the high-resolution 640×360 LCD from the Sony Ericsson U5 Vivaz to your project.

About the phone and LCD

The Sony Ericsson U5 Vivaz LCD is a 3.2″ 640×360 TFT with a nice high pixel density of 229ppi. Replacement panels are available on ebay for as little as GBP 6.00.

Key to any reverse engineering effort is the ability to find the schematic for the phone in question. A bit of googling turned up the relevant repair manual and I was pleasantly surprised to see that the LCD connector had the 8080 interface signals that indicate the presence of an onboard controller.

Why the surprise? Well, every high-resolution display that I’ve investigated to-date has had either a parallel or MIPI-DSI interface designed to be connected to a system-on-a-chip (Soc) controller that would supply both pixel data and display timing signals. These interfaces are unsuitable for the small STM32 and AVR microcontrollers that we all enjoy programming because of the requirement to provide a frame buffer and display signals that are difficult to synthesize. The presence of an 8080 controller on this LCD definitely caught my eye. Time to order a panel.

That’s the front view, let’s take a look at the back.

There’s nothing unusual about the panel compared to the others that I’ve reverse engineered in the past. The FPC is in a reasonable position and is a useful length.

The connector is a board-to-board type and there’s an array of SMD capacitors situated close to it.

The connector

The second critical part of the reverse engineering effort is the ability to identify and source the FPC connector required by the panel. In this case the repair manual provided the necessary information. Right underneath the diagram for the LCD connector is a label AXE534124.




Click for a larger image

Off to Google I go and the answer is immediately located. It’s a 34-pin 0.4mm Panasonic AXE534124 and better still it’s in stock at digikey.com. I won’t try to link directly to it here as external links to shop pages tend to be brittle. Just go over to digikey.com and search for AXE534124.

Note that for reasons I don’t understand Digikey will not sell this connector off any site except digikey.com so if you’re not in the USA you must switch to use the USA site to make your order.

Soldering this connector was a little bit more fiddly than the others I’ve dealt with because the legs do not protrude very far from the connector body making it very likely that you’ll slip with your soldering iron at least once and melt a little bit of the body plastic.

The connector footprint

I designed a footprint for the connector using my copy of Protel, here’s how it looks.

Now I know that a significant number of you will be using CADSoft Eagle as your design package so here are the dimensions from the footprint that you’ll need to create your own in Eagle. All the following positions and dimensions are in millimetres.

The pin size is 0.23 x 0.5 with a solder mask expansion value of 0.051. The pin with a crosshair on it is at (0,0). The pin immediately to the right is at (0.4,0). The pin directly above is at (0,2.4).

The pad size for the four supporting posts is 0.9 x 0.9 with a solder mask expansion of 0.051. Starting at the top-left and moving clockwise the positions are (-1,2.2), (7.4,2.2), (7.4,0.2), (-1,0.2).

The final part in the connector identification process is to try to figure out which way around it goes. I know from the schematic that the pins are arranged in two columns, A and B. Often I can use the position of the ground pins and check continuity between them with a multimeter to verify which pin is which. In this case that approach is thwarted because the the ground pins are arranged symmetrically across the connector.

I solved the problem by doing a little bit of cross-referencing. The schematic includes a block diagram of the main phone PCB:

The LCD connector is labelled X2821 in the diagram and rather helpfully it has the position of the pins labelled on it. Now I needed to know which way the plug on the FPC connects into that connector so I searched around and found a video on youtube that showed how to disassemble the phone. The sequence where the presenter disconnected the LCD revealed all that I needed to know.

The frame grab shows the plug on the FPC in the correct orientation relative to the connector. Now I have all the information that I need.

Powering the backlight

The schematic shows that there’s a single pair of power lines connected to the backlight, LCD_BL_K and LCD_BL_A. That means that the LEDs that form the backlight are arranged in series and I’m going to need a step-up driver to power them. Every parallel LED configuration that I’ve seen has all the cathodes exposed for individual feedback into the constant current driver IC.

The schematic doesn’t tell me how many LEDs make up the backlight so I’m going to have to make an educated guess that it’s at least four but is more likely to be six. The NCP5007 device that I used to drive the backlight in the Nokia displays will not be sufficient because it’s limited to five LEDs so I need to choose something else.

The device I selected is the AP5724 from Diodes Inc. It’s available from Digikey in the SOT26 package that I selected for this design.

An early guess at the controller

Before designing a breakout board I decided to have a look around for datasheets that might belong to the controller. There were not many candidates. An exact match was the R61523 by Renesas. Another possibility was the SSD1963 by Solomon Systech that has flexible support for a large range of resolutions.

I decided to follow up on the Renesas option and tried some cross-referencing using Google to try to find any commercial relationship announcements between Sony-Ericsson and Renesas at about the time of the phone’s launch date. There was just such a press-release, dated around 2007. The case for the R61523 was getting compelling and I decided to run with it as the main candidate.

Flicking through the R61523 datasheet to the electrical specifications I found the desired voltage levels. TFT panels like these generally require two supply voltages, one for the digital IO (IOVCC) and one for the panel power (VCI). If you’re lucky then both fall within the same range and coincide with your MCUs core voltage.

I wasn’t so lucky with this one. IOVCC has a desired range of 1.65-3.6V which is fine but VCI’s range is 2.6-3.0V. I will be able connect up my 3.3V STM32 directly to the IOs but I’ll need to add a 2.8V LDO regulator to the design to supply VCI within its desired range.

A breakout board schematic

Now I’ve got hold of the schematic and the connector I have enough information to create a schematic for a breakout board.




Click preview for a full size PDF

The schematic is divided into annotated sections, let’s take a look at each one in turn.

The VLCD power supply

I mentioned earlier in this article that the R61523 was specified to require a 2.8V panel power supply and the datasheet indicates that the maximum current consumption will be a mere 4mA. Any LDO regulator will handle this load but an additional requirement imposed by my design is that the dropout voltage must be less than 0.5V because the input to the regulator will be 3.3V.

I chose the ZXCL280H5TA from Diodes Inc. in SC-70 format. The dropout voltage at 4mA is so small it’s hard to determine from the chart in the datasheet. It’s somewhere around 5mV.

Decoupling and power indicator

The board includes a 100µF capacitor for low frequency decoupling and a 1206 footprint amber LED for a power indicator. The series resistor is calculated to allow about 4mA through the LED which is plenty for an indicator.

Backlight driver

My (guessed) requirements for the backlight are discussed earlier in the article. My assumption is that there are six backlight LEDs which I know are connected in series and that means that I need an appropriate step-up driver.

The AP5724 from Diodes Inc. satisfies the requirements and my implementation is straight from the example application circuit in the datasheet. The feedback resistor, R7, has a value of 5.1&ohm; which results in a constant output of 20mA.

The LCD connector

The circuitry around the LCD connector mirrors that which I found in the schematics for the actual phone. A familiar selection of capacitors provide decoupling on the supply lines. The BL_A and BL_K 56pF capacitors are rated at 50V because of the high voltage on those lines. RD and CS are pulled up and down, respectively, because they are rarely used in designs and so can be left disconnected if required.

The breakout header

The breakout header is a 15×2 header with the standard 2.54mm pitch. Here’s a table that describes how to hook it up to an MCU.

Pin Function Description
TE Tearing effect VSYNC and optionally also HSYNC output. Used for sychronising your drawing with the LCD refresh to avoid flicker.
BL_PWM Backlight PWM Optional output for a PWM signal that can be used to dim the backlight. Connect to the EN pin if this functionality is used.
D0..15 Data bus 16-bit data bus for commands and data.
RS Register select Set high when writing data or low when writing a command. The datasheet calls this line DCX.
CS Chip select The controller will ignore you unless this line is low. Pulled down by default.
WR Write enable Active low write strobe.
RD Read enable Active low read strobe. Reading is hugely slower than writing and so is not often used. Pulled up by default
RESET Hard reset Active low hard reset.
EN Backlight enable Tie to VCC to fix the backlight to maximum (an adjacent pin is provided to enable easy connection with a jumper). Alternatively supply a PWM signal from your MCU to dim the backlight. Better still, connect to BL_PWM and program the controller to provide the PWM signal.
VCC 3.0 to 3.3V Power for the LCD, inputs to the AP5724, flash and ZXCL280H5TA.
GND Ground Connect up one of the GND pins to your circuit’s ground.


SPI flash

I have lots of space on the board so I decided to include a separate breakout area for a SPI flash IC that can be loaded up with graphics, fonts and other resources that might be required by a program. The SOIC-8 footprint is designed to take any 208mil ‘wide’ package but will happily also accept the smaller 150mil footprint. SPI flash ICs pretty much all have the same pinout so you can insert any one that fits your requirements.

Bill of materials

Here’s the complete bill of materials for the schematic.

Code Value Footprint
C1,C2,C3 56pF 50V 0603 ceramic
C4,C5,C6 100nF 0603 ceramic
C7,C10 2.2µF 0603 ceramic
C8,C11 1µF 0603 ceramic
C9 1µF 50V 0805 ceramic
C12 100µF Electrolytic
R1,R2,R3,R4,R5,R6 22k&ohm; 0805. Exact value not important.
R7 5.1&ohm; 1% 0805
R8 390&ohm; 0805
PWR LED Yellow 1206
D1 Schottky SOD-123. Many types can be used.
L1 22µH Many types can be used.
P1 3×2 male 2.54mm
P2 AXE534124 0.4mm x 34
P3 15×2 male 2.54mm
U1 any SPI flash SOIC-8 (208 or 150mil)
U2 ZXCL280H5TA SC-70
U3 AP5724 SOT26


Building the PCB

Many of my previous designs have featured a wrap-around design whereby the LCD occupies one side of the PCB and the flex cable wraps around to the connector on the other side that also houses the circuitry. I could probably have done that this time as well but decided instead to opt for a larger 10x10cm PCB and fit the whole design on to one side including space for M3 screw mount holes.



The front side has all the components and a vacant space down at the bottom that’s calculated to hold the LCD panel mounted on some double-sided sticky pads.




Click either image for a PDF

Given the spacious size of PCB the routing did not present any challenges. Now with the design complete it’s time to send it off to one of the Chinese online sellers for manufacturing. In the past I’ve used ITead Studio and occasionally Seeed Studio. This time I used a relative newcomer, Elecrow, because they don’t charge extra for coloured PCBs and I wanted mine to be blue.

I uploaded my design and waited the three or so weeks that it takes to arrive. I had cause to visit Shenzhen, China on business during the waiting time and could have dropped in and picked it up while I was there!

Anyway, the PCBs arrived in the post and as usual they were perfectly made.


My process for building the PCB involves several stages. First I flux and tin the pads so they all have little solder bumps on them. The flux used for this stage is the highly active Fluxite brand. The board is then washed to remove all trace of the corrosive flux before doing anything else.

I then reflow the ICs and fine-pitch connector using a hot plate and then reflow the discrete components using a hot air gun. Then I inspect my work under a microscope and use a regular fine-tip iron to touch up any joints that have not reflowed correctly. Finally the through-hole components are soldered using a regular iron and then the board is washed in soapy water, rinsed and left to dry for 24 hours.

Here’s a photograph of the completed board before the LCD is attached.

And here’s the board again with an LCD attached this time. The protective peel-off plastic film is still attached to the front of the LCD.

Programming the controller

Now that I’ve got a breakout board the first thing to do is to verify my guess that the controller is a Renesas R61523. The datasheet defines a command Device Code Read that will return four bytes that identify the controller.

If you’re looking to write your own R61523 driver then do note that many of the useful commands are locked after reset and require you to send the MCAP command with a paramter of 4 to unlock them.

I hooked up the screen to my STM32 F103 development board and wrote some code to reset the board and issue the Device Code Read command. Great news, it worked and it returned the expected sequence of bytes. Now I know for sure that I’m dealing with an R61523 I can go ahead and write STM32 and Arduino drivers.

The TE output signal

LCD controller manufacturers use the acronym TE or Tearing Effect for signals that the rest of the world calls VSYNC and HSYNC. LCD panels require a signal to tell them where the top of the display is (VSYNC) and another one to tell them where the start of each line is (HSYNC).

If you need to a produce a display that doesn’t flicker (or ‘tear) then you have to do your drawing when the LCD is not busy taking data from the video RAM and sending it to the screen. You can do this by starting your drawing when you receive the VSYNC signal and using that vertical blanking time to write out your data. It’s not as powerful as double-buffering but on a display like this it’s all you’re going to get.




Click for larger

The above image shows the TE signal configured to output a pulse on VSYNC only. We can see that the LCD is running with about a 60Hz refresh rate.




Click for larger

The above image shows the TE signal configured to output a pulse on VSYNC and HSYNC. Clearly there are a lot more pulses in this mode.

An stm32plus driver

I tackled the STM32 driver first and it wasn’t long before I had it up and running. 16, 18 and 24 bit colour depths are all supported in portrait or landscape mode. Tearing effect output (vsync and/or hsync) is supported as is the controller-generated PWM output for backlight dimming. On the STM32 this saves us programming a timer peripheral to provide the backlight dimmer.

I was pleasantly surprised with the quality of the clone screen that I got on ebay. It is bright and contrasty with a wide viewing angle and high-fidelity colours. There is a visible increase in the quality of gradients when moving up from 16 to 18 and 24 bit colours meaning that even the clone screen isn’t faking the higher colour depths.

For comparison purposes I also bought a display advertised as ‘original’ for a few pounds more. The original improves slightly on the clone in the definition of the blacks. Blacks remain a deep black even with the backlight on the full 20mA current whereas the clone tends to go a deep grey instead. Given the small difference in price I would recommend going for an original display panel because even though I had good luck with the clone display that does not mean that all clones will be good.

stm32plus 3.0.2 includes a driver for all the R61523 modes and a graphics library demo that will work on the F103 or F4. Naturally the F4 is the top performer with outstanding blink-of-an-eye fill rates but the F1 is not far behind at all.

A note on the STM32F4 Discovery

This is a high speed LCD. The F4 is a high speed MCU. Together they make a perfect couple but like many perfect couples their relationship can be a fragile one. If you intend to wire this LCD board up to the F4 Discovery board then you must take care with the quality of your wiring. Remember that you are creating a high-speed bus, and this advice applies equally well to all high-speed MCUs.

  • Keep your wires short and all of equal length.
  • Avoid a ‘rats nest’. Try to avoid twisted and crossed over wires.
  • Ensure your wires mate well with the pin headers. For god only knows what reason ST decided to put short pins on the ‘business side’ of the discovery board making wired interconnects precarious.

If you get a ‘glitchy’ display with random small areas of corruption then it’s most probably your wiring. I’ve been known to wire and rewire the discovery board three times to get a 100% reliable connection. Maybe my interconnects are junk, I did get them on ebay after all :)

You can see the graphics library demo running glitch-free on the STM32 F4 Discovery board in the following video. The display is running in 24-bit mode (two 16-bit transfers per-pixel).

Click here to view the video on YouTube.


An Arduino Mega driver

A few months ago I presented a generic 16-bit LCD adaptor that can be used to connect panels such as this to the Arduino. I connected up the R61523 to this adaptor and began writing a driver that would use the highly optimised GPIO access mode that I presented in the adaptor article. With 640×360 pixels to drive every clock cycle counts and a poorly optimised driver will result in a sluggish user experience.

Version 3.0.2 of my xmemtft library has support for the 64K colour mode in portrait and landscape mode. You can see for yourself how well the library performs in the following video.

Click here to view the video on YouTube.


Gamma setting

After a reset both the clone and the original panels are slightly too dark and benefit from a custom gamma curve to lift the bottom end of the grey scale up a bit so that detail in the shadow areas of photographs is not lost.

The R61523 is extremely flexible with its gamma settings and you can program separate levels for each of the RGB components if you need to. I didn’t need to adjust the colours so I flattened the 78 different parameters into just 13 levels designed to view the curve as greyscale.

In the demo code the curve is declared and applied like this:

uint8_t levels[13]={ 0xe,0,1,1,0,0,0,0,0,0,3,4,0 };
R61523Gamma gamma(levels);
_gl->applyGamma(gamma);

In the driver code I apply the curve like this:

   /**
     * Apply the panel gamma settings
     * @param gamma The collection of gamma values
     */

    template<Orientation TOrientation,ColourDepth TColourDepth,class TAccessMode>
    inline void R61523<TOrientation,TColourDepth,TAccessMode>::applyGamma(const R61523Gamma& gamma) const {

      applyGamma(r61523::GAMMA_SET_A,gamma);
      applyGamma(r61523::GAMMA_SET_B,gamma);
      applyGamma(r61523::GAMMA_SET_C,gamma);
    }


    template<Orientation TOrientation,ColourDepth TColourDepth,class TAccessMode>
    inline void R61523<TOrientation,TColourDepth,TAccessMode>::applyGamma(uint16_t command,const R61523Gamma& gamma) const {

      uint8_t i;

      _accessMode.writeCommand(command);

      // positive and negative

      for(i=0;i<2;i++) {
        _accessMode.writeData(gamma[0]);
        _accessMode.writeData(gamma[1]);
        _accessMode.writeData(gamma[3] << 4 | gamma[2]);
        _accessMode.writeData(gamma[5] << 4 | gamma[4]);
        _accessMode.writeData(gamma[6]);
        _accessMode.writeData(gamma[8] << 4 | gamma[7]);
        _accessMode.writeData(gamma[10] << 4 | gamma[9]);
        _accessMode.writeData(gamma[11]);
        _accessMode.writeData(gamma[12]);
      }
    }

The onboard backlight PWM driver

In nearly all my previous LCD driver projects I have used the MCU to generate a PWM signal that is connected to the LCD driver’s EN pin in order to dim the backlight. The R61523 includes an onboard PWM generator that does the job for us and saves us an output pin and a timer peripheral on the MCU. The controller will even do a smooth transition between backlight levels.

Both the stm32plus and the Arduino drivers come with a backlight driver class that allows you to customise the PWM frequency, polarity and whether you want the smooth transition mode to be enabled or not.

Using the onboard SPI flash

The development board that I created for the LG KF700 was the first to include a SPI flash IC that could be used to store resources such as graphics and fonts.

This time around I have included the Spansion S25FL208K 8Mbit flash IC on board. The S25FL208K is a standard SPI flash device so it can be programmed using the spi_flash_program example code included with stm32plus.

The example code reads files from an SD card, writes them to the flash IC at the locations you specify and prints out status messages to a USART port.

Print your own PCBs

Want to have a go at assembling one of these boards yourself? Head on over to my downloads page and grab yourself a copy of the zip file containing the Gerber CAM files. An online service such as that offered by Seeed Studio, ITead Studio or Elecrow can be used to get 10 copies for a very reasonable price.

Update: another panel type found

I recently bought another clone panel on ebay to mount on a second board that I made up. When this panel arrived the first thing that I noticed was that the breakout area on the FPC cable where all the capacitors are mounted looked quite different to the original Sony panel.

If you compare this to the first image in this article you can see that the grid of capacitors is replaced by a selection of capacitors and ICs. Interesting. I hooked it up to the board and fired up some test code.

As well as flickering wildly the displayed image was compressed on to one side of the screen and appeared to be mirrored on the other side. I thought the screen was broken but decided to investigate further to see if it was something I could fix in code. It was.

To understand how the fix works it helps to understand how the R61523 is designed. The R61523 has a region of registers that it calls manufacturer commands. These registers define parameters specific to the physical panel such as power settings and panel driving parameters.

A region of non-volatile RAM (NVRAM) is programmed at the factory with the correct values for the manufacturer commands and the R61523 reads the NVRAM and applies the values after the device is reset. This allows the R61523 to be used in panels sourced from many different manufacturers.

I wrote some code to read out the manufacturer command values from the original Sony panel and compared them to the values obtained from this latest clone. The original panel had some values that were different from the power-on defaults documented in the datasheet indicating that its NVRAM had been programmed at the factory. The clone only had the standard R61523 power-on defaults indicating that it had not been programmed.

I sat down and went through the registers that could possibly cause the mirrored display and flickering that I observed and soon found the two settings. They were panel_driving_setting (C0h) and power_setting_common (D0h).

The thing I don’t understand is how these panels could ever work in the actual phone because the phone will only be coded to work with the originals which load up the parameters correctly from NVRAM. Maybe they don’t work in the phone? It wouldn’t be the first time that ebay sellers have been sold a pup by their Chinese suppliers.

The correct additional initialisation code for these panels is shown below:

// this panel needs SM=1 (first parameter). the others are the same
// as the original Sony. if SM is not set to 1 the image will appear
// duplicated on the left and right of the display.

TAccessMode::writeCommand(r61523::PANEL_DRIVING_SETTING);
TAccessMode::writeData(0x8);
TAccessMode::writeData(0x9f);
TAccessMode::writeData(0);
TAccessMode::writeData(0);
TAccessMode::writeData(2);
TAccessMode::writeData(0);
TAccessMode::writeData(1);

// this panel needs VC2/VC3 set to 0x5 (second parameter). This is the
// optimum setting that reduces flicker. the other values are the same
// as the original Sony.

TAccessMode::writeCommand(r61523::POWER_SETTING_COMMON);
TAccessMode::writeData(0);
TAccessMode::writeData(0x55);
TAccessMode::writeData(0xc0);
TAccessMode::writeData(0x8f);

I have modified my stm32plus and Arduino drivers to cope with this panel. Following the convention that I established with my Nokia drivers this panel can be used by appending _TypeB to the driver name. For example, R61523_Landscape_16M_TypeB on the STM32 and R61523_Landscape_64K_TypeB on the Arduino. I have pushed these changes to their respective github repo on the master branch.

stm32plus 3.1.1: Supporting the STM32 VL Discovery

$
0
0

If you’ve been following the releases on my stm32plus github repo then you’ll already be aware that version 3.1.1 has been released. The main feature of this new release is support for the STM32 Medium Density Value Line devices, exemplified by the STM32 Value Line Discovery board from ST Micro.

stm32plus has long supported the high density F103 and the powerful F4 series of MCUs and so I decided it was time to add support for the lower end devices that are cheap to buy and easy to work with. The F100 medium density (MD) value line (VL) devices come in configurations with up to 128Kb flash and 8Kb SRAM in packages as easy to work with as the LQFP48 (in my opinion anything with pins sticking out the sides is easy to work with by modern standards!)

Naturally, being a value line series the on-board peripherals are limited compared to the high-density and F4 series so many of the stm32plus examples are not available but the core peripherals such as the timers, USARTs and SPI are all present.

Building for the Medium Density Value Line

Assuming that you’ve downloaded and extracted the source code archive from github then you can build the library and all the compatible examples from a terminal prompt:

scons mode=debug mcu=f1mdvl hse=8000000 -j4

Some notes on the above command:

  • You need to have installed scons on your system. Consult your package management system for the exact installation syntax. On Ubuntu it would be sudo apt-get install scons.
  • Windows users must use a Unix-alike subsystem such as Cygwin or msys. I use Cygwin on Windows 7 x64.
  • Where I have used the mode=debug option I could also have used small or fast mode options to build the library optimised for size or speed, respectively.
  • The parameter to the -j option should reflect the number of cores in your build system.

OpenOCD and the ST-Link V1 debugger

Interactive debugging using the Eclipse CDT edition does not need any additional hardware because there is an ST-Link chip built on to the board.

To drive the ST-Link you need to get a copy of the freeware OpenOCD utility. At the time of writing the latest version is 0.7.0 and the source can be downloaded from Sourceforge.

If you’re not interested in building from source then Linux users can install it using their package manager, e.g. for Ubuntu it would be sudo apt-get install openocd.

Windows users can download compiled binaries from this location.

OpenOCD runs in the foreground as a server process so you need to fire up a terminal window and run it with the appropriate options. I like to create trivial scripts that I can use to start OpenOCD on demand. For example, this is my script for Windows 7 x64 on Cygwin:

#!/bin/sh

cd openocd-0.7.0
bin-x64/openocd-x64-0.7.0.exe -f scripts/board/stm32vldiscovery.cfg

If it’s all working then you should see output like this when you run OpenOCD:

$ ./openocd-stlink-f1vl.sh 
Open On-Chip Debugger 0.7.0 (2013-05-05-10:44)
Licensed under GNU GPL v2
For bug reports, read

http://openocd.sourceforge.net/doc/doxygen/bugs.html

Info : This adapter doesn't support configurable speed
Info : STLINK v1 JTAG v11 API v2 SWIM v0 VID 0x0483 PID 0x3744
Info : stm32f1x.cpu: hardware has 6 breakpoints, 4 watchpoints

At this stage OpenOCD is running as a server and you can either telnet to it and issue direct commands or you can use Eclipse to flash the board and do some visual debugging. Let’s first look at directly sending commands to it using a telnet session.

Controlling OpenOCD with telnet

Here’s a log of a real telnet session to OpenOCD.

$ telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> reset init
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x0801a5d8 msp: 0x20002000
> flash write_image erase p:/r61523_mdvl.hex
auto erase enabled
device id = 0x10016420
flash size = 128kbytes
wrote 117760 bytes from file p:/r61523_mdvl.hex in 13.424768s (8.566 KiB/s)
> reset

Let’s take a look at what’s going on here. Firstly I use telnet to establish a session with OpenOCD:

telnet localhost 4444

OpenOCD responds with a one-liner greeting and a simple > prompt. The first thing I need to do is to reset the board and halt it so that I can flash my program.

reset init

OpenOCD logs the fact that the MCU was reset and is now halted awaiting my next command. I will now flash the board with an ihex file that was produced when I built the stm32plus package and all its examples.

flash write_image erase p:/r61523_mdvl.hex

OpenOCD logs its progress and eventually tells me that it’s written 117760 bytes to the MCU. The device is still halted so now I’m going to reset it and this time let it go so my program will run.

reset

My program is running, and very pretty it is too even if I say so myself. That’s it down there towards the end of this article in the YouTube video.

Controlling OpenOCD with Eclipse

If you’re using Eclipse to do your development then you can have all the OpenOCD interaction automated by the gdb debugger and you’ll get visual debugging with breakpoints and variable/memory inspection. Pretty much everything you’d expect to get from a PC-based debugger.

Assuming that you’ve got a project that builds and produces a .elf file as its output, open the Run -> Debug Configurations form and create a new GDB Hardware Debugging configuration for your project.

All the important options are on the first three configuration tabs. Rather than list them all by rote I’ll show some screenshots of one of my configurations. It should be easy for you to adapt these details to your project:

Now when I launch this configuration the latest build of the project will be automatically flashed to the board and it will reset and halt. I can then use the Resume Eclipse command to start the program.

Driving 16-bit LCDs

Support for many different LCD panel controllers is one of the main features of the stm32plus library so it’s only natural that I would look to provide support for the VL Discovery board. The MD VL range of MCUs does not have the FSMC peripheral that we often use to simplify access to an 8080-compatible LCD which means that we must do it with GPIO access.

Back in my reverse engineering the LG KF700 LCD article I presented an optimised GPIO access mode for the 72MHz STM32 F1 that used full GPIO port access to rapidly transfer 16-bits of data at a time. Naturally this is the method that I want to use on the VL discovery board because it will provide the fastest and most responsive user experience.

Unfortunately the VL Discovery board ships in a configuration that makes it impossible to access an entire port at once for IO because at least one of the pins on every available port is reconfigured for something other than IO.

All is not lost however because ST have provided ways to reconfigure the board to get back some of those port pins if you don’t want the reconfigured functionality. In my example I will disable the external 32kHz oscillator designed to provide an accurate RTC clock and that will free up all of GPIO port C for IO. So warm up that soldering iron and here’s the modifications that you need to make to the board:

  1. Connect solder bridge SB14.
  2. Connect solder bridge SB15.
  3. Remove resistor R15 (it’s an 0R rating so not really a ‘resistor’). It’s a fairly small 0603 package and I had to use my hot air gun to get it off cleanly.

Here’s a picture of the connected solder bridges:

And here’s the empty space where R15 was before I removed it.

Creating the GPIO driver for my stm32plus open source library was quite straightforward because all I had to do was take the one that I created for the 72MHz F103 and remove all the delays because the maximum GPIO toggle speed on the F100 at 24MHz is 6Mhz. (2x clock cycles to go in one direction and 2x clock cycles to come back again). That’s actually 2MHz slower than the 16MHz Arduino.

The driver source code is available here on github, and a special stm32plus demo designed just for the VL discovery board and the 640×360 Sony Ericsson Vivaz U5 LCD is here.

The overexposed still image shows the VL Discovery board hooked up to the LCD and playing the graphics library demo. The following video shows the whole demo running on the VL discovery board. As you can see the 6MHz GPIO speed is just about enough to provide a good user experience.

Click here to view the video on YouTube.


Final words

I’m happy with the support I’ve provided for the low end VL range. They provide a cheap and easy way into the ARM Cortex M3 family in packages and speeds that you might actually be able to work with successfully on your own PCBs.

Next off the production line from me will be support for the Cortex M0 ’51′ range (STM32F051) and I’m actually almost done bar a whole load of regression testing. At 48MHz the F0 outpaces the F1 VL range but there isn’t as much flash on offer, the peripherals are limited and the M0 core doesn’t have the full ‘thumb’ instruction set. Anyhow, I won’t spoil the fun, look out for a future article with the full details.

stm32plus 3.2.0: Supporting the STM32F0 Cortex M0

$
0
0

A few months ago I made the decision to start supporting the lower priced, hobbyist friendly STM32 devices in my stm32plus C++ library. These lower-end devices come in lower pin-count, smaller packages that are easier to work with and they have reduced clock speeds that make for fewer PCB layout headaches.

The first low-end STM32 series to be supported was the medium density ‘value line’ F1 (Cortex M3) as exemplified by ST’s Value Line Discovery board. This was supported in stm32plus 3.1.1. Now with release 3.2.0 stm32plus is supporting the STM32F0 (Cortex M0) series of MCUs.

About the STM32F0

The STM32F0 is an implementation of the 32-bit ARM Cortex M0 core. The Cortex M0 core is very similar to that of the M3 and M4 except that some of the instructions and addressing modes are not present. This is not something that you will notice as a C++ programmer but it does impose limitations if you like to dabble in assembly language now and then.

The device in the picture is the STM32F051C8T7. At the time of writing this MCU costs £2.60 plus tax from Farnell in single units. For that you get a 48MHz core with 64Kb of flash and 8Kb of SRAM in an LQFP package that does not require an external oscillator, thereby saving even more off the total design cost. This compares very favourably with similarly resourced 8-bit MCUs from companies such as ATmel.

The same flat 32-bit address structure is present on the M0 with the flash memory, SRAM and peripheral registers all mapped in to the usual address regions. If you’ve programmed the Cortex M3 or M4 then you’ll be right at home with the M0.

ST provide a very low cost development board for the M0 in the ‘discovery’ range.

The STM32F0 Discovery comes with an STM32F051R8T6 on board as well as an ST-Link v2 USB debugger interface. It’s on sale from Farnell at the moment for £6.78 plus tax.

The diagram above shows the pinout of the LQFP64 package included on the F0 discovery board. The 16-bit GPIO ports A, B and C are included in their entirety with a few pins each from ports D and F.

There’s a very important warning buried deep inside reference manual RM0091 that applies to GPIO ports PC13 to 15. It’s well hidden in the power control (PWR) section and I’m going to quote it here because you need to know this.

Due to the fact that the analog switch can transfer only a limited amount of current (3 mA), the use of GPIOs PC13 to PC15 in output mode is restricted: the speed has to be limited to 2 MHz with a maximum load of 30 pF and these IOs must not be used as a current source (e.g. to drive an LED).


stm32plus support

The M0 support in stm32plus is generic enough that it should work on all of the M0 devices, however I did the development against the F0 discovery board so support is officially for the F051 series. All of the examples target the F051 at 48MHz with 64Kb flash, 8Kb SRAM and running off the internal 8MHz oscillator (HSI).

Assuming that you’ve downloaded and extracted the source code archive from github then you can build the library and all the compatible examples from a terminal prompt:

scons mode=debug mcu=f051 hse=8000000 -j4

Some notes on the above command:

  • Even though the examples use the 8MHz HSI oscillator you still need to supply a value for the hse parameter. 8000000 is a suitable default value.
  • You need to have installed scons on your system. Consult your package management system for the exact installation syntax. On Ubuntu it would be sudo apt-get install scons.
  • Windows users must use a Unix-alike subsystem such as Cygwin or msys. I use Cygwin on Windows 7 x64.
  • Where I have used the mode=debug option I could also have used small or fast mode options to build the library optimised for size or speed, respectively.
  • The parameter to the -j option should reflect the number of cores in your build system.

OpenOCD and the ST-Link V2 debugger

Interactive debugging using the Eclipse CDT edition does not need any additional hardware because there is an ST-Link chip built on to the board. It’s interesting to note that ST have implemented the ST-Link interface using an STM32 F103 MCU.

To drive the ST-Link you need to get a copy of the freeware OpenOCD utility. At the time of writing the latest version is 0.7.0 and the source can be downloaded from Sourceforge.

If you’re not interested in building from source then Linux users can install it using their package manager, e.g. for Ubuntu it would be sudo apt-get install openocd.

Windows users can download compiled binaries from this location.

OpenOCD runs in the foreground as a server process so you need to fire up a terminal window and run it with the appropriate options. I like to create trivial scripts that I can use to start OpenOCD on demand. For example, this is my script for Windows 7 x64 on Cygwin:

#!/bin/sh

cd openocd-0.7.0
bin-x64/openocd-x64-0.7.0.exe -f scripts/board/stm32f0discovery.cfg

If it’s all working then you should see output like this when you run OpenOCD:

$ ./openocd-stlink-f0.sh 
Open On-Chip Debugger 0.7.0 (2013-05-05-10:44)
Licensed under GNU GPL v2
For bug reports, read

http://openocd.sourceforge.net/doc/doxygen/bugs.html

srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : This adapter doesn't support configurable speed
Info : STLINK v2 JTAG v14 API v2 SWIM v0 VID 0x0483 PID 0x3748
Info : Target voltage: 2.888784
Info : stm32f0x.cpu: hardware has 4 breakpoints, 2 watchpoints

At this stage OpenOCD is running as a server and you can either telnet to it and issue direct commands or you can use Eclipse to flash the board and do some visual debugging. Let’s first look at directly sending commands to it using a telnet session.

Controlling OpenOCD with telnet

Here’s a log of a real telnet session to OpenOCD.

$ telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> reset init
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0xc1000000 pc: 0x08000aec msp: 0x20002000
> flash write_image erase p:/stm32plus-examples-blink.hex
auto erase enabled
device id = 0x20006440
flash size = 64kbytes
wrote 3072 bytes from file p:/stm32plus-examples-blink.hex in 0.531030s (5.649 KiB/s)
> reset

Let’s take a look at what’s going on here. Firstly I use telnet to establish a session with OpenOCD:

telnet localhost 4444

OpenOCD responds with a one-liner greeting and a simple > prompt. The first thing I need to do is to reset the board and halt it so that I can flash my program.

reset init

OpenOCD logs the fact that the MCU was reset and is now halted awaiting my next command. I will now flash the board with an ihex file that was produced when I built the stm32plus package and all its examples.

flash write_image erase p:/stm32plus-examples-blink.hex

OpenOCD logs its progress and eventually tells me that it’s written 3072 bytes to the MCU. The device is still halted so now I’m going to reset it and this time let it go so my program will run.

reset

The program is now running and the onboard LED is blinking at a rate of 1Hz.

Controlling OpenOCD with Eclipse

If you’re using Eclipse to do your development then you can have all the OpenOCD interaction automated by the gdb debugger and you’ll get visual debugging with breakpoints and variable/memory inspection. Pretty much everything you’d expect to get from a PC-based debugger.

Assuming that you’ve got a project that builds and produces a .elf file as its output, open the Run -> Debug Configurations form and create a new GDB Hardware Debugging configuration for your project.

All the important options are on the first three configuration tabs. Rather than list them all by rote I’ll show some screenshots of one of my configurations. It should be easy for you to adapt these details to your project:

Now when I launch this configuration the latest build of the project will be automatically flashed to the board and it will reset and halt. I can then use the Resume Eclipse command to start the program.

Finally

Support for the F0 is now a core feature of stm32plus and will be maintained accordingly in each release. I’ve got a project or two in mind that will use the F0 MCU and of course those projects will be written up here on this website as they come to fruition.

The KSZ8051MLL Ethernet PHY revisited

$
0
0

It’s been more than a year now since I brought you my design for an ethernet PHY based on the Micrel KSZ8051MLL. The design was an unqualified success and I’ve been using it successfully with both the STM32F107 and the STM32F4 series MCUs coupled to the TCP/IP stack that I wrote for these MCUs.

To briefly recap, the KSZ8051MLL implements the physical layer (PHY) of the ethernet specification. It handles the interface between the MAC layer and the RJ45 socket. There are two protocols that are commonly used for MAC to PHY communication: MII and RMII. The KSZ8051MLL implements MII.

Many larger MCUs such as the STM32F107 and the STM32F4 have an ethernet MAC built-in. By connecting the MCU to this PHY you have all the hardware you need to do full-duplex ethernet communication at up to 100Mb/s. Though of course you’re not going to be able to achieve a sustained speed like that in an MCU.

Today I’m going to revisit the design, make a few tweaks here and there and basically bring it up to date. Of course I’ll make the schematic and gerbers open source and I’ll have a limited number of assembled boards for sale for those that don’t fancy tackling the task of soldering the SMD devices, I know it’s not everyone’s cup of tea.

Changes from the original design

The most significant change is that the RJ45 jack has changed from the TE 6605424-1 to the HanRun HR911105A. The TE connector that I was using is now discontinued so I looked around for a suitable replacement that would be cost-effective and easy to find online.

A further advantage of the HanRun device is that the two activity LEDs are built in, saving a few external components on the board itself. The physical footprint of the Hanrun connector is the same as that of the TE with the addition of the extra pins for the LEDs.

It would be nice if there was a standard for the pin assignments on the RJ45 that placed the critical TX+/TX- and RX+/RX- differential pairs right next to each other so that board designers could just route those signals in a straight line. Unfortunately there is no such standard and so we end up having to route those differential pairs in a sub-optimal way.

The SMD electrolytic capacitor used for low-frequency decoupling has been replaced with a through-hole model. The through-hole models are cheaper and occupy about the same amount of board-space.

Two of the larger 22µF ceramic decoupling capacitors have been replaced by tantalum devices. At this size tantalum devices are cheaper and just effective as ceramics. Be careful when aligning tantalum capacitors because, like electrolytics they are polarised. The end with the bar drawn across it indicates positive.

The section of the bottom ground plane used for the RJ45 chassis and magnetics is redesigned slightly to ensure that its path to the board’s ground connector keeps it away from the PHY IC.

Schematic

The schematic has been updated to reflect the changes documented above.




Click the thumbnail for a full-size PDF

Bill of materials

Identifiers Value Footprint Description
C1,C7 22µF 1206 Tantalum capacitor
C2,C3,C6,C8,C9,C11,C12 100nF 0603 Ceramic capacitor
C4,C5 30pF 0603 Ceramic capacitor
C10 2.2µF 0603 Ceramic capacitor
C13 47µF Radial 2x6x6 Electrolytic capacitor
D3 red 0603 Power LED
FB1 0603 Ferrite bead
P1 1×10 way 2.54mm header
P2 1×7 way 2.54mm header
P3 1×8 way 2.54mm header
P4 HR911105A RJ45 Ethernet connector
R1,R2 220Ω 0603 Resistor
R3 10kΩ 0603 Resistor
R4 6.49kΩ 0603 Resistor
R5 4.7kΩ 0603 Resistor
R6 1kΩ 0603 Resistor
R7 330Ω 0603 Resistor
R10-R19 33Ω 0603 Resistor
R20 0603 Resistor
U1 KSZ8051MLL TQFP-48 Ethernet PHY
Y1 TXC HC-49S-9C 25MHz custom Crystal oscillator

The table above shows the full Bill of Materials for this design. I think all of the components are easy enough to source from the usual online suppliers. I usually use Farnell for my orders. The exception is the HanRun RJ45 connector, these are available for about £1.20 on ebay in single units.

Micrel’s application notes recommend that a ferrite bead be included between the digital and analog supplies (FB1 in this schematic) but they don’t tell you a suitable value. I chose to use the Murata BLM18PG221SN1D which has a low 0.1Ω DC resistance.

You can use a 25MHz crystal oscillator from any manufacturer as long as it fits the same footprint as the TXC HC-49S-9C that I chose. Note that the values for C4 and C5 are dependent on the load capacitance of the crystal. I give the formula that I use to choose these capacitors in the original article.

R20 can be a solder bridge if you don’t have any 0Ω ‘resistors’ to hand. It just connects together the two ground planes. The presence of this bridge as a discrete component in the design is really a hack to work around a limitation in the design software that will not allow you to overlap two polygons that are connected to different net names.

PCB layout

The design still fits within a 50mm square.


The main difference from the previous design is the change to the ground planes on the bottom layer and the additional routing to reach the LED terminals in the RJ45 connector. You can download the Gerber files for this design from my downloads page.

There’s a ground pin on each of three header rows. If you can, connect all of them back to your MCU board but if you’re short of ground pins then at least connect the one that’s on the row containing RXD[0..3].

Return currents follow the path of least impedance back to ground and so it makes sense for the ground pin closest to where the RJ45 ground connections are located to be connected back to the power supply ground on your MCU board. This should keep any noise from the transformer magnetics in the RJ45 from interfering with the PHY.

Building the board

With the board now fully routed and the gerber files generated I went ahead and ordered a batch of 10 from Elecrow. Similar services are available from Seeed Studio and ITead Studio but I chose Elecrow this time around because they offer coloured PCBs at no extra cost. I’m almost certain that all these companies are using the same fabrication house.


With all the parts to hand I assembled the board by first reflowing the KSZ8051MLL and the oscillator using my hotplate. Next the discrete surface mount components are reflowed using my hot air gun and finally the through-hole components are soldered into place using a normal soldering iron.


Testing with an MCU

Now the board’s built of course I need to test it. Confidence was high in the workshop that this design would work because it’s an incremental set of improvements over the previous design. Really the only thing that could go wrong was if I’d managed to mis-read the pinout of the Hanrun connector.

I hooked it up to my WaveShare ‘Port107V’ STM32F107 development board and connected the MAC MII pins in the correct manner. The following table shows the MII pinout that I used for the test.

Pin Function
PC3 TXC
PB12 TXD0
PB13 TXD1
PC2 TXD2
PB8 TXD3
PB11 TXEN
PA1 RXC
PD9 RXD0
PD10 RXD1
PD11 RXD2
PD12 RXD3
PB10 RXER
PD8 RXDV
PA0 CRS
PA3 COL
PC1 MDC
PA2 MDIO


To test the board I ran the net_udp_send example that is included with my stm32plus C++ library for the STM32 family of microcontrollers.

stm32plus uses a modular C++ TCP/IP stack written by myself and optimised for the MAC included in the larger STM32 devices.

Declaring the physical and datalink layers of the TCP/IP stack to use the KSZ8051MLL with the pinout in the above table is as simple as this:

typedef PhysicalLayer<KSZ8051MLL> MyPhysicalLayer;
typedef DatalinkLayer<MyPhysicalLayer,RemapMiiInterface,Mac> MyDatalinkLayer;

I ran the example and was happy to note that it worked first time. The ethernet link was automatically established and the DHCP client component in the stack successfully obtained IP address details from my router and the example proceeded to send out UDP packets.

I left it running for a while to ensure that the adaptor was stable. There were no problems so I can happily say that the design is a success.

Download the gerbers

Want to build your own boards? If you head on over to my downloads page then you can download the gerber files for this board. Upload them to one of the online PCB printing services and you’ll get ten copies for around $10 plus postage.

The bill-of-materials table in this article should give you all the information that you need to source the components necessary to build the board.

Some boards for sale

It would be a shame to shelve the additional boards that I got from Elecrow so I’ve built up a few additional complete boards. They’re exactly like in the photographs here in this article and they’re all fully tested using my Port107V board.


Location




An open-source Cortex-M0 halogen reflow oven controller with TFT LCD

$
0
0

Introduction

It’s been so long since I had the idea for this project that I can’t remember why I had the idea in the first place. At least I blame it on the passage of time although this engineer is getting on a bit now so it could easily be memory rot on my part. So here we are then, a reflow oven controller. Let’s quickly recap what a reflow oven is for those that are new around here.

The two main processes used in industry to build printed circuit boards are wave soldering and reflow using a very large industrial oven that you probably can’t afford and if you could afford to buy it you probably couldn’t afford to house or run it.

Reflow on the large scale is achieved by applying solder paste to the printed circuit board using a laser-cut stencil with cutouts placed precisely where the pads are located. The solder paste itself is a mixture of flux and tiny balls of solder. A pick-and-place machine lifts the components from their packaging, e.g. a tape and reel dispenser and places them on to the board with their pads resting in the little gobs of solder paste.

The board then gets placed into the oven where a carefully controlled temperature profile is applied over the course of about 5 minutes. During this time the solder paste melts and the components ‘sit down’ into place before the solder sets as the board cools.

We can apply this basic technique to the hobbyist world with a simple plan of action. The pick-and-place machine will be replaced by my right arm and a pair of tweezers. A cost of zero so far, great stuff. Stencils and solder paste are both available to the hobbyist but the cost of the stencils are relatively high if you’re going to be making only a few boards and the solder paste needs refrigerated storage and only has a short shelf life. I’ll replace this part with a simple tinning of my boards using a soldering iron. It’ll take longer but should work just as well.

Finally we have the oven itself. Small ovens in various forms are available on ebay, amazon and they might even sell them in real shops made out of real bricks and staffed by real people. The fun part of the question is how do we make our oven follow a pre-programmed temperature profile instead of just powering up to a target temperature and staying there until the dinger goes ding and your chicken is roasted.

Of course we’re going to use a microcontroller to do it and that’s what the bulk of this article is about. Please read on.

Prior art

I’m not the first to build an MCU-based reflow oven controller and I’m probably not going to be the last. A quick google around throws up the following results, and these are just the guys that have put virtual pen to paper and taken the time to publish their work.

  • Here’s an Arduino shield-based controller that looks professionally produced and generally well thought out.
  • Dan Strother’s effort is a simple affair but his firmware is very good. While you’re there take a look at his Spartan-6 BGA test board. I can’t believe he did it with Eagle, there’s only so much pain a man can take.
  • Sparkfun did one once and then pulled it. Goodness knows why.
  • There’s even an instructable that you can follow.

There’s more. There’s a lot more, so why another one? The commercially available controllers didn’t look particularly inspiring to me so a self-build was going to be the way to go. I could either follow someone else’s schematic or come up with my own. Well I felt up to the challenge of doing it myself and bringing my own bit of flair to the presentation as well as making it highly cost-effective.

Component selection

A significant number of components need to come together harmoniously for this project to work. In this section I’ll go over the main component parts and how I selected them.

The Solid State Relay

A solid state relay (SSR) is designed to allow you to switch mains voltages on and off using low voltage DC control, i.e. a microcontroller. They do this by using an optocoupler to internally isolate the two sides from each other. When you trigger the photodiode the mains current is allowed to flow. However, it’s not quite as simple as that. The relay will only change the state of the AC output at the two points in the AC sine wave where it crosses zero so that the input and output waveforms are maintained, the zero-crossing. That imposes limits on the frequency we can use to switch it on and off, something which I will tackle in the firmware design.

The best quality SSRs are made in the USA and you do get what you pay for. There are cheap Chinese Fotek SSRs on ebay for a fraction of the cost of the USA-made SSRs. I knew I was going to need a heatsink so I got one on ebay that came with one of the Fotek’s attached. They were so cheap that I thought I’d get one just to see what they were like.

I tested the Fotek using a light load — literally — it was a desk lamp and it did what it was supposed to do, no problems there. Reports on the internet from users of the Fotek range from ‘it caught fire’ to ‘I’ve been using it for years with no problem’. I suspect that some users don’t realise that you must cool an SSR. It probably doesn’t help that the Fotek only comes with a little aluminium plate on the base that does not shout out its role as a heatsink.

I unscrewed the base to take a look at the innards but could only get it out a few millimetres without damaging the internal wiring. From what I could see there is a PCB housed towards the top of the unit and then a set of bare wires come down to the aluminium base plate where an unidentifiable component is soldered directly to it. This must be the heat generating component.

The overall cheapness of the Fotek did not fill me with confidence so I loitered some more on ebay and managed to pick up a brand new USA-made device for about £8.

The difference in quality is immediately obvious. This device is completely solid and resin-filled. It feels like a proper industrial component and a stamp on the side declares that it’s ’200% tested’. My only reservation is that the AC and DC terminals are only air-gapped. The Fotek, for all its faults, physically separates the two sides so arcing or shorting from the AC to the DC would be practically impossible.

Just look at that base plate. If you were ever in any doubt that this is a device designed to be attached to a heatsink then you won’t be after you see that. I unscrewed the Fotek from its heatsink, applied some thermal transfer gunk that I had left over from a computer HSF kit and screwed in the Opto-22 device. I’m now confident that there won’t be any smoke coming from my mains control unit.

The temperature sensor

After doing some research it turns out that the most popular IC for handling the amplification and analog to digital conversion of the thermcouple readout is the MAX6675 and its successor, the MAX31855 from Maxim. The two devices have the same footprint and pinout but are not software compatible and may not all work with the same thermocouples.

The MAX6675 can read a range of 0 to 1023°C with 0.25°C resolution. The MAX31855 extends the capabilities by being able to read below zero but it adds the limitation that the thermocouple must not be grounded. The MAX6675 can be grounded and in fact it must be grounded if you want to take advantage of its ability to sense an open (disconnected) state. I decided to design the board to be able to handle either type of sensor IC using a jumper to ground it and I’ll be using the MAX6675 on my own build.

A defective MAX6675

The MAX6675 (and its successor, the MAX31855) are rather expensive little chips but I noticed that they were available on ebay for just a few pounds so I bought one. Mistake. I wasted about half a day trying to figure out why the readings I was getting from it were scaled up by a factor of 8. It just didn’t make any sense.

I couldn’t just say, yeah whatever and write my software to scale down by 8 because the maximum reading would be 128°C which is way too low for a reflow oven. This issue had to be resolved.

Eventually I decided to try removing it from the board and replacing it with one that I got from a different seller on ebay that had slightly different numbering printed on the case, perhaps indicating a different batch. It worked perfectly. Lesson learned, it’s just not safe to buy ICs on ebay because you don’t know where they’re sourced from. Next time I’ll swallow the cost and stick to the usual major name suppliers that I usually use.

Anyway, in case it helps anyone else here’s a picture of the bad IC. If you’ve got one with the same ‘+120′ numbering and it’s misbehaving then drop-kick it into the waste bin and replace it with one from a reputable supplier.

Now that the sensor is selected I’m going to need a compatible thermocouple…

The thermocouple

Thermocouples have different designations depending on the temperature range that they are designed to handle. If you’re interested, Wikipedia has a complete list of all the types. The MAX6675 sensor requires a ‘K’ type thermocouple which has a range of -200°C to +1350°C, far more than we will ever need. ‘K’ type thermocouples are available on ebay for just a few pounds and the user feedback for them is very good so I bought-one-now and waited forever for it to arrive from China.

This is a grounded thermocouple. That is, there is electrical continuity between the blue wire spade terminal and the cable braid and the outer metal end of of the sensor itself. This will work with the MAX6675 but I’m not at all sure that you’ll be able to use it if you choose the MAX31855.

The controller

All of the components listed above need to be linked together with an MCU-based controller board. The basic requirements of the controller board are as follows:

  • Accept user control input.
  • Provide user feedback via a display.
  • Interface with the MAX6675 temperature controller.
  • Implement a PID algorithm to control the state of the SSR.

I decided to integrate a Cortex M0 MCU with the 640×360 TFT LCD from the Sony U5 Vivaz mobile phone. If you haven’t already done so then you can read all about my prior efforts in reverse engineering that display in this article. The graphical user interface will be supported by an 8Mbit SPI flash device from Spansion.


The 3.2″ Sony Vivaz LCD

When I started this design there were a couple of aspects where I was operating on a hunch that things would work out. Firstly, the STM32F051C8T7 MCU comes with 64Kb flash. I wasn’t sure whether that was going to be enough to hold the debug builds of the firmware that I would need to do in-circuit debugging with my ST-Link/V2 debugger.

Secondly, I wasn’t certain that the SPI flash interface was going to be fast enough to create a responsive user interface. I really dislike sluggish user interfaces, only instant responses are acceptable to me. The killer feature here is the DMA channels linked to the SPI interface in the STM32F0. If I run the SPI interface at 24MHz (the fastest available) then I should be able to get a 1.5 megapixel/sec sustained transfer rate using DMA because each pixel is 16-bits wide. That should be enough for a responsive UI but I will only know for sure after it’s too late to go back.

Controller schematic



Click on the thumbnail to see the full schematic PDF. I’ll go through the each section of the schematic in detail but first lets take a look at the bill of materials.

Designator Description Footprint Quantity Value
BOOT0, RESET Push-to-make button PCB button 2
C2 Capacitor 0603 1 56pF 50V
C3 Capacitor 0603 1 56pF
C4, C8 Capacitor 0603 2 2.2µF
C5, C15, C17 Capacitor 0603 3 1µF
C6, C7, C11, C12, C13 Capacitor 0603 5 100nF
C9 Electrolytic capacitor Radial 2x6x6 1 10µF
C10 Tantalum capacitor 1206 1 22µF
C14 Capacitor 0603 1 10nF
C16 Capacitor 0603 1 10nF
C18 Capacitor 0805 1 1µF 50V
C19 Electrolytic capacitor Radial 2x6x6 1 100µF
C20 Electrolytic capacitor Radial 2x6x6 1 4.7µF
C21, C22 Capacitor 0603 2 100nF
D1, D2, D4 ESD Suppressors SOD-923 3 OnSemi ESD9B5.0ST5G
D3 Schottky Rectifier SOD123 1 B0530W
DEBUG Header, 10-Pin, Dual row 2.54mm pitch 1
FB1, FB2 Inductor 0603 2 BLM18PG221SN1D
L1 Inductor CDRH5D28 1 22µH
LCD Panasonic connector AXE534124 1 (only digikey US)
Left, Right, OK PCB terminal block 2 pin 3
MCU Header, 3-Pin 2.54mm pitch 1
ON: SENSE, USART Header, 2-Pin 2.54mm pitch 2
POWER PCB terminal block 2 pin 1
PWR SEL Header, 2-Pin 2.54mm pitch 1
Q1 TSM3442 MOSFET SOT26A-6AN 1
Q1.1 Generic MOSFET SOT23-3N 1
R1, R2, R3, R7, R8, R9, R10, R11 Resistor 0805 8 10kΩ
R4, R5, R6 Resistor 0805 3 1kΩ
R12 Resistor 0805 1 100Ω
R13 Resistor 0805 1 5.1Ω
SPI Header, 5-Pin 2.54mm pitch 1
SSR PCB terminal block 2 pin 1
U1 2.8V LDO regulator SOT353-5N 1 ZXCL280H5TA
U2 3.3V regulator SOT223-4N 1 AMS1117
U3 8Mbit serial flash SOIC8N_N 1 S25FL208K
U4 ARM Cortex M0 LQFP48_N 1 STM32F051C8T7
U5 Thermocoupleto A/D Converter SOIC127P600-8AN 1 MAX6675
U6 Backlight boost converter SOT26A-6AN 1 AP5724

The LCD interface

This is a direct copy of the connector schematic from my Vivaz reverse engineering article. It’s a proven design so the decision to drop it into this schematic is an easy one to make. The Vivaz backlight is a string of six white LEDs in series. We have to provide the power for that string but the R61523 controller in the Vivaz LCD has a programmable PWM output that we can use to provide a programmable brightness by connecting that PWM output to the enable pin of the voltage boost converter.

TFTs always need two voltage inputs, one for the digital interface and one for the analog display driver. If you’re lucky they’re both the same and fit the same range as your MCU. We’re not so lucky with the R61523. The digital interface will take 3.3V but the analog voltage requires 2.8V. That’s no problem, we just drop in this small and cheap regulator.

That string of 6 white LEDs in series needs 19 point something volts to light up. The best way to do that is with a constant current backlight generator such as this AP5724 from Diodes Inc. The feedback resistor R13 sets the desired current to 20mA and the EN pin allows us to vary that down to provide a dimmer function.

The power regulator

3.3V power is provided by an AMS1117 3.3V regulator. The maximum current output, 1A, is way above what we will need but the main reason for selecting this regulator is its high input range of up to 15V. I’ve bought some illuminated panel buttons for the physical case that require a 12V input to light up so I’ll be able to use a single 12V input to power the controller board and illuminate the panel buttons.

The ESD diode is optional and can be omitted if you trust your 12V input power supply. The AMS1117 will operate quite happily with just the 22µF output capacitor but I took a belt-and-braces approach and added a 10µF electrolytic capacitor to the input side as well. In this design the power supply needs to be smooth because of the sensitivity of the MAX6675 to noise so the output capacitor should be chosen carefully. I chose a tantalum device because they perform just as well as a ceramic in this role and are more cost-effective in the 22µF size.

The PWR SEL jumper allows me to isolate the AMS1117 from the controller board. Why would I want to do that? When I need to program the SPI flash with the graphics for the UI I will connect up an STM32 development board and use it to program the flash. When I do this I will need to power my board from the same supply as the STM32 development board. The 3-pin MCU header allows me to do this but I must also isolate the AMS1117 because voltage regulators don’t like to receive a reverse current on their output pin. The 3-pin header also has an MCU_RES pin that is connected to the RESET pin of the Cortex M0. When I’m programming the flash using an external MCU I will connect this pin to ground so that the Cortex M0 is held in a reset state and does not try to initialise and interfere with the programming session.

The MAX6675

This module handles the MAX6675 interface. The power, ground and SPI connections are all so-so, nothing special there. However the the T+ and T- inputs to the device are handled with some care. I’ve made provision for filtering the inputs using a 10nF capacitor and ferrite beads as well as protecting the device with ESD protection diodes. All of these features are optional and may be omitted from the final design but it doesn’t hurt to get the footprints for the filtering components on to the board just in case we need them.

The SENSE jumper block allows the T- terminal to be grounded when the jumper is connected. When the MAX6675 is grounded it is able to sense if the thermocouple becomes disconnected. I use this feature in the firmware to provide an emergency abort if the thermocouple link is broken during the reflow process.

If you plan to use the successor to the MAX6675, the MAX31855 then this jumper must be left in the OFF position because that device does not like being grounded. Although I do have a couple of 31855′s in the workshop I have elected to use the simpler 6675 in my implementation because the 31855 comes with some limitations on the type of thermocouple that you can use.

The SPI flash

The Spansion flash device is connected up using a standard SPI interface. This device supports ordinary 1-bit SPI output and also a fast dual-output mode that uses both MOSI and MISO to output 2 bits per clock. If we were using an FPGA then we could use this feature to double the output data rate. However the MCU SPI peripheral doesn’t understand this proprietary feature so we will be using 1-bit mode at 24 megabits/sec.

The debugger

This 20-pin header is designed to interface directly with the JTAG cable that comes with the ST-Link/V2 debugger. The ST-Link/V2 will be used to upload programs as well as mediate between an OpenOCD server and the Cortex M0 MCU using ST’s SWD protocol so that I’ll be able to do full IDE debugging using Eclipse.

BOOT0 and RESET buttons

Having a reset button is a convenience just in case I need to force an MCU reset. The pin is pulled up since reset is active low and a small capacitor provides some protection against transient dropouts causing a spurious reset.

I decided to include a button that would allow me to control BOOT0 just in case I wasn’t able to get SWD debugging to work. If BOOT0 is high when the MCU is reset then it will boot from the internal bootloader which can then download a flash image from the USART pins. I hoped I wouldn’t need this but it does give me another way to flash the device if SWD didn’t work (it did work…)

The input buttons

This design will feature three buttons for navigating the user interface. There’ll be a left, right and an OK button. I considered, and rejected the idea of a touch screen because when I operate this device my hands will be dirty and a touch screen would quickly become fouled up. The three buttons will be ‘active high’ so they are pulled down and get connected to 3.3V when the user presses the button.

The MCU

Here’s the heart of the system, the STM32F051C8T7 MCU in an LQFP-48 package. These MCUs are extremely competitively priced. For £2.60 + tax at Farnell you get a 48MHz 32-bit ARM MCU with 64Kb flash, 8Kb SRAM, loads of on-board peripherals and a DMA controller. You don’t even need an external oscillator as long as you can accept up to 1% deviation from the stated clock speed. They’re even available for less than a pound at Future Electronics and if it wasn’t for their punishing international delivery charges that’s where I’d get them from.

We’ll use all of port B for the LCD 16-bit data bus. That means with the help of an optimised assembly language stm32plus driver I’ll be able to push pixels to this device at 12MHz because the Cortex M requires 2 clocks to write to a GPIO pin and we need to do that twice per pixel write-cycle. 12MHz, or 83ns is very close to the maximum supported by the R61523 LCD driver which is very handy for us.

PA0..2 are mapped to the LCD control signals. PA5..7 correspond to the SPI1 MCU peripheral pins and PA3 and PA4 are the chip select signals for the SPI flash and the MAX6675, respectively. We will share the SPI1 bus between the flash and the 6675 and use the chip-select signals to choose which one we are talking to at any one time.

PA9 and 10 are mapped to the MCU USART1 peripheral TX and RX. I mentioned earlier on that I would use the USART to flash the MCU via its bootloader if I had problems with the SWD debugger. In the end I did not have any SWD problems so I decided to use the USART as a way of exporting reflow session data as a CSV file to a connected computer.

PC14, PF0 and PF1 handle the button inputs and PA11 is the output control pin for the SSR.

The VDD and VSS (ground) connections as well as the decoupling capacitors all follow ST’s recommendations.

The SSR output

The relay output is a simple on-off switch which I’m implementing as a logic level N-channel MOSFET. A high level pulse on the gate will cause 3.3V to flow through the SSR’s control pins and back to ground. We already know that the SSR has a 1kΩ resistor between its control pins so we don’t need to add any more resistance of our own.

I happen to have quite a few TSM3442 MOSFETs in stock so that’s the one I’m using. However, it does come in a rather obscure SOT23-6 footprint when nearly all the competitors come in a SOT23-3 format. To make it easier for others to implement this design I’m including footprints for both devices on the PCB and you just need to use the footprint that fits the device you can obtain.

The PCB design

With the schematic compiled and ready it’s time to start laying down the PCB footprints and traces. The LCD panel measure 80x45mm and I want the PCB to double as a mounting base for the LCD so I’m going to work on a 100x100mm PCB board size. Here it is.




PCB design thumbnail, click for PDF

On one side there is the LCD and its connector. The physical layout of the LCD panel is such that when the LCD is facing upwards the connector is facing downwards which makes it easy to mount on to the board. This is the side of the board that is designed to be pressed up against a window cut out from the box in which the board will be mounted. The other side of the board contains all the components and connectors. This will face down into the box and therefore be accessible for debugging purposes.

The highest frequency signals are going to be between the MCU and the flash and the MCU and the LCD therefore these signals are kept as short as possible. The MAX6675 needs to be kept safe from noise so it’s placed away from the digital components and its power line takes as direct a route from the regulator as possible and there is an unbroken ground plane beneath it.

I generated gerber files from the design and uploaded them to Elecrow for printing. Other, similar services from ITead and Seeed Studio are available but I’ve had good service from Elecrow and their prices are the best (at the moment) so I continue to use and recommend them.

After the usual multi-week wait for the slow postal service to deliver the boards they arrived safely and every one’s a winner as far as I can see. Click on an image for a larger version.



Most of my vias are large and untented because you never know when you’re going to need an impromptu probe test point on your board but there are some tented vias around the LCD connector where there would be a danger of the FPC tail coming into contact with the circuit board.

These tented vias are necessarily small otherwise the solder mask will just fall into the hole instead of tenting over the top. Unfortunately the reality of these cheap prototype services is that total tenting is very hit and miss, usually the mask does cover the annular ring but then collapses into the hole.



Note that these boards do not feature the Q1/Q1.1 dual-footprint option for the SSR MOSFET switch that I talked about earlier. I added that to the design after these had been printed.

Now it’s time to build it. The build was straightforward in that there’s nothing on there that I haven’t soldered before but there are rather a lot of components and there is the additional complexity of components that require reflow on both sides of the board.

The way I dealt with that problem is to reflow the board in stages using my hotplate. The LCD connector on the reverse side went on first as it’s probably the most troublesome with its 0.4mm pin pitch. After that was down I divided the top of the board into sections containing ICs that could be reflowed one section at a time by holding the board partially on the hotplate so the part with the LCD connector fitted to the back hung off the edge.




After the ICs were reflowed into place I then went back to my hot air gun for the SMD passive components and a normal iron for the remaining through hole parts. As you can see from the image above it all worked out rather well. Let’s have a look at the board modules.

The AMS1117 is closely accompanied by its supporting electrolytic and tantalum capacitors. The PWR_SEL jumper in the foreground is normally on, allowing power to come from the external 12V wall adaptor. When programming the flash device from an external MCU this jumper should be OFF to prevent the AMS1117 from seeing a reverse current on its output pin. The 3.3V/GND/RESET header is then used to apply power from the board that’s being used as the programmer and the RESET pin is tied to GND to prevent the Cortex M0 from ever coming alive while programming is taking place. You can’t achieve this by isolating the power pins because the voltages that the MCU sees on its IO pins will cause it to unexpectedly power up — you have to hold it in the reset state.

The AP5724 backlight boost converter circuit and its associated inductor and schottky diode. The output capacitor C18 is going to be seeing around 19V so it needs to be rated accordingly. I used a 50V rated 0805 part.

The STM32F051C8T7 MCU and the 4.7µF electrolytic capacitor recommended by ST. This MCU does not require an external oscillator, the clocks are generated from an internal 8MHz oscillator trimmed by ST to around 1% accuracy. Soldering the LQFP-48 part was remarkably easy. To most people the legs on these ICs look the same but having soldered so many different ICs I can tell you for sure that some are made of metal that attracts solder much more readily than others. This IC is one of them. Just add flux, touch the legs and the solder runs up those legs like its magnetically attracted. I’ve seen the same with Xilinx FPGAs and it’s a real relief when a device behaves like that.

The 20-pin header originally specified for JTAG. For every signal there is a neighbouring ground line. Very good for minimising crosstalk on a parallel cable but very profligate on pin usage. We are going to be using ST’s Serial Wire Debugging (SWD) protocol which only requires a small number of pins that you can see labelled on the board here. The pinout is designed to match the 20-pin cable that comes with the ST-Link/V2 debugger and the little bump in the silkscreen aligns with the bump on the cable connector.

The Spansion flash device is placed close to the MCU, keeping signal lines short and helping with signal integrity. It’s placed at a 45° angle because that’s just the way it worked out so that the traces were the shortest they could be.

The MAX6675 footpring is accompanied by a jumper that determines whether the T- pin should be grounded or not. For the MAX6675 this needs to be ON so that the open circuit state can be detected. For the MAX31855 this must be OFF. In my design I left space for ferrite beads and ESD diodes but in the end I did not need them as I get a nice clean reading without them so the ferrites are replaced by 0R resistor bridges and the ESD diode footprints are left blank.

The SPI breakout header allows me to access the two SPI devices for testing and, for the flash device, programming with an external MCU. When programming the flash I hardwire nCS_OVEN to 3.3V so that it can’t interfere with the programming process.

The navigation buttons break out to terminal blocks at the edge of the PCB suitable for wiring directly to a momentary push-to-make button. A pull-down resistor causes the unpressed state to be GND and a small-ish series resistor helps to smooth out unwanted noise from the button contacts.

The output terminals for the SSR control pins break out to a standard screw terminal block. The actual (small) load is switched with a logic level MOSFET. You can see the TSM3442 in the photograph with its unusual SOT23-6 pinout. This photograph was taken before I added the choice of footprints between this one and the more standard SOT23-3.

The two switches allow me to manually reset the MCU and also to control BOOT0. The ability to reset is useful during debugging but not essential and can be omitted. Controlling BOOT0 was part of my belt-and-braces approach to being able to flash the board. I wasn’t 100% certain that my SWD interface was going to work so I needed a way to cause the MCU to use its internal bootloader to source the flash image from the USART pins as a failsafe. In the end though the SWD interface worked perfectly so this BOOT0 switch is redundant and can be omitted.

In the above paragraph I explained why I might have needed access to the USART pins for programming purposes but since that scenario did not come to pass I was left with a couple of potentially useful pins looking for a purpose. What I did in the end was program the firmware to have an option to output the (T,degrees) points from a reflow curve to the USART so that they can be viewed on a computer charting program.

The firmware

Time to write the firmware and for me that’s definitely the fun part. I decided to use my own stm32plus C++ library to take the strain out of interacting with the hardware and to allow me to just get on with producing a good interface.

I decided that the interface would feature a main setup ‘control’ screen where you can change the reflow parameters followed by an ‘action’ screen that would actually perform the reflow according to the parameters I’d defined. Time to fire up Photoshop and create a mockup UI that would serve as a template for my control screen.



Click for full 640×360 size

This full size 640×360 mockup allowed me to extract and save the individual graphics, including all the numeric characters saved as graphics against their correctly coloured backgrounds. These graphics will be converted to a raw format by the stm32plus bm2rgbi utility and then uploaded to the Spansion flash device on the controller.

The tiled format, with apologies to Microsoft, gives a nice and clear UI that I will be able to navigate around using the three buttons on the controller. The SnPb and SnAgCu tiles allow me to select between lead and lead-free profiles. The mockup shows both tiles checked because I’ll need to save out both those check-boxes to flash due to the different coloured backgrounds (the graphics are anti-aliased by photoshop against the background colours and I need to preserve that).

The purple reflow buttons takes us to the ‘action’ screen. The grey ‘flame’ button shows a constantly updating readout of the current oven temperature which is useful during the cool down phase.

The blue buttons allow me to adjust the co-efficients of the PID algorithm. The defaults will be 1/1/1 and after simulating the algorithm on my PC I know that I only need to be able to move up and down in integer steps to increase or decrease the effect of each component of the algorithm.

The selected parameters are saved to a page in the flash memory and are automatically restored on power-up.

You have to be careful when selecting a font due to commercial licensing issues that cover most of those very nice looking ones that come with Windows so the font that I used throughout is Titillium Web from the Google open source web fonts project.

Fast SPI graphics

There’s three words you don’t often see used together. Surely a 1-bit interface is never going to be fast enough for interactive graphics? Well, it is. By running the SPI interface at the fastest 24MHz rate offered by the MCU and utilising DMA to transfer big blocks at once we achieve bursts of 1.5 megapixels/sec. This is more than fast enough to update just the changing parts of the display in real-time.

My implementation is realised by a FlashGraphics class that initialises the SPI interface on construction and de-initialises it on destruction. This allows me to freely write graphics or read a temperature from the oven even within the same function without having to care much about who owns the SPI bus. The bus is owned by whichever controller class happens to be in scope at that time.

Here’s the main drawBitmap method from the FlashGraphics class.

  /*
   * Read the bitmap from SPI and write it out to the display
   * We'll use the Read Data (03H) command because our max frequency of 24MHz
   * is lower than the device's max of 44MHz so we don't have to use the Fast Read
   * command that would incur a speed penalty due to the dummy writes.
   *
   * The strategy here is to use DMA to read in the bitmap from the flash device into
   * a buffer in chunks. When half the buffer is full we transfer it to the display
   * while DMA is filling the remainder. When DMA has filled the remainder we transfer
   * it to the display. This allows us to get a good utilisation out of the SPI bus.
   *
   * Note that in master mode the SPI clock will not tick unless we transmit something.
   * Without a ticking clock the flash slave device will not latch out the data. Therefore
   * we use DMA to "transmit" fake zero bytes just to get the clock to tick so that there
   * will be data for us to receive. This is one of the oddities of ST's SPI implementation
   * that you just have to learn.
   */

  void FlashGraphics::drawBitmap(const Rectangle& rc,uint32_t offset,uint32_t length) {

     uint8_t zero,bytes[4];
     Panel::LcdPanel& gl(_panel.getGraphicsLibrary());
     Panel::LcdAccessMode& accessMode(_panel.getAccessMode());

     // set up the drawing rectangle and get ready for receiving data

     gl.moveTo(rc);
     gl.beginWriting();

     // first 32-bits are the read command and the offset

     bytes[0]='\x3';
     bytes[1]=(offset >> 16) & 0xff;
     bytes[2]=(offset >> 8) & 0xff;
     bytes[3]=offset & 0xff;

     // select our device

     SpiNssManager nss(*_spi);

     // write out as four 8-bit transfers

     _spi->send(bytes,4);

     // get a temporary buffer and set the dummy byte to zero

     uint8_t buffer[READ_BUFFER_SIZE];
     zero=0;

     while(length>=READ_BUFFER_SIZE) {

       // start a read and wait for half complete

       _rxdma.beginRead(buffer,READ_BUFFER_SIZE);
       _txdma.beginWrite(&zero,READ_BUFFER_SIZE);

       while(!_rxdma.isHalfComplete());

       // transfer the first half to the display while the other half is finishing off

       accessMode.rawTransfer(buffer,READ_BUFFER_SIZE/4);

       // wait for the full complete

       while(!_rxdma.isComplete());

       // transfer the second half

       accessMode.rawTransfer(&buffer[READ_BUFFER_SIZE/2],READ_BUFFER_SIZE/4);
       length-=READ_BUFFER_SIZE/2;
     }

     if(length>0) {

       // receive and transfer the remainder synchronously

       _spi->receive(buffer,length);
       accessMode.rawTransfer(buffer,length/2);
     }
   }

To prove the theory I set up my logic analyser to capture the DMA transfer. You can see the results in the screenshot below.



Click for larger

The serial clock is ticking at a continuous 24MHz while the data is being latched out of the device on the MISO line. This is all as expected and validates this part of the design nicely. I’ve documented the process for flashing the graphics to the IC in the readme page that accompanies my github repo.

The reflow page

When the desired reflow profile and parameters have been chosen the purple ‘reflow’ icon takes us to the page where all the action is. This is a separate and distinct area of the firmware. All memory used by the control page is freed and the reflow page is constructed to take its place.



Click for full 640×360 size

Again I mocked up how the page might look in Photoshop before starting the coding. All the necessary graphics were saved off and added to the UX resources that would be written to the SPI flash memory.

When the user lands on this page the oven is off and we’re ready to go. At this time the user can activate the ‘go’ icon which is selected by default or the ‘exit’ icon can be activated to return to the control page.

Assuming the user hits go then the action starts. The PID algorithm will be repeatedly run to select a low-frequency PWM signal for the halogen lamp as the algorithm attempts to track the reflow profile. The actual temperature will be displayed in orange just below the desired temperature. A red-line will plot across the chart to show how the oven is performing.

The PID algorithm

The Proportional, Integer, Derivative (PID) control algorithm is documented at length on the internet so I won’t go over how it works again here. The actual algorithm itself is very simple indeed. The core of my PID implementation is shown below and can also be seen here and here on Github.

  /*
   * Update the algorithm with the current error and get a percentage value back
   * that can be used as a PWM duty cycle (0..100). This method should be called at
   * a fixed time interval.
   */

  uint8_t Pid::update(variable_t desiredTemperature,variable_t currentTemperature) {

    variable_t error,pwm,derivative;

    // current error term is the difference between desired and current temperature

    error=desiredTemperature-currentTemperature;

    // update the integral (historical error)

    _integral+=error;

    // the derivative term

    derivative=error-_lastError;

    // calculate the control variable

    pwm=(_kp*error)+(_ki*_integral)+(_kd*derivative);
    pwm=Max(Min(100.0,pwm),0.0);

    // save the last error

    _lastError=error;

    // return the control variable

    return static_cast<uint8_t>(pwm);
  }

The datatype used by the algorithm can be either fixed or floating point (a variable_t typedef in my code above) and that means we have a choice to make. The lazy man’s default choice is the IEEE double type that’s built into the C++ language. It’s a shame that the built-in type manages to be the worst performing choice without hardware assistance and, being a binary fraction suffers from accuracy issues that serve to trip up the unwary.

So now that I’ve slated it I decided to be that lazy man and see whether it would do for me or whether I would need to optimise. Sure enough as soon as the double type and the necessary calculations were added the firmware size jumped up by 8 Kbytes in debug mode. A fixed-point implementation could surely knock that down to less than 1 Kbyte but wasn’t necessary because I was nowhere near the 64Kb flash size limit and it would have been a case of premature optimisation.

Logo screen

With so much of the MCU and external flash resources still available I decided to design a logo screen to show for a few seconds when the device powers up. Out came Photoshop again and after an hour or so of faffing around here’s what I came up with.



Click for larger

The logo graphic is DMA’d from flash to the LCD whilst the backlight is switched off and then I fade up the backlight, hold for a few seconds then fade back out, construct the ‘Control’ screen and fade back in again. The end result is a smooth transition between states.

Final code size

With everything enabled and using inefficient double-precision numbers for the PID algorithm, here’s the resulting code sizes.

Mode Optimisation Text data bss
debug none 44268 2128 1144
fast -O3 41568 2128 1144
small -Os 25592 2128 1144

The debug size is the most important because if that one breaks through the MCU limit then I won’t be able to attach to the running instance and debug it with Eclipse and gdb. The small size is particularly impressive, just 25Kb is needed for an optimised version of the firmware.

PWM and the SSR

A zero-crossing SSR such as the Opto-22 device that I’m using cannot be operated with a high-frequency PWM signal in the same way that you can dim an LED. The only way to do it reliably is with an external zero-crossing feedback circuit and a pulse-density algorithm. Essentially it would work like this:

  1. Each second, run the PID algorithm and get a percentage density for the next second. Distribute 1′s and 0′s evenly across 1 second according to that percentage density that you have. For example a 100% density will result in 100 1′s for people in a 50Hz mains country. A 50% density would result in 100 alternate 1′s and 0′s. A 25% density would result in 0-0-0-1 repeated until you’ve got 100 digits. Reset your interrupt’s index position to zero.
  2. Hook up the zero-crossing detection line to an external interrupt (EXTI on the STM32). It should fire at twice your mains frequency.
  3. When the interrupt fires pick the 1 or 0 from the array you created at the interrupt index position. If you get a 1, switch on the SSR, a zero will switch it off. Increment the interrupt index position and reset it to zero if you hit the end of the array.

I don’t have a zero-crossing feedback signal in my design but that wasn’t going to stop me trying the above algorithm anyway with a fixed 100Hz interrupt. It didn’t work reliably at all. It seems you really do need to be in sync with the actual zero crossing.

No matter, although it would have been nice to dim the halogen lamp it’s not really necessary. The heat-up/cool-down speed is so slow that you can easily just use a PWM signal with a low frequency (I chose 2Hz). The length of the on-off periods in a 2Hz signal is high enough to span a lot of zero crossings so it doesn’t matter if you miss one at the start or the end.

The hardware build

The controller’s built, the firmware’s written. Now it’s time to put it all together and test it in the real world.

The oven



Click for larger

This is the halogen oven that I bought. It cost about £25 on ebay for a 12 litre model rated at 1300W. When it arrived it was bigger than I thought it would be. 7 litre models exist but I chose the larger 12 litre model because they come with the more powerful 1300W bulb that I thought I might need to get the temperature up as high as I need it to go.

Halogen versus toaster oven. Why choose halogen? Lots reasons put together really. Toaster ovens aren’t as common in the UK as they are in the USA, halogen ovens are very common and low cost. Halogen ovens heat up faster than toaster ovens and that’s important for the ramp-up stage of the reflow profile. Halogen ovens have amazing visibility. Not only is the whole thing glass but there’s that extremely bright halogen lamp as well. Let’s take a look at the modifications that I’ve made to the oven to make it reflow-friendly.

Mounting the thermocouple probe

The thermocouple probe needs to be mounted inside the oven and it comes with a screw connector near the probe end that can be used to secure it to the inside of a frame. The problem is that the halogen oven is entirely glass and that means I’m going to have to grind a hole in the side of it.

Grinding a hole through glass is remarkably easy. You just need a rotary tool and a diamond-coated engraving and drilling bit. I bought this set on ebay.

It comes with all manner of bits for engraving and grinding. This is the one to use for grinding a hole through glass.

The procedure for boring the hole is that you need to use your rotary tool on its lowest speed and you must keep the grinding area wet. I marked a point on the edge of the bowl just slightly above the higher of the two internal metal stands and then I put a large drop of water on that spot and set about grinding through. The water forms a white paste with the ground glass which helps to grind further down as well as protecting your bit from damage. I worked slowly and was soon through the bowl without any hassle. I then had to spend a few more minutes working the hole larger by rotating the bit around and grinding outwards until the hole was large enough to take the thermocouple.

There it is, no cracks or splinters. It really is easy to grind through glass if you take it slowly.

And there it is with the thermocouple screwed in and ready so that only the probe is exposed to the heat and not the cable that runs back to the control unit. The probe is positioned just above the stand so that it will sense the temperature within millimetres of the board surface.

The controller housing

I decided to mount the controller inside an ordinary black project box. A rough hole was cut in the front panel where the LCD would show through. The rough edges were then masked with a black square of thin plastic film with an accurate cutout for the LCD visible area. Then I placed a 10x10cm square of clear acrylic over the top and the four screws go all the way through the panel and the PCB mounting holes.



Click for larger

Further holes were drilled for the buttons, the 2.5mm SSR cable, the 2.1mm power cable, the power button on the back and the thermocouple cable. Insulation tape is wrapped around the back of the thermocouple cable to prevent the metal braid from shorting anything in the box.

Here it is switched on. The front panel buttons are illuminated and the control page is being displayed. It’s notoriously difficult to photograph an LCD display and this photograph does little to convey just how bright and colourful it is in reality. The controller box feels very light in the hand, maybe I should glue a brick inside to give it that ‘quality’ feel :)

The AC control unit

You’re an engineer, you don’t need to be warned about the potentially lethal dangers of working with your household mains supply do you? Stay safe, learn why and how.

In my design I have elected to separate the SSR and its mains interface away from the sensitive low voltage MCU controller and particularly the MAX6675 thermocouple AD converter. That means that I need to find a second chassis that is safe to house mains current. I chose to use an old computer PSU from a long forgotten AT-format computer that was languishing on a shelf in my garage.

A computer power supply case is ideal for this task because it’s designed to handle mains voltage, has an earth post on the chassis, and it has an in-built fan for cooling. Furthermore, these old AT supplies come with a passthrough connector that was intended for your monitor. In my design this outlet will be used for the oven routed internally via the SSR. I discarded the 80mm 12V DC fan that came with the PSU and replaced it with a 240VAC 80mm fan that I found on amazon.

I noted that the power connectors are rated for 10A but the oven comes with a 13A fuse in its wall plug. It would be a bad idea for the connectors to melt before the fuse blows so I decided to fit a 10A fuse into the wall-plug.

I wasn’t immediately certain how many amps a 1300W oven would draw until I searched around on the internet and found that the camping fraternity were successfully using these ovens at sites that have 10A limits along with all their other kit without tripping the circuit breakers. Halogen lamps are filament lamps and so they should be almost purely resistive with a power factor close to 1.0. That means that the current draw at 1300W ought to be around 5 or 6 amps.

Here’s the PSU chassis after I modded it to hold the mains parts of my project. The SSR is screwed to the heatsink which is screwed to the bottom of the chassis. The yellow wires are the control signals that connect back to the control box with a 2.5mm ‘headphone jack’ cable connected into one of the existing holes in the PSU chassis that originally held one of the power cables.

The perspective of the photograph makes that 2.5mm socket look a lot closer to the mains SSR terminals than it actually is but still I would prefer to have a physical barrier over those mains terminals and indeed Opto22 do sell a cover separately. I may do something about this issue later but at least for now I know that the chassis is earthed all the way back to my house supply which is itself protected by a very sensitive RCD board.

Testing

For my first test run the oven was unmodified except for the hole where the temperature probe is fitted. The firmware performed as expected, which was a relief, and the P-I-D co-efficients were set to 1-1-1.

It should be evident from the photograph of the display at the end of the run that we have some problems that need to be solved.

  1. The ramp up time is too slow meaning that the oven cannot keep up with the profile. There could be a few reasons for this.
    1. The surface of the glass, particularly the top, gets very hot. This heat loss can be addressed with the addition of some insulation material.
    2. There’s a metal guard below the lamp that must be there to shield the lamp from fat spatter when the oven’s being used to cook food. The guard is drilled but nonetheless it serves to reflect back a significant portion of the lamp’s output.
  2. The PID algorithm doesn’t react fast enough to the cooling down phase at the end. I can address this by simply stopping the process at the end, or perhaps tuning the P-I-D coefficients. P (the present) needs to be more significant than I (the past) to make it react faster to change.

So there I was watching the above test run and in the background my wife was peering in with that ‘now what’s he up to?’ look on her face. The test finished and I was muttering to myself about heat loss and how I was going to have to go off to B&Q to see if I could find some oven insulation. “Why don’t you line it with tin foil?” came a voice from over my shoulder. You know what, that might just work was what I thought.

So I lined the bottom and sides of the bowl with foil, shiny side inwards, leaving the top uncovered so I could get a good bird’s eye view of the board being cooked and then I re-ran the test when everything had cooled back down to room temperature. Did it work? Hell yes! At 100% power the ramp up rate is much faster than that required by the reflow curve and it easily reaches the peak temperature.

After playing with the PID variables I settled on 30/1/1. These values result in a very close tracking of the target curve.

As you can see cooling is still an issue and with no way to automatically vent heat – all I can do is cut the power – the best way to achieve the cool down phase is to simply lift the lid of the oven. That’s what I did in the above test run. I also modified the firmware to make it not advance time until the 25° profile starting temperature is reached.

Fan modding

There’s a metal AC fan in the lid of the oven that operates at a constant high speed while the oven is operational. It’s designed to circulate the hot air to help cook food quickly and evenly. I want to keep the fan so that heat is distributed evenly but the problem is that it’s so strong that it blows small components clean off the PCB. I need to modify it.

I first thought about slowing it down using an inline capacitor but on closer inspection I noticed that the fan is actually two fans on the same spindle. Up above the inner fan there’s another set of blades inside the housing that I can just see through the vents in the plastic. It’s obviously there to cool the electronics from all that heat below and that’s a valuable feature that I want to keep. I decided to modify the lower fan blades to reduce their impact on the PCB in the oven.

Here’s the unmodified fan. I decided that the easiest thing to try would be to bend the blades from their default steep angle to a much shallower angle. That should result in less air being moved and the air that is moved ought to create a circular vortex around the outside of the bowl.

As you can see the modifications are not too drastic and leave me room to flatten the blades further if necessary but in my first test an 0603 capacitor placed on a dry PCB didn’t move at all during the entire test run so I don’t think I’ll need to compress the blades any more.

Watch the video

I put together a video of the oven executing the SnPb reflow profile. You can see it by clicking on the player below. Alternatively, click here to view it on the youtube website.



The video shows the oven during one of its test runs. There’s a small test PCB inside the oven with some 0603, 0402 and 0201 components placed at strategic locations to check that the fan doesn’t blow them away. The embedded close-up of the controller shows how well the oven tracks the reflow profile. The ideal track is in green, the actual track is in red. The panel at the top right shows the actual temperature, ideal temperature and the 2Hz PWM duty cycle being applied to the oven’s mains supply by the SSR.

Source code and Gerbers

The source code’s available from Github. It’ll work with the MAX6675 out of the box. If you want to use it with the MAX31855 then you’ll need to create an equivalent to Max6675TemperatureReader.h and Max6675TemperatureReader.cpp then change the typedef for DefaultTemperatureReader to reflect the name of your new class. I’ll happily accept a pull request for this modification.

The Gerber files are available from my download page. These will be accepted as a 10x10cm board order by any of the usual suspects such as Seeed, ITead or Elecrow.

Final words

This has been a fun project, the outcome of which has provided me with a real enabling tool that will make all my future builds much controlled and repeatable than they ever have been before. I mean, I was getting pretty good with a hotplate but there was no way the plate heating ever followed a standard profile and the danger of burning the bottom of the board that is in contact with the plate was always just a few seconds away.

If you’re considering building your own reflow oven and have any questions or comments on what I’ve presented here then please feel free to leave a comment or send me a message using my website contact form.

Viewing all 38 articles
Browse latest View live