PDA

View Full Version : LED 'Dot' Bargraph Issues - Newbie Code!



SOTASOTA
- 11th February 2013, 02:20
Hello all.
I have a PIC16F88 with ADC on PortA.0. I am measuring the voltage on a supply rail from 0-5VCD. I have a 35 LED display (3mm LEDs each) that is wired in a 7x5 matrix. I only want one LED to come on at any one time. Hence a moving 'dot'. The code works for the most part, but I have a minor annoyance. If I slowly raise the voltage I am measuring, there will be certain places on the dot display that has 2 x LEDs coming ON and flickering. It is like the ADC is just between bits and oscillates between two thus displaying 2 LEDs instead of one.

The code most certainly can be re-written a whole lot better and more efficiently. In my code, I simply look for values of adval and turn on the appropriate LED. On each LED turn-on, I also run a subroutine that first sets all the LEDs OFF. The ADC is set up as 8 bit. I know that is way more than what I need for resolution. 6 bit would probably work just fine.

Any help including a re-write would be greatly appeciated!

The code:




'Initialize variables PIC16F88
#CONFIG
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_OFF & _PWRTE_OFF & _WDT_OFF & _INTRC_IO
__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
#ENDCONFIG

OSCCON=$60 'Set Internal Oscillator to 4MHz

DEFINE OSC 4
DEFINE ADC_BITS 8 ' 8 bit A/D Conversion
DEFINE ADC_CLOCK 3 ' Set clock source (rc = 3)
DEFINE ADC_SAMPLEUS 50 ' 50 uS A/D sample time

ANSEL = %00000001 ' Set pin (AN0) to analog input, the rest to digital
ADCON0 = %11000000 ' Set up A/D converter - Right Just., VDD REF., CH 0, ON
CMCON = 7

adval var word

pause 1000 'settle

high PortA.1 'Col1
high PortA.2 'Col2
high PortA.3 'Col3
high PortA.4 'Col4
high PortB.0 'Col5

low PortB.1 'Row1
low PortB.2 'Row2
low PortB.3 'Row3
low PortB.4 'Row4
low PortB.5 'Row5
low PortB.6 'Row6
low PortB.7 'Row7

Col1 var PortA.1
Col2 var PortA.2
Col3 var PortA.3
Col4 var PortA.4
Col5 var PortB.0

Row1 var PortB.1
Row2 var PortB.2
Row3 var PortB.3
Row4 var PortB.4
Row5 var PortB.5
Row6 var PortB.6
Row7 var PortB.7

mainloop:

ADCIN 0, adval

if adval <5 then
gosub ResetLEDs
goto mainloop 'Zero, all LEDs OFF
endif

if adval >4 and adval <13 then
'Led2
gosub ResetLEDs
Row1 = 1
Col1 = 0
endif

if adval >12 and adval <21 then
'Led3
gosub ResetLEDs
Row2 = 1
Col1 = 0
endif

if adval >20 and adval <29 then
'Led4
gosub ResetLEDs
Row3 = 1
Col1 = 0
endif

if adval >28 and adval <36 then
'Led5
gosub ResetLEDs
Row4 = 1
Col1 = 0
endif

if adval >35 and adval <44 then
'Led6
gosub ResetLEDs
Row5 = 1
Col1 = 0
endif

if adval >43 and adval <52 then
'Led7
gosub ResetLEDs
Row6 = 1
Col1 = 0
endif

if adval >51 and adval <59 then
'Led8
gosub ResetLEDs
Row7 = 1
Col1 = 0
endif

if adval >58 and adval <67 then
'Led9
gosub ResetLEDs
Row1 = 1
Col2 = 0
endif

if adval >66 and adval <75 then
'Led10
gosub ResetLEDs
Row2 = 1
Col2 = 0
endif

if adval >74 and adval <83 then
'Led11
gosub ResetLEDs
Row3 = 1
Col2 = 0
endif

if adval >82 and adval <90 then
'Led12
gosub ResetLEDs
Row4 = 1
Col2 = 0
endif

if adval >89 and adval <98 then
'Led13
gosub ResetLEDs
Row5 = 1
Col2 = 0
endif

