Strange display. LM35 to seven segment display

General discussion on mikroBasic PRO for PIC.
Post Reply
Author
Message
chris11jed
Posts: 156
Joined: 15 Jun 2011 06:37

Strange display. LM35 to seven segment display

#1 Post by chris11jed » 13 Apr 2014 10:01

Hi all,

Would like to get some ideas on a strange problem I'm having.

I'm using mikroBasic Pro for PIC v.6.0.0 with easyPIC v7 and PIC18F45K22

I am testing out a portion of a larger program I'd like to get right. I want to display the temperature from the LM35 temperature sensor on the seven segment display. I have the LM35 connected to RE1. My Vcc is set to 5V

Here's my test code that does display the correct temperature...to a point.

Code: Select all

' Name: LM35_to_7_SD_V01
' Voltage: Vcc = 5V (J5 to 5V)
' PIC: PIC18F45K22
' OSC: - 8MHz HS Crystal
'      - 4xPLL enabled
' S/W: mikroBasic PRO for PIC V.6.0.0
' Dev-Board: easyPIC V7
' Libraries: - ADC
' Sensor: LM35 (J25 to RE1)
' Jumpers: - J25 to RE1 - LM35 to RE1 (AN5)
'          - J5 to 5V - VCC = 5Volts
' Switches: - SW8.1, SW8.2, SW8.3, SW8.4 to "ON"
' Notes: - first write, to test out getting temperature from LM35 to 7 segment display
'        - program is a mash of examples: LM35 Sensor (Temperature measurement) &
'                                         Seven Segment Display (The 'Hello World' example for the Seven Segment Display)
' Problems: - Two numbers are shown. If temperature is around 23.92 Degrees Celsuis, 23.92 is shown for a period of time
'             and then another unrelated number is shown eg. 17.57
'           - These two numbers flicker. At the Delay_ms(2000) the flicker is slow, at Delay_ms(200) the flicker between
'             them is fast. It is easier to see the two numbers as more time is added to the delay.
'
program LM35_to_7_SD_V01
'-------------------------------------------------------------------------------Declarations section
const VREF as byte  = 5.00

dim temp_res as word

dim shifter, portd_index as byte
    digit, number as word
    portd_array as byte[4]

sub function mask(dim num as word) as word
  select case num
    case 0
      result= $3F
    case 1
      result= $06
    case 2
      result= $5B
    case 3
      result= $4F
    case 4
      result= $66
    case 5
      result= $6D
    case 6
      result= $7D
    case 7
      result= $07
    case 8
      result= $7F
    case 9
      result= $6F
  end select
end sub

sub procedure interrupt
  LATA = 0                          ' turn off all 7seg displays
  LATD = portd_array[portd_index]   ' bring appropriate value to PORTD
  LATA = shifter                    ' turn on appropriate 7seg. display

  ' move shifter to next digit
  shifter= shifter << 1
  if (shifter > 8) then
    shifter = 1
  end if
  ' increment portd_index
  Inc(portd_index)
  if (portd_index > 3) then
    portd_index = 0                 ' turn on 1st, turn off 2nd 7seg.
  end if
  TMR0L = 0                         ' reset TIMER0 value
  TMR0IF_bit = 0                    ' Clear TMR0IF
end sub


main:
'-------------------------------------------------------------------------------Main program
  ANSELA = 0          ' Configure PORTA pins as digital
  ANSELD = 0          ' Configure PORTD pins as digital
  ANSELE = 0x02       ' Configure RE1 pin as analog
  TRISE1_bit = 1      ' Configure RE1 pin as input

  TRISA = 0           ' Configure PORTA as output
  LATA = 0            ' Clear PORTA
  TRISD = 0           ' Configure PORTD as output
  LATD = 0            ' Clear PORTD
  
  ADC_Init()          ' Initialize ADC

  T0CON = 0xC4        ' Set TMR0 in 8bit mode, assign prescaler to TMR0
  TMR0L = 0           ' clear TMROL
  digit = 0
  portd_index = 0
  shifter = 1

  GIE_bit = 1
  TMR0IE_bit = 1
  
  while TRUE
    temp_res = ADC_Get_Sample(6)                ' Get 10-bit results of AD conversion
    number = (temp_res * VREF * 100)/10.240     ' Convert to Celsuis
    
    digit = number / 1000                       ' extract thousands digit
    portd_array[3] = mask(digit)                ' and store it to PORTD array
    digit = (number / 100) mod 10               ' extract hundreds digit
    portd_array[2] = mask(digit) + 0x80         ' and store it to PORTD array, then add a decimal point
    digit = (number / 10) mod 10                ' extract tens digit
    portd_array[1] = mask(digit)                ' and store it to PORTD array
    digit = number mod 10                       ' extract ones digit
    portd_array[0] = mask(digit)                ' and store it to PORTD array
    
    delay_ms(2000)                              ' wait a time (could be 200ms, 500ms, 1sec or 2sec)
  Wend
end.
As you can see in the comments section at the start, in the "Problems" bit, I get a display of the correct temperature and another number. In the example I get 23.92 Deg. C. and another number (17.57). :roll: :roll:

