Posted on Leave a comment

Revisiting Older Projects – 8 Bit Calculator – Part 1

19 April 2025

I found a box with all the parts for a project I designed back in 2013 that I never finished. It was called the 8 Bit Calculator. It used an Atmel AVR ATmega1284 microcontroller to drive an eight digit, seven-segment LED display and scan an 8×8 matrix of buttons, intending it to be a calculator. I made a batch of ten (10) PCBs and built two of them, but never finished the firmware. I can’t find the two that I built up at the moment.

I took one of the remaining PCBs and mostly built it up. I don’t have any more Atmel AVR ATmega1284 chips in stock. Well, I might, but I’m not going to use them if I do. What I would like to do is to build a drop-in replacement PCB in the 40 pin form factor of the original 1284 chip but use a CH32V RISC-V chip as the brain, instead.

My first candidate for the job is the brand new CH32V006K8U6. To figure out if this can even work or not, I’m going to wire up one of the -006 development boards from WCH via jumpers to the 40 pin IC socket on the calculator PCB.

First I need to map out the pins of the -006 chip to the nodes in the calculator circuit. The layout of the calculator circuit is trivial, which was by design. There are eight (8) column lines that were originally connected to PA0-7 of the 1284 chip. These connect to both the LED array and the key matrix. There are also eight (8) lines connected to the segment drivers, which are contained in a ULN2803 driver IC. These were originally driven by the PC0-7 lines of the 1284 chip. An additional eight (8) lines are used to connect to the rows of the key matrix. They were originally connected the PB0-7 lines of the 1284 chip.

20 April 2025

So I’m going to need a total of 24 I/O lines to interface with the calculator circuit. The -006 has a maximum of 31 available I/O lines. That’s a lot of lines, considering the largest package only has 32 pins, plus a ground pad. There are, in fact, no ground pins on the device, only a single VDD connection on pin 17 of the QFN32 package. Ground connection is made solely through the underside ground pad.

Let the pin mapping begin!

Here is a list of the available pins on the CH32V006K8U6 device, in “port, pin” order, along with their multiplexed functions:

GPIOA   PA0 5   T1/T2
        PA1 6   T1/T2/OPN0/XI/A1
        PA2 7   T1/T2/U2TX_/OPP0/XO/A0
        PA3 9   T1/T2/U2RX_
        PA4 32  U2TX_/OPN2
        PA5 2   U2RX_/OPO1
        PA6 3   U2TX_
        PA7 4   U2TX/RST

GPIOB   PB0 10  T1/NSS_
        PB1 11  T1/T2/SCK_
        PB2 12  T1BK/T1/MISO_
        PB3 1   SWCK/T2/U2RX
        PB4 22  T1ETR_
        PB5 29  TX/SCL_
        PB6 30  T2/RX/SDA_
        PB7 -   no connection

GPIOC   PC0 13  T1C1N_/T1/T2/TX_/MOSI_
        PC1 14  T1C2N_/T2/RX_/SDA/NSS
        PC2 15  T1C3N_/T2/SCL
        PC3 16  T1/T2
        PC4 18  T1C1_/T1/MCO/A2
        PC5 19  T1C2_/T1/T2/TX_/SCK
        PC6 20  T1C3_/T1/RX_/MOSI
        PC7 21  T1C4_/T1/T2/MISO

GPIOD   PD0 8   T1/TX_/SDA_/OPN1
        PD1 23  SWIO/T1/SCL_/RX_/OPP3
        PD2 24  T1/T2/U2TX_/A3
        PD3 25  T1/T2/U2RX_/NSS_/OPP2/A4
        PD4 26  T1/T2/SCK_/OPO0/A7
        PD5 27  T2/TX/RX_/MISO_/A5
        PD6 28  T2/RX/TX_/MOSI_/A6
        PD7 31  T2/OPP1

Note:   Functions with underscore suffix are optional, alternate mappings

Looking at the list, I see there is almost a one-to-one correspondence with the available I/O pins of the 1284, which is why this chip topped my list of possible replacement candidates. The other candidates include the CH32X035 and CH32V203.

We can almost use the original port assignments from the ATmega1284 version… almost. There are just a couple of issues preventing this. The first is that PB7 is not implemented in this chip. The other is that while the 1284 chip had a dedicated -RESET input pin (9), the -006 multiplexes this function, here called RST, on PA7.

The first issue (no PB7) can be overcome by using one of the otherwise unused pins from GPIOD. This will only slightly complicate the software. I hope this isn’t as naive an assumption as it sounds.