if adval >97 and adval <106 then
'Led14
gosub ResetLEDs
Row6 = 1
Col2 = 0
endif

if adval >105 and adval <114 then
'Led15
gosub ResetLEDs
Row7 = 1
Col2 = 0
endif

if adval >113 and adval <121 then
'Led16
gosub ResetLEDs
Row1 = 1
Col3 = 0
endif

if adval >120 and adval <129 then
'Led17
gosub ResetLEDs
Row2 = 1
Col3 = 0
endif

if adval >128 and adval <137 then
'Led18
gosub ResetLEDs
Row3 = 1
Col3 = 0
endif

if adval >136 and adval <144 then
'Led19
gosub ResetLEDs
Row4 = 1
Col3 = 0
endif

if adval >143 and adval <152 then
'Led20
gosub ResetLEDs
Row5 = 1
Col3 = 0
endif

if adval >151 and adval <160 then
'Led21
gosub ResetLEDs
Row6 = 1
Col3 = 0
endif

if adval >159 and adval <168 then
'Led22
gosub ResetLEDs
Row7 = 1
Col3 = 0
endif

if adval >167 and adval <175 then
'Led23
gosub ResetLEDs
Row1 = 1
Col4 = 0
endif

if adval >174 and adval <183 then
'Led24
gosub ResetLEDs
Row2 = 1
Col4 = 0
endif

if adval >182 and adval <191 then
'Led25
gosub ResetLEDs
Row3 = 1
Col4 = 0
endif

if adval >190 and adval <199 then
'Led26
gosub ResetLEDs
Row4 = 1
Col4 = 0
endif

if adval >198 and adval <206 then
'Led27
gosub ResetLEDs
Row5 = 1
Col4 = 0
endif

if adval >205 and adval <214 then
'Led28
gosub ResetLEDs
Row6 = 1
Col4 = 0
endif

if adval >213 and adval <222 then
'Led29
gosub ResetLEDs
Row7 = 1
Col4 = 0
endif

if adval >221 and adval <229 then
'Led30
gosub ResetLEDs
Row1 = 1
Col5 = 0
endif

if adval >228 and adval <237 then
'Led31
gosub ResetLEDs
Row2 = 1
Col5 = 0
endif

if adval >236 and adval <245 then
'Led32
gosub ResetLEDs
Row3 = 1
Col5 = 0
endif

if adval >244 and adval <253 then
'Led33
gosub ResetLEDs
Row4 = 1
Col5 = 0
endif

if adval >252 then
'Led34
gosub ResetLEDs
Row5 = 1
Col5 = 0
endif


GoTo mainloop


ResetLEDs:
high PortA.1 'Col1
high PortA.2 'Col2
high PortA.3 'Col3
high PortA.4 'Col4
high PortB.0 'Col5

low PortB.1 'Row1
low PortB.2 'Row2
low PortB.3 'Row3
low PortB.4 'Row4
low PortB.5 'Row5
low PortB.6 'Row6
low PortB.7 'Row7
return

End

Chirpy
- 11th February 2013, 06:05
maybe take like 10 values and average them, then display the average instead? it may stop the jumpy LEDs a little.

SOTASOTA
- 12th February 2013, 14:00
Good idea Chirpy! I will give it a try....

Darrel Taylor
- 12th February 2013, 21:18
When you find yourself writing the same section of code over and over with only changes to the values ....
It usually means you are doing it the hardest and least efficient way. :eek:

Instead of a bunch of IF/THEN's, use a little math, and a couple FOR/NEXT loops.

'Initialize variables PIC16F88
#CONFIG
__CONFIG _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_OFF & _MCLR_OFF & _PWRTE_OFF & _WDT_OFF & _INTRC_IO
__CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF
#ENDCONFIG

DEFINE OSC 4
OSCCON=$60 'Set Internal Oscillator to 4MHz

DEFINE ADC_BITS 10 ' 10 bit A/D Conversion
DEFINE ADC_CLOCK 3 ' Set clock source (rc = 3)
DEFINE ADC_SAMPLEUS 50 ' 50 uS A/D sample time

