Skip to main content

A Forth-based Embedded Controller Development System

Having been a fan of the computer language Forth for many years, I began a project some time ago to create a version for embedded control based on the Microchip dsPIC33. DS blog posts have covered my progress to this version for the Clicker 2 board.

Parts list

Qty Product Part number
1 MikroElektronika Clicker 2 for dsPIC33 MCU Add On Board MIKROE-2567 144-8343
1 Development Kit USB to UART Interface for use with CP2102N USB Bridge 184-0913
1 MikroElektronika MIKROE-1154 791-6485
1 Microchip Microstick II MCU Development Kit DM330013-2 749-6445
1 FTDI Chip USB to UART Cable for Raspberry Pi - TTL-232R-Rpi 767-6200
1 Parallax Inc, BoE Prototyping Shield for Arduino - 35000 781-3027

The Project

This write-up will be a repository for FORTHdsPIC documents such as the latest User Manual and source code (see Downloads below). The source code is fully annotated and may yield some useful assembler-language routines to anyone working with the Microchip dsPIC33 digital signal controller chip. I’ll also add FORTH source code for my various robotic projects as and when.

The FORTH Programming Language

In the early 1970’s a computer language appeared that bore little resemblance to the heavyweight mainframe languages available at the time, such as FORTRAN, ALGOL and COBOL. Called FORTH, it had a number of features making it very popular amongst engineers seeking a high-level language compiler able to create code for real-time applications running on the early microprocessors. These features included:

  • Does not need an operating system (OS) - simple development board required, not an SBC.
  • High-level compiled structures such as BEGIN….UNTIL and BEGIN….WHILE…. REPEAT.
  • 16-bit single-precision numbers with 32-bit double-precision available.
  • High-speed (for the time) program execution.
  • It has both an interpreter to execute a line of code immediately, and a compiler to create new 'words'.
  • Stack-oriented operation for maximum efficiency (as in LIFO push-down stack).   

 

Professional software engineers saw no problem with the last item in the list above, but the non-algebraic source code format (Reverse-Polish Notation) ensured that those new to programming would stick to BASIC or Pascal! To find out more about Forth and its origins, see this excellent website. Also look at the Forth Interest Group Archive and the Jupiter Ace Archive. Both contain lots of useful, downloadable material including original manuals and free ebooks.

Forth's ability to create new commands or 'words' beyond the basic set is considered to be one of its most powerful features. Type VLIST at the console command prompt of any version of Forth and you will get a list of all the words it 'understands'. Look at the screen dump for FORTHdsPIC below. A program for making a simple humanoid robot walk has just been downloaded and compiled. The first block of core words may be executed immediately from a command prompt or used to create new word definitions. The second block provide the structure of a compiled program and cannot be executed directly. The third set of words were created when the user program was compiled.

VLIST printout from FORTHdsPIC vsn 1.0 running on a MikroElektronika Clicker 2 dsPIC33E board.

Each of the words LEANLEFT, ROTATELEFT and LEANLEFTBACK defines a single movement of a particular joint servomotor on the left leg. They are combined in the definition for STEPLEFT which causes the left leg to move forward by one step. The other words do the same for the right leg. STEPLEFT and STEPRIGHT are used in the definition for STEP which moves the whole robot one step forward. Finally, STEP is executed repeatedly by a loop in WALK, making the robot 'walk' forward continuously. These new words could become the 'core' words of a robot programming language. ROBOFORTH from ST Robotics is a commercial robot arm programming language that was created in just this way.

What is FORTHdsPIC?

