Wednesday, December 5, 2018

SOSS-S8: Simple Open Source Servo STM8S


SOSS-S8 is a DIY servo drive using feedback position control for DC motors with an incremental encoder. It is based on the low cost STM8S103F3 processor with all the functions handled properly by peripherals. Its main puropse is education, by allowing anyone to implement a closed loop position servo using very low cost components and completely open source software tools. You can learn many things with SOSS-S8:
  • Basic concepts in feedback control systems implementation (PID etc),
  • Convert continuous time designs into discrete time (digital control),
  • High precision fixed point math implementation in a tight control loop timing,
  • Writing code for real-time execution and using simple tools to check (LED, cheap logic analyser etc.),
  • Using sdcc, GNU make and standard peripheral library to write a software project for STM8S processor,
  • Simple digital filter implementation,
  • And others...

Currently a textbook PID algorithm is implemented. It is very satisfactory see the motor follow the reference signal, observe overshoot, steady state error, instabilities etc. or tune the control loop using textbook methods like Ziegler Nichols to achieve fast response and zero steady state error. But the real value comes when you connect the SOSS-S8 to your computer and see the control signals on your computer, in real-time, using the excellent SerialPlot software, which is also open source. The development was done in Linux, but it is perfectly valid for Windows or MacOS-X also.

It looks like this...
SOSS-S8 prototype. A PCB may follow someday...

Video here:


Specifications:

  • Control sampling frequency: 2kHz (can be an order of magnitude higher)
  • PWM frequency: 8kHz which is barely audible.
  • PWM resolution at 8kHz: 2000 counts
  • 16bit PID coefficients with 1/32 precision (scaling implemented for speed)
  • Hardware quadrature encoder reading suitable for high resolution encoders (500ppr, 1000ppr etc.)
  • 16 bit control parameters sent in real-time at 500 samples/second for graphing
  • Hardware PWM and timebase generation.
  • Generates its own time based step reference signals, or reads from a potentiometer using ADC.

Hardware:

SOSS-S8 is designed to use very low cost, off the shelf components:
Most of the components are already laying around in a typical workshop, but even if you buy them all, the maximum cost is less than $20. The circuit connections are very simple and can be done on a breadboard using the common "DuPont" patch cables in a few minutes.

I was too lazy to draw a proper schematic, especially since most people will probably be building it on a breadboard. So I drew a connection diagram instead.Next to each module there is a box with the names of connections of that module. Match the names of the connections in the box that is at the other end of the line.

Connection diagram. Match the same pin names at the same position at both ends of the lines.
(Sorry about the raster graphics -JPG- Blogspot does not allow vector graphics...)

How to install and run: Firmware

The firmware source code can be downloaded from my GitHub repository: SOSS-S8. The circuit connections can be found in the file motor.c as comments at the begining of the file. To build the firmware, you need to have sdcc installed as well as STM8S Standard Peripheral Library (one simple modification to the library is needed to make it sdcc compatible), a programmer software for the device programmer and  GNU make. I discuss how to install the development environment, in another blog post. You should test a few simple firmware projects to make sure they work before attempting this one. You can try the timebase project for example. About the only modification to the source is to modify both 'Makefile's, one in the main directory, and the other within 'libs' directory, to tell the compiler your path to the ST libraries. Windows users beware backslash '\' should replace forward slash '/' in the path values! Finally, all you should need to do is:
 $ make flash
and it should automatically compile first the files in the 'libs' folder, produce the local library file 'projectlib.lib', then continue with motor.c, and finally, if SOSS-S8 is connected to your computer, it will be uploaded and ran.

Remember that since this is a control system, the motor rotation direction is important. I took clockwise as viewed from the front of the motor as positive. That means if you apply a positive control signal to the motor, it should rotate in the positive direction, and the measured motor angle must be increasing, otherwise the control loop becomes positive feedback. If either of them are opposite, you should correct them: If the motor turns in the wrong direction, swap its terminals to the power amplifier, or if it turns in the right direction but the encoder reading is decreasing, swap the A and B leads of the encoder. If both are opposite, it will still work, but in the opposite direction to your reference commands. Of course you can also choose counterclockwise as the positive direction.

The communication settings