The second issue (RST on PA7) can be dealt with in a couple of ways. While the mapping of the RST signal to PA7 is optional, it’s an option that is selected at the factory and all chips are shipped that way. The first option is to re-route this signal to another unused pin on GPIOD. The second option is to re-write the ‘user option byte’ setting ‘RST_MODE’ in software.

For now, I can continue to wire the development board to the calculator PCB and start poking ones and zeros here and there to see if things are working as expected. Once I have the basic outline of the user interface working, I can go back and ‘fix’ the two missing pins, one way or the other.

Next up is to determine the appropriate values for the series current-limiting resistors for the LED display. I’m using the same ROHM LB-602MA2 dual seven-segment displays that I was using in the -203 clock project. Each segment has a maximum continuous current limit of 20 mA, but can withstand up to 60 mA when the duty cycle is 1:5 or greater and the pulse width is 1 ms or less. The -006 has a limit of ±30 mA on each pin, and a package limit of 100 mA.

The LB-602MA2 is configured as two separate seven-segment displays with right-hand decimal points. The display is internally wired with a common anode configuration. The calculator PCB circuit uses a transistor array to provide additional current to each of the segment lines. The anode for each digit is connected, via the current-limiting resistors, to the column driver output pins. The display multiplexing function should then select a single segment by taking that line high, which will provide a low-impedance path to ground for the segments. The -006 will then drive high the appropriate column lines, by checking which digits need the currently selected segment illuminated. This is either ‘backwards’ or 90° away from how I normally think about scanning these displays, which assumes a common cathode display, where you select a single digit at a time, then drive the appropriate segments. However, it’s at the same time exactly equivalent as far as how the display lights up and shows the intended content.

Some quick calculations on the current budget suggest that 10 mA per segment should be possible. This chip is running on +5 VDC, so the value selection process is a little different from the -203 based clock circuit, which ran at 3.3 VDC. Referring back to the published findings, I see that I didn’t document the voltage drop across each segment, and I should have. I can roughly derive the information from what is available, however. A quick guess is that 270Ω should drive ~10 mA of current through each segment. I’ll try one at a time and see. In theory, this should only take two connections from the development board to the calculator PCB.

To illuminate segment ‘a’ of digit 1 (right-most), I will need to drive COL0/PA0 high while simultaneously driving SEGA/PC0 high as well.

This does not work as my “theory” about only needing two connections turned out to be proven false. I also need a ground connection to make things more interesting.

Adding a ground jumper does, indeed, make things more interesting. Segment ‘a’ of digit 1 illuminates at a reasonable brightness. I measured ~1.98 V across the 270Ω resistor, which indicates that only about 7 mA of current is flowing, which is less than I predicted. However, it should be noted that ‘guessing’ the values of non-linear functions without knowing their precise values is problematic, at best. So I will try slightly smaller values until I get to the desired target value, which in this case is ~10 mA.

Resistance  Voltage Current
ohms        volts   mA
----------  ------- -------
270         1.98    7.33
220         2.03    9.22
180         1.80    10.0
150         1.82    12.1

And again we have a winner. With 150Ω in series with all the other circuitry, I measure just over 12 mA through the LED. If all eight digits have the same segment lit, that would represent a total of just under 100 mA total current being supplied by the -006. As the rated operating current of the device itself is under 5 mA going full tilt, we should be OK (most of the time).

Now that the row of resistors is firmly soldered into the PCB, the circuit is complete, minus the controlling apparatus. Time to connect one of the switches to the microcontroller and see if I can determine when it is being pressed. This involves adding a single wire to the existing circuit, this time to the row input ROW0/PB0.

I modified the code to set all the column drivers low. The row inputs have been configured as inputs with their internal pull-up resistors enabled. If a button is pressed, and its column line is low, it will pull down the corresponding row line. I check this in a short section of code within the endless while() loop in the main() function. If detected, the ‘a’ segment of the right-most digit is illuminated. If not, it is extinguished. Here is the code:

COLUMN_PORT->OUTDR = 0x00; // all column pins low

if((ROW_PORT->INDR & 0x01) == 0x00) { // button pressed
    COLUMN_PORT->BSHR = 0x01; // COL0 = high
    SEGMENT_PORT->BSHR = 0x01; // SEGA = high
} else { // not pressed
    COLUMN_PORT->BCR = 0x01; // COL0 = low
    SEGMENT_PORT->BCR = 0x01; // SEGA = low
}

Now as there are no roll-over diodes in the switch matrix, this calculator will not properly decode when multiple buttons are pressed at the same time.

This code only works for SW1, which is the intersection of ROW0 and COL0 in the switch matrix. But it proves that the basic circuit works. Now to extend it to the seven remaining rows and columns.