FORTHdsPIC has its origin back in 1982 when I was designing a portable instrument for measuring battery state-of-charge. The complex algorithm made it an ideal candidate for using digital processing, so a design emerged based on the then-ubiquitous Zilog Z80 chip. Forth was all the rage then; articles appearing regularly in computer magazines. There was even a home computer on the market - the Jupiter Ace - that ran Forth rather than the usual BASIC, from the designers of the Sinclair ZX Spectrum. After finding some outline code for a Z80-based compiler in a computer journal, I created LUT-Forth which fitted into a 4KB UV EPROM. The same code also ran on a Kemitron computer under the 8-bit operating system CP/M. The compiled code it produced was blown into another 4KB EPROM which was then plugged into a second socket on the instrument PCB. I didn’t know it at the time, but the result was what we now call an ‘embedded’ computer!

Many years later I gave in to an impulse to convert my original LUT-Forth Z80 code (actually a subset: 8085 code) to a more modern processor and see how it performed. I chose the dsPIC33 and FORTHdsPIC was born. Current microcontrollers have many features that the poor old microprocessors of the 1970/1980's didn't, so it wasn't going to be too difficult to create a better Forth:

  • On-chip non-volatile programmable Flash memory. And lots of it.
  • Huge variety of on-chip peripheral hardware interfaces.
  • Powerful instruction set with hardware multiply/divide instructions.
  • And a whole lot more besides.

 

First version: 40MIPS dsPIC33FJ128MC802 in 28-pin DIL package

Arduino_dsPIC_1_89d1b38946bee54b28f8865d09cf75f70cfcc513.jpg

Flash memory: 128KB   RAM: 16KB

The first host I used for this project was an Arduino-format board made by a firm in Hong Kong and obtained via eBay. It was perfect for this job as it had a USB-UART Bridge chip on board - a Silicon Labs CP2102, and a USB-B connector. This device is so common, all I had to do was connect the USB to a PC with Internet access and the driver was downloaded and installed automatically. The USB link supplies the all-important terminal or console connection needed by FORTHdsPIC for program entry and display.

The board also featured a Microchip PICkit 3 compatible ICSP header. A PICkit 3 provides the programming/debug interface and link to the MPLAB IDE running on the PC.

By the time I'd got to version 0.5 of the firmware I realised that it would be a good idea to switch to a more widely-available development platform. The Microchip MicroStick II seemed ideal as it retained nearly all the functionality of the old board and came with a built-in programmer/debug tool. The latter eliminated the need for the PICkit 3. I described the transition in this DS blog post.

Microstick_II1_447c7e1b3e723c5b4e1cec46cdf32fffc6e3a0c7.jpg

Of course you lose the convenient Arduino header sockets, but gain header pins which will plug into any standard prototyping breadboard block. The USB connector provides a link to the PC running MPLAB IDE and the solder pads at the opposite end give access to the chip's UART1 used for the console. Very few PCs have serial COM ports nowadays, so to provide a console, a UART-USB bridge cable (767-6200) is needed together with terminal emulator software. I used (and still do) the freely available TeraTerm.

Size matters

It's amazing what you can do with 28 pins, but I began to realise that FORTHdsPIC was starting to out-grow the SPDIP package. At least as a development tool. A specific application might make do with an 8-pin chip and a 2-wire I2C bus to drive external peripherals. See my blog post on I2C serial communication. The 28-pin dsPIC33 device contains far more peripheral function modules than external connections, and pin assignments are programmable by the user. But I wanted all functions to be available at the same time, necessitating the change to a chip with many more pins.

Second version: 70MIPS dsPIC33EP512MU810 in 100-pin TQFP package

Flash memory: 512KB   RAM: 52KB

Clicker2_a2893286f53aa1b9c31cd0bb9f61b43993d83227.png

The new home for the current (I hesitate to say final) version of FORTHdsPIC: 0.9, is a MikroElektronika Clicker 2 development board featuring a 70MIPS dsPIC33EP part replacing the old 40MIPS FJ.

Like its predecessors, this is not a Single-Board Computer (SBC) as it does not have the DDR RAM, GPU and other paraphernalia necessary to support an operating system such as Linux or Windows. In fact the only other major chip present, besides the dsPIC itself, is a power management device (PMIC) which reconciles the various power sources available including an optional Li-Polymer rechargeable battery.

