# Converting a PIC DVM to output decimal

#### Remembermyname

Joined Sep 6, 2015
91
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:

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.

Last edited:

#### jayanthd

Joined Jul 4, 2015
945
Can you post a picture which shows how the 10 LEDs display (digit) look like. Post the schematic and the code you have written. Are the hex codes you have made for the mask correct ?

Are you trying to implement the functioning of BCD to Decimal chip using PIC ?

it is using 2 byte HEX to build the segment patterns for seven segment displays
It is 1 byte, 10 x 1 byte mask values for 7 Segment Display (SSD).

0x07 = 1 byte. Each hex digit is 1 nibble (4 bits).

0x07 is a Hex number and 0 and 7 are two hex digits.

It looks like an easy task. Feed a 74H145 with BCD values 0 to 9 and notedown the 10 bit pattern at its output for each input value. These will be the mask values.

connect as many digits you want to drive and also use one transistor per digit and then multiplex using Timer Interrupt.

Last edited:

#### Remembermyname

Joined Sep 6, 2015
91
The output was to indicate one LED per digit per row. I wasn't certain if using the hex codes were appropriate for this type of display as only one LED represents one digit.

Below is the schematic that I have so far:

Below is the code I was attempting to alter:

*****
Decimal_DVM1.h

***

/*
* File: Decimal_DVM1.h
* Author: Lawrence Hallman
*
* Created on August 10, 2015, 4:57 AM
*/

// PIC16F1826 Configuration Bit Settings

// 'C' source line config statements

#include <xc.h>
#include <htc.h>

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

// CONFIG1
#pragma config FOSC = INTOSC // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = ON // PLL Enable (4x PLL enabled)
#pragma config STVREN = OFF // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = ON // Low-Voltage Programming Enable (Low-voltage programming enabled)

***

*****
*****

Decimal_DVM1.c

***

/*
* File: Decminal_DVM1.c
*
* Created on August 10, 2015, 5:20 AM
*/

#include <stdio.h>
#include <stdlib.h>
#include "Decimal_DVM1.h"
#include <htc.h>
#include <xc.h>

#define SPORT PORTA
#define DPORT PORTB
const char SegCode[11] = {0x400,0x200,0x100,0x080,0x040,0x020,0x010,0x008,0x004,0x002,0xFF};
// 0 1 2 3 4 5 6 7 8 9
const char Column[3] = {0x02,0x01,0x04};
static char Segment[3] = {0x7f,0x7f,0x7f};
static unsigned char ColCount=0x00;
void CPU_SETUP(void);
void Display(void);
void HTO7S(unsigned int Num);
void delayMs(int x);

unsigned int result;
void interrupt timer1_isr(void)
{
if(PIR1bits.TMR1IF==1)
{
PIR1bits.TMR1IF = 0;
TMR1H=0xDF;
Display();
}
}
void main()
{
unsigned char i;

CPU_SETUP();
while(1)
{
result=0;
for (i=0;i<3;i++)
{

delayMs(1);
}
HTO7S(result/3);

delayMs(200);
}

}

void CPU_SETUP()
{
CM1CON0 =0;
CM2CON0 =0; //Turn off comparator module
TRISA=0b00100000;
PORTA=0x27;
TRISB=0b00000000;
PORTB=0x37;

T1CON= 0x00;
TMR1H=0xDF;
INTCONbits.GIE =1;
INTCONbits.PEIE=1;
PIE1bits.TMR1IE =1;
T1CONbits.TMR1ON=1;
}
{
unsigned int res;
return(res);
}
//-------------------------------------
// Display routine
//-------------------------------------
void Display()
{
PORTA = 0b00011111; // off all digits column and Segment G
PORTB = 0b11111111; // off segment a-f

if (ColCount>=3)
ColCount=0;

DPORT = Segment[ColCount];
SPORT = ((Segment[ColCount] & 0b01000000)>>1) | (Column[ColCount]^0x07);
ColCount++;
}

//--------------------------------------
// Convet HEX 2 byte to 7-Segment code
//--------------------------------------
void HTO7S(unsigned int Num)
{

unsigned int res;

res = ((30*Num)%1023)/100;
if(res==10)
{
Num=Num+1;
res=0;
}
Segment[2]=SegCode[res];

res = (30*Num)/1023;
Segment[1]=SegCode[res%10];

Segment[0]=SegCode[res/10];
if (Segment[0]==0x40)
Segment[0]=0xFF;

}