SOSS-S8 periodically sends measured values over the serial port. Currently it sends the measued motor angle, position reference and control signal at 500 samples/sec. This is quite fast even for the small and fast motor that I used in the prototype. To get the highest sample rate from the microprocessor at a given baud rate, we must minimize the number of bytes that are sent for each sample. The data is packed in the following way:
  • A constant synchronization sequence: 0xAA 0x55,
  • Parameter 1 formatted as 16 bit int, MSB first,
  • Parameter 2, 3, etc. in the same format,
  • No end delimiter or checksum.
For example, using 115200bps, to send 3 measurements, we need to send 8 bytes/sample. That means about 1400 samples/second can be sent, which is quite respectable! Roughly, it should be possible to send about 10 variables at 500 samples/sec before communication speed bottoms out. To add more measurements to the sent data, see the file motor.c. It is trivial; just type-cast the data to 16bit int, separate into two 8 bit u_int's and add them to the print buffer. The rest is taken care of automatically.

SOSS-S8 sends the data in binary form to minimize the processor overhead of formatting and to minimize the amount of data transferred over the serial connection. The serial data transfer is done in a non-blocking fashion: There is a print buffer, a buffer pointer, a message length variable and a "printing in progress" flag. A short code after the control routine periodically checks if the flag is set, and if the UART transmit buffer is empy, puts the next byte out. I did not bother writing a circular buffer for this because of the unnecessary computation overhead of pushing and popping data from the circular buffer. If you need, I have a sample circular buffer, let me know.

