RSS

Posts Tagged ‘Software Development’

4 Digit, 7 Segment Display

Thursday, May 17th, 2012

In early May I started working on a module to display a 4 digit number on a 7 segment LED display. I developed this as far as a working proof of concept developing the hardware and software necessary to prove that the module was feasible. At this point I discovered that development was ongoing by two other teams and decided to stop development at this point taking what I had learnt onboard and move on to other projects.

This article presents the work so far as this is a working project and demonstrates some of the principles used in the STM8S articles written so far this month. Namely:

  • GPIO output
  • Timers

As already stated, this project is working but incomplete and there is no intention to take the hardware further than the proof of concept as presented here.

Hardware Design

The system will use four 7-segment LED modules in place of a single 4 digit, 7 segment module. The hardware design requires the following circuit components:

  • Microprocessor controller
  • Power supply to the 7-segment display modules
  • Decoding for the 7-segments

The software will use multiplexing to allow one chip to be used to control the four 7-segment modules. The basic process is as follows:

  1. Turn power to all segments off
  2. Send the data for a digit to the 74LS47 chip
  3. Turn on the power for the digit
  4. Wait a short time
  5. Move on to the next digit and go back to start

This will give the illusion that all of the LEDs are powered all of the time.

Microcontroller

The microcontroller chosen was the STM8S003 as this is supported by the Netduino team for designers who wish to create their own modules. This chip has several free development environments available and the IAR environment has been used in this blog to document several small projects using this family of chips.

The project as it stands requires the use of 9 pins from the STM8S. Four pins will be used for power control, one line per digit. A further four pins will be used to tell the 74LS47 which of the segments are to be lit. The final pin will be used to indicate if the digit zero is to be shown for a zero value. More on this in the decoding section below. The use of this number of pins makes this unsuitable as it stands for use as a Netduino GO! module. Further refinement is required to take this forward as a module.

7-segment Power Control

The LED power is controlled by using a PNP transistor (the 7-segments share a common anode). In this circuit the power is run through a single current limiting resistor. This will mean that numbers with few digits (for example, 1) will be displayed more brightly then those with a large number of segments (say 8). The final circuit would need to refined to have a current limiting resistor per segment or using a constant current driver. This component of the circuit looks like this:

Power control for a single 7-segment LED module.

The resistor R5 is connected to the output of the microcontroller and the output of R9 going through to the power pin on the 7-segment display.

Decoding the 7-segments

The decoding of the output from the microcontroller is performed by the 74LS47 chip. This is a dedicated decoder circuit for common anode 7-segment LED modules. The system takes a binary coded decimal number and then sinks the current from the appropriate segments of the display. The result is that the module will display the appropriate digit.

We will also take advantage of one final line, the RBI line. By turning this line on and off we can determine if the digit will display the digit 0 when the input to the decoder is zero. So why would we change this line? We we could fix the line so that a digit will always be displayed. So when the microcontroller wants to show 0 it will actually show either a blank display or 0000. The blank display is obviously not desirable as the user will not be able to determine if the display is showing 0 or if it is turned off. Showing four zeroes will give the user the correct information. A nicer solution is for the display to show a single zero when the microcontroller outputs 0.

The resulting decoder wiring looks something like the following:

7-segment decoder circuit

Where pins A, B, C and D will output the binary version of the digit to be displayed.

Full Circuit

This PDF File contains the full circuit diagram for the proof of concept module. The fully assembled circuit looks like this:

Prototype Circuit

Software

In order to test the circuit we will write a small program which will start at 0 and count to 9999 before returning to 0 and starting counting again.

The first thing we need to do is to initialise the system. The following code resets the system clock source, configures the GPIO lines and then starts the timer which contain the code which will perform the multiplexing.

//
//  Initialise the clock
//
CLK_DeInit();
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);                // CPU Prescaler = 1.
CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);                // Prescaler = 1, 16 MHz.
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO,              // Automatically switch
					  CLK_SOURCE_HSE,                   // Switch to internal timer.
					  DISABLE,                          // Disable the clock switch interrupt.
					  CLK_CURRENTCLOCKSTATE_DISABLE);   // Disable the previous clock.