Oh, and when you read the word 'flicker' in the problem section, that just relates to the display of one number and then the other. Nothing is wrong with the seven segment display itself, ie. it does not 'flicker' as an issue with the display rate or anything.

My question is why am I getting this other number?
What can I do to stop it and only get the correct temperature reading?

Any help or ideas would be cool....

Thanks
Chris

Jim Walsh
Posts: 55
Joined: 06 Mar 2014 01:38
Location: Pittsburgh, PA

Re: Strange display. LM35 to seven segment display

#2 Post by Jim Walsh » 13 Apr 2014 15:09

Hi Chris,

I was trying something similar in MikroC with a DS1820 and I was having the same kind of problem with strange numbers showing up. I think it was because the timer interrupt that strobes the 7-seg displays was updating them faster than the data read from the DS1820 was being converted, so the displays were showing the unprocessed values. I tried disabling the timer interrupt right before doing a temperature read, then turning it back on after the conversion was done, and that seemed to take care of the problem with the numbers, but it did add a slight flicker on the displays.

Jim

chris11jed
Posts: 156
Joined: 15 Jun 2011 06:37

Re: Strange display. LM35 to seven segment display

#3 Post by chris11jed » 14 Apr 2014 01:06

Hi Jim Walsh,

Thanks for your reply. 8) I've used the DS1820 Digital temperature sensor before, I'd displayed the temperature on a VFD (Vacuum Fluorescent Display) that I got from an old calculator and had no problems. The VFD uses the exact same process as a 7 LED segment display. I can't remember if I'd stopped the timer for the VFD refresh and then did the temperature read, then restarting the timer though, will have to re-go over that past project. I did notice the slight flicker you mentioned. That was to do with the read I'm sure, but it was so random i never went any further with investigating it.

Still, I'll give your suggestion a go. All in the name of a scientific experiment and all :wink:

Thanks
Chris

chris11jed
Posts: 156
Joined: 15 Jun 2011 06:37

Re: Strange display. LM35 to seven segment display

#4 Post by chris11jed » 14 Apr 2014 02:03

Hi all,

For what it's worth, I'm for the most part satisfied but still wondering...

I followed the suggestion by Jim Walsh. That is stopping TMR0 and taking a read, then restarting TMR0. And that has done the trick. I set the delay in the main program part to some different values (100ms, 200ms, 500ms, 700ms, 1sec and 2sec). I found that the problem of a change or flicking between two values, one the correct value and another unrelated value, still occurred at delay values bellow 700ms. At 2 seconds there wasn't anything and that is good. At 1 second delay the only flickering or bouncing between values happened when I tried heating and cooling the LM35 by touching it for a second or two, however the values were correct, it may have been the LM35 changing temp and not me seeing one correct value and another unrelated value e.g. 22.45 degree C and say bouncing to 17.98 or something. And for the record I haven't seen any flickering resulting from the 7 segment display refresh rate being interrupted, so there's a win :lol: It all looks great!

I am still a bit curious as to why the solution needs to be stopping TMR0 and not some other thing that's happening. I'm thinking that for the ADC read part, if I was to use one of the trim pots as per the ADC example and show the result on the LEDs or the LCD, or I suppose the 7 segment display, would I really need to stop the timer? I don't see this happening in the examples, so that is why I'm still wondering about what's going on. If there's another reason I'd welcome it. But for now as I said I am happy with the current fix.

Anyway here's the code I've got now:

Code: Select all

' Name: LM35_to_7_SD_V02
' Voltage: Vcc = 5V (J5 to 5V)
' PIC: PIC18F45K22
' OSC: - 8MHz HS Crystal
'      - 4xPLL enabled
' S/W: mikroBasic PRO for PIC V.6.0.0
' Dev-Board: easyPIC V7
' Libraries: - ADC
' Sensor: LM35 (J25 to RE1)
' Jumpers: - J25 to RE1 - LM35 to RE1 (AN5)
'          - J5 to 5V - VCC = 5Volts
' Switches: - SW8.1, SW8.2, SW8.3, SW8.4 to "ON"
' Notes: - first write, to test out getting temperature from LM35 to 7 segment display
'        - program is a mash of examples: LM35 Sensor (Temperature measurement) &
'                                         Seven Segment Display (The 'Hello World' example for the Seven Segment Display)
' Problems: - V01
'           - Two numbers are shown. If temperature is around 23.92 Degrees Celsuis, 23.92 is shown for a period of time
'             and then another unrelated number is shown eg. 17.57
'           - These two numbers flicker. At the Delay_ms(2000) the flicker is slow, at Delay_ms(200) the flicker between
'             them is fast. It is easier to see the two numbers as more time is added to the delay.
'           - V02
'           - Stopping TMR0 and taking a read, then restarting TMR0 works. However the flicker between two values remains
'             if the delay in the main program is less than 1 second. That is, one correct value and one unrealted value.
'             Heating up the LM35 by touching it and letting it cool will give a bouncing between two correct values, 
'             until a more stable temperature upon the LM35 is reached.
'           - Set the delay no less than 1 second and for a satisfactory result. No 7 segment refreshing flicker either.
'
program LM35_to_7_SD_V02
'-------------------------------------------------------------------------------Declarations section
const VREF as byte  = 5.00

dim temp_res as word