void delayMs(int x)
{
int i;
for (x ;x>0;x--)
{
for (i=0;i<=110;i++);
}
}

*****

#### Remembermyname

Joined Sep 6, 2015
91
At first, I had pondered using BCD output to feed a 74LS145 to convert it to decimal but I was attempting to create a one chip solution as the edge-lit displays are intended to be much smaller than the large antique ones made by General Radio or similar to the order of the miniature 3 digit DVM's found online.

#### jayanthd

Joined Jul 4, 2015
945
Your circuit seems wrong. You have connected emitter of NPN transistor to anodes of LEDs. For driving common anode displays PNP transistors are used. Change the transistors to PNP type.

If you want I can write you a code for 3 digit or more digits display but I need the mask values. You use a 74H145 and feed it binary values 0 to 9 and note down the 74H145 output values and post it here. If you provide the values then it takes 1 hour to finish the coding. Alternately you can look into the datasheet of 74H145 ans see if it provides the output values for 0x0 to 0x9 BDC input values. The sooner you post the values, the faster you will get solution.

If you have the image showing how the 10 LED digit looks like then attach it here.

#### jayanthd

Joined Jul 4, 2015
945
The 74H145 has 4 inputs A, B, C, D, I want to know which is LSB and which is MSB.

The 74H145 has 0 to 9 outputs and I want to know which is LSB and which is MSB. This will help me in getting the mask values.

#### Remembermyname

Joined Sep 6, 2015
91
? The BC557 is a PNP transistor used to drive common anode seven segment display.
I had posted previously that the 10 LED's are not a segmented display. Instead, they are intended to represent one LED per number to be used in an edge lit display like the one below:

Except the aim was to reduce the components involved and turn the physical design into a software solution.

#### jayanthd

Joined Jul 4, 2015
945
Yes, later I saw the part number and it was BC557 a PNP transistor.

Instead, they are intended to represent one LED per number to be used in an edge lit display like the one below:
I still don't have a clear picture about how the display look like. You have 10 LEDs per digit and you have 3 digits and you want to display a 3 digit number like 300 on the display right ?

If yes, Each 10 LED driven by a transistor forms one digit. Right ?

You want to implement the work done by 74HC145 using a PIC. Right ?

So, if you provide the 10 values the 74HC145 gives at its output for BCD inputs 0 to 9 to the 10 LEDs you have connected to the PIC then it should display values 0 to 9. Right ?

If yes, all I want to know is in 74HC145 inputs (A, B, C, D) which is LSB and which is MSB. I also want to know which is LSB and which is MSB in 74HC145's outputs.

If this is known then I will create an array of unsigned int with 10 elements and each element will hold a value representing 0 to 9.

Then all that is to be done is assign 8 bits of the required array element to PORTB and 2 bits to RA8 and RA& and then turn on the required transistor to display units, tens or hudredths digits.

I assume that 10 LEDs connected to one transistor has the ability to display values 0 to 9. Is it correct ?

#### MrAl

Joined Jun 17, 2014
9,533
Hello there,

If you need to convert the binary ADC count to BCD, try the "double dabble" routine you should be able to find on the web. That allows you to convert any count to any number of digits.
Once you have the three digits, they will be stored in registers like A=2, B=3, C=4 for 234 decimal.
You should be able to convert that to a single 10 bit port output (using two 8 bit ports), and one way would be to just load two 8 bit registers with 00000000 00000001, then shift once to the left for each decimal count. This means zero would not shift anything, so that '1' would be output on say Port A bit 0, but if the decimal digit was 2 then it would have gotten shifted to 00000000 00000100 and that would drive the 2nd LED up from the bottom rather than the bottom LED because it would be output on Port A bit 2. Obviously you have to shift both registers so you can make the whole 9 count if needed for digit counts 8 and 9. Repeat for the other two (or more) digits. Alternately you could branch for counts greater than 7 and just shift the upper register by the count minus 8 and that would be faster too because you dont have to shift two registers just one or the other.
For the right display brightness, you have to either stuff the code with extra bytes for low digit counts or use a timer and an interrupt to get the timing to be the same for any digit. If the timing differs due to different counts, the brightness will vary with the count for each digit which will not look right. If it doesnt vary too much it may not matter that much, but equal timing is better.
You should be able to multiplex the three digit groups. Turn one column on while turning the right LED row on, then go to the next column and do the same, etc. I think the slowest scan rate is 1/30 seconds for humans, but a little faster is probably better anyway.