ANSEL = %00000001 ' Set pin (AN0) to analog input, the rest to digital
ADCON1.7 = 1 ' Right Justify A/D results

LED VAR BYTE
Row VAR BYTE
Col VAR BYTE
Idx VAR BYTE
Row_Pin VAR BYTE
Col_Pin VAR BYTE
adval VAR WORD

LEDcount CON 35
Spaces CON LEDcount + 1
Margin CON 1023 / Spaces

;-------------------------------------------------------------------------------
Main:
ADCIN 0, adval

LED = adval * LEDcount ; Determine which LED to light
LED = DIV32 (1023 - Margin) - 1

Row = -1 ; Turn OFF all dots
Col = -1 ; to prevent ghosting
GOSUB ShowDot

Row = LED // 7 ; convert LED to Row/Column
Col = LED / 7
GOSUB ShowDot
GOTO Main

;-------------------------------------------------------------------------------
ShowDot:
FOR Idx = 0 To 6 ; Set Row's
LOOKUP Idx,[1,2,3,4,5,6,7],Row_Pin
IF Idx = Row THEN
HIGH Row_Pin
ELSE
LOW Row_Pin
ENDIF
NEXT Idx

FOR Idx = 0 TO 4 ; Set Column's
LOOKUP Idx,[9,10,11,12,0],Col_Pin
IF Idx = Col THEN
LOW Col_Pin
ELSE
HIGH Col_Pin
ENDIF
NEXT Idx
RETURN

And, for the two lights on at a time ... you may want to add this ...
http://www.picbasic.co.uk/forum/content.php?r=248-Analog-to-digital-averaging-with-hysteresis

SOTASOTA
- 13th February 2013, 15:48
Wow, I cannot thank you enough Darrel! What elegant and efficient code! The difference between a newbie and a master. :-)
I will study the code and get familiar with LOOKUP (never used that fuction before) as well as understanding how it all works.

For example, setting Row = -1, I never knew you could set to a minus value. Don't really know what that is.

Brilliant code. Thanks a ton!!

SOTASOTA
- 13th February 2013, 20:35
Hi Darrel,
I tried the averaging routine but could not get it to work. In the code is has:




GOTO OverAverage
' -=-=-=-=-=-= Average Analog values -=-=-=-=-=-=-=-=-=-=



however, there is no OverAverage routine. Also, I replaced the word "Value" with "adval", but that did nothing.

Sorry, I am just not seeing this.
Thanks!

Darrel Taylor
- 13th February 2013, 20:48
The OverAverage label is at the end of the code in that thread.
Maybe you didn't copy enough of it.

SOTASOTA
- 14th February 2013, 05:06
Hi Darrel,
Here is the entire code on that link:



'************************************************* ***************
'* Name : Average_Hyst.pbp *
'* Author : Darrel Taylor *
'* Date : 7/19/2005 *
'* Version : 3.0 *
'* Notes : Modified to use Hysteresis *
'* : Use as an INCLUDE file *
'************************************************* ***************
' This routine will keep a "Running Average" of 10-bit Analog Values
' Instead of adding a bunch of values together and then dividing by the number
' of samples, it averages each sample into the final result immediately.
' This eliminates the need for 32 bit math.
' To allow it to "Catch up" to large changes, set the FAspread to an
' acceptable range.
' Simply place the new number in VALUE and GoSub Average
' The Average (with Hysterisis) will be returned into the same variable VALUE

'---- [ User Options ] ---------------------------------------------------------
HystLevel CON 7 ' 7 = .7 of the Least Significant Digit
' Valid HystLevel is from 6 to 13
AvgCount CON 16 ' = Number of samples to average. For best response
' times, keep AvgCount as small as you can
FAspread CON 50 ' = Fast Average threshold +/-
' FAspread should be larger than the worst possible
' noise in the A/D conversion.
'-------------------------------------------------------------------------------

AVGchanged VAR BIT ' 1 indicates that the Average Value has changed
' you have to clear this bit after using it
ADavg VAR WORD ' Stores the current Average
ADhyst VAR WORD ' Stores the current Value for Hysterisis
Value VAR WORD ' Input / Output variable to be averaged
spread CON FAspread * 10 ' adjust spread *10 for Hyst.