//
//  Initialise GPIOs
//
GPIO_DeInit(GPIOC);
GPIO_Init(DIGIT0_BI_PORT, DIGIT0_BI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT1_BI_PORT, DIGIT1_BI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT2_BI_PORT, DIGIT2_BI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT3_BI_PORT, DIGIT3_BI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT_A0_PORT, DIGIT_A0_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT_A1_PORT, DIGIT_A1_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT_A2_PORT, DIGIT_A2_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(DIGIT_A3_PORT, DIGIT_A3_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(RBI_PORT, RBI_PIN, GPIO_MODE_OUT_PP_LOW_FAST);
//
//  Setup timer 2 to interrupt every 2048 clock pulses.
//
TIM2_DeInit();
TIM2_TimeBaseInit(TIM2_PRESCALER_2048,      // Interrupt every 2048 clock pulses.
				  1);                       // Period is one.
TIM2_ITConfig(TIM2_IT_UPDATE, ENABLE);      // Enable the overflow interrupt.
TIM2_Cmd(ENABLE);

The next thing we need to do is to look at displaying a digit. To do this we will turn off all of the digits and then convert the digit into binary outputting the value on the four pins connected to the 74LS47 decoder.

GPIO_WriteHigh(DIGIT0_BI_PORT, DIGIT0_BI_PIN);
GPIO_WriteHigh(DIGIT1_BI_PORT, DIGIT1_BI_PIN);
GPIO_WriteHigh(DIGIT2_BI_PORT, DIGIT2_BI_PIN);
GPIO_WriteHigh(DIGIT3_BI_PORT, DIGIT3_BI_PIN);
if (digit & 0x01)
{
	GPIO_WriteHigh(DIGIT_A0_PORT, DIGIT_A0_PIN);
}
else
{
	GPIO_WriteLow(DIGIT_A0_PORT, DIGIT_A0_PIN);
}
if (digit & 0x02)
{
	GPIO_WriteHigh(DIGIT_A1_PORT, DIGIT_A1_PIN);
}
else
{
	GPIO_WriteLow(DIGIT_A1_PORT, DIGIT_A1_PIN);
}
if (digit & 0x04)
{
	GPIO_WriteHigh(DIGIT_A2_PORT, DIGIT_A2_PIN);
}
else
{
	GPIO_WriteLow(DIGIT_A2_PORT, DIGIT_A2_PIN);
}
if (digit & 0x08)
{
	GPIO_WriteHigh(DIGIT_A3_PORT, DIGIT_A3_PIN);
}
else
{
	GPIO_WriteLow(DIGIT_A3_PORT, DIGIT_A3_PIN);
}
GPIO_WriteLow(port, pin);

The final statement will turn on the PNP transistor connected to the selected digit by connecting the base of the transistor to ground.

The final piece of code we need (before the main program loop) is the interrupt handler for the timer. This will turn off all of the power to the LED modules and then work out which digit to show and write the data to the 74LS47 decoder. This interrupt service routine can be found in the file stm8s_it.c. See previous STM8S post on interrupts for further information. Look for the following code in this file:

INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13)
{
}

And replace with the following code:

INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13)
{
    if (_currentDigit == 0)
    {
        DisplayDigit(_displayValue[0], DIGIT0_BI_PORT, DIGIT0_BI_PIN);
        GPIO_WriteHigh(RBI_PORT, RBI_PIN);
        _currentDigit++;
    }
    else
    {
        if (_currentDigit == 1)
        {
            DisplayDigit(_displayValue[1], DIGIT1_BI_PORT, DIGIT1_BI_PIN);
            if ((_displayValue[2] != 0) || (_displayValue[3] != 0))
            {
                GPIO_WriteHigh(RBI_PORT, RBI_PIN);
            }
            else
            {
                GPIO_WriteLow(RBI_PORT, RBI_PIN);
            }
            _currentDigit++;
        }
        else
        {
            if (_currentDigit == 2)
            {
                DisplayDigit(_displayValue[2], DIGIT2_BI_PORT, DIGIT2_BI_PIN);
            if (_displayValue[3] != 0)
            {
                GPIO_WriteHigh(RBI_PORT, RBI_PIN);
            }
            else
            {
                GPIO_WriteLow(RBI_PORT, RBI_PIN);
            }
                _currentDigit++;
            }
            else
            {
                DisplayDigit(_displayValue[3], DIGIT3_BI_PORT, DIGIT3_BI_PIN);
                GPIO_WriteLow(RBI_PORT, RBI_PIN);
                _currentDigit = 0;
            }
        }
    }
    TIM2->SR1 = (uint8_t) (~(uint8_t) TIM2_IT_UPDATE);  // Clear the interrupt.
}

One interesting thing to look at is the setting of the RBI_PORT. Note that this is set to high for the first digit (i.e. rightmost digit) and low for all of the other digits. This is the piece of magic which ensure that we always display a digit for zero in the rightmost digit but only display a value for no zero digits on the remaining three digits.

If this were to be taken forward then the code above would need to be optimised to ensure that the ISR completed in the shortest time possible.

Now we have all of the elements in place we can start to write our main program loop. The code will loop from 0 to 9999 displaying each digit and the restarting the count. The individual digits will be put into an array of four integers, one for each LED in the display. This gives us the following code:

enableInterrupts();                         // Make sure interrupts are enabled.
int value = 0;
while (1)
{
	_displayValue[0] = value % 10;
	_displayValue[1] = (value / 10) % 10;
	_displayValue[2] = (value / 100) % 10;
	_displayValue[3] = (value / 1000) % 10;
	if (value == 9999)
	{
		value = 0;
	}
	else
	{
		value++;
	}
	//
	//  Now delay otherwise we'll be counting too fast for anything
	//  to appear on the display.
	//
	for (int outerDelay = 0; outerDelay < 5; outerDelay++)
	{
		for (int delay = 0; delay < 0xffff; delay++)
		{
			nop();
		}
	}
}

The full source code for this project can be downloaded here.

Conclusion

This was an interesting project to put together and took about a day to work out and get everything working. To take this further would take a lot more work, many times the amount already put into the project. As two other groups are working on similar ideas I have decided to archive this for now and move on to other projects.

STM8S SPI

Wednesday, May 16th, 2012