The 10 LED per digit idea is kind of neat too. It will make a novel display for the DVM.

#### jayanthd

Joined Jul 4, 2015
945
Here I am attaching the circuit I made. It doesn't show the 10 resistors between PIC and cathodes of LEDs. See if this circuit is what you need. Maybe the mask values I have used ia also not correct but that can be changed. If connections to cathodes of LEDs are different then only code inside IAR has to be changed. I have used mikroC PRO PIC Compile but it can be easily ported to XC8 code. It is still not working in Proteus. I have also attached Proteus 8.2 SP2 format file. If you have Proteus then you can make changes to my proteus file and attach it here.

C:
unsigned int mask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200};
unsigned int display[3] = {0, 0, 0};
char select = 0;

//Timer1
//Prescaler 1:1; TMR1 Preload = 63536; Actual Interrupt Time : 2 ms

//Place/Copy this part in declaration section
void InitTimer1() {
T1CON = 0x01;
TMR1IF_bit = 0;
TMR1H = 0xF8;
TMR1L = 0x30;
TMR1IE_bit = 1;
INTCON = 0xC0;
}

void Interrupt() {
if(TMR1IF_bit) {
TMR1H = 0xF8;
TMR1L = 0x30;
LATB = 0xFF;
LATA = (LATA | 0b11000111);

switch(select) {
case 0:
LATB = display[0] >> 2;
LATA = (LATA & 0b00001011) | ((display[0] & 0x03) << 6);
}
break;
case 1:
}
else {
LATB = display[1] >> 2;
LATA = (LATA & 0b00001101) | ((display[1] & 0x03) << 6);
}
break;
case 2:
LATB = display[2] >> 2;
LATA = (LATA & 0b00001110) | ((display[2] & 0x03) << 6);
break;

};

if(++select == 3)select = 0;

TMR1IF_bit = 0;
}
}

void main() {

CM1CON0 = 0x00;

ANSELA = 0x08;
ANSELB = 0x00;

TRISA = 0x08;
TRISB = 0x00;

PORTA = 0xC7;
PORTB = 0x00;

LATA = 0xC7;
LATB = 0x00;

InitTimer1();

while(1) {

Delay_ms(20);

}
}
}

}
}
}

#### Attachments

• 98.9 KB Views: 11

#### jayanthd

Joined Jul 4, 2015
945
I found this.

http://www.tubeclockdb.com/news/326-edge-lit-display-update.html

Is my understanding correct ?

You want to display 3 digits number like 145.

In the image at the above link I see that for 1 digit there are 10 LED patterns which represent 0 to 9 digits. Am I right ?

When you say you have 10 LEDs to display a digit (which can take values 0 to 9) you actially have like 20 LEDs making a pattern like 1, 2, 3, 4, 5, 6, 7, 8, 9. Am I right ? So, you will have 20 LEDs connected in series and so a total of 20 x 10 channels.

So, only one output pin of 74H145 will be low at a time based on the input BCD values 0x00 to 0x09 and when input to 74H145 is 0x00 then its output will be 0b0111111111 and so the 20 LEDs connected in series which make the pattern 0 will light up and the other LED patterns (each having 20 LEDs in series) will be off. Am I right ?

#### jayanthd

Joined Jul 4, 2015
945

Look at the image and tell if this is how you want the dsiplay to work and if this is how the edge lit display work.

These are the values I made from the output of 74H145 for BCD inputs 0 to 9.

Code:
unsigned int mask[] = {0x1FF, 0x2FF, 0x37F, 0x3BF, 0x3DF, 0x3EF, 0x3F7, 0x3FB, 0x3FD, 0x3FE};
Please confirm whether my understanding of the working of Edge Lit Displays are correct.

In the above array element 0 value displays 0 and element value 10 displays 9 but by reversing the array elements we can make element 0 value represent 9 and element 10 value represent 0.

Last edited:

#### Remembermyname

Joined Sep 6, 2015
91
@jayanthd - your like to the display is correct. The intention of the circuit is to to display 3 numerical digits in a configuration like 145.