Porting the code from MicroStick to Clicker 2 was not without a few niggling problems causing some frustration, but I got there in the end. Two recent DS blog posts here and here document the journey.

Click and the mikroBUS

A microcontroller development board is no earthly use to a designer unless the attachment of, and communication with, application-specific sensor/actuator circuits is both quick and easy. For me, that means at least one pair of parallel socket headers per expansion board should be provided, allowing matching peripheral modules to be mounted. This twin-header approach makes for a mechanically stable arrangement and is featured on Arduino, Beaglebone and MikroElektronika processor boards which take expansion modules called Shields, Capes and Clicks respectively. I’ve always thought the GPIO connector on one side only of the Raspberry Pi was a weak point in the design. Old-timers like me may remember the notorious 16K RAM Pack of the Sinclair ZX81 that flapped about in the breeze losing all your code just after you’d spent ages typing it in….

The Clicker 2 boards have a ‘belt and braces’ approach to expansion connectors: as well as sockets for two Click modules, there is provision for a 26-way GPIO header along each side of the board (See picture above). The functions of the 16-pin headers on a Click module are defined by the mikroBUS standard. Half of them are dedicated to communication: UART, I2C and SPI buses. Four are for power supply: +3.3V, +5V and two Gnd connections. The remaining four are given specific functions, but as they are tied to GPIO pins, they can be assigned for any purpose.

Forth functions available on Clicker 2 mikroBUS sockets

mikroBUS 1: UART channel 1 (Console), ADC channel 1, PWM channel 1

mikroBUS 2: UART channel 2, SPI channel 2, ADC channel 2, PWM channel 2

Forth functions available on Clicker 2 26-way headers

LH header: I2C channel 2, Servo channels 1 to 4, Tacho channels 1 & 2, SPI channel 3 CS 1 & 2

RH header: SPI channel 3, ADC channels 3 & 4

mikroBUS ShieldClick_Remote_1_316574f3847aa9ed510874a69cd392513d165342.png

As it stands, the Clicker 2 board only supports two Click peripheral device modules. Even a moderate-sized development project is likely to need more than that. Here’s where those 26-way connectors come in. I soldered stacking headers to each side of the board: these are socket headers with extended solder pins enabling the Clicker 2 to take an expansion board on top and plug in to another underneath. The hand controller for my wireless mobile robot project needed four Click modules: two joysticks, a wireless module and a UART-USB bridge for program development. The mikroBUS Shield (791-6485) provided the answer: it has two sets of Click headers and a useful ‘prototyping’ area as well.

MikroElektronika’s modular system based on Click boards with their mikroBUS connectors is well thought out and allows robust prototypes to be built without the need for fragile wire links. My robot wireless remote-control unit is a good example: it’s strong enough to be tested without any danger of it falling to bits! The only precaution necessary is to keep fingers off the wireless module and its antenna.

Clicker2_plus_blog_3_c19ad45475e0e2a36fca0f01243cb52459557904.png

The mikroBUS Shield provides two more Click sockets yielding access to different channels of UART, SPI and I2C communication to those on the Clicker 2 board itself.

Forth functions available on mikroBUS Shield sockets

mikroBUS 1: SPI channel 3 (CS1), I2C channel 2, ADC channel 4, Servo channel 1

mikroBUS 2: SPI channel 3 (CS2), I2C channel 2, ADC channel 3, Servo channel 2

Forth functions available on mikroBUS Shield 26-way headers

As for Clicker 2 board.

Off-board connectionsP1000361_smaller_93968b062d7d7e3bdb106895de4f617e6d2b7006.png