In this latest in the series I am taking a look at communication between two systems (namely a Netduino Mini and a STM8S103) using SPI.

As with previous articles the system will use the IAR compiler and the STD Peripheral Library. The Netduino Mini will use Visual Studio and .NET to act as the master for the SPI bus.

I ran into some problems along the way and had to ask for help in the Netduino Forums. Special mention to Chris Walker and CW2 for their help.

Netduino Mini Application

SPI is one of the protocols supported by the NETMF framework on the Netduino platform. As such there is little work to do. The Mini will have to act as the SPI master as this is the only role which is supported on the Netduino for this bus. We simply need to set up the SPI bus with the correct parameters and then start writing data to the bus.

To remove as many possible problems as possible we will perform a simple task, have the Netduino Mini write some data to the bus on a regular basis. In this case we will write three bytes with the values 1, 2 and 3. The code on the Mini is as simple as the following:

public static void Main()
{
	SPI.Configuration config = new SPI.Configuration(SPI_mod: SPI.SPI_module.SPI1,
							   ChipSelect_Port: Pins.GPIO_PIN_5,                        // Enable pin is D5.
							   ChipSelect_ActiveState: false,                           // Active Low
							   ChipSelect_SetupTime: 0,                                 // 0 uS setup time.
							   ChipSelect_HoldTime: 0,                                  // 0 uS hold after data transmission has completed.
							   Clock_IdleState: false,                                  // Clock line is low when the device is inactive.
							   Clock_Edge: false,                                       // Data is sampled on the clock falling edge.
							   Clock_RateKHz: 500);                                     // 500 KHz clock speed.

	SPI spi = new SPI(config);

	byte[] buffer = new byte[] { 1, 2, 3 };
	while (true)
	{
		spi.Write(buffer);
		Thread.Sleep(1000);
	}
}

This code configures the bus as follows:

  • Chip select will be D5 on the Netduino Mini
  • Chip select will be active low
  • Clock is low when the device is inactive
  • Data is sampled on the falling edge of the clock signal
  • Clock rate is 500 KHz

The selection of these values is deliberate as they match the values used on the GO! bus.

STM8S Code

This is where it got tricky and where I need some help. We have a simple application on the Netduino Mini which simply sends out three bytes, waits a second and then repeats. In order to know that we have received this data OK, the application on the STM8S will read these bytes from the SPI bus, add 100 and then send the result back to the Mini.

The first thing we need to do is to set up a project similar to the first article in this series. The main STD Peripheral Library module we will be using here is the SPI library which can be found in stm8s_spi.c. Add this to the project and then reference the stm8s_spi.h in the main program file.

Now we have the SPI library referenced and the library file added to the application we need to start using it. So the first step we need to take is to configure the library ready for use. The following code should achieve this matching the parameters we have set up in Netduino Mini project above.

SPI_DeInit();
SPI_Init(SPI_FIRSTBIT_MSB,                      // MSB first.
		 SPI_BAUDRATEPRESCALER_32,              // For slave comms this does not matter as the clock is controlled by the master.
		 SPI_MODE_SLAVE,                        // SPI Slave
		 SPI_CLOCKPOLARITY_LOW,                 // Clock is high when the bus is idle.
		 SPI_CLOCKPHASE_2EDGE,                  // Falling clock edge clocks the first data bit.
		 SPI_DATADIRECTION_2LINES_FULLDUPLEX,   // Full duplex transmission.
		 SPI_NSS_HARD,                          // Disable slave management by software.
		 0x07);                                 // CRC style.
SPI_ITConfig(SPI_IT_RXNE, ENABLE);              // Interrupt when the Rx buffer is not empty.
SPI_ITConfig(SPI_IT_TXE, ENABLE);               // Interrupt when the Tx buffer is empty.

The first this we do is call SPI_DeInit to reset any SPI bus configuration.

Next we call SPI_Init to configure the system, as a SPI slave using the same parameters as the SPI master (i.e. the Netduino Mini). You should not that although the baud rate prescalar parameter is set here it will be ignored. The master controls the clock on the SPI bus.

The final two statements (SPI_ITConfig statements) determine which actions will generate an interrupt. In this case we are instructing the system to generate an interrupt in the following cases:

  • When the Rx buffer is not empty (i.e. data has been received)
  • When the Tx buffer is empty (i.e. the system is ready to send more data)

Interrupt Handler

The data will be handled by an interrupt handler. This handler will be called when we need to send data or when we have data ready for processing. As before, the handlers can be found in the file stm8s_it.c. The handler we will be looking for is the SPI_IRQHandler. Locate this handler and replace the code with the following:

INTERRUPT_HANDLER(SPI_IRQHandler, 10)
{
    if (SPI->SR & SPI_SR_RXNE)
    {
        //  u8 ch = SPI_ReceiveData();
        u8 ch = SPI->DR;
        //  SPI_SendData(ch + 100);
        SPI->DR = ch + 100;
    }
    SPI->SR = (u8) 0;
}

The two lines commented out are the equivalent calls into the SPI library. Remember that ISR’s should be as fast as possible. Replacing the calls with the appropriate expansions will decrease the amount of time required to execute the ISR.