dim shifter, portd_index as byte
    digit, number as word
    portd_array as byte[4]

sub function mask(dim num as word) as word
  select case num
    case 0
      result= $3F
    case 1
      result= $06
    case 2
      result= $5B
    case 3
      result= $4F
    case 4
      result= $66
    case 5
      result= $6D
    case 6
      result= $7D
    case 7
      result= $07
    case 8
      result= $7F
    case 9
      result= $6F
  end select
end sub

sub procedure interrupt
  LATA = 0                          ' turn off all 7seg displays
  LATD = portd_array[portd_index]   ' bring appropriate value to PORTD
  LATA = shifter                    ' turn on appropriate 7seg. display

  ' move shifter to next digit
  shifter= shifter << 1
  if (shifter > 8) then
    shifter = 1
  end if
  ' increment portd_index
  Inc(portd_index)
  if (portd_index > 3) then
    portd_index = 0                 ' turn on 1st, turn off 2nd 7seg.
  end if
  
  TMR0L = 0                         ' reset TIMER0 value
  TMR0IF_bit = 0                    ' Clear TMR0IF
end sub


main:
'-------------------------------------------------------------------------------Main program
  ANSELA = 0          ' Configure PORTA pins as digital
  ANSELD = 0          ' Configure PORTD pins as digital
  ANSELE = 0x02       ' Configure RE1 pin as analog
  TRISE1_bit = 1      ' Configure RE1 pin as input

  TRISA = 0           ' Configure PORTA as output
  LATA = 0            ' Clear PORTA
  TRISD = 0           ' Configure PORTD as output
  LATD = 0            ' Clear PORTD

  ADC_Init()          ' Initialize ADC

  T0CON = 0xC4        ' Set TMR0 in 8bit mode, assign prescaler to TMR0
  TMR0L = 0           ' clear TMROL
  digit = 0
  portd_index = 0
  shifter = 1

  GIE_bit = 1
  TMR0IE_bit = 1

  while TRUE
    TMR0IE_bit = 0                              ' Stop tmr0
    temp_res = ADC_Get_Sample(6)                ' Get 10-bit results of AD conversion
    number = (temp_res * VREF * 100)/10.240     ' Convert to Celsuis
    TMR0L = 0                                   ' Clear TMROL
    TMR0IE_bit = 1                              ' Set TMR0
    TMR0IF_bit = 0                              ' Clear flag
    
    digit = number / 1000                       ' extract thousands digit
    portd_array[3] = mask(digit)                ' and store it to PORTD array
    digit = (number / 100) mod 10               ' extract hundreds digit
    portd_array[2] = mask(digit) + 0x80         ' and store it to PORTD array, then add a decimal point
    digit = (number / 10) mod 10                ' extract tens digit
    portd_array[1] = mask(digit)                ' and store it to PORTD array
    digit = number mod 10                       ' extract ones digit
    portd_array[0] = mask(digit)                ' and store it to PORTD array

    delay_ms(1000)                              ' wait a time (could be 200ms, 500ms, 1sec or 2sec)
  Wend
end.
Any more thoughts, ideas and alternative solutions are still welcome. Thanks to Jim Walsh again for his input.

Cheers
Chris

User avatar
marina.petrovic
Posts: 2986
Joined: 18 Apr 2013 08:11

Re: Strange display. LM35 to seven segment display

#5 Post by marina.petrovic » 14 Apr 2014 13:20

Hi,

I am glad that you manage to solve your problem and thank you for sharing your code/project with our users.

Like Jim Walsh already explained problem that you describe may occurs because the timer interrupt that you use for displaying data on Seven Segment display was updating the display faster than data that you read from the LM32 is converted.

When you showing data on LCD or LEDs you do not use timer interrupt, you use it only for displaying data on Seven Segment display.

Best regards,
Marina

Jim Walsh
Posts: 55
Joined: 06 Mar 2014 01:38
Location: Pittsburgh, PA

Re: Strange display. LM35 to seven segment display

#6 Post by Jim Walsh » 14 Apr 2014 15:42

Hi Chris,

Cool, I'm glad you got it working. In my case, the reason for disabling the timer interrupt was because the DS1820 can take up to 750 ms to do the temp conversion, and the timer interrupt was firing much faster than that, and that's why the bogus data was being shown on the displays. I don't think the LM35 takes that long, but my guess would be that even in the time it takes to do this...

Code: Select all

temp_res = ADC_Get_Sample(6)                ' Get 10-bit results of AD conversion
    number = (temp_res * VREF * 100)/10.240     ' Convert to Celsuis
...the interrupt was updating the displays fast enough that the 'number' variable had the wrong value when it was sent to be output on the displays.


Peace,
Jim

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

Re: Strange display. LM35 to seven segment display

#7 Post by janni » 14 Apr 2014 17:55

chris11jed wrote:I am still a bit curious as to why the solution needs to be stopping TMR0 and not some other thing that's happening.
Indeed. Even if your problem was caused by lack of synchronization and not by error in calculations, there's no need to stop the timer. Synchronization issues should be solved in a different way that does not disturb the clock of 7-seg control. Best solution is to buffer the result array and synchronize both threads (result forming and display) using flags.

For example

Code: Select all