Going to the receiving end; we use the excellent program SerialPlot by hyOzd. The measurement data streaming from the serial port of SOSS-S8 in real-time is displayed on the PC. SerialPlot should be configured to synchronize to SOSS-S8 synchronization sequence, and accept int16 values in big endian format. This is configured in the "Data Format" tab of SerialPlot. Do the following settings:

  • "Custom Frame"
  • "Frame Start:" AA 55 (this is taken as hex without 0x prefix).
  • "# Channels:" 3
  • "Frame Size:" Fixed Size: 6 (use 2* # Channels)
  • "Number Type:" int16
  • "Endianness:" Big Endian
  • "Checksum:" unclick enabled.
That's it. Connect your USB-Serial converter, determine the port name (top dropdown list), click "Open" and you should see the motor parameters floating by, similar to an oscilloscope. For best results, adjust the sample size, graph size etc, from the "Plot" tab. I use 2000 samples for a good display. The graph here belongs to the motor shown in the top photo, with a reference that changes repeatedly every second, between 0 and 300 pulses.

You can take a snapshot of the data and save it as a csv file, to be plotted using your preferred graphing software (I like gnuplot, or perhaps Scilab, Octave, Matlab etc.). You can also pause the data, zoom into it and many other things.

Program structure:

The program is made up of the main part in motor.c, and the peripheral drivers in the individual files inside the libs folder. Here I will describe how the peripherals are used. I have first written the code using the functions provided by the STM8 Peripheral Libraries from the manufacturer. However, the code size quickly inflated. I designed the project in several parts (control, communication, timing etc.) and each part on its own quickly reached the ROM size limit. So I re-wrote the code by taking the individual register operations from the libraries and discarded everything else. So the code no longer depends on the library and the ROM size is now under 5kb. If you look at the source code, you will see the original library function calls commented out above their replacements.

Timer 1:

TIM1 is used as the hardware incremental encoder counter. It is initialized in the encoder mode. There is no interrupt associated with TIM1; it is read at control sampling times to calculate the control algorithm. The pins for A, B encoder inputs are PC6 and PC7. To be able to use PC6 and PC7 as TIM1 encoder inputs, it is necessary to program AFR0. To set it up you can write this into the configuration register:

 $ echo "00 00 ff 01 fe 00 ff 00 ff 00 ff" | xxd -r -p > TIM1CH1_Options.bin
 $ stm8flash -c stlinkv2 -p stm8s103f3 -s opt -w TIM1CH1_Options.bin
The associated files are: tim1.c and tim1.h.

Timer 2:

TIM2 is used as the hardware PWM generator for the power amplifier. I used it in asimple way where either TIM2 CH1 or CH2 generate the PWM waveform and the other is kept at ground level. After the calculation of the control signal, the PWM compare registers are adjusted. The PWM frequency is a compromise between frequency and resolution. We want high frequency so that it is not audible but at the same time we want the PWM count limit to be high for high control signal resolution. However their product equals the processor clock frequency. As a reasonable compromise, I selected 8kHz PWM frequency and 2000 count limit. The pins for PWM output are TIM2 CH1: PC5 and CH2: PD3. Again, PC5 can only be used as TIM2 output after the same modification of AFR0 explained above (no need to do anything else). The associated files are: tim2.c and tim2.h.

Timer 4:

TIM4 is used as a timebase interrupt generator. It is a simple 8 bit counter with an inflexible prescaler that can be set only to powers of 2 to divide the processor main clock. I set the prescaler to 1/32 and the count limit to 250 to obtain 2kHz INT. There is a further counter which raises a global flag at every millisecond to generate a ms event for easy timing. The MS event is counted in main() for several jobs. The associated files are: tim4_tbase.c and tbase.h.

ADC 1 CH2:

ADC is used as a reference measurement. A potentiometer can be conected here as a voltage divider. One minor simplification is that a new reading is initiated at the end of control signal calculation rather than at the begining. This is to reduce overhead before control calculation. We use ADC1 CH2 (shared with PC4).

main() triggers the ADC conversion. At EOC (End Of Conversion) an INT is produced. The conversion value is stored in the global variable ADC_RES to be used at the earliest convenience... Conversion is right aligned.

UART:

UART1 transmits variables to a PC. It is initialized to 115200Baud. Some simple string send functions are provided. At the moment, only transmit is used, but command receive methods will be later implemented.

ETC:

This rounds up the more significant peripherals. Of course there is more to it; the clock configuration, GPIO configuration and 7 segment LCD interfaces:

The files clock.c and clock.h manage the clock settings. HSI (High Speed Internal) clock is used and the processor clocked ad 16MHz. It also switches on the peripherals which are needed. The other peripherals are left powered off. Please check clock.c if you want to add more peripherals.

The files gpio.c and gpio.h are used to initialize the GPIO ports (LEDs etc.). Fairly straightforward.

The files HT1621_NoLib.c and HT1621_NoLib.h are for driving the LCD display. I think I had ported them from Arduino library to STM32, and then to STM8 here. The source was further modified to remove dependency on the STM Libraries.

Main function:

Let me explain a little about the main() last but not least. It is in motor.c. It starts typically with the peripheral and variable initializations. Especially the control variables structure ControlValues_t  and EncoderValues_t are initialized. Here the control gains are also set. I wanted a simple way of assigning square wave periodic reference. it is stored in the array references[]

Particularly, the define "ANALOG_REFERENCE" drives the control from ADC. If this is commented out, square wave reference is produced.

The main loop has the following functions:

Check the MS event flag if(MS_TickEventFlag==TRUE) and increment various counters. This makes it simple to generate timed events. For example, the reference value is updated every second.

If the flag for control signal reference is set, the motor encoder is read and motor position is displayed on the LCD.  If analog reference is asserted, first a low pass filter is used to smooth out the voltage reading (most pots are noisy!). The filter is implemented using int manipulation; we shift left, do the arithmetic and shift right 3 times which gives 1/8 precision. Then error calculation is done and control function is called. Finally the next ADC reading is triggered.

If the reference switch time has come if(REF_count>=REF_PERIOD), the next reference value is selected from the array references[].

If the time has come to transmit the next control values to the PC (I call that print): if(PRINT_count>=PRINT_PERIOD), the print buffer is populated. First the synchronization byets 0xAA and 0x55, then the variables are written to the buffer. I typecast the values to int16_t, and then write them to the buffer in big endian format. The printing state variable is also set to TRUE so that the non blocking print routine can send the characters.

Finally the non blocking print routine sends the characters one by one as soon as the UART transmit buffer is empty.

Finaly, the apply_control() function. It calculates the control algorithm and applies it to TIM2 for PWM generation. A simple anti-windup is also performed here. The time spent in the control algorithm can be used to check the processor utilizatin using an oscilloscope. I am hoping to find time to implement other control algorithms for demonstration in this function.


So, go ahead and build yourself a SOSS-S8 and try your hand at closed loop control implementation!

Saturday, September 22, 2018

STM8S time base using SDCC

The standart "hello world" program is usually blinking LEDs. The straightforward way to do this is to use a loop which wastes CPU time. This is simple but imprecise! What to do if we want to blink a LED at, say, 1Hz?

The solution is to use a hardware timer. We set a hardware timer to count from the system clock. If we know the clock rate, then we can calculate the number of counts required. When the timer overflows, we know the predefined time has passed.

We can either keep checking and wait for the timer to overflow, or set an interrupt service routine (ISR) so that it notifies the program of the overflow. Here I demonstrate the latter. A blocking timer function "Delayms" is given. Its argument is the number of milliseconds that the function will block. Non blocking timing is also demonstrated. It uses the signal set by TIM1 overflow ISR.

The above is general knowledge, but my intention here is to show you the details of how the timer (TIM1) is set up for overflow interrupts, how the blocking and non blocking delay can be implemented and how external variables are declared and used (a variable that is set and used by functions in different files).

I also show how multiple-file projects can be implemented in SDCC, with several Makefiles. This project is made up of several files. Some reside in a local directory "libs', others reside in the STM8 provided peripheral libraries. All of the "other" files are first compiled into respective object files (.rel extension), and then stored in a local library built specifically for this project. In this project, the library file is "libs/projectlib.lib". The Makefiles check if "projectlib.lib" is up-to-date and if not, also generate it.

For the source code, see my Github site:
https://github.com/ahmetonat/STM8S-time-base-using-SDCC

To build and flash, type:
$ make flash

For an introduction to STM8 programming in SDCC, see another post of mine.

After building and flashing, you should see the LED blink rapidly for 10 pulses first; this uses the blocking delay function. Then it should settle into a double blink pattern, using the non-blocking metod.


Tuesday, January 2, 2018

Programming STM8S using SDCC and GNU make

Programming STM8S using SDCC and GNU make


Recently I started looking at the STM8 microcontroller from  STMicroelectronics. I have used various microprocessors during the years, from the Z80 around 1990, through Hitachi H8, some TI 320C31~ 320C6711 DSPs, and more recently ARM F1~F4 series. The STM8S sounds like a downgrade, especially since during the time of writing, there is not a great price difference between, say 32 bit ARM F0 and S8, for small scale production or hobby work. 

However, STM8S is an interesting processor. It is simple enough that a newcomer can understand the complete inner workings, while it is a modern processor with great peripherals, especially since the architecture is suitable to be programmed in 'C'. Boards are dirt cheap, I have bought this, with a STM8S103F3P6 board for around $2:

I had also bought a ST-Link V2 programmer clone from China; mine is from DealExtreme.com:
Cheap ST_Link V2 ans SWIM programmer
Cheap and cheerful ST-Link V2 clone!

ST libraries available, but...

ST provides a very nice (and free) standard peripheral 'C' library for initializing and using the on chip peripherals of this processor (V2.2.0 at the time of writing). Using the library, you can very quicky setup and use all of the peripherals of this chip. They have even included some examples for various configurations, making this an even better processor for beginners.

However, there is a catch. The library is only intended to be used wirh proprietary 'C' compilers. That means, you will need to fork out the cash and buy the compiler, or live with code limitations the companies impose with the "free" versions of the compilers. Also, you will need to download quite large installers to start using these toolchains. Perhaps more importantly, you need to register to these companies to be able to download. Which I don't like.

Don't get me wrong, proprietary compiler suites are good for professional work where you need to have a someone to ask questions and expect a minimum performance guarantee with large code spaces and long projects, but probably an overkill for hobby work.

SDCC to the rescue

Small Device C Compiler (SDCC) is a 'C' compiler suite with the GPL licence, supporting many 8 bit microcontrollers. It has recently started support for the S8 processor from V8.4.0V3.4.0. I am using V8.5.0V3.5.0, on Ubuntu Linux 16.04. The basic compiler is provided as well as many components even including a simulator (check the licence of each component, not all are GPL). SDCC can be installed easily in Linux by the command:
$ sudo apt-get install sdcc

Of course GNU make can be used to automate building your code. I like to use emacs as a text editor. With a proper Makefile, it is as quick as using a GUI to develop and debug  your systems. The good side is, nothing is hidden from view in drop down lists in the GUI; they are all in plain view when you edit the Makefile. Also the development environment is very light as it is only a text editor.  BTW, I recommend using Meld to compare files, it is visual, light and very intuitive to use.

So all we need to do is to write a Makefile that will allow us to build S8 code using STM standard peripheral libraries.

All code is in Github

I prepared a sample code that will compile a multi-file 'C' project using SDCC for STM8S processors , which you can download from my Github account. One header file of the STM library must be slightly modified for the library to compile under SDCC (it involves modifying the syntax for inserting inline assembly instructions. This is clearly mentioned in the Github pages.

Please try it out, and let me know if I can improve it!