So, here we check to see if the ISR is being called because the Rx buffer is not empty. If the buffer is not empty then we extract the value from the buffer, add 100 to it and then put this in the Tx buffer.

The final thing we do is to clear the interrupt from the status register.

Main Program Loop

The code in the main program queues up a byte ready for transmission, enables the interrupts and then repeatedly waits for more interrupts. The code looks like this:

int main(void)
{
    Initialise();
    SPI_SendData('*');                          // Ready to send before master can initiate transfer.
    enableInterrupts();                         // Make sure interrupts are enabled.
    while (1)
    {
        wfi();
    }
}

So What Happened?

After spending a fair amount of time with the logic analyser and the debugger I hit a brick wall. The interrupts we not getting fired as I expected. This is where Chris Walker and CW2’s help was required. The point I was missing regarded the SPI_NSS signal. On the STM8S103 chip I was using this function is an alternative function for the labelled pin. In order to allow this pin to act as the chip select for the SPI bus it was necessary to set the alternative function register on the chip. Once this was set correctly the system started to generate interrupts as expected.

STM8S Interrupts With the STD Peripheral Library

Saturday, May 12th, 2012

So you have just purchased that wonderful sensor and now need it to grab the attention of the STM8S – enter the interrupt. In this post I will run through how to set up a GPIO port to have an incoming logic signal generate an interrupt.

As in previous posts on the STM8S, this post will use the IAR development environment and the STD Peripheral Library from ST.

Configuring the Port

The first step is to configure a port for input. The following code configures a pin on GPIOB for input:


GPIO_DeInit(GPIOB);
GPIO_Init(GPIOB, GPIO_PIN_5, GPIO_MODE_IN_FL_IT);

the next thing we need to do is to tell the system that this pin is allowed to generate an interrupt:


EXTI_SetExtIntSensitivity(GPIOB, 
                          EXTI_SENSITIVITY_RISE_ONLY);

So that should be all that’s required to configure the system to accept interrupts. Now we just need to process them.

Interrupt Handler

If you have followed my previous posts you will know that we need to copy some standard files across in order to access the STD Peripheral Library. One of the files we add to the project is stm8s_it.c. This holds the interrupt table for the chip. Scroll through the file and you will eventually reach some code which looks like this:


INTERRUPT_HANDLER(EXTI_PORTB_IRQHandler, 4)
{
  /* In order to detect unexpected events during development,
     it is recommended to set a breakpoint on the following instruction.
  */
}

Just place the code to be executed in this method. Remember, interrupt handlers should be small and concise.

Modules

This post requires that you add the code files for the following STD Peripheral Library modules:

  • stm8s_gpio.c
  • stm8s_exti.c

Conclusion

A relatively short post but there’s not much too it. I have successfully used this code to generate an interrupt from a sensor which generates a square wave of up to 500 KHz. The conditions I was using it in gave a maximum frequency of 20 KHz.

STM8S Timers

Saturday, May 12th, 2012

This post has been superseded by the post Using Timers on the STM8S in The Way of the Register series.

In the first post about this chip I showed how the internal clock could be output on a single pin. Here I am going to use one of the internal timers to generate my own clock signal. So in this post we will use two new concepts:

  • GPIO pins to output a signal.
  • Timer 2 to control the timing
  • Interrupt Service Routines (ISR) to do the work

The program will work by setting up timer 2 to generate an interrupt on a regular basis. When the timer interrupt fires we will reverse the direction of a GPIO pin. The net result will be a clock signal with a frequency of twice that of the timer.

As with the previous post I will be using the IAR environment with the STD Peripheral Library.

Setting up the GPIO

The application sets up the GPIO port using the following code:

#define CLOCK_OUTPUT_PORT       GPIOD
#define CLOCK_OUTPUT_PIN        GPIO_PIN_3

These #defines hold the port and pin numbers we are going to use for the clock signal. So in this case we are using pin 3 on port D. Next we need to configure the port.

GPIO_DeInit(CLOCK_OUTPUT_PORT);
GPIO_Init(CLOCK_OUTPUT_PORT, CLOCK_OUTPUT_PIN, GPIO_MODE_OUT_PP_LOW_FAST);

The first statement resets the port so that we can configure the port. The second sets up pin 3 of port D for use. Note that you can use each pin on a port in a different way, so we could have pin 2 on port D as an input whilst pin 3 is an output. To do this you simply add more GPIO_Init statements under the first. Now let’s have a look at the final parameter to the GPIO_Init statement. This defines how the pin will be set up for us. You can decode this as follows:

  • GPIO_MODE_OUT – this is an output port
  • PP – the pin will be used in Push-Pull (as opposed to open drain). This means Setting the pin to 1 will make the pin high, setting it to 0 will make it low.
  • LOW – the pin will start out low
  • FAST – the pin will be used for fast signalling.

Setting up Timer 2

Next step is to configure the timer to interrupt on a regular basis. The first step is to set up the clock to use a known clock signal. For simplicity we will set this up to use the internal high speed clock source which runs at 16MHz. This code is similar to the code in the first post.