const VREF as word  = 5000

dim temp_res as word

dim shifter, portd_index as byte
    digit, number as word
    portd_array, buff_array as byte[4]
    ready as bit

sub function mask(dim num as word) as word
  select case num
    case 0
      result= $3F
    case 1
      result= $06
    case 2
      result= $5B
    case 3
      result= $4F
    case 4
      result= $66
    case 5
      result= $6D
    case 6
      result= $7D
    case 7
      result= $07
    case 8
      result= $7F
    case 9
      result= $6F
  end select
end sub

sub procedure interrupt
  LATA = 0                          ' turn off all 7seg displays
  LATD = portd_array[portd_index]   ' bring appropriate value to PORTD
  LATA = shifter                    ' turn on appropriate 7seg. display

  ' move shifter to next digit
  shifter= shifter << 1
  if (shifter > 8) then
    shifter = 1
  end if
  ' increment portd_index
  Inc(portd_index)
  if (portd_index > 3) then
    portd_index = 0                 ' turn on 1st, turn off 2nd 7seg.
    if ready then
     portd_array[0] = buff_array[0]
     portd_array[1] = buff_array[1]
     portd_array[2] = buff_array[2]
     portd_array[3] = buff_array[3]
     ready = 0
    end if
  end if
  'TMR0L = 0                         ' reset TIMER0 value - no need
  TMR0IF_bit = 0                    ' Clear TMR0IF
end sub


main:

'-------------------------------------------------------------------------------Main program
  ANSELA = 0          ' Configure PORTA pins as digital
  ANSELD = 0          ' Configure PORTD pins as digital
  ANSELE = 0x02       ' Configure RE1 pin as analog
  TRISE1_bit = 1      ' Configure RE1 pin as input

  TRISA = 0           ' Configure PORTA as output
  LATA = 0            ' Clear PORTA
  TRISD = 0           ' Configure PORTD as output
  LATD = 0            ' Clear PORTD
  
  for shifter=0 to 3  ' blank display
   portd_array[shifter]=0
  next shifter

  ADC_Init()          ' Initialize ADC

  T0CON = 0xC4        ' Set TMR0 in 8bit mode, assign prescaler to TMR0
  TMR0L = 0           ' clear TMROL
  digit = 0
  portd_index = 0
  shifter = 1

  GIE_bit = 1
  TMR0IE_bit = 1
  ready = 0

  while TRUE
    temp_res = ADC_Get_Sample(6)                ' Get 10-bit results of AD conversion
    number = longword(temp_res * VREF) >> 10    ' Convert to Celsuis

    while ready wend                            ' just precaution
    digit = number / 1000                       ' extract thousands digit
    buff_array[3] = mask(digit)                 ' and store it to PORTD array
    digit = (number / 100) mod 10               ' extract hundreds digit
    buff_array[2] = mask(digit) + 0x80          ' and store it to PORTD array, then add a decimal point
    digit = (number / 10) mod 10                ' extract tens digit
    buff_array[1] = mask(digit)                 ' and store it to PORTD array
    digit = number mod 10                       ' extract ones digit
    buff_array[0] = mask(digit)                 ' and store it to PORTD array
    ready = 1                                   ' result ready

    delay_ms(2000)                              ' wait a time (could be 200ms, 500ms, 1sec or 2sec)
  Wend
end.
BTW, one usually does not present the raw measurement results - ADC is much faster than the useful presentation speed so many measurements may be collected and result prepared before next display change. This way one may get rid of many types of noise, and as regularly made measurements are the simplest filter, it's good to make them in ISR with regular time base (which you already have there).

chris11jed
Posts: 156
Joined: 15 Jun 2011 06:37

Re: Strange display. LM35 to seven segment display

#8 Post by chris11jed » 15 Apr 2014 00:11

Hi janni,

Thank you for your ideas, I find the theory of the buff_array and ready bit (flag) a very good alternative to stopping timer 0. I tried this, to the letter, and had a few issues.

First the change:

Code: Select all

const VREF as word  = 5000

and then the line:

Code: Select all

number = longword(temp_res * VREF) >> 10    ' Convert to Celsuis

These gave a shifted result, eg say 25.67 would read 02.56

So I decided to keep the original:

Code: Select all

const VREF as byte  = 5.00
and:

Code: Select all

number = (temp_res * VREF * 100)/10.240

...just to keep things simple.


However I still got the bouncing between two values :roll: And I got the bounce/flicking between value and weird value when using your code (to the letter) and my original VREF and Celsius conversion code. The same original issue using both methods...
And I really can't explain why, as I said I agree that adding the flag and buffer is a sound idea :? Weird huh :lol:

For the record I tried changing the LM35 with another and my 17.57 strange value changed to 19.48 or something, so I will cancel out the actual device as an issue.

Knowing the two solutions I have gratefully been given by both Jim Walsh and by janni I can say that both are excellent ideas and maybe there's something strange going on in the PIC18F45K22 IC, or ...er...um ghosts :| .

Anyway to all a big Thank you :D
Chris

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

Re: Strange display. LM35 to seven segment display

#9 Post by janni » 15 Apr 2014 01:56