GOTO OverAverage
' -=-=-=-=-=-= Average Analog values -=-=-=-=-=-=-=-=-=-=
Average:
Value = Value * 10 ' Scale value up for hysterisis
IF Value = ADavg Then NoChange ' if they're the same, nothing to do
IF ABS (Value - ADavg) > spread OR Value < AvgCount Then FastAvg
IF ABS (Value - ADavg) < AvgCount Then RealClose
ADavg = ADavg - (ADavg/AvgCount) ' Subtract 1 samples worth
ADavg = ADavg + (Value/AvgCount) ' Add in the new sample portion
GoTo AVGok
FastAvg: ' Catch up to the changing value
ADavg = Value ' Set Average to the current value
GoTo AVGok
RealClose: ' Reduce the sample size when
ADavg = ADavg - (ADavg/(AvgCount/4)) ' the average and the sample are
ADavg = ADavg + (Value/(AvgCount/4)) ' "Real Close"
AVGok:
IF ABS (ADavg - ADhyst) > HystLevel then ' If it changed > HystLevel +/-
ADhyst = ((ADavg + 5) / 10) * 10 ' Round ADavg to get new Value
AVGchanged = 1 ' Indicate that Average changed
ENDIF
NoChange:
Value = ADhyst / 10 ' Scale the result down
Return

OverAverage:
'---------------------------------------------------------------


It may not have been put in there properly. Can you click the link and see what you get?

From the code, it jumps over the section staring with:
' -=-=-=-=-=-= Average Analog values -=-=-=-=-=-=-=-=-=-=

It does not seem to make sense to me.

Thanks. :-)

HenrikOlsson
- 14th February 2013, 06:10
Hi,
The label OverAverage is right at the bottom of the code you just posted.
Usually you include the file somewhere around the top of your program. When the program starts from the beginning it eventually reaches the code which is in the include file. If you don't want it to execute the code at that point you must jump over it and that's exactly what's happening here.

Then, when you actually WANT to execute the code from within your main routine or whatever you GOSUB Average, that way it will enter the code after the GOTO OverAverage and execute the code until it hits the RETURN which returns the execution to the place it was called from.

/Henrik.

Darrel Taylor
- 14th February 2013, 06:30
The averaging code is an INCLUDE file.

Save it to a file called "Average_Hyst.pbp".
Put the file in the same folder as your project, or the PBP folder.

Then at the top of the code in post #4, add this line.

INCLUDE "Average_Hyst.pbp"

The GOTO OverAverage line just jumps over the code, making sure execution doesn't "Fall Into" the subroutine without being called by a GOSUB.

Then, change these lines to use the "Value" variable, instead of adval.

Main:
ADCIN 0, Value
GOSUB Average

LED = Value * LEDcount ; Determine which LED to light
LED = DIV32 (1023 - Margin) - 1


Edit: Whoops, sorry Henrik.
Took me an hour to get his darn post to go through.
The forums going up and down like an elevator.
Ya beat me to it.

SOTASOTA
- 15th February 2013, 18:52
OK, that is what I thought but could not get my head around it. Too many late nights!
Thanks, I will give it a shot and see if it works.
I sure learn a great deal on these forums. I have to start using the LOOKUP command more...very useful!!

SOTASOTA
- 15th February 2013, 19:25
Alrighty, I just implemented the code and for the most part it works real well! Thanks!
I still get flickering on a single LED now at some in-between value.

Not sure it this can be gotten rid of and have a nice solid LED. I have these values in:

'---- [ User Options ] ---------------------------------------------------------
HystLevel CON 13 ' 7 = .7 of the Least Significant Digit
' Valid HystLevel is from 6 to 13
AvgCount CON 32 ' = Number of samples to average. For best response
' times, keep AvgCount as small as you can
FAspread CON 100 ' = Fast Average threshold +/-
' FAspread should be larger than the worst possible
' noise in the A/D conversion.
'-------------------------------------------------------------------------------