STC89C I/O Ports push-pull

The (English) datasheet contains Chapter 4, which explains how the I/O ports work as standard in the traditional 8051 way: so-called ‘quasi-bidirectional’. In this mode, when a ‘1’ is written to a port bit, the I/O floats high, with a weak pull-up resistor, and external devices are able to pull the pin low, so the port can work as both an output and an input. If you’re using the pin as an output, say to drive an LED, then the pin can only sink current (or source current very weakly); and to use the pin as an input, say a momentary push button that connects it to GND, your program has to write a ‘1’ to that I/O and then read the same I/O to see if the button is pressed and pulling the pin low.

The datasheet describes (sections 4.2 – 4.4) how the I/O can be reconfigured into one of three other modes: push-pull output, input-only, or open-drain output. However, nowhere (as far as I can see) does it explain how to actually configure the microcontroller into these other modes. Certainly, the newer STC devices, like the STC8, can be configured to do this, as I explain below. My experiments, so far, seem to indicate that the STC89 can’t, and the datasheet is a misleading fiction, where the whole of chapter 4 has just been cut-and-pasted from the datasheets that really apply to the newer microcontrollers in the STC range.

How it works on the STC8

Let’s consider Port 1 as an example. The STC8 has the usual port sfr (special function register), P1 at location 0x90, but it also has two other sfrs, P1M1, and P1M0, at locations 0x91 and 0x92 respectively. You can control the mode of each I/O bit individually, but to keep it simple, let’s say you want to configure all eight bits of the port in the same mode: these are the values you’d write to the P1M1 and P1M0 registers:

Push-pull output0x000xFF
Input only0xFF0x00
Open-drain output0xFF0xFF

Of course, there are corresponding PxM1 and PxM0 registers for each of the I/O ports.

Now if the STC89 family also had configurable I/O, we’d expect it to have the appropriate sfrs (possibly at different addresses to the STC8 ones), but if we look at the STC89C51RC-RD-en datasheet, section 5.1, we find that P1 is located at the same 0x90 address, but that 0x91 and 0x92 are just blank. I wrote a program that assumed P1M1 and P1M0 were at those addresses, and tested it, but the port just remained in the standard quasi-bidirectional mode. Here’s my test program (for SDCC):

// test whether STC89 can actually do push-pull, etc. or if the datasheet is just wrong

#include <8052.h>

__sfr __at (0x91)  P1M1; // these are the addresses for the STC8, following P1 at 0x90.
__sfr __at (0x92)  P1M0; // the STC89 datasheet has P1 at the same 0x90 address, but nothing at 0x91, 0x92

void delay(unsigned int ms) { // delay approximiately ms milliseconds
  unsigned int i;
  while (ms--) {
    for (i = 0; i < 230; i++) { // 230 seems about right for 6T (fast) mode

void main(void) {
  P1M0 = 0xAA;
  P1M1 = 0xF0; // this (should) give P1.0/1 as quasi-bidirectional; P1.2/3 as push-pull; P1.4/5 as input only; P1.6/7 as open-drain output
  while (1) {
    P1 = 0x00; // all port bits low
    P1 = 0xFF; // all port bits high
    delay(500); // roughly one-second cycle
    // result - it doesn't work - all 8 outputs work in quasi-bidirectional mode, where they can pull low hard and only pull high weakly

And here’s the resulting ihx (hex) file.


If anyone has any more information on this topic, maybe confirmation that I’m right, or the actual addresses for P1M1 and P1M0 on the STC89 if they do exist, I’d be grateful if they’d leave some comments.







2 responses to “STC89C I/O Ports push-pull”

  1. Hummusprince avatar

    Thank you for this article, I thought that I was going insane…
    I did check it out logically – STC MCUs have a rather nasty habit to return the lower address byte when unimplemented addresses are read.

    So I’ve written some code that toggle the value of certain SFR – One that we know that exists, one that should not exist, and our SFRs in question.
    Then I’ve read their values through serial port, each is toggled a few times.
    The interesting part is something like this:

    __sfr __at 0x91 P1M1; //In question
    __sfr __at 0x92 P1M0; //In question
    __sfr __at 0xfb RND; //Should not exist, random address
    __sfr __at 0xa9 SADDR; //Does exist

    //UART read code into “buffer” here…

    //Repeat that several times in a loop
    *buffer++ = P1M1;
    *buffer++ = P1M0;
    *buffer++ = RND;
    *buffer++ = SADDR;

    //Send “buffer” through uart here…

    Guess what I got?
    The reads from 0xFB returned 0xFB constantly, as expected.
    The reads from SADDR returned 0x00 and 0xff alternately, as expected.
    The reads from P1M1 and P1M0 returned… yeah, 0x91 and 0x92 respectively.

    So probably there’s no control over GPIO modes of STC89 after all, bummer.
    Could use that test to find other SFRs though. Didn’t bother.

    Thanks for proving me sane,

    1. ceptimus avatar

      Thanks for checking it out and letting me know. I’ve written programs for the more modern STC 8051-style chips – the STC12, STC15, and STC8 for example. They all do have the enhanced modes for their GPIO ports – plus, of course, they also have other enhancements and run much faster. If you’re thinking of starting an 8051-based project using an STC micro, I’d recommend choosing one of those.

      The STC89 range is still fine if you’re not looking for great performance, and just want a basic chip that implements the classic 8051-style behavior (and the STC89, like the STC8, STC15, and STC12, does have a convenient ISP bootloader for programming): the only problem with the STC89, is that the GPIO ports don’t fully work as described in the (English) datasheet.

Leave a Reply

Your email address will not be published. Required fields are marked *