Ah, now I see that I wrongly assumed that you had the signal from LM35 amplified to use full input scale of ADC - hence wrong scaling of my formula. The accuracy with which you display the result, with two digits after the decimal dot, has let me believe it was the case.

The problem I saw with this formula

Code: Select all

number = (temp_res * VREF * 100)/10.240
was that, as temp_res may nominally take values form 0 to 1023, the result in parenthesis is out of range of word variable already for temp_res>131. (And using floating-point arithmetic takes a lot of code.) Naturally, without amplification, the measurement corresponding to, say 23.92 Degrees Celsius is just 49 with 5V reference for ADC - far from the limit. (Unfortunately, this also means that the measurement resolution is around 0.5 degree and accuracy even worse.) Still, this formula

Code: Select all

number = (temp_res * VREF) >> 10
with VREF now declared as 50000 will lead to twice smaller code and several times faster calculations.

As for your original problem, try manual control of the ADC. For example, assign 0x19 to ADCON0 before the main loop, and in the main loop replace

Code: Select all

temp_res = ADC_Get_Sample(6)
with

Code: Select all

    GO_NOT_DONE_bit = 1                         ' start ADC
    while GO_NOT_DONE_bit wend                  ' wait for measurement
    Lo(temp_res) = ADRESL
    Hi(temp_res) = ADRESH
This way ADC input will not be momentarily switched, as it is in ADC_Get_Sample, and possibly the flicker will be gone.

chris11jed
Posts: 156
Joined: 15 Jun 2011 06:37

Re: Strange display. LM35 to seven segment display

#10 Post by chris11jed » 15 Apr 2014 08:12

Hi janni,

Sorry for not mentioning that the LM35 is in the easyPIC V7 designated slot and not being amplified in any way.

Thank you for your help. If I may ask for more help, that would be great. 8)

I've figured out your code and it's short and to the point. Understanding the ADC process, of the LM35 output into the ADC and the ADC outputting a digital value, I can get (0 to 5V = 0 to 1023). The example you gave of 23.92DegreeC equating to 49 I can follow. I can follow the conversion code, 49 times VREF (lets say 5) equals 245, (lets say VREF is 50000) and >>10 equals 2392 Then it's off to the mask sub and shown on the display.

So here's the fun part :wink:

The value I'm getting is a bit off the mark. At 22.5 degree C I get a result of 32.75 using my VREF and Celsius conversion code. With VREF as a word at 5000 and your Celsius conversion I get 00.07 With VREF as longword at 50000 and your Celsius conversion I get 01.47 with the 0 missing the top-left segment. :shock: I also note that the value is fixed. If I heat the LM35 with touch the value does not change. Something isn't quite right. And I would like your thoughts if it's not too much trouble.

There isn't any bouncing between different values.

Here's the code. The comments at the start detail what I'm doing and the results. Note that I've labeled (OLD), (Experiment 1) and (Experiment 2) in the comments by the code where I've tried things out. Again maybe if you see what I'm doing you may see something I'm missing.

Code: Select all

' Name: LM35_to_7_SD_V04
' Voltage: Vcc = 5V (J5 to 5V)
' PIC: PIC18F45K22
' OSC: - 8MHz HS Crystal
'      - 4xPLL enabled
' S/W: mikroBasic PRO for PIC V.6.0.0
' Dev-Board: easyPIC V7
' Libraries: - ADC
' Sensor: LM35 (J25 to RE1)
' Jumpers: - J25 to RE1 - LM35 to RE1 (AN5)
'          - J5 to 5V - VCC = 5Volts
' Switches: - SW8.1, SW8.2, SW8.3, SW8.4 to "ON"
' Notes: - first write, to test out getting temperature from LM35 to 7 segment display
'        - program is a mash of examples: LM35 Sensor (Temperature measurement) &
'                                         Seven Segment Display (The 'Hello World' example for the Seven Segment Display)
' Problems: - V01
'           - Two numbers are shown. If temperature is around 23.92 Degrees Celsuis, 23.92 is shown for a period of time
'             and then another unrelated number is shown eg. 17.57
'           - These two numbers flicker. At the Delay_ms(2000) the flicker is slow, at Delay_ms(200) the flicker between
'             them is fast. It is easier to see the two numbers as more time is added to the delay.
'           - V02
'           - Stopping TMR0 and taking a read, then restarting TMR0 works. However the flicker between two values remains
'             if the delay in the main program is less than 1 second. That is, one correct value and one unrealted value.
'             Heating up the LM35 by touching it and letting it cool will give a bouncing between two correct values,
'             until a more stable temperature upon the LM35 is reached.
'           - Set the delay no less than 1 second and for a satisfactory result. No 7 segment refreshing flicker either.
'           - V03
'           - Following on from the suggested code by janni in the forums, the "ready" flag bit is tried.
'           - Theory is sound but i still get bouncing between values.
'           - In janni's example "const VREF as word = 5000" and "number = longword(temp_res * VREF) >> 10"
'             trying this gave a value shifted to the right by 1, so 25.67 would be 02.56
'             So I have kept the original VREF as byte at 5.00 and the "number = (temp_res * VREF * 100)/10.240" lines
'           - V04
'           - Following on from more suggestions by janni in the forums, new ADC code is tried.
'           - (OLD) Keeps my VREF and Celsius conversion code.
'             Result is 32.75 being shown with no change if LM35 is heated/cooled by touch.
'           - (Experiment 1) Changes VREF as word and equal to 5000, and Celsius conversion to "number = (temp_res * VREF) >> 10"
'             Result is 00.07 being shown with no change if LM35 is heated/cooled by touch.
'           - (Experiment 2) Changes VREF as longword and equal to 50000, and Celsius 
'             conversion to "number = longword(temp_res * VREF) >> 10"
'             Result is 01.47 (0 has top-left segment missing) and no change if LM35 is heated/cooled by touch.
'
program LM35_to_7_SD_V04
'-------------------------------------------------------------------------------Declarations section
'const VREF as byte  = 5.00               ' (OLD)
const VREF as word = 5000                ' Experiment 1
'const VREF as longword = 50000           ' Experiment 2

