# Seven Segment driving and AD conversion

Status
Not open for further replies.

#### denujith

##### Member level 5
Hi,

I am using a PIC (18F2550) to drive SSD and AD conversion (interfacing a 's-type' thermocouple). I have moved the AD conversion logic to ISR. But someone told me that it is better to move SSD driving logic to ISR and and keep the AD logic in main().

His argument was SSD driving logic is important in this specific application (since the increase in temperature is very slow).

Regards,
/Lakmal

#### alexan_e

The code inside an interrupt should be as small as possible because your code execution stops while you are in it, you don't have to put any of the two in the interrupt.
Normally you get a value, set a flag and get out of the interrupt, then in the main loop you read that flag or the value that has changed , do the calculations and you show the result.
The LCD or any kind of display shouldn't be in the interrupt, these routines usually take long time to execute and you will remain in the interrupt for an extended amount of time.
It may not matter in your application but you shouldn't get used to use ISR this way.
The display should be handled inside the main loop , if the conversion code is small you can do it in the interrupt but is is better to just store the result of the conversion and do the conversion in the main too.

Alex

jlon and denujith

Points: 2

### jlon

Points: 2

#### denujith

##### Member level 5
Thank you Alex for your valuable input. (Y)

Lakmal

#### alexan_e

I think I have to make a few additions to my previous post

I have assumed that the interrupt you talk about is the ADC interrupt, in that case you just want to get the ADC result.

If you are using seven segment display with multiplexing done in the mcu then you obviously need to refresh the digits in a specified rate interval so that the result is clearly visible and doesn't flicker.

In that case you can use an interrupt which is called when a timer overflows but the only thing that should go inside that interrupt would be to turn off the previous digit and light the next
Basically you write a new value to a port, this is done very fast so it shouldn't be a problem but anything that has to do with the calculation of the value of the display should be in the main part, in general you calculate the value and write it in an array (only when it has changed) and the display shows that value.

Depending on the code you have you may also do the refreshing in the while loop with a flag assuming that the while loop is executed several times before the display needs to refresh.

Alex

#### denujith

##### Member level 5
Alex, I didnt understand
In that case you can use an interrupt which is called when a timer overflows but the only thing that should go inside that interrupt would be to turn off the previous digit and light the next

Anyway below are code segments I have written. Please let me know whether does the below code match with your suggestion.

Code C - [expand]1
2
3
4
5
6
7
main(){
while(;){
displayNumber(iOut);
}
}

displayNumber() function is called all the time irrespective of the value in iOut. I have to include the logic to call displayNumber() if iOut has been changed.

Code C - [expand]1
2
3
4
5
6
7
8
9
void interrupt ISR(){
if(TMR0IE && TMR0IF){
GODONE = 1;
while(GODONE) continue;
TMR0IF=0;
}
}

Code C - [expand]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void displayNumber(unsigned int val ){
char ssd_val[] = {0x5f, 0x06, 0x9b, 0x8f, 0xc6, 0xcd, 0xdd, 0x07, 0xdf, 0xc7};

div_t res;

res         = div(val, 1000);
thousands   = res.quot;
res         = div(res.rem, 100);
hundreds    = res.quot;
res         = div(res.rem, 10);
tens        = res.quot;
ones        = res.rem;

PORTB = 0; PORTC = 0;
PORTB = ssd_val[ones];
PORTC = 1;
DelayMs(1);

PORTB = 0; PORTC = 0;
if (val > 9){
PORTB = ssd_val[tens];
PORTC = 2;
DelayMs(1);
}

PORTB = 0; PORTC = 0;
if (val > 99){
PORTB = ssd_val[hundreds];
PORTC = 4;
DelayMs(1);
}

PORTB = 0; PORTC = 0;
if (val > 999){
PORTB = ssd_val[thousands];
PORTC = 128;
DelayMs(1);
}
}

#### alexan_e

I'm not familiar with the PIC registers so I will describe what I mean

Code C - [expand]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// this array should hold the segment port values that light the correct segments: ssd_val[0] light segments to show 0, val[8] light segments to show 8 etc
ssd_val[] = {0x5f, 0x06, 0x9b, 0x8f, 0xc6, 0xcd, 0xdd, 0x07, 0xdf, 0xc7};

// I see 4 digits so you should have an array of four char
char display[4];
// these 4 chars will be shown to the four ssd,
display[0] // to the first
display[1] // to the second
display[2] // to the third
display[3] // to the fourth
// to shown a value like 1234, the array content should be 0x1 0x2 0x3 0x4 (values not ADCII character representation)

// then when you write ssd_port=ssd_val[display[0]]; the port will have the correct value to show number 1
// or ssd_port=ssd_val[display[2]]; the port will have the correct value to show number 3 (assuming that the array content is 1234 as above)