>In the image at the above link I see that for 1 digit there are 10 LED patterns which represent 0 to 9 digits. Am I right ?

Right, for instance, the number 0 will be represented by the first LED being lit individually, the number 1 will be represented by the second LED being lit individually, ... and the number 9 will be represented by the 9th LED be lit individually in a single row.

>When you say you have 10 LEDs to display a digit (which can take values 0 to 9) you actially have like 20 LEDs making a pattern like 1, 2, 3, 4, 5, 6, 7, 8, 9. Am I right ? So, you will have 20 LEDs connected in series and so a total of 20 x 10 channels.

Not sure I follow you on that one. Each column of LED's (10 LED's) display one LED per one decimal number output. The over all result is an array of 3 columns x 10 LED's. Each column will display one LED to represent 0-9, Combined, the array will display a representation of 000 - 999. But in the case of this circuit, it would handle up to 30 VDC. Or display the numbers 300 at most. The decimal point is left out as it can be physically added to the edge lit displays. The far right digit or Least Significant Digit will be Tenths. (.0 - .9)

@MrAl - The double dabble algorithm would be possibly a great idea. I think I was attempting something similar to that but I believe I had it all wrong and I didn't know what it was supposed to be called or how to do it correctly. Looking at the wikipedia entry. It was explained in detail that made more sense as to how it worked.

Original seven Segment BCD

gbd cefa <- segment identifiers

1 0100 0000 0
2 0101 0111 1
3 0010 0010 2
4 0000 0110 3
5 0001 0101 4
6 0000 1100 5
7 0000 1000 6
8 0101 0110 7
9 0000 0000 8
0 0000 0100 9

Attempted decimal output BCD

0 0100 0000 0000 400
1 0010 0000 0000 200
2 0001 0000 0000 100
3 0000 1000 0000 080
4 0000 0100 0000 040
5 0000 0010 0000 020
6 0000 0001 0000 010
7 0000 0000 1000 008
8 0000 0000 0100 004
9 0000 0000 0010 002

#### Remembermyname

Joined Sep 6, 2015
91
@jayanthd - According to the datasheet of 74LS145, The output channels are Q0 - Q9 and have assumed that the count started with number 0 instead of number 1. As opposed to your binary output in your diagram above, mine ( in the previous post) are inverted and may be wrong.

#### Remembermyname

Joined Sep 6, 2015
91
@jayanthd - I attempted to open the zip file but it returns an error. The code files within return 0 bytes at this time.

#### absf

Joined Dec 29, 2010
1,968
I have no problem opening the zip file and here's @jayanthd's code:
Code:
unsigned int mask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200};
unsigned int display[3] = {0, 0, 0};
char select = 0;

//Timer1
//Prescaler 1:1; TMR1 Preload = 63536; Actual Interrupt Time : 2 ms

//Place/Copy this part in declaration section
void InitTimer1() {
T1CON = 0x01;
TMR1IF_bit = 0;
TMR1H = 0xF8;
TMR1L = 0x30;
TMR1IE_bit = 1;
INTCON = 0xC0;
}

void Interrupt() {
if(TMR1IF_bit) {
TMR1H = 0xF8;
TMR1L = 0x30;
LATB = 0xFF;
LATA = (LATA | 0b11000111);

switch(select) {
case 0:
LATB = display[0] >> 2;
LATA = (LATA & 0b00001011) | ((display[0] & 0x03) << 6);
}
break;
case 1:
}
else {
LATB = display[1] >> 2;
LATA = (LATA & 0b00001101) | ((display[1] & 0x03) << 6);
}
break;
case 2:
LATB = display[2] >> 2;
LATA = (LATA & 0b00001110) | ((display[2] & 0x03) << 6);
break;

};

if(++select == 3)select = 0;

TMR1IF_bit = 0;
}
}

void main() {

CM1CON0 = 0x00;

ANSELA = 0x08;
ANSELB = 0x00;

TRISA = 0x08;
TRISB = 0x00;

PORTA = 0xC7;
PORTB = 0x00;

LATA = 0xC7;
LATB = 0x00;

InitTimer1();

while(1) {

Delay_ms(20);

}
}
}

}
}
}
The hex code and schematic were also included in the zip.

Allen

#### Remembermyname