Adding another wire from PA1 to PA1 (that was easy), I should now be able to light up the ‘a’ segment of digit 2 now. Let’s see if that works or not.

Well, it doesn’t. I suspect that because PA1 and PA2 are normally associated with the external quartz crystal driver for the main clock, the MounRiver Studio 2 (MRS2) default application has configured them for oscillator duty instead of GPIO. Let’s find out.

No, that was a good guess but still a wrong guess. The system default in the code is to use the internal HSI oscillator, 24 MHz, routed through the PLL for a result of 48 MHz. The PA1PA2_RM bit, which is a little better named than the previous “PA12_RM” bit is not set in the AFIO->PCFR1 remapping register, so both PA1 and PA2 should be available for GPIO duties.

It looks like I have misread the manufacturer’s schematic of the development board. There is a 24 MHz quartz crystal installed on the board. There are also some uninstalled zero-ohm resistors (jumpers) connecting the XI and XO pins of the chip to the PA1 and PA2 labeled pins on the header. I had assumed, incorrectly, that this meant that the crystal was there but not connected, but could easily be connected if you wanted it. But in reality, it seems that the crystal is connected all the time and only optionally connected to the PA1 and PA2 pins on the header blocks if the jumpers are installed.

I can test this by enabling the crystal as the main system clock and see if it shows up as being “ready” in the RCC status register.

Or so one would think. Since the crystal lines were encumbered with the LEDs and what-not, the system failed to start. Instead of gracefully degrading back to the HSI, it simply locked up and refused further programming. Luckily, I am well versed in the ways of the unbricking of these chips. It’s built in to the MRS2 IDE now, under “Download Settings”, where you can select “Erase Code Flash:” and “By Power off”. This works well.

Instead, I added some solder jumpers to positions R4 and R5 on the development board and now the first seven digits can light up their ‘a’ segments. I haven’t addressed the “PA7 is programmed to be the RST input” issue yet. I will do that in due time.

I took a short detour to connect the USART TX & RX pins from the development board to the WCH-LinkE programming adapter. While there’s not a place on the calculator PCB to bring out these signals, I could still put some sort of header on the CPU replacement PCB. A USART is very often handy for debugging and other tasks. It worked as expected, even though I initially wired up the transmit wire to the wrong pin. I started to add the USART initialization code to the project, but found it was already there, as the default application in MRS2 is a serial console echo for this chip.

Now I have wired up all the segment driver lines. This set up is getting more delicate as I go. Luckily, there are only seven more wires to go. Then it will be complete with only the small matter of “all of the software” to write.

Let’s take a look at all those segments. Now I will have to test out the multiplexing in order to see more than one segment at a time. This will let me know just how bright the overall display will be in actual use. The present static tests look awfully nice, but when actually displaying numbers and so forth it will be dimmer.

At what I assumed was a 60 Hz refresh rate, there still seemed to be some visible flicker in the display when I jiggled it around. I increased it to 120 Hz and it looks better. The brightness is OK. It’s not terribly bright, but that’s OK, too.

Instead of writing a clever loop, I coded it the hard way:

// *** debug *** test segment drivers

//COLUMN_PORT->OUTDR = COLUMN_0; // enable only digit 1 (right-most)
COLUMN_PORT->OUTDR = COLUMN_3 | COLUMN_2 | COLUMN_1 | COLUMN_0; // right four digits

SEGMENT_PORT->OUTDR = SEGMENT_A;
Delay_Ms(1000 / 8 / 120); // 120 Hz refresh
SEGMENT_PORT->OUTDR = SEGMENT_B;
Delay_Ms(1000 / 8 / 120); // 120 Hz refresh
SEGMENT_PORT->OUTDR = SEGMENT_C;
Delay_Ms(1000 / 8 / 120); // 120 Hz refresh
SEGMENT_PORT->OUTDR = SEGMENT_D;
Delay_Ms(1000 / 8 / 120); // 120 Hz refresh
SEGMENT_PORT->OUTDR = SEGMENT_E;
Delay_Ms(1000 / 8 / 120); // 120 Hz refresh
SEGMENT_PORT->OUTDR = SEGMENT_F;
Delay_Ms(1000 / 8 / 120); // 120 Hz refresh
SEGMENT_PORT->OUTDR = SEGMENT_G;
Delay_Ms(1000 / 8 / 120); // 120 Hz refresh
SEGMENT_PORT->OUTDR = SEGMENT_DP;
Delay_Ms(1000 / 8 / 120); // 120 Hz refresh

For this testing, that’s fine. It works and proves all the wires are still stuck where I stuck them.

Leave a Reply