Close
0%
0%

89C52 clock board

A board designed to mate with the cascadable display boards I designed

Similar projects worth following

This is a clock board designed to mate with the 6-segment or 7-segment display boards that I previously published. It interfaces to a DS3231 module for accurate timekeeping. The design is conventional. As there is plenty of room on the board, it supports options, annotated in the schematic. That's why it's not fully populated. The firmware is at Github, see the links.

I managed to get a professional STC89C52 MCU to pose for this picture instead of the WFH model shown in the virtual 3D rendering. Also the PCB insisted on coming dressed in blue soldermask so I had to indulge it a bit. 🤣

Time passes. I was forced to take a many-month break due to renovations on my home. Finally I got around to installing the missing connector and plugging in a test 4 digit display board. You can see the result in the gallery. Sorry for the fisheye distortion, I used my phone camera. I really need to set up a proper photographing environment for my projects. You can see the RTC in one photo. The firmware was tweaked a little, you can see the details in the Github repo but no substantial changes as testing had already been done with the QX-mini51 development board. Now I can finally mark this project completed. I'll probably couple it to the 6-segment display that I built in #6 segments suffice and give it to a friend as a novel memento.

I've added a couple of photos to the gallery of the clock board piggybacked onto the 6-segment display. You may notice that the 0 is not the same as in my project #6 segments suffice . I'm experimenting with using the upper loop as a 0, but it's compromised by the gap in the middle most clearly exhibited by the 5.

Update, 2025-02-15: I still have a few unpopulated boards and looking at them I realised that I disliked many things about that design. Among them:

  • It's designed to take 12V from a wall wart but I've used Bornier connectors. I could use a barrel socket and not have to cut off the barrel jack from the cord.
  • Since all 12V barrel jacks that I know of are centre positive, I don't need the reverse polarity protection diode.
  • I don't need to cater for both the TO-220 and the TO-92 package variants of the LM7805 linear regulator since I have a pile of the larger, but none of the smaller package now and don't intend to buy any.
  • I don't need 4 tactile switches, I can get by with 2 with my now polished mode/increment UI button scheme.
  • I can incorporate the cathode drivers on board. Unfortunately the STC89C52 doesn't have push-pull outputs, only the old weak pullup of the original 8051. By using cathode drivers, I don't need the pullup resistor network.
  • Similarly I can substitute driver ICs for the high side anode digit driver transistors. All 8 bits of the port can be used instead of 6.
  • The DS3231 RTC module, if used, can be connected by leads to other port pins. I2C is handled by bit banging, as the STC89C52 has no hardware I2C support.
  • I don't need to cater for both polarities of the reset line since I only have one AT90S8515 AVR MCU.
  • I don't need to make the board in 100x100 mm form factor if I give up the anode drive connectors. These can be flying leads anyway, only one is needed per digit which doesn't warrant daisy-chained connections.

So I designed a second version of the board which is 100 mm wide but only half as tall with a more compact layout. I ordered 5 boards which should exhaust my old-fangled STC89C52 stock. It uses ancient-fangled TTL 7406 and 7407 open collector drivers. I make no apology for them as they are what I have on hand.