Not all projects can be constructed entirely from Click modules, so I use the 26-way socket headers on the Clicker 2 for making single-wire connections to other boards. The mobile robot on the other end of the wireless link from the remote-control unit needs just such a connection for its two ultrasonic rangefinders. The SRF08 device is ‘intelligent’, receiving commands and sending back distance data via an I2C bus. I2C is a genuine ‘2-wire’ communication system with a complex protocol that can handle over a hundred addressable devices with just those two wires: one for data (SDA) and the other for the data clock (SCL). The dsPIC has two independent I2C channels: channel 1 appears on both mikroBUS connectors of the Clicker 2 board, channel 2 feeds the sockets on the mikroBUS Shield board (if fitted). For the time being, I’ve only written the driver for channel 2 as the Clicker 2 sockets only need UART or SPI comms, with channel 2 available on the 26-way header for off-board peripherals. My blog post on the wireless robot contains a lot more practical info on the I2C bus, including how to avoid some of those niggling design errors that can drive you crazy!

Benchmarks - how fast does it go?

FORTHdsPIC_benchmarks3_7327d94c2c424667545071b6e20dabae04e34421.png

A set of 16 benchmark routines were developed a long time ago for the purpose of comparing the performance of different Forth implementations. I recently came across the figures for my 1982 4MHz Z80 version and took the opportunity for comparison with FORTHdsPIC.

In order to time each Z80 test with a stopwatch, it was repeated 10000 times giving a result measured in seconds. The overhead of the 'Magnifier' DO...LOOP (BM1) is subtracted from the other tests BM2 to BM16. FORTHdsPIC is timed with the on-chip USER timer and the results are rather more accurate as a result!

The modern chip provides an average speed improvement of around 150 times. The maths routines are around 350 times faster, but that's no surprise as the old Z80 was only an 8-bit machine and featured no multiply or divide hardware. LUT-Forth did, however, acquit itself very well when pitted against many of its contemporaries that were also based on the Z80.

Fixed-Point Arithmetic

In a recent DS blog post I discussed the basics of number formats used in computer systems: integer, fixed-point and floating-point. For many years, microcontroller devices were only able to perform arithmetic on integer (signed whole) numbers with no fractional component, that is, with no decimal point present. Which to use depends on the application and the hardware available.

  • Integer arithmetic is standard on all microcontrollers and allows for some very fast code, but as you can see below, the range of numbers is limited and intermediate results in a complex algorithm may overflow without careful scaling.
  • Fixed-Point introduces a Decimal Point capability which may make results more ‘readable’ to humans, but the underlying code is still integer in format, with no difference to the numeric range available.
  • Floating-Point involves a complete change to the way numbers are stored and arithmetic performed (see blog post above). Most microcontrollers are not equipped to handle floating-point numbers and implementation must be in (slow) software. But it may be worth it if a huge numeric range is more important than speed. An increasing number of microcontrollers do have hardware floating point units (FPU). Any device with an ARM Cortex-M4F core, for example, can crunch IEEE-754 single-precision numbers as fast as integers.

At its most basic level, Forth can only perform signed (2’s Complement) integer arithmetic on Single-Precision (SP) 16-bit numbers in the range -32,768 to +32,767, or on Double-Precision (DP) 32-bit numbers between -2,147,483,648 and +2,147,483,647. No fractions (numbers less than 1) are allowed. For most embedded applications that’s not a serious problem because most sensor devices supply data in the form of 8-, 12- or 16-bit integers. Likewise, output devices such as actuators and displays have no interest in numbers with decimal-points. But there are situations where the mathematical algorithm you are coding is expressed in terms of fractional numbers, say, taking data from a keyboard and outputting results to a visual display.

A trivial example

Code inputs the radius r of a circle in metres to three decimal places and outputs the area A in square metres to the same resolution. The formula is of course, A = πr2, and this is what you get from a pocket calculator:

If r = 1.512 and π = 3.142 then r2 = 2.286144 and so πr2 = 7.183064448 sq.m

Note how each multiplication adds another 3 decimal places: we only want 3 not 9, so the excess digits are simply truncated to give A = 7.183.

Simple Scaling