dim temp_res as word

dim shifter, portd_index as byte
    digit, number as word
    portd_array, buff_array as byte[4]
    ready as bit

sub function mask(dim num as word) as word
  select case num
    case 0
      result= $3F
    case 1
      result= $06
    case 2
      result= $5B
    case 3
      result= $4F
    case 4
      result= $66
    case 5
      result= $6D
    case 6
      result= $7D
    case 7
      result= $07
    case 8
      result= $7F
    case 9
      result= $6F
  end select
end sub

sub procedure interrupt
  LATA = 0                                            ' turn off all 7seg displays
  LATD = portd_array[portd_index]                     ' bring appropriate value to PORTD
  LATA = shifter                                      ' turn on appropriate 7seg. display

  ' move shifter to next digit
  shifter= shifter << 1
  if (shifter > 8) then
    shifter = 1
  end if
  ' increment portd_index
  Inc(portd_index)
  if (portd_index > 3) then
    portd_index = 0                                   ' turn on 1st, turn off 2nd 7seg.
    if ready then                                     ' in case (lm35 value to celsius) conversion not done...
       portd_array[0] = buff_array[0]                 ' ...just display the old value.
       portd_array[1] = buff_array[1]
       portd_array[2] = buff_array[2]
       portd_array[3] = buff_array[3]
       ready = 0
    end if
  end if

  TMR0L = 0                         ' reset TIMER0 value
  TMR0IF_bit = 0                    ' Clear TMR0IF
end sub


main:
'-------------------------------------------------------------------------------Main program
  ANSELA = 0          ' Configure PORTA pins as digital
  ANSELD = 0          ' Configure PORTD pins as digital
  ANSELE = 0x02       ' Configure RE1 pin as analog
  TRISE1_bit = 1      ' Configure RE1 pin as input

  TRISA = 0           ' Configure PORTA as output
  LATA = 0            ' Clear PORTA
  TRISD = 0           ' Configure PORTD as output
  LATD = 0            ' Clear PORTD

  for shifter=0 to 3  ' blank display
      portd_array[shifter]=0
  next shifter

  'ADC_Init()
  ADCON0 = 0x19       ' Enable ADC on AN6 


  T0CON = 0xC4        ' Set TMR0 in 8bit mode, assign prescaler to TMR0
  TMR0L = 0           ' clear TMROL
  digit = 0
  portd_index = 0
  shifter = 1
  ready = 0

  GIE_bit = 1
  TMR0IE_bit = 1

  while TRUE

    'temp_res = ADC_Get_Sample(6)
    
    GO_NOT_DONE_bit = 1                         ' start ADC (ADCON0 bit 1 - GO/notDONE)
    while GO_NOT_DONE_bit wend                  ' wait for measurement
    
    Lo(temp_res) = ADRESL
    Hi(temp_res) = ADRESH
    
    'number = longword(temp_res * VREF) >> 10    ' experiment 2
    number = (temp_res * VREF) >> 10            ' experiment 1
    'number = (temp_res * VREF * 100)/10.240     ' Convert to Celsuis (OLD)
    
    while ready wend                            ' just precaution
    
    digit = number / 1000                       ' extract thousands digit
    buff_array[3] = mask(digit)                 ' and store it to PORTD array
    digit = (number / 100) mod 10               ' extract hundreds digit
    buff_array[2] = mask(digit) + 0x80          ' and store it to PORTD array, then add a decimal point
    digit = (number / 10) mod 10                ' extract tens digit
    buff_array[1] = mask(digit)                 ' and store it to PORTD array
    digit = number mod 10                       ' extract ones digit
    buff_array[0] = mask(digit)                 ' and store it to PORTD array
    ready = 1                                   ' result done

    delay_ms(2000)                              ' wait a time (could be 200ms, 500ms, 1sec or 2sec)
  Wend
end.
Thanks
Chris

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

Re: Strange display. LM35 to seven segment display

#11 Post by janni » 15 Apr 2014 12:55

The reason why you've got unchanging input is removal of ADC_Init - I didn't give you a code replacement for that so it should have stayed.

Experiment 1, as we have already established, has little sense. Experiment 2 does not require VREF to be declared longword as 50000 is within word range but that's not important. The strange results you obtained are caused by too large values obtained from ADC conversion. Without ADC_Init the result is left-justified and conversion clock too fast.