CLK_DeInit();
CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);                // CPU Prescaler = 1.
CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV1);                // Prescaler = 1, 16 MHz.
CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO,              // Automatically switch
                      CLK_SOURCE_HSI,                   // Switch to internal timer.
                      DISABLE,                          // Disable the clock switch interrupt.
                      CLK_CURRENTCLOCKSTATE_DISABLE);   // Disable the previous clock.

The only real difference is that the prescaler for the system clock is set to 1 to give the full clock speed.

The next task is to set up the timer to run at a particular frequency:

TIM2_DeInit();
TIM2_TimeBaseInit(TIM2_PRESCALER_4096,      // Interrupt every 4096 clock pulses.
                  1);                       // Period is one.
TIM2_ITConfig(TIM2_IT_UPDATE, ENABLE);      // Enable the overflow interrupt.
TIM2_Cmd(ENABLE);

The comment interrupt every half millisecond is approximate as you will see when we come to take some measurements later.

The Interrupt Service Routine

The final piece of the puzzle is the interrupt service routine. These should always be small and fast. In this case we will simply toggle the specified clock pin to generate a clock signal.

All of the ISR’s are defined in the file stm8s_it.c. The ISR we will be working with is the timer 2 update overflow handler. This will fire when the counter for the timer overflows or becomes zero and the specified number of occurrences of this event have occurred. So in this file we need to locate this code:

INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13)
{
}

and translate it to this:

INTERRUPT_HANDLER(TIM2_UPD_OVF_BRK_IRQHandler, 13)
{
    GPIO_WriteReverse(CLOCK_OUTPUT_PORT, CLOCK_OUTPUT_PIN);     // Generate a clock signal.
    TIM2->SR1 = (uint8_t) (~(uint8_t) TIM2_IT_UPDATE);          // Clear the interrupt.
}

The first line of the ISR toggles the specified bit. The second line is important as this clears the interrupt. If we do not do this then the system will return to this ISR as soon as we exit the routine.

Clock Signal

Compiling and deploying this code to the processor results in the following output on pin 3 of port D:

One millisecond clock signal on port D, pin 3.

The scope we set up for 2 milliseconds per division and this looks about right. The exact frequency count comes out to be 976.6 Hz which is right if you divide the clock signal by the prescaler.

Connecting up the STM8S003

Sunday, April 29th, 2012

A few days ago I wrote about my first attempt at coding for the STM8S003 but forgot to mention how I connected up the chip to the PC. So here is a quick post explaining how to do it.

The STM8S Chip

The STM8S chip chosen was the STM8S003F3 in a TSSOP20 package. This form factor is incredibly small and there is no way to mount it on a breadboard without some for of adapter. Luckily Sparkfun have a SSOP to DIP Adapter 20-Pin in their catalog. However, I could not get hold of one in the UK so I ended up getting the 28 pin version instead. A bit of a waste but it worked.

So armed with the adapter, a chip and some connectors it was time to break out the soldering iron. A brief experiment with drag soldering did not work and I finally settled on soldering each pin very carefully with the finest soldering iron tip I had.

STM8S on SSOP to DIP Adapter

The next job was to mount all of this on the breadboard. There are only really two additional components required for this circuit, 1uF and 0.1uf ceramic capacitors. You can see both of these to the left of the chip in the above picture. The 0.1uf capacitor connects ground and +3.3V and acts as a power smoothing capacitor. The 1uF capacitor connects Vcap and ground.

ST-Link/V2 Programmer and Debugger

The chip is programmed using the low cost (£20 approx) ST-Link/V2 programmer and debugger. This works with both the STM8 and STM32 range of processors.

ST-Link/V2 Programmer Debugger

Putting it all Together

If we put the two components together we get something which looks like this:

STM8S On Breadboard

The red PCB between the programmer and the STM8S circuit is a breadboard power supply which has been set to supply 3.3C to the circuit. The wires connecting the STM8 to the programmer are as follows:

Colour Description
Red Power (3.3V)
Black Ground
Green Reset
Yellow Data

Netduino Go! Module Development – the First Steps

Friday, April 27th, 2012

The last few weeks I’ve been getting things together to start to look at developing a smart module for the Netduino GO!. This post looks at the first steps and ends up with some working code on a STM8S003 chip.

STM8S Discovery

My starting point was to get the software tools installed and working. In order to verify this I purchased the STM8S Discovery board. This has the STLink module and a STM8S105 microcontroller built into a convenient package for less than £7.00. This board itself was easy to set up and worked straight out of the box.

Development Environment

Having the board is one thing but we need a development environment to work with. I tried several environments and in the end I found I was happiest with the IAR environment so for the moment I’m sticking with it whilst I find my feet. Set up was easy and it found the STM8S Discovery straight away.

Start With Something Simple

I wanted to start with a really simple project which would not require any additional hardware other than a scope to check that the application was working. That way I could transfer the program to a microcontroller on a breadboard and know that I had as few possible points of failure as possible. To this end I decided that I would put the microcontroller clock pulses out on one of the pins. I could verify that everything was working but uploading a new program which changed the clock scaler and if all was well I would see a different trace on the scope.

This choice of application was no accident as the data sheet for the chip shows that it has a pin labelled CCO. You can configure this pin to output the controller’s clock signal. Not only that but you can also use the microcontroller’s internal clock generator as the clock signal. So in theory, all I needed to do was add a few discrete components to the breadboard along with the STM8S and I should have a small, simple, self contained system to test the tool set.