We could turn r and π into integers by multiplying each by 1000. Recalculating A with 1,512 and 3,142 gives us 7,183,064,448. Oh dear, even the DP format can’t handle numbers that big. Intermediate scaling is required which means dividing the result of each multiplication by 1000. Once again, now using scaled integer arithmetic:

r2/1000 = 2286 and then πr2/1000 = 7182

Note that the result is still a scaled integer and needs to have the decimal point inserted in the correct place when printed out.

Handling numbers in Forth

All versions of Forth have a useful function for scaling called */ or ‘Star-Slash’. It takes three parameters off the stack, multiplies the second by the third and divides the result by the first. It executes considerably quicker than * and / separately and includes a very useful bonus: the intermediate result is Double-Precision. It means that all three input numbers can use their full SP range with no problem with overflows. A Forth definition to calculate the area A of a circle of a given radius r could look like this:

: AREA DUP 1000 */ 3142 1000 */ ;                             Stack: (r → A)

Note that AREA assumes that the input radius r has been scaled (multiplied) by 1000 to make it an integer to match the scaled value of π. When run on FORTHdsPIC with r = 1512, you get this:

10> 1512 AREA .

7182 OK

10>

The result A = 7812 needs to be ‘de-scaled’. To print out the result with a decimal point in the right place, a new definition based on Forth’s numeric output formatting words needs to be created. SP3 converts an unsigned SP integer into an ASCII character string, saving it in an area of RAM called the PAD before TYPE sends it to the console screen. 46 HOLD inserts the ASCII code for “.” into PAD. The zero before the <# is the most-significant half of the expected DP number.

: SP3 0 <# # # # 46 HOLD #S #> TYPE SPACE ;   Stack: (n →)

So, using the previous result:

10> 7182 SP3

7.182 OK

10>

New FORTHdsPIC words

Standard Forth has built-in words for printing signed SP integers (. and R.), signed DP integers (D. and D.R) and unsigned SP integers (U.). I’ve added two more to FORTHdsPIC for handling signed DP fixed-point fractional numbers: F. and F.R plus a new system variable FPL.

But first, an odd feature of Forth literals; the presence of a decimal point within a literal tells the interpreter/compiler that it should be stored as a double-precision number. The position of the decimal point merely determines the value of a system variable DPL indicating the number of decimal places. It is not used by any of the standard printing words and the number is always stored as an integer. For example:

1234 will be saved as a 16-bit integer 1234 with DPL = -1

123.4 will be saved as a 32-bit integer 1234 with DPL = 1

1.234 will be saved as a 32-bit integer 1234 with DPL = 3

The new system variable FPL is set by the user to the number of decimal places required in the printed output. Once this has been done, F. will print out a signed DP number with the decimal point in the selected position:

10> 3 FPL !

OK

10> 7182 S->D F.

7.182 OK

10>

Note the S->D word which converts the SP number to DP. In this case, as it’s a literal being output rather than the more usual result of a calculation on the top of the stack, 7182. could have been typed to create the number directly in DP format.

You may have noticed that all the numbers used in the examples are single-precision only. That’s because the scaling function */ only works with SP numbers. For double-precision, a D*/ is needed which would work with a 64-bit intermediate result! That’s something I might think about creating in the future.

END statement

Another new word has been added to FORTHdsPIC’s vocabulary, END, which is used to terminate source code saved on the host PC’s hard disk.

The new word summarises the compilation of a source code file in terms of the number of errors detected, the amount of code space remaining and the percentage of the available code space used. If an error is flagged, END forces a software reset clearing the partially compiled code from RAM. If no errors are found, the code can be run immediately.

 

Engineer, PhD, lecturer, freelance technical writer, blogger & tweeter interested in robots, AI, planetary explorers and all things electronic. STEM ambassador. Designed, built and programmed my first microcomputer in 1976. Still learning, still building, still coding today.
DesignSpark Electrical Logolinkedin