ST
STM8

ST has been dumping the STM8 in the Chinese market, so it’s just about the cheapest general-purpose microcontroller you can buy these days (and that includes parts from STC and Holtek — two staples found in low-cost products made in China). I recently spent 72 Yuan (~$11) on 50 of the most famous STM8 part: the STM8S003F3P6. Yes, that works out to 22 cents a piece for an 8K flash / 1K RAM microcontroller with 20 pins, 7 CAPCOM channels, 128 bytes of EEPROM, and 5 channels of 10-bit 430 ksps analog-to-digital conversion. And the fact that reputable U.S. suppliers sell the part in similar quantities for just north of $0.50 / unit should indicate the tremendous bargain the STM8 is.

The part we’re reviewing is the STM8S005K6, a much-beefier 32K/2K part in a 32-pin QFP

Standard Peripheral Library

The preferred method of developing on the STM8 is with the Standard Peripheral Library: a ZIP file containing runtime libraries, example projects, and an alright-but-not-amazing help file. Modules are separated based on peripheral; preprocessor definitions are used to include / exclude different peripheral libraries, making the same library compatible with all STM8 devices.  It’s delivered in source form with separate header and implementation folders; it’s up to you to configure STVD’s include path appropriately, and to add any required source files to your project so they get built.

The library is function-parameter-based, not struct-based as some ARM SDKs are. This is usually suitable for 8-bit MCUs, but the STM8’s complex peripherals require verbose, bulky initialization functions that end up being a lot of copy-pasta for duplicated peripherals (multiple PWM channels, as an example). These functions require some knowledge about the peripheral being configured; I found the classic “copy-and-paste from the demo” methodology to be the easiest to make work — and there are enough demos included that cover all the basics.

The library is easy to use in an environment with text-completion, as all the enum constants — which you’ll be using often — are named with their module and functional type, as well as their value. So, GPIO_Init() takes a GPIO_Mode_TypeDef, which is going to be something like GPIO_MODE_OUT_PP_HIGH_FAST.

Other than that, there’s not much to say. The libraries expose basic functionality but avoid losing generality by implementing anything extraneous. For example, the SPI driver can initialize the SPI peripheral; send a byte; receive a byte; return various interrupt flags; and that’s basically it. If you want something as basic as a blocking SPI_SendByteArray()-sort of function, you’ll be implementing it yourself. You’ll obviously be on your own with any interrupt-based work, too. Peripheral initialization is the only place where the Standard Peripheral Library attempts to do anything for you; everything else is essentially just single-register manipulation.

I think this is most evident in an I2C example function that reads one or more bytes from an I2C EEPROM into an array:

void I2C_EE_BufferRead(u8* pBuffer, u16 ReadAddr, u8 NumByteToRead)
{
	while(I2C_GetFlagStatus(I2C_FLAG_BUSBUSY));
	I2C_GenerateSTART(ENABLE);
	while (!I2C_CheckEvent(I2C_EVENT_MASTER_START_SENT));
	I2C_Send7bitAddress(EEPROM_ADDRESS, I2C_DIRECTION_TX);
	while (!I2C_CheckEvent(I2C_EVENT_MASTER_ADDRESS_ACKED));
	I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED);
	I2C_SendData1u8)(ReadAddr >> 8%MINIFYHTML74baf0254cfaa592adbede3c65e64c9b24%; /* MSB */
	while (!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	I2C_SendData2u8)(ReadAddr%MINIFYHTML74baf0254cfaa592adbede3c65e64c9b25%; /* LSB */
	while (!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED));
	I2C_GenerateSTART(ENABLE);
	while (!I2C_CheckEvent(I2C_EVENT_MASTER_START_SENT));
	I2C_Send7bitAddress(EEPROM_ADDRESS, I2C_DIRECTION_RX);
	while (!I2C_CheckEvent(I2C_EVENT_MASTER_ADDRESS_ACKED));
	I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED);
	while(NumByteToRead)
	{
		if(NumByteToRead == 1)
		{
			I2C_AcknowledgeConfig(I2C_ACK_NONE);
			I2C_GenerateSTOP(ENABLE);
		}
		if(I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_RECEIVED))
		{
			*pBuffer = I2C_ReceiveData();
			pBuffer++;
			NumByteToRead--;
		}
	}
	I2C_AcknowledgeConfig(I2C_ACK_CURR);
}

As you can see, the peripheral library exposes only the lowest-level functions necessary; it’s up to the developer to organize the appropriate logic (and certainly including understanding the I2C protocol).

Development Environment

STVD — ST Visual Develop — is the official (and free) IDE for the STM8 microcontroller. It can hook into Cosmic or Raisonance STM8 compilers, and provides project management, a text editor, and integrated debugging. There’s other options out there, but I can’t comfortably recommend any of them. Raisonance has Ride7, but it only works with their expensive RLink debugger. Cosmic has IDEA, which is little more than a text editor — it doesn’t even support debugging. There’s an open-source SDCC + GDB + OpenOCD project that heavily targets Eclipse, but it’s clunky to set up, and uses SDCC — which struggles to produce reasonably fast code.

As far as proprietary vendor IDEs go, STVD is actually pretty good. There’s plausible (though unintelligent) text completion and syntax-highlighting; customizable text editor colors; Workspace and Project-level file management; quick debugging with bog-standard features; and a fairly generic “just works” interface that anyone who’s used Windows 98 before will feel right at home with.

STM8 has support for several compilers — open-source, free-as-in-beer, and commercial. I did not evaluate any of the compilers other than Cosmic, but Philipp Klaus Krause’s site, ColecoVision, has an excellent STM8 compiler shoot-out. I shamelessly reproduce two of his graphs above.

Footnotes   [ + ]

1.u8)(ReadAddr >> 8
2.u8)(ReadAddr

Leave a comment