Creating a New Project

First thing to do is create a new workspace and project using IAR. Simple enough, enter a name for both and a new main.c is presented to you. Good game but not much use as it stands.

Luckily ST provide a standard library of drivers for the various features available on their chip sets. I downloaded version 2.1.0 of the library and this contained the drivers and some sample applications. The trick is to understand the way in which you access the libraries. It’s really simple and basic. In effect, you add the C source files for the library to your application and compile your application and the libraries at the same time. Both of these are then deployed as a single binary to the microcontroller. Basic but it works. The source files are broken down by microcontroller feature. So, all of the routines etc. for timer 2 are in the header file stm8s_tim2.h and the source code is in the file stm8s_tim2.c.

To make things easier (well, easier for me anyway) I have taken to copying the source and include files from the standard library provided into each project directory I create. That way you simply need to add the source file for a feature as you need it. In this case the only feature I’m going to be using is the clock and so I need to make sure I add the stm8s_clk.c file to the project.

The stm8s.h file contains definitions for a whole range of controllers. It is therefore necessary to define a preprocessor macro for the type of controller the application is targeting. This can be done in one of two ways, either in the stm8s.h file itself (as this in included in the other standard library files) or as a macro in the project. I personally prefer to do this in the project as it makes updating the library code easier.

So Let’s Get Started

First thing to do is to get a workspace and project created. So open IAR and create a new workspace. Next, go to the Project Menu and create a new project. The following dialog should appear:

New Project Dialog

Select a C project and click OK. The next dialog asks you to select a name and location for the project. So decide on a location and name. Being very creative, I went for Test as the project name. Once completed I had a project which looked like this:

New C Project.

The next job is to add the include and source files for the standard peripheral library. So copy the include and source files from the library and add them to the directory containing your project. I normally put the include files in the inc directory and the sources in the src directory. The next thing to do is add these files to the project. So right click on the project name (in this case Test – Debug and select Add – Add Group. Enter the name STD Peripheral Include

Add new file group

Repeat this to add the source file group with the name STD Peripheral Source. Your project should now look like this:

File Groups Added to Project

Now we need to add the files to the groups. For the include files I add all of the files. For the source files I only add the ones I will be using. To do this you right click on one of the file groups we have created, say the STD Peripheral Include group and select Add – Add Files…. For the include files navigate to the directory containing the copied files and select them all.

Repeat for the source files but this time we only select the files we will be using. In this case the clock file stm8s_clk.c. Your project should now look something like this:

All Files added

There are still a few extra steps before we can write some code. The STM8S include files require that the type of chip is defined before any code will compile so we will need to define a preprocessor macro indicating which chip we are using. Next we need to tell the compiler about the location of the include files. Finally, we need to tell the system how to deploy and debug the application.

So let’s start off with the chip family. Firstly we want to tell the compiler about the target family. So right click on the project name and this time select Options. Under General options, select the chip family to be targeted. For the Discovery board the family is STM8S105, for the final module we will be targeting the STM8S003.

Select chip family.

The next step is to tell the preprocessor about the chip family and the location of the include files. From the same dialog as above, select the C/C++ compiler and the Preprocessor tab. Add the entry $PROJ_DIR$inc to the include files and define a symbol for the chip set. Define STM8S105 for the Discovery board and STM8S003 for the final module.

Pre-processor Options

The final step is to tell the IDE how to deploy and debug the application. From the Options dialog select the Debugger category and set the Driver to ST-LINK.

Debugger Options.

Now click on OK to set the options. We can now start to write some code.

Let’s Write Some Code

Replace the code in the main.c file with the following:

#include "stm8s.h"
#include "stm8s_clk.h"

int main(void)
{
    CLK_DeInit();
    CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);                // CPU Prescaler = 1.
    CLK_SYSCLKConfig(CLK_PRESCALER_HSIDIV8);                // Prescaler = 8, 2 MHz.
    CLK_CCOConfig(CLK_OUTPUT_HSI);                          // Output clock on CCO pin.
    CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO,              // Automatically switch
                          CLK_SOURCE_HSE,                   // Switch to internal timer.
                          DISABLE,                          // Disable the clock switch interrupt.
                          CLK_CURRENTCLOCKSTATE_DISABLE);   // Disable the previous clock.
}

Connect up board/chip and hit Ctrl-D. This will compile and deploy the application. The debugger should start with the first line of main highlighted. Press F5 and the application should start. Hooking up the oscilloscope gave the following trace:

Oscilloscope Output

The clock frequency of the chip using the internal high speed oscillator is 16 MHz. Diving this by 8 should give a 2 MHz signal (as indeed the above trace shows). Just to check if this is a coincidence, change to CLK_PRESCALER_HSIDIV8 to say CLK_PRESCALER_HSIDIV4 and the scope output should change to show the new clock frequency and sure enough it does.

STM32F4 Discovery – First Impressions

Monday, March 19th, 2012

I recently came across the STM32F4 Discovery board. This board is an ARM processor on a board with 1MB flash and 192KB RAM, ADC, Audio processing, motion detection USB for both debugging and HID support – not bad for less than £10. Given the price and a bit of research and I thought I’d risk it.