char enable ssd[4];  // array that holds the values to turn on/off the segments
// then assign the values so that
enable_ssd[0]; // turns on only the first digit, depends on the port pins you use, for example  0b00001000 if ssd0 on/off is in bit 3
enable_ssd[1]; // turns on only the second digit, depends on the port pins you use, for example 0b00000100 if ssd1 on/off is in bit 2
enable_ssd[2]; // turns on only the third digit, depends on the port pins you use, for example  0b00000010 if ssd2 on/off is in bit 1
enable_ssd[3]; // turns on only the fourth digit, depends on the port pins you use, for example 0b00000001 if ssd3 on/off is in bit 0

ADC interrupt{ // called when result is available
// write code to get ADC result to a variable
}

TIMER interrupt { // to refresh the display, use timer overflow interrupt called about 400 times/sec so that refresh rate is 100Hz
static char index; // a variable that holds the index value when you re-enter the interrupt

// lets assume that the four ssd are controlled from PORTA bits 3 2 1 0, they turn on with high(1) and off with low(0)
PORTA=PORTA & 0xf0; // clear bits 3 2 1 0 so that all ssd are off
ssd_port=ssd_val[display[index]];  //set the new value to the port that control the segments
PORTA=PORTA | enable_ssd[index]; // turn on ssd number "index"
index++ // increment the index for the next interrupt
if (index==4) index=0; // after the fourth digit go to the first again
}

void main (void){

while(1){
// use a variable to hold the previous result to avoid doing conversions for the same result
{ last_result=ADC_result // store the new value

// do the conversion
thousands   = res.quot;
res         = div(res.rem, 100);
hundreds    = res.quot;
res         = div(res.rem, 10);
tens        = res.quot;
ones        = res.rem;

display[0]=thousands;  // you can obviously use this above instead of the thousands variable
display[1]=hundreds;
display[2]=tens;
display[3]=ones;
}

}

}

if you don't want to use the timer interrupt then you can use the refresh inside the while loop.
I hope you can understand what I'm doing in the code.

Alex

denujith

### denujith

Points: 2

#### denujith

##### Member level 5
That's great Alex... I learned a lot from your code (Y) I'll do as you have pointed out. Thank you for your great effort put forward in helping me.

Lakmal

#### alexan_e

The only thing that I haven't done in the above code it to enable recursive interrupt inside the timer interrupt so that in case of an AD result the execution goes immediately to the ADC interrupt to store the result and then returns in the timer interrupt code.
You should enable recursive isr only inside the timer interrupt because you don't want the ADC to stop and jump to the timer, I hope it is the same as the AVR where you can enable specific interrupts to be interrupted from another isr.

Even if you don't do that it will not matter much in this case because the ADC result should be the same even after the timer isr finishes because it takes a small amount of time but you should be aware of that for other cases where you should get the ADC result immediately.

Alex

#### denujith

##### Member level 5
I am planning to use low and high priority interrupts. Refreshing can be moved to high priority interrupt and ADC can be moved to low priority interrupt.

Since Thermocouple's temperature varies slowly with time it may not be a problem in this specific case as you have pointed out.

Thank you

Lakmal

---------- Post added at 20:42 ---------- Previous post was at 20:24 ----------

Alex, I don't understand why do you set PORTA to 0 as:
Code:
PORTA=PORTA & 0xf0; // clear bits 3 2 1 0 so that all ssd are off

PORTA = 0xf0;

and, same goes with:
PORTA=PORTA | enable_ssd[index];

Please explain why do you use '&' and '|'.

Lakmal

#### alexan_e

As I said in my example PORTA is the port that controls the four transistors that turn on/off the displays.
Suppose than the four bits used for that purpose are b3,b2,b1,b0 (0b00000000)
when you want to set the value of these four bits to 0 without changing b7,b6,b5,b4 you have to and the bit with 0.
Any value ANDed with 0 will result to 0 and any value ANDed with 1 will result in the value it had
On the other hand any value ORed with 1 will result to 1 and any value ORed with 0 will result in the value it had

this code
PORTA=PORTA & 0b11110000;
keeps b7,b6,b5,b4 to the value they had and clears b3,b2,b1,b0

PORTA= 0b11110000; doesn't have the same result because it forces value 1 to b7,b6,b5,b4

so when you want to set a bit you OR it (|) with 1, to clear a bit you AND it (&) with 0;

for example
0b11111111 & 0b01111110 results to 0b01111110
0b00000000 | 0b00011000 results to 0b00011000

Alex

Last edited:
denujith

### denujith

Points: 2

#### denujith

##### Member level 5
I got the idea. (Y)

Lakmal

Status
Not open for further replies.