There's not much to replacing ADC_Init, if you want to do it. All it takes is to initialize ADCON1 and ADCON2. (ADCON1 is actually 0 by default, but it's always safer to have explicit initialization.)

ADCON1 = 0
ADCON2 = 0x96 ' Fosc/64, right justified

These settings are optimized for processor clock of 32MHz and should actually be preferable to what ADC_Init presets, i.e. the internal oscillator clock.

chris11jed
Posts: 156
Joined: 15 Jun 2011 06:37

Re: Strange display. LM35 to seven segment display

#12 Post by chris11jed » 15 Apr 2014 23:53

Hi janni,

I've been reding the ADC section of the datasheet for PIC18F45K22 over and over. Can't see the trees for that dang forrest :lol:

The code now stands as such:

Code: Select all

' Name: LM35_to_7_SD_V05
' Voltage: Vcc = 5V (J5 to 5V)
' PIC: PIC18F45K22
' OSC: - 8MHz HS Crystal
'      - 4xPLL enabled
' S/W: mikroBasic PRO for PIC V.6.0.0
' Dev-Board: easyPIC V7
' Libraries: - ADC
' Sensor: LM35 (J25 to RE1)
' Jumpers: - J25 to RE1 - LM35 to RE1 (AN5)
'          - J5 to 5V - VCC = 5Volts
' Switches: - SW8.1, SW8.2, SW8.3, SW8.4 to "ON"
' Notes: - first write, to test out getting temperature from LM35 to 7 segment display
'        - program is a mash of examples: LM35 Sensor (Temperature measurement) &
'                                         Seven Segment Display (The 'Hello World' example for the Seven Segment Display)
' Problems: - V01
'           - Two numbers are shown. If temperature is around 23.92 Degrees Celsuis, 23.92 is shown for a period of time
'             and then another unrelated number is shown eg. 17.57
'           - These two numbers flicker. At the Delay_ms(2000) the flicker is slow, at Delay_ms(200) the flicker between
'             them is fast. It is easier to see the two numbers as more time is added to the delay.
'           - V02
'           - Stopping TMR0 and taking a read, then restarting TMR0 works. However the flicker between two values remains
'             if the delay in the main program is less than 1 second. That is, one correct value and one unrealted value.
'             Heating up the LM35 by touching it and letting it cool will give a bouncing between two correct values,
'             until a more stable temperature upon the LM35 is reached.
'           - Set the delay no less than 1 second and for a satisfactory result. No 7 segment refreshing flicker either.
'           - V03
'           - Following on from the suggested code by janni in the forums, the "ready" flag bit is tried.
'           - Theory is sound but i still get bouncing between values.
'           - In janni's example "const VREF as word = 5000" and "number = longword(temp_res * VREF) >> 10"
'             trying this gave a value shifted to the right by 1, so 25.67 would be 02.56
'             So I have kept the original VREF as byte at 5.00 and the "number = (temp_res * VREF * 100)/10.240" lines
'           - V04
'           - Following on from more suggestions by janni in the forums, new ADC code is tried.
'           - (OLD) Keeps my VREF and Celsius conversion code.
'             Result is 32.75 being shown with no change if LM35 is heated/cooled by touch.
'           - (Experiment 1) Changes VREF as word and equal to 5000, and Celsius conversion to "number = (temp_res * VREF) >> 10"
'             Result is 00.07 being shown with no change if LM35 is heated/cooled by touch.
'           - (Experiment 2) Changes VREF as longword and equal to 50000, and Celsius
'             conversion to "number = longword(temp_res * VREF) >> 10"
'             Result is 01.47 (0 has top-left segment missing) and no change if LM35 is heated/cooled by touch.
'           - V05
'           - Following on from more suggestions by janni in the forums, ADC_Init() is kept, or, manually replaced.
'           - VREF as word at 50000, ADC is manually set up in ADCON0, ADCON1 and ADCON2 to reflect PIC settings.
'           - Celsius conversion is longword(temp_res * VREF) to number.
'           - Finally success!!!! Thank you janni for your patience, help and time. Temperature reacts to 
'             heating/cooling by touch and no strange values bouncing/flicking on the display.
'
program LM35_to_7_SD_V05
'-------------------------------------------------------------------------------Declarations section
const VREF as word = 50000

dim temp_res as word

dim shifter, portd_index as byte
    digit, number as word
    portd_array, buff_array as byte[4]
    ready as bit

sub function mask(dim num as word) as word
  select case num
    case 0
      result= $3F
    case 1
      result= $06
    case 2
      result= $5B
    case 3
      result= $4F
    case 4
      result= $66
    case 5
      result= $6D
    case 6
      result= $7D
    case 7
      result= $07
    case 8
      result= $7F
    case 9
      result= $6F
  end select
end sub

sub procedure interrupt
  LATA = 0                               ' turn off all 7seg displays
  LATD = portd_array[portd_index]        ' bring appropriate value to PORTD
  LATA = shifter                         ' turn on appropriate 7seg. display

  ' move shifter to next digit
  shifter= shifter << 1
  if (shifter > 8) then
    shifter = 1
  end if
  ' increment portd_index
  Inc(portd_index)
  if (portd_index > 3) then
    portd_index = 0                      ' turn on 1st, turn off 2nd 7seg.
    if ready then                        ' in case (lm35 value to celsius) conversion not done...
       portd_array[0] = buff_array[0]    ' ...just display the old value.
       portd_array[1] = buff_array[1]
       portd_array[2] = buff_array[2]
       portd_array[3] = buff_array[3]
       ready = 0
    end if
  end if

  TMR0L = 0                              ' reset TIMER0 value
  TMR0IF_bit = 0                         ' Clear TMR0IF