So the board arrived and now it’s time to set up the tool chain. There are several toolchains available but the one which was attractive was the Atollic toolchain. The LITE version 2.3 IDE had no code limitations and instead it limited the features available through the IDE. Note that since buying the board and downloading the software Atollic have gone down the route of removing the restrictions on the IDE and placing a code size restriction on the environment. This fits in with the approach taken by several of their competitors.

Installing the Software and the Board

Installation of the software and the board seemed to go OK. Connecting the board found a few problems. The first problem came from the samples. I imported the directory containing the samples and this seemed to pull in the samples for the Atollic system and also the samples for other development environments. This resulted in multiple projects with the same name and this (understandably) upset the Atollic environment. So a quick play with explorer deleting the extra projects and the remaining projects imported with now issues.

The second project came when I tried to compile, deploy and debug one of the samples. The debugger kept complaining that it could not find the board. Hello Google. The problem was simple to solve and all it required was to reinstall the driver for the board. All this required was to open control panel, selecting the board and then update the driver.

So far, so good. I have the board installed along with the software and I can deploy the sample projects.

Can I write my own software?

My First Application

For my first application I wanted to do a bit more than just flash the onboard LEDs, not much more but I wanted some indication of the speed of the board. So I decided to simply toggle one of the ports and look at this through a logic analyser.

So the first thing to do is to create a new project. So off to the file menu and I selected new project. The restricted version of the development environment does not allow C++ projects so I went for a C project and selected Embedded C Project:

Next I selected the board and hardware settings for the build:

And the final step is to configure the debugger settings:

Next thing to do was to modify the default sample code. I opened the main.c file and replaced the main program loop with the following:

GPIO_InitTypeDef  GPIO_InitStructure;

STM_EVAL_LEDInit(LED3);
STM_EVAL_LEDInit(LED4);
STM_EVAL_LEDInit(LED5);
STM_EVAL_LEDInit(LED6);

/* Turn on LEDs */
STM_EVAL_LEDOn(LED3);
STM_EVAL_LEDOn(LED4);
STM_EVAL_LEDOn(LED5);
STM_EVAL_LEDOn(LED6);

int flag = 0;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
while (1)
{
    if (flag)
    {
        GPIO_SetBits(GPIOD, GPIO_Pin_0);
        flag = 0;
    }
    else
    {
        GPIO_ResetBits(GPIOD, GPIO_Pin_0);
        flag = 1;
    }
}

Hooking up the logic analyser to Port D, pin 0 gave a square wave with a frequency of about 2.25 MHz.

Success!

Back to the samples and lets see where it takes us.

Silverlight 5 Released

Saturday, December 10th, 2011

Just read that Silverlight 5 has just been released. Off to the downloads page and some replanning of this weekends activities.

For more information visit the Silverlight home page.

Cascading TLC5940 LED Drivers

Wednesday, November 2nd, 2011

A while ago I created a circuit and some code to control the TLC5940 LED driver using a Netduino Mini. This chip allowed the programmer to control 16 LEDs using a serial interface. In the conclusions I noted that it should be possible to control two or more of these chips by cascading the serial output from one chip into another. This article discusses the additional hardware required along with the software to control up to 32 LEDs using two of these chips linked together.

It is assumed that you have read and understood the previous article before you proceed further.

Hardware

Linking two of these chips is a relatively simple affair and requires the LEDs you wish to control and only 1 additional component, namely a 2K2 ohm resistor, and some additional wire (assuming you are using breadboard).

Starting with the simplest part, the 2K2 ohm resistor connects the IREF pin on the second TLC5940 to ground. This sets the reference voltage for the LEDs connected to the second TLC5940.

The next part of the problem is to connect the two chips together. This is achieved by connecting the following pins:

TLC5940 (1)TLC5940 (2)
SCLKSCLK
BLANKBLANK
XLATXLAT
GSCLKGSCLK
VPRGVPRG
SOUTSIN
XERRXERR

DCPRG on TLC5940 is connected to Vcc as it is on the first driver chip and the LED controller pins connected to the appropriate LEDs.

This PDF (PWMLEDControlv0.03.pdf) contains the full circuit diagram showing the connections required.

Software

Now we have the additional LEDs in the circuit we will need to be able tell the software driver for the chips how many LEDs we want to control and then be able to control the brightness of the LEDs. In order to do this an additional parameter was added to the constructor numberOfLEDs. This parameter allows the system to work out how many TLC5940’s should be connected together. Once we have the number of chips calculated we can then work out the size of the buffers for the Dot Correction and the Grey Scale data.

The class uses two buffers for both the Dot Correction and the Grey Scale data. This may seem superfluous but doing this allows two different modes of operation:

  • Basic – The LEDs can be controlled using an interface which is simpler to use but is slower.
  • Fast – This mode is faster but requires the programmer to perform some of the tasks in the driver. The assumption here is that the programmer will have more understanding of the data which is changing and can therefore write more optimised code.

For the moment we will be discussing the basic mode. In this mode, the software driver hides much of the implementation and the user only really needs to perform three tasks:

  • Construct a new instance of the software driver.
  • Set the brightness of the LEDs (only the lower 12 bits are used).
  • Tell the driver to output the data to the TLC5940 chips.

