Artistic shot of the experiment

Exploring STC MCU part 4 – Better times

Remember back in part 3 I wrote the timing code? That code only works with a 24MHz clock. And I didn’t have the watchdog running yet. So I am gonna fix it.

Changes to the test circuit

A photograph of the experiment with IAP15W4K61S4 with USB to Serial adapter and 30MHz clock
A photograph of the experiment with IAP15W4K61S4 with USB to Serial adapter and 30MHz clock

Since the phone call to STC, I managed to solve the I/O pin bug. Now I can go back to the original plans of using the STC15W4K61S4-30I-PDIP40 as the development chip.

From the same call I obtained the STC U8W-Mini ISP/ICD probe, so the circuit is slightly altered to accommodate. I have also added a USB to Serial adapter for the upcoming UART experiments.

The test circuit of IAP15W4K61S4 at 30MHz, ISP/ICD probe and USB-Serial adapter attached.
The test circuit of IAP15W4K61S4 at 30MHz, ISP/ICD probe and USB-Serial adapter attached.

Updated Timing Code

I have promised an update regarding UART, but since UART on STC 8051 chips are always clocked by the onboard timers, I need to fix that code first.

Unlike the traditional 8051 microcontrollers like AT89C51RC, STC parts comes with a somewhat different timer design. The less used 13-bit mode is replaced with a 16-bit mode with automatic reloading. Since the MCU is not going to work anywhere close to 64MHz, the timing code is changed to interrupt once per millisecond, while the values of the underlying Timer 0 counter is used to calculate the microseconds.

This design allowed the timing code to work under any clock frequency from about 32kHz up to 64MHz.

#include "systick.h"

#include <STC15F2K60S2.H>
#include "wdt.h"

void systick_init(void)
{
	unsigned short reload = 65535 - F_CPU / 1000 + 1; // Interrupt every millisecond.
	
	TMOD = (TMOD & 0xf0) | 0x00; // Timer 0: 16-bit with autoreload
	AUXR |= 0x80; // Run timer 0 at F_CPU
	TL0 = reload; // Set reload value
	TH0 = reload >> 8;
	ET0 = 1; // Enable timer 0 interrupt
	TR0 = 1; // Start timer 0
}

volatile unsigned long systick_counter = 0;

void systick(void) interrupt 1
{
	systick_counter++;
}

unsigned long millis(void)
{
	unsigned long overflow;
	
	EA = 0; // Enter critical section
	overflow = systick_counter;
	EA = 1; // Exit critical section
	
	return overflow;
}
unsigned long micros(void)
{
	unsigned long overflow;
	unsigned char count_high;
	unsigned char count_low;
	unsigned short count;
	
	EA = 0; // Enter critical section
	overflow = systick_counter;
	do
	{
		count_high = TH0;
		count_low = TL0;
	} while (count_high != TH0);
	EA = 1; // Exit critical section
	
	count = 65535 - (count_high << 8 + count_low); return overflow * 1000 + count * 1000000 / F_CPU; } void delay(unsigned long time) { long end = millis() + time; wait(end - (long)millis() > 0);
}

void delayMicroseconds(unsigned long time)
{
	long end = micros() + time;
	wait(end - (long)micros() > 0);
}

Watchdog

The watchdog on STC microcontrollers are fairly straightforward to use, as you just need to enable it and keep kicking it. I wrote a small wrapper around the registers to allow the underlying hardware to change without breaking the application code.

#include "wdt.h"

#include <STC15F2K60S2.H>

void wdt_init(void)
{
	WDT_CONTR |= 0x10; // Enable watchdog timer.
}

void yield(void)
{
	WDT_CONTR |= 0x80; // Kick the dog.
}

Coming up

The next part in this series will finally focus on actually implementing the UART.

Leave a Reply