Joined Sep 6, 2015
91
I'm still unable to open the archive. I attempted with gunzip 1.5 and winzip 19.5 and 7-zip (x64) 9.20. :/
What are you using to open the archive with?

#### MrCarlos

Joined Jan 2, 2010
400
Hello Remembermyname
Change the file extension to RAR rather than ZIP.

#### Remembermyname

Joined Sep 6, 2015
91
Ah! That worked. Thanks MrCarlos!

#### jayanthd

Joined Jul 4, 2015
945
The forum was not allowing me to attach rar files and hence I renamed the rar file to zip file but it opened here with winrar for me. Use WinRAR.

Previously I said this

>When you say you have 10 LEDs to display a digit (which can take values 0 to 9) you actially have like 20 LEDs making a pattern like 1, 2, 3, 4, 5, 6, 7, 8, 9. Am I right ? So, you will have 20 LEDs connected in series and so a total of 20 x 10 channels.

What I mean is you have 10 channels at the output of 74H145 and only one channel will be low (0) at a time based on the BCD inputs to it. A BCD digit can have only values from 0x00 to 0x09.

0 is a digit
1 is a digit

12 is a number of two digits.

145 is a number of 3 digits

When BCD input to 74H145 is 0x00 the output will be 0x1FF and so the 0th channel will be 0 and as cathodes of LEDs are connected to these channels the 0th channel LED will lit.

My question is to display 0 will you use 1 LED connected to 0th channel or will you use 10 or 20 LEDs in series which take the same 10 mA current and make a pattern in the shape of 0 and lit it ?

Whether you use only 1 LED to represent (display) the values 0, 1, 2,... or whether you put 10 to 20 LEDs in series in each channel and make patterns (shapes) like 0, 1, 2, .... the current requirment will be same but the voltage applied at anodes vary.

One issue I see is you provide 5V to anodes and base-emitter voltage will be less than 5V and this makes the transistor forward-biased and I think all the three transistors are getting foreward-based at all times and this will make the display display the same digit on all the three displays.

Original seven Segment BCD
Attempted decimal output BCD
You don't have to deal with BCD. You only have to use the 10 values the output of 74H145 gives and make an array of these 10 values and based on what decimal value for each digit of a number you send the corresponding mask value to the cathodes and turn on the respective transistor.

For example you want to display 145 a three digit number.

So, from left to right (digit 1 to digit 3)

you send 0x2FF to the 10 LEDs bus and as it is hundredth place digit you turn ON transistor 1 for a while (say 3 ms)
then you turn OFF transistor 1 and then place 0x3DF on the 10 LEDs bus and turn ON transistor 2 for 3 ms and then turn it OFF then you place 0x3EF, on the 10 LEDs (cathodes) bus and turn ON 3rd transistor for 3 ms and this cycles infinitely.

So, first the 1st channel (of 10 channels) will be low (0) and hundredth place digit will be lit.
Then 5th channel will be low (0) and tens place digit will be lit.
Then 6th channel will be low (0) and units place digit will be lit.

This cycles infinitely. It appears that LED 2 of digit 1, LED 5 of digit 2 and LED 6 of digit 3 are all lit at the same time.

If you want patterns then you will connect 10 to 20 LEDs in series for each channel (10 channels total) and make patterns of shape like 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.

If LED bus has value 0x1FF then 0th channel will be low and there will be 10 to 20 LEDs in series in that channel and it will be made in the shape of 0 and so you will see a 0.

We here call 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 as decimal digits
0x00 - 0x09 as BCD digits

and

123, 145, 642, etc,... as decimal numbers.

These are wrong values and they dont reperesent the 10 values the 74H145 outputs.
Code:
unsigned int mask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200};
These are correct values the 74H145 outputs.

Code:
unsigned int mask[] = {0x1FF, 0x2FF, 0x37F, 0x3BF, 0x3DF, 0x3EF, 0x3F7, 0x3FB, 0x3FD, 0x3FE};
but the order may be reverse. 74H145 has A, B , C, D inputs and I assumed A as LSB and D as MSB and found these 10 values. If A is MSB and D is LSB then the array reverses and the array will be

Code:
unsigned int mask[] = {0x3FE, 0x3FD, 0x3FB, 0x3F7, 0x3EF, 0x3DF, 0x3BF, 0x37F, 0x2FF, 0x1FF};
So, these are the 10 mask values which represent decimal digits 0 to 9.

Last edited: