Converting a PIC DVM to output decimal

dannyf

Joined Sep 13, 2015
2,197
one approach that can greatly simplify the build is to use a chip that has 6x2 consecutive output pins that match the pins of a typical 4-digit 7-segment display. You can simply "seat" the led display right on top of the chip and it would work beautifully, without any resistors, etc.

I have done that with a 16f193x chip. The 2xK22 chips can be used here as well. and I'm sure other chips too.

for things like that, I like to use a display buffer that saves the segment information. A timer invokes the display routines in the background updating the leds based on the content in the display buffer.

In the main loop, you do your adc and convert its results to be segment information and store that in the display buffer.

With this approach, the display routines are fully transparent to the user / user code.

There is simply no reason that you have to go a pic24f to get this done.
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
Your questions are very valid. In my original post. My request was to seek help in converting the output of a seven segment display to a single segment decimal display system that would have been akin to the old edge lit non-linear systems displays (similar to driving Nixie tubes but without the high voltage and 74141's) and by learning which points in the code to change. However, I feel it is best to learn how the display algorithm actually works and go from there. I had originally chosen the PIC16F1826 as I thought it may be a good fit to start out with. My objective to this was that once the decimal display algorithm was understood, it would possibly give rise to other applications other than just a digital volt meter. Other devices like counters, timers, clocks, etc. I do agree that a PIC16F should be more than capable of performing this task replacing the 74HC145 and similar functions.
 

jayanthd

Joined Jul 4, 2015
945
The price of PIC18F26K22 and PIC24FJ64A102 is almost same. As PIC24 has 16 bit port the code is simple and very easy to understand. PIC24 runs from 3.3V and hence less power consumption.

The code written for PIC18F26K22 (common anode and common cathode displays) were both correct and they were working as I tested it in debugger. OP was telling that it is not working and hence to make the code simple and explain it to OP I chose PIC24.
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
That is true. The size of the PIC18 and PIC24 are also the same. Even the qfn packages are tiny. However, the code I ran on the 26K22 worked to a point. The LSD row works fine. It rolls from 0 to 9 without problem. it was the middle and MSD rows that behave differently. I have checked the display circuit carefully. It is pretty much literally connected as if looking at a schematic. What I have noticed is when I power on the circuit, all LED's are on with no reaction or the chip appears to have hanged. If I bump the power, the chip starts up. I am using a regulated 5vdc power supply. I though of the possibility of ripple in the power supply output but it is clean and my leads to the MCU are short. The capacitors are next Vss and Vdd pins. The MCU was connected on a breadboard. Could this be the cause of the problems? I mean, can it be THAT sensitive? Some further details. I have been programming the 26K22 with a JDM programmer using a 16550 UART serial port. Not a USB serial dongle. Yes, I know it's old tech but would it make any major difference to use a newer programmer like the PICKit2/3?

When I ran the simulation in Proteus, I noticed that I was getting the odd results when the middle and most significant digits roll over at other than the 9 position. The digits don't advance from one to the next. They tend to skip about. When I ran Revision F on Proteus, it would simply error out at Node 0004 and complain of other features not modelled in for PIC18. I'm not certain which differences between your Proteus and the one I'm accessing. But something isn't matching up. If it is working on your end then great! But I'm stating that it is not what I am experiencing. Has anyone else had a chance to take a whack at this? Perhaps your experience may be more positive than mine.
 

jayanthd

Joined Jul 4, 2015
945
I have checked common cathode version for PIC18F26K22, common anode version for PIC18F26K22 and common cathode version for PIC24FJ64GA102 again in debugger and all are working fine.

I am using current version of Proteus which is 8.5 SP1.
 

dannyf

Joined Sep 13, 2015
2,197
My objective to this was that once the decimal display algorithm was understood
the code presented so far is so poorly written that I don't think you are making headways against that goal.

Here is what I use to drive 14-segment 4-digit LEDs. I took out the conditional compilation stuff to make it easier to read:

Code:
//initialize the led display
void led_init(void) {
    //reset the display dig pins
    DIG_OFF(DIG0_PORT, DIG0); IO_OUT(DIG0_DDR, DIG0);
    DIG_OFF(DIG1_PORT, DIG1); IO_OUT(DIG1_DDR, DIG1);
    DIG_OFF(DIG2_PORT, DIG2); IO_OUT(DIG2_DDR, DIG2);
    DIG_OFF(DIG3_PORT, DIG3); IO_OUT(DIG3_DDR, DIG3);
   
    //reset the segment pins
    SEG_OFF(SEG0_PORT, SEG0); IO_OUT(SEG0_DDR, SEG0);
    SEG_OFF(SEG1_PORT, SEG1); IO_OUT(SEG1_DDR, SEG1);
    SEG_OFF(SEG2_PORT, SEG2); IO_OUT(SEG2_DDR, SEG2);
    SEG_OFF(SEG3_PORT, SEG3); IO_OUT(SEG3_DDR, SEG3);
    SEG_OFF(SEG4_PORT, SEG4); IO_OUT(SEG4_DDR, SEG4);
    SEG_OFF(SEG5_PORT, SEG5); IO_OUT(SEG5_DDR, SEG5);
    SEG_OFF(SEG6_PORT, SEG6); IO_OUT(SEG6_DDR, SEG6);
    SEG_OFF(SEG7_PORT, SEG7); IO_OUT(SEG7_DDR, SEG7);
    SEG_OFF(SEG8_PORT, SEG8); IO_OUT(SEG8_DDR, SEG8);
    SEG_OFF(SEG9_PORT, SEG9); IO_OUT(SEG9_DDR, SEG9);
    //SEG_OFF(SEG10_PORT, SEG10); IO_OUT(SEG10_DDR, SEG10);
    //SEG_OFF(SEG11_PORT, SEG11); IO_OUT(SEG11_DDR, SEG11);
    //SEG_OFF(SEG12_PORT, SEG12); IO_OUT(SEG12_DDR, SEG12);
    //SEG_OFF(SEG13_PORT, SEG13); IO_OUT(SEG13_DDR, SEG13);
}

//update the display
void led_display(void) {
    uint16_t tmp;                            //display pattern - for > 8 segments
    //uint8_t tmp;                            //display segment pattern - for 8 segments or less
    static uint8_t dig=0;                    //current dig to be displayed on
   
    //IO_FLP(LED_PORT, LED);                //for debug only
   
    //turn off all digits
    DIG_OFF(DIG0_PORT, DIG0);
    DIG_OFF(DIG1_PORT, DIG1);
    DIG_OFF(DIG2_PORT, DIG2);
    DIG_OFF(DIG3_PORT, DIG3);
   
    //load the segments (seg0..10]
    tmp = LRAM[dig];                        //current digit to be displayed
    if (tmp & (1<<0)) SEG_ON(SEG0_PORT, SEG0); else SEG_OFF(SEG0_PORT, SEG0);
    if (tmp & (1<<1)) SEG_ON(SEG1_PORT, SEG1); else SEG_OFF(SEG1_PORT, SEG1);
    if (tmp & (1<<2)) SEG_ON(SEG2_PORT, SEG2); else SEG_OFF(SEG2_PORT, SEG2);
    if (tmp & (1<<3)) SEG_ON(SEG3_PORT, SEG3); else SEG_OFF(SEG3_PORT, SEG3);
    if (tmp & (1<<4)) SEG_ON(SEG4_PORT, SEG4); else SEG_OFF(SEG4_PORT, SEG4);
    if (tmp & (1<<5)) SEG_ON(SEG5_PORT, SEG5); else SEG_OFF(SEG5_PORT, SEG5);
    if (tmp & (1<<6)) SEG_ON(SEG6_PORT, SEG6); else SEG_OFF(SEG6_PORT, SEG6);
    if (tmp & (1<<7)) SEG_ON(SEG7_PORT, SEG7); else SEG_OFF(SEG7_PORT, SEG7);
    if (tmp & (1<<8)) SEG_ON(SEG8_PORT, SEG8); else SEG_OFF(SEG8_PORT, SEG8);
    if (tmp & (1<<9)) SEG_ON(SEG9_PORT, SEG9); else SEG_OFF(SEG9_PORT, SEG9);
    //if (tmp & (1<<10)) SEG_ON(SEG10_PORT, SEG10); else SEG_OFF(SEG10_PORT, SEG10);
    //if (tmp & (1<<11)) SEG_ON(SEG11_PORT, SEG11); else SEG_OFF(SEG11_PORT, SEG11);
    //if (tmp & (1<<12)) SEG_ON(SEG12_PORT, SEG12); else SEG_OFF(SEG12_PORT, SEG12);
    //if (tmp & (1<<13)) SEG_ON(SEG13_PORT, SEG13); else SEG_OFF(SEG13_PORT, SEG13);

    //turn on current digit, and advance to the next digit (max = 4)
    switch (dig) {
        case 0: DIG_ON(DIG0_PORT, DIG0); dig = 1; break;
        case 1: DIG_ON(DIG1_PORT, DIG1); dig = 2; break;
        case 2: DIG_ON(DIG2_PORT, DIG2); dig = 3; break;
        case 3: DIG_ON(DIG3_PORT, DIG3); dig = 0; break;
    }

}
led_init() initializes the pins for digits and segments, and led_display() shows the output segments on the current digit.

The code is totally unrelated to hardware, and can be ported to any mcu - all it takes is a recompilation on the target.

You can install the display routine through a timer call back function, like this:

Code:
    led_init();                                //reset the led display module
    tmr2_init(LED_PS, LED_PR);                //reset tmr2
    tmr2_act(led_display);                    //install user handler
    ei();                                    //enable global interrupts
It initializes the led module, sets up timer 2 to interrupt periodically, and then installs led_display as the user handler in the timer isr.

After that, the display will be updated automatically, based on the information you may store into the display buffer LRAM[].

Code like that separates the logic from the actual implementation. This approach allows the use of highly portable (=proven) code from one project to another.

Hope it helps.
 

dannyf

Joined Sep 13, 2015
2,197
On a 16f1826, the above code compiles to less than 200 bytes in code space, with the overhead. So yes, going to a 24f part is just wasteful.
 

jayanthd

Joined Jul 4, 2015
945
Where is the adc reading and adc data mapping code ? He needs to display floating point value total 3 digits. values like 3.25, 32.5 and 325.
 

dannyf

Joined Sep 13, 2015
2,197
here is a simulation of the above code, driving a 3x10led display.

Code:
        //prepare adc value
        adc = (++adc) & 0x3ff;                //adc is a 10-bit value
        volt = ADC2VOLT(adc);                //convert adc to VOLT -> 0..300/1024
        //prepare for display
        tmp = volt;
        LRAM[0]= led_font[tmp % 10]; tmp /= 10;    //lsb conversion
        LRAM[1]= led_font[tmp % 10]; tmp /= 10;
        LRAM[2]= led_font[tmp % 10]; tmp /= 10;
        LRAM[3]= led_font[tmp % 10]; tmp /= 10;    //msb conversion
As you can see, the adc results are simulated. ADC2VOLT() converts the 10-bit adc result to a number in the 0..300 range, held in volt. volt is then being converted into segment information held in our display buffer LRAM[].

Here is a screen shot below.

In this particular case, adc = 19, and volt = 5. The least significant display (right most led bar) is displaying volt = 5 -> 5 leds are turned on. So the display works as expected.
16f1826_leddvm.PNG
 

dannyf

Joined Sep 13, 2015
2,197
the same code can be easily configured to work with 7-segment or 14-segment displays of arbitrary digits. In theory, it supports up to 16 segments. and the number of digits is only limited by refreshing frequency and the processing power.
 

jayanthd

Joined Jul 4, 2015
945
@dannyf

The OP wants something like this.

1. He wants to display data like 3.12, 31.2 and 312.

2. Left most display (Bar Graph) is MSB and right most Bar Graph LSB

3. To display 31.2, LED 4 (counting starts from 0, 0-9 for each Bargraph) of MSb is turned ON and then LED 2 of middle Bargraph is turned and also the middle displays decimal point LED is turned ON and then 3rd LED of LSB display is turned ON.
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
@dannyf This code simplifies so many things! I'm not 100% familiar with its semantics yet but it almost appears self explanatory. Is this written in CCS? If so, I wish to persue this language further. I was reading your blog and can see the parallels of the MCU based VU meter at:

https://dannyelectronics.wordpress.com/2016/07/16/mcu-based-led-vu-meter/


@jayanthd Let's start with the basics. It can run in bar mode and dot mode. Floating decimal point is not always necessary at this time. The pins on the 1826 remaining are 7,10 and 11.

I am currently checking through my hardware for the possible cause of errors and have considered getting a different programmer as my JDM programmer and serial port leave me questioning my faith in it.
 
Last edited:

jayanthd

Joined Jul 4, 2015
945
Then buy mikroProg for PIC from mikroElektonika. it is one universal (programmer/debugger) (corrected from program) which supports all Microchip microcontrollers (PIC10F, PIC16F, PIC18F, PIC24, dsPIC, PIC32). You will get updates for it as new devices are added and as I am going to provide you mikroC PRO PIC or mikroC PRO dsPIC code you will be able to debug using this programmer/debugger.
 
Last edited:

hp1729

Joined Nov 23, 2015
2,304
Greetings,
There are a few of the PIC DVM found on the web using a PIC16F676. They are designed to drive seven segment LED displays. I have been aiming to attempt to build one to output decimal instead of seven segment in order to drive a set of edge-lit displays. The only example of such a circuit uses the rare and obsolete CA3162e Intersil IC (A/D converter) and a 74HC145 (BCD to Decimal driver) in the image below:

View attachment 91249

The aim was to create a more modern version of this using the PIC micro. The PIC16F676 doesn't have enough I/O pins to handle ten LED's in a row. The Microchip search tool led me to the PIC16F1826 as it has enough I/O's and has a 10bit A/D converter. But looking at the C code for the 676 DVM, it is using 2 byte HEX to build the segment patterns for seven segment displays. I wasn't certain that this would work so easily for ten LED's as I ended up creating 3 byte HEX (or 12 bits) for each single LED pattern and the PIC16F1826 is an 8bit device. I attempted to compile the code with XC8 under MPLAB X IDE 3.05 to learn that the code appeared to be made for another compiler. I had to make changes to the code to fit the new pin arrangements and it compiled. I ran the MCU but at this point, the three multiplexers are working but there is no output. Is there a more correct way to drive the ten LED's/row? I haven't been able to find a different way to drive the LED's in this fashion. I am not a programming expert and am sure I'm missing the point somewhere and I have an inclination that I may be going about this the wrong way. I have the schematic and the code I've been trying to work on if requested. Any help would be greatly appreciated.
You want to go from 7-segment to decimal (1 of 10)? I would use a PROM or EPROM and go from 7 segment to BCD then BCD to Decimal. With one EPROM you could handle two digits.
 

dannyf

Joined Sep 13, 2015
2,197
Another approach that you could take is to use shift registers to drive those LEDs. It will greatly reduce the number of pins needed as well as code complexity.

In your case, 4 shift registers can do the job.
 

Thread Starter

Remembermyname

Joined Sep 6, 2015
91
Sorry for being away for so long. @dannyf , I'm definitely more inclined to believe that the shift registers design would be the way to go. This can result in a much smaller MCU being utilized in many cases and achieving the same results. I'm working on this still, albeit slowly, but I learn more along the way. I was able to do this with an arduino with a lot of trial and error but has no longer become a project to produce a DVM but to serve as a basis display for many other projects. Here is the arduino version of the basic display I was able to produce and am working to produce a PIC version soon:

https://forum.allaboutcircuits.com/...with-dual-74hc595-output-from-arduino.138164/

I was able to locate shift registers for the eventual pic version that are as small as the QFN pics themselves.
 
Last edited:

jayanthd

Joined Jul 4, 2015
945
Completed the project ? Need any help ?

I was checking post 130 by dannyf. How does it display values like

0.23V
1.23V
12.3V
123V

?

if unsigned int variable is used for adc, volt and tmp then there will be no value between 0 and 1 then how does it display 230 mV ?

I am trying make two versions of the DVM which uses 2x and 4x shift registers.

In 4x shist registers version 32 bits are used and 30 bits are for display and 2 bits are for decimal points. 8 pin PIC12F is used.

In 2x shift register version, 10 bit data is shifted with 2 bit decimal point data and so total 10 bits are shifted. 18 or 20 pin PIC18F is used.

If all SMD components are used then both versions will have a very small hardware.



@dannyf

>>>>>>>In this particular case, adc = 19, and volt = 5. The least significant display (right most led bar) is displaying volt = 5 -> 5 leds are turned on. So the display works as expected.<<<<<<<


6 leds are ON. How do you say 5 leds and it is equal to 5 ?
 
Last edited:

jayanthd

Joined Jul 4, 2015
945
I have made PIC18F46K22 version of DVM which doesn't use shift registers. You can contact me for the code. It is working fine. I have used 4x 10 segment bargraphs.

So, you can measure something like -123.4V

Sign and decimal point leds are included.

I have written code for both dot and bar type displays (both in same code) A button switches between the dot and bar modes.

I am designing the PCB layout. I will get two PCBs fabricated. if you provide me your contact address then I will send 1 board to you. I am using 10 digit bargraph displays red color. It will take 1 week for the PCB to get facbricated.


The code is 231 lines.
 
Last edited:
Top