Constructor

Making a new instance of the class in it’s most basic form is simply a case of telling the constructor how many LEDs you wish to control. In this case we have two TLC5940s connected together and so we have 32 LEDs:

Tlc5940 leds = new Tlc5940(numberOfLEDs: 32);

You can of course use the additional parameters to define the pins used for the various control signals or just use the defaults.

Setting a LED

The class contains a method which overloads the array indexing operator. Setting the brightness for a LED is simply a case of using this operator as follows:

leds[10] = 767;

This will set the brightness of LED 10 to the value 767.

An important note here is that the LEDs are numbered from 0 to 15 on the TLC5940 connected to the Netduino Mini, 16 to 31 on the next LED in sequence and so on.

Lighting the LEDs

The final piece of the puzzle is to output the data to the series of TLC5940s connected to the Netduino Mini. This is achieved by calling the Display method in the Tlc5940 class.

Source Code

The source code for the controller class and a simple main program can be found here (CascadedTLC5940.zip).

Conclusion

The work with this chip has not yet finished as there are still some points which need addressing in order to round off this class:

  • Dot Correction – this current outputs all 1’s and so there is no fine control for any of the outputs.
  • Error Detection and Status Information – It is possible to detect two types of errors and retrieve the status information from the TLC5940. This needs implementing.

One final item which needs addressing is to round off the code once these final feature have been implemented. A topic for another day.

Persistence Of Vision

Saturday, June 11th, 2011

By chance last week end I came across a post discussing persistence of vision. This set me wondering if I could demonstrate this using the Netduino. First a little background.

Background

Persistence of vision is the phenomena which allows you to watch a movie. The projector shows a single still image on the screen. This is then replaced 1/24th of a second later by a new image. The projector achieves this by covering the image with a shutter whilst the frame is moved into place. We do not see the blank screen, just the previous frame. A fuller description can be found on this Wikipedia page.

We will be using this effect to control 27 LEDs using only 11 control lines. These lines will be the outputs from two 74595 shift registers which in turn will be connected to a Netduino Plus.

The Problem

The 27 LEDs will be split into three banks of 9. The aim will be to write a small program which will turn on one or more of the of the 9 LEDs in each bank in turn. If we do this slowly we will see bank 1 turn on whilst banks 2 and 3 are off. Next we will see bank 1 turn off, bank 2 will turn on and bank 3 will remain off, and so on. In theory, if we do this fast enough, we will see all three banks on at the same time.

Hardware

This project uses the hardware and principles from two previous projects:

  1. Netduino Controlling an External LED (using a transistor as a switch)
  2. Counting Using 74595 Shift Registers

A picture is worth a thousand words so lets have a look at the schematic:

Persistence of Vision Schemaic

The two shift registers are connected to the Netduino using the SPI interface. The output from IC1 is fed to the serial input of IC2 creating 16 output lines (8 from each register). These lines will power the LEDs and select the bank to turn on.

The LEDs will be powered using bits 0 to 7 of IC1 and bit 0 of IC2.

The top three bits of IC2 will select which bank to turn on. This is done using a transistor as a switch.

The trick to controlling the banks and LEDs lies in the way the circuit is wired up. The anodes of LED1, LED10 and LED19 are connected together and then connected to QA of IC1. Similarly, the anodes of LED2, LED11 and LED20 are connected to QB of IC1. This continues until all 27 LEDs have been connected to the 9 control lines (QA-QH of IC1 and QA of IC2).

The next part of the trick is to connect the cathodes from the LEDs in bank 1 together. These are then connected to a current limiting resistor which is in turn connected to the collector of a 2N2222 NPN transistor. This is repeated with banks 2 and 3 being connected similarly to their own transistor.

The base of each transistor is connected through a resistor to one of the bank control outputs pins (QF, QG and QH) of IC2. The emitter is connected to ground.

Note that in theory it is possible to turn on all three banks at once by setting the appropriate bits in the shift register but the object of this exercise is to show how to make a still image using each bank in turn.

Software

The software uses SPI to send two bytes to the 74595 shift registers. The values sent will control which LEDs are turned on / off. The code looks like this:

SPI.Configuration config;
config = new SPI.Configuration(SPI_mod: SPI.SPI_module.SPI1, ChipSelect_Port: Pins.GPIO_PIN_D9, ChipSelect_ActiveState: false, ChipSelect_SetupTime: 0, ChipSelect_HoldTime: 0, Clock_IdleState: true, Clock_Edge: false, Clock_RateKHz: 400);
SPI spi = new SPI(config);
byte[] value = new byte[2];
value[1] = 0xff;
while (true)
{
    value[0] = 0x81;
    spi.Write(value);
    value[0] = 0x41;
    spi.Write(value);
    value[0] = 0x21;
    spi.Write(value);
}

If you run this code you will see a still image with all three banks looking as though they are permanently on. Set a breakpoint on the line value[0] = 0x81; and run the program again. Single stepping will show each bank being turned on in turn. The reason we see the still image is because the banks are turned on and off quickly.

Putting it all together and wiring it up on breadboard gives you this:

Persistence Of Vision On Breadboard

Persistence Of Vision On Breadboard