end sub


main:
'-------------------------------------------------------------------------------Main program
  ANSELA = 0                             ' Configure PORTA pins as digital
  ANSELD = 0                             ' Configure PORTD pins as digital
  ANSELE = 0x02                          ' Configure RE1 pin as analog
  TRISE1_bit = 1                         ' Configure RE1 pin as input

  TRISA = 0                              ' Configure PORTA as output
  LATA = 0                               ' Clear PORTA
  TRISD = 0                              ' Configure PORTD as output
  LATD = 0                               ' Clear PORTD

  for shifter=0 to 3                     ' blank display
      portd_array[shifter]=0
  next shifter

  ADCON0 = 0x19                          ' Enable ADC on AN6
  ADCON1 = 0                             ' CCP5 special trigger, AVdd and AVss voltage reference
  ADCON2 = 0x96                          ' Fosc/64, right justified



  T0CON = 0xC4                           ' Set TMR0 in 8bit mode, assign prescaler to TMR0
  TMR0L = 0                              ' clear TMROL
  digit = 0                              ' get 7-SD variables ready
  portd_index = 0                        '
  shifter = 1                            '
  ready = 0                              '

  GIE_bit = 1                            ' TMR0 flags
  TMR0IE_bit = 1                         '

  while TRUE

    GO_NOT_DONE_bit = 1                         ' start ADC (ADCON0 bit 1 - GO/notDONE)
    while GO_NOT_DONE_bit wend                  ' wait for measurement

    Lo(temp_res) = ADRESL
    Hi(temp_res) = ADRESH

    number = longword(temp_res * VREF) >> 10    ' Celsius Conversion

    while ready wend                            ' Just precaution

    digit = number / 1000                       ' extract thousands digit
    buff_array[3] = mask(digit)                 ' and store it to PORTD array
    digit = (number / 100) mod 10               ' extract hundreds digit
    buff_array[2] = mask(digit) + 0x80          ' and store it to PORTD array, then add a decimal point
    digit = (number / 10) mod 10                ' extract tens digit
    buff_array[1] = mask(digit)                 ' and store it to PORTD array
    digit = number mod 10                       ' extract ones digit
    buff_array[0] = mask(digit)                 ' and store it to PORTD array
    ready = 1                                   ' result done

    delay_ms(1000)                              ' wait a time (could be 200ms, 500ms, 1sec or 2sec)
  Wend
end.
The code works quite well I'm pleased to say. :wink:

I tested out the delay time at the end, at 300ms there's a bit of flicking between values BUT those values are very similar and I believe just the changing temperature the LM35 is picking up.
At 750ms this flicker is virtually gone. And it is happily gone at 1 second.
No bouncing/flicking from one correct and one strange value at all. 8)
Touch heating and then cooling work well. :D

I thank you very much janni, for your time, patience and all the suggestions/help. 8) 8) 8)

Chris

janni
Posts: 5373
Joined: 18 Feb 2006 13:17
Contact:

Re: Strange display. LM35 to seven segment display

#13 Post by janni » 16 Apr 2014 00:26

You're welcome :D . BTW, if you want the results to be stable, some averaging should do it. For example, you could replace the single measurement

Code: Select all

    GO_NOT_DONE_bit = 1                         ' start ADC (ADCON0 bit 1 - GO/notDONE)
    while GO_NOT_DONE_bit wend                  ' wait for measurement

    Lo(temp_res) = ADRESL
    Hi(temp_res) = ADRESH
with

Code: Select all

    temp_res = 0                                ' collect 16 measurements
    for i=1 to 16
      GO_NOT_DONE_bit = 1                       ' start ADC
      while GO_NOT_DONE_bit wend                ' wait for measurement
      temp_res = temp_res + ADRESL
      delay_us(4)
    next i
(note that the higher byte, ADRESH, is not used, as it should be zero for temperatures of interest).

To keep the same calculation formula, you'll need to modify VREF declaration to

Code: Select all

const VREF as word = 50000 >> 4
And you'll need to declare the loop counter:

Code: Select all

dim i as byte

chris11jed
Posts: 156
Joined: 15 Jun 2011 06:37

Re: Strange display. LM35 to seven segment display

#14 Post by chris11jed » 16 Apr 2014 07:19

Hi janni,

I'll try out the averaging of reads code you have provided soon. For the next few days I'm going to be deprived of any electrical device. Yep, going away on a camping trip with some friends :D

But as soon as I'm back I'll give it a go. :wink: :)

I'm glad you could help me nut out the flickering problems before I go away. If I'd gone, with such a silly issue bouncing around in my head, into the wilderness I think I would have driven a few of my friends and their kids mad :lol:

Thank you again, and these forums :D

Chris

Post Reply

Return to “mikroBasic PRO for PIC General”