I doubt if anybody is interested in this design, but if you are because you have some 8051 descendant to use, there are probably better driver chips that can interface to the multiplexed LEDs. Or not multiplex the LEDs and use power shift registers. I'm eyeing LED filaments for large digits. They take quite a bit of current to drive.

  • Interfacing a DHT22 (AM2302) temperature and humidity sensor

    Ken Yap07/22/2025 at 11:21 0 comments

    There are a few spare I/O lines on the STC89C52 board and looking at my small collection of sensors, I formed the idea to interface a DHT22 sensor, also known as an AM2302, for temperature and humidity and display them on request.

    Electrically this sensor is easy to wire up. It only requires 3.3 V to 5 V power and one data line. A one-wire protocol is used to read it. However this uses a custom one-wire protocol and not any of the ones supported by MCU hardware. Probably because the maker wanted to avoid paying licensing fees. The datasheet has all the needed details. 40 bits of data (16 each for humidity and temperature, plus 8 bits of checksum) are sent by the sensor serially. 0 is represented by high for 26-28 µs while 1 is represented by high for 70 µs. The relative humidity is multiplied by 10 and sent as an unsigned 16-bit integer, while the temperature is multiplied by 10 and sent as a sign bit followed by an unsigned 15-bit integer. For example, 765 represents a RH of 76.5%, while 234 represents a temperature of 23.4 C. For my use case I ignore negative temperatures. The checksum is the sum of the previous 4 unsigned bytes truncated to 8 bits.

    The tricky operation is how to measure the high times of the 40 bits. This is a time-critical part of the code as the sensor will not wait. The decoding can be done afterwards. For this I thought I could enlist Timer 2 in the 8052 architecture. This can be configured as a counter incrementing at the rate of crystal frequency ÷ 12. With a 12 MHz crystal, this means one count per µs. We also need to detect a non-responsive sensor so that the clock doesn't freeze waiting for a data line that won't change. We can use the overflow flag of the timer for this; 16 bits gives up to 65 ms to complete the read. So my first attempt was something like this:

    start timer at 0
    for i from 0 to 39 
            wait for high or overflow
            start_time = timer 
            wait for low or overflow
            end_time = timer 
            duration[i] = end_time - start_time
    stop timer
    

    On overflow, the loop terminates prematurely and the function signals a failed read.

    First I coded synthetic test data to test the decoding routine that could be enabled by setting a #define. This showed that my decoding was correct.

    Next I wired up the sensor to my development board, described in #Adventures with a STC89C52 development board , as depicted in the photo.

    Unfortunately in testing I often got overflow timeouts and even when I didn't the data didn't pass the checksum validation.

    Eventually I realised that this MCU wasn't fast enough. Most instructions take 1 µs and some take 2 µs. This means precious few instructions can be completed in the core of the loop. This included reading the 16-bit counter with two 8-bit reads and also taking care of the carry race condition.

    I could configure this MCU to 6T mode instead of normal 12T mode, meaning that instructions run twice as fast. But there is no scaler for the clock so it would also overflow twice as fast.

    I decided to ditch the timer for duration measurement, but retain it for timeout detection. Instead in the heart of the loop I did the simplest thing possible: increment a counter. So the value of the counter would not represent µs but some fraction of it. Hopefully there would be enough difference in count between a 0 and a 1 bit. They turned out to be around 2-3 and 8 respectively. So in the decoding I used a threshold of 5. Here's the loop in question.

    for (uchar i = 0; i < 40; i++) {
            while (!TF2 && !DHT22D) // wait for 1
                    ;       
            uchar count = 0;
            while (!TF2 && DHT22D)  // wait for 0
                    count++;
            hightime[i] = count;
            if (TF2) {      // timed out, didn't collect bits
                    TR2 = 0;// turn off T2
                    TF2 = 0;// clear overflow
                    DHT22H; // pull up 
                    return i + 1;
            }       
    } 

    With this change I could reliably read the sensor. But the code is working at the limit of this MCU's power. A more modern and faster MCU would break no sweat. For sure the Atmega 328P in the...

    Read more »

  • Refactoring the code for multiple MCU targets

    Ken Yap01/25/2024 at 09:47 0 comments

    As mentioned in #Adventures with a STC89C52 development board  I bought that development kit because it could accept some AVR MCUs that are almost pin-compatible with the STC89C52. This got me thinking of how I could use the couple of ancient AVR chips that I have. Ideally I would like to reuse firmware that I have written and not have to develop from scratch. So as an exercise in learning more about AVR architecture, I decided to see if I could modify the STC89C52 code so that it could be compiled for more than one type of MCU.

    My requirements are:

    • Make it possible to select MCU type with a #define.
    • Restrict the MCU specific code to as few files as possible. This means that lots of #ifdefs in the main functions of the code are not acceptable.
    • Share the rest of the files unmodified. It's ok for these files to contain macro invocations that expand differently depending on the target.

    I went in and hacked at it until I had something acceptable. I was lucky in some respects:

    • The STC89 and AT90S families are similar in that 4 8-bit GPIO ports are exposed. They both also have two timers. If the families had been more disparate, it would have been a harder job.
    • Both avr-gcc and SDCC accept a fairly comprehensive set of C language features, and the differences are easy to use macros to hide.
    • The business logic (which is for a clock) is quite independent of the MCU architecture so large chunks of code contain no hardware specific manipulation.
    • The hardware specific parts of the code only have to deal with GPIO ports, timers, and interrupt handlers. A more complex MCU application might have more MCU specific code.

    Some constants, like those used for time constants for the button UI can be defined in common include files. Other constants, such as the values loaded into MCU registers, are in MCU specific include files.

    The overhead is very acceptable, only increasing the binary size by tens of bytes due to some inline code being turned into functions.

    I noted that for avr-gcc it's ok for static functions to be defined in .h files because avr-gcc is smart enough to not emit code for the body if the function is not used later in the .c file. In SDCC the functions need to be in a .c file and global rather than static otherwise if in a .h file there will be a copy for every .c file it's included in.

    The changes have been pushed to the Github archive.

View all 2 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates