• Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

The use of an LCD on port B, together with an analog output RB0 (AN12)

Status
Not open for further replies.

Winch

Member
Hello,
On port B of a 16F883 I connected a LCD. Now I want to use the analog output of RB0 (AN12).
I do not get the correct digital result on my screen? See below the code I use.
With ANSELH and TRISB I first make the right zettings, then I read Adcin 12 and write this to AN12.
However, the result is a too high digital number?
It must be possible to use together a display and an analog input? Or not?
Has anyone done this before?

Code:
While startbutton = 3
    Gosub timer
    ADCON0 = %01110011  'Channel 02 (AN0/AN3) as a/d selected (CHS2:CHS0), ADCS1&ADCS0 bit are selected as Fosc/8
    Adcin 4, an4  'read AN4 and write value to AN4
  
    If timer1 = 5 Then
    timer1 = 0
        ANSELH = %00010000
        TRISB = %00000001
        Adcin 12, an12  'read AN12 and write value to AN12
        ANSELH = %00000000
        TRISB = %00000000
    Endif
  
    Lcdcmdout LcdLine1Pos(1)  'write from first position
    Lcdout "C      v"  'write the word "Colt" and "volt" on the screen!
    Lcdcmdout LcdLine1Pos(5)  'write from position 5
    Lcdout #an4  'just to see on the screen what the digital number is!

    Lcdcmdout LcdLine2Pos(1)  'write from first position
    Lcdout "W      v"  'write the word "Warm" and "volt" on the screen!
    Lcdcmdout LcdLine2Pos(5)  'write from position 5
    Lcdout #an12  'just to see on the screen what the digital number is!

    While select_sw2 = 0
        startbutton = 0
        timer1 = 0
        Lcdcmdout LcdClear
        selectbutton = 4
        Gosub selectbutton4
    Wend

WaitMs 100
Wend
 
Last edited:

Dr_Doggy

Well-Known Member
Hi,
does the lcd code work, does it display c&v ?
a quick glance suggests that you are all over the place with the analog pins, also i think you have confused what ANSELH and ADCON0 do.
Not sure how the %operator works ther nor have i ever heard of Adcin, but that may just be me!


Here is function that reads analog pins nicely:

Code:
unsigned int read_analog(unsigned char chnl)
{
    unsigned int ret= 0;
    ADCON0bits.ADON = 1;    //Turns on ADC module
    ADCON0bits.CHS = chnl; //Selects channel  
    ADCON0bits.GO = 1;   //Starts ADC conversion
    while (ADCON0bits.nDONE) continue;   //wait till ADC conversion is over   
    ret=((ADRESH<<8) + ADRESL); //Merging the MSB and LSB  
    ADCON0bits.ADON = 0;   
    return ret;
}
dont forget to set it up , here is example but you will want to modify it properly to your own settings:
Code:
     // set pin direction
    TRISA = 0b11111111;    PORTA = 0b00000000;
    TRISB = 0b11111111;    PORTB = 0b00000000;
    TRISC = 0b11111111;    PORTC = 0b00000000; 

    ANSEL = 0b11111111;       // select all analog pins to read
    CMCON0= 0b00000000;  // comparitors off
    ADCON1bits.ADCS  = 6;   //Selecting the clk division factor = FOSC/ 
    ADCON0bits.ADFM = 1;    //Result right justified
    ADCON0bits.VCFG = 0;
 

sagor1

Active Member
Silly question, but are AN4 and AN12 defined as words? If defined as bytes, you will get strange values from a 10 bit A/D reading.
You don't show that part of the code. Also, one would assume RB0 is dedicated to reading analog input, so why switch it on and off over and over again? Just define it at the start of the code.
If trying to use it as digital then analog at times, external circuits may cause unpredictable readings - not a wise idea.
 

Winch

Member
This is the code I use by declaring the variable. As you see I want to use 6 analog inputs.
But the AN12 is the RB0 which is also used for the LCD.
The fact that one time the ANSELH and TRISB is zet as input and later as an output is with the thought that I can read the analog input and write the value to "DIM an12 as Word".

I'm actually assume that you could measure the analog input in a snapshot, and show the result on the LCD!
P.S. AN1 up to AN4 are "port A" related in the 16F883.

Code:
Dim an0 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN0)
Dim an1 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN1)
Dim an2 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN2)
Dim an3 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN3)
Dim an4 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN4)
Dim an12 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN12)
 
Last edited:

Pommie

Well-Known Member
Most Helpful Member
Your code looks fine but you may be getting spurious results due to the extra capacitance caused by the LCD. Try making the acquisition time longer.

Mike.
 

Winch

Member
Thanks for your comments!

Today, I first removed all the extra ballast out of the code.
This gives a better overview!
I try to make the acquisition time longer but it does not improve! I tried that with a simple 500 milliseconds! (see below)
I am still convinced that it should be able to work, But just do not know where to look for the right answer?

Both anaolg inputs have an equal height of DC voltage in the test, but the result will give an uneven number.
AN4 behaves correctly, at 0 volt is the digital number also 0 and when the voltage rises, then the digital number also.
AN12 starts with a high number it starts at 0 volts with the number 856 when the voltage rises, then the digital number rises also but not in the same ratio!

In other words, it works but not in the correct proportions!

Does anyone have a suggestion?

Code:
'DEVICE PIC16F883

Define CONFIG1 = 0x23e4
Define CONFIG2 = 0x3eff
Define CLOCK_FREQUENCY = 8

OSCCON = %01110101  'Oscillator control (datasheet page 66)

Define LCD_BITS = 4
Define LCD_DREG = PORTB
Define LCD_DBIT = 0
Define LCD_RSREG = PORTB
Define LCD_RSBIT = 4
Define LCD_EREG = PORTB
Define LCD_EBIT = 5
Define LCD_RWREG = PORTB
Define LCD_RWBIT = 0

AllDigital  'alle ports digital

'port names
Symbol led1 = PORTA.4  'control LED for microcontroller
Symbol select_sw2 = PORTC.2  'pushbutton for function "Select"
Symbol start_sw3 = PORTC.1  'pushbutton for function "Start"

'declaring variable
Dim an0 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN0)
Dim an1 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN1)
Dim an2 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN2)
Dim an3 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN3)
Dim an4 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN4)
Dim an12 As Word  'voltage as variable "word (0 to 65535)"(here the measured analog value on input AN12)

'declaring variable for the TIMER
Dim var_timer As Byte  'variable "TIMER" value "As Byte"
Dim timer_limite As Word  '"TIMER" limite "As Byte"
Dim timer1 As Byte  'variable timer1 setting "As byte 0 to 255"

OPTION_REG.7 = 0  'RBPU must be cleared for individual pull-up 'PORTB' pull-ups
OPTION_REG.INTEDG = 0  'Interrupt on falling edge of GP2/INT pin
OPTION_REG.T0CS = 0  'Internal instruction cycle clock (CLKOUT)
OPTION_REG.T0SE = 0  'Increment on low-to-high transition on GP2/T0CKI pin
OPTION_REG.PSA = 0  'Prescaler is assigned to the TIMER0 module
OPTION_REG.PS2 = 1  'Set the prescaler
OPTION_REG.PS1 = 1  'to increment TMR0
OPTION_REG.PS0 = 0  'every 128th instruction cycle

'A gate set to the desired value
'76543210
PORTA = %00000000  'make all ports "PORTA" low
TRISA = %11101111  '1 = set for input RA5,RA3,RA2,RA1,RA0. 0 = set for output RA4,!
ANSEL = %00011111  '1 = Analog input. AN0,AN1,AN2,AN3,AN4 are assigned as analog input.

'B gate set to the desired value
'76543210
PORTB = %00000000  'make all ports "PORTB" low
TRISB = %00000000  '0 = set for output / all ports are set for output!

'C gate set To the desired value
'76543210
PORTC = %00000000  'make all ports "PORTC" low
TRISC = %11111111  'port RC1 & RC2 (CCP1 & CCP2) are set for output

'converts the analog input to the desired value
'76543210
ADCON0 = %01001011  'Channel 02 (AN0/AN3) as a/d selected (CHS2:CHS0), ADCS1&ADCS0 bit are selected as Fosc/8
ADCON1 = %00000000  'configured as analog inputs (see datasheet page 103)

'The INTCON register is a readable and writable register, which contains the various Enable And flag bits For TMR0 register overflow,
'GPIO port change And external GP2 / int pin interrupts.
INTCON.GIE = 1  'Enables all unmasked interrupts
INTCON.T0IE = 1  'Enables the TMR0 interrupt (TMR0 Overflow Interrupt Enable bit)
INTCON.T0IF = 1  'TMR0 register has overflowed (must be cleared in software)
TMR0 = 0  'Clear TMR0 initially, (reset de timer0 module register)

Lcdinit 0  'no cursor
WaitMs 250

led1 = 0  'PCB control LED green
an0 = 0
an1 = 0
an2 = 0
an3 = 0
an4 = 0
an12 = 0

timer1 = 0  'set timer1 to 0

var_timer = 0  'variable waarde van de timer is 0
timer_limite = 12  'intern klok 8mhz/4 = 2mhz (2mhz/(128*128)) = 122 Hz pulse every 1 second

main:  'endless loop

    Adcin 4, an4  'read AN4 from port-A and write value to "DIM AN4 AS WORD"
   
        ANSELH = %00010000  'Setting the ANSELH bit high wil make an analog input
        TRISB = %00000001  'Setting the TRISB high wil make it set as a analog input
        WaitMs 500
        Adcin 12, an12  'read AN12 from port-B and write value to "DIM AN12 AS WORD"
        ANSELH = %00000000  'setting the ANSELH bit low so it will be an digital I/O
        TRISB = %00000000  'Setting the TRISB bit low so it will be an output
       
    Lcdcmdout LcdLine1Pos(5)  'write from position 5
    Lcdout #an4  'just to see on the screen what the digital number is!

    Lcdcmdout LcdLine2Pos(5)  'write from position 5
    Lcdout #an12  'just to see on the screen what the digital number is!

WaitMs 100

Goto main
End  'end program                                

On Interrupt  'Interrupt timer
    Save System  'Save
        TMR0 = 128  'set "TMR0" on 0 (start counting from 128 to 255)
        var_timer = var_timer + 1  'increment var_timer
    INTCON.T0IF = 0  'clear Interrupt flag
Resume  'jump back where it started              

'*************************************************
'* subroutin TIMER will increase the timer every second!
'*************************************************
timer:
If var_timer > timer_limite Then  'when var_timer is greater than timer_limite (122) then....
        var_timer = 0  'set "var_timer" back to 0
        timer1 = timer1 + 1  'increase "timer1" by 1
Endif

Return
 

Dr_Doggy

Well-Known Member
it sounds to me that maybe the additional circuitry on pin is pulling things up a bit ... maybe bad ground? .. r u using scope or meter? are you grounding meter at the pic? .. do you get same issues on adc12 if extra circuitry is removed?

I used a equation like this to convert dynamic solar intensity from different panels to percent value for additional factoring(color shading specifically for me):
float percent = (((float)(value - Min))/((float)(Max-Min)));
RED = (unsigned char)((float)R * percent)
 
Last edited:

Winch

Member
It is an ordinary voltage divider at the input of the AN12. The intention is to read a 1-10 volt control!
 

Dr_Doggy

Well-Known Member
sry, it will work for you too, just define what your ADC MAX and MIN ranges are, value-- should be your adc value

since your output range is 1-10, just change this line to:
OutputRange = (unsigned char)((float)10 * percent)
 

Winch

Member
I'm afraid I can not follow your answer? Maybe I need more explanation?

I'm trying to measure a 1-10 volt at the input AN12 which is also used for the LCD!
Because in the PIC 16F883 is the analog AN12 also the digital I/O RB0.
And I get the feeling that this is just not going to work together with that voltage divider!
 

Dr_Doggy

Well-Known Member
for the expression: float percent = (((float)(value - Min))/((float)(Max-Min)));
calculation for percent is: % = value/max
where value is a number between max and min
max is the number to divide by and when min=max we get 100% or 1
min is the true value for 0...
but because the min value is >0 we need to subtract in our percent equation, but if we do that to numerator, then math rule is we need to do it to denominator hence:
value -Min
and
max - min


instead of using algebra expression , and to simplify description, you could also do it like this:
if ((value> 100)& value < 200){OutputRange = 1;}
if ((value> 200)& value < 300){OutputRange = 2;}
if ((value> 300)& value < 400){OutputRange = 3;}
if ((value> 400)& value < 500){OutputRange = 4;}
...
 
Last edited:

sagor1

Active Member
Using the same pin for Digital out and Analog in can be set up according to the data sheet, but results can be unpredictable if the digital out is set high (5V). To quote the datasheet: (3.4.1)

"The state of the ANSELH bits has no affect on digital
output functions. A pin with TRIS clear and ANSELH
set will still operate as a digital output, but the Input
mode will be analog. This can cause unexpected
behavior when executing read-modify-write
instructions on the affected port."

Also, perhaps check the weak pullups, turn them off. Finally, having a voltage divider on a digital out, which is then read as analog may cause some other issue with the digital out not working properly if the voltage divider is set high. If using high resistive values (100k), this may not be too big an issue. But, if using something like 5k resistors or less, it may be impacting other things. Hard to isolate digital out on a pin with a voltage input from another circuit unless you can switch them in and out.
Maybe try with less TRIS changes, just set it as digital out, and use ANSELH to set it as analog input without changing TRIS, see what happens.

As I see it, if digital out RB0 is high, the A/D will read the 5 volts if it overrides the input voltage divider network (sources up to 25mA). If digital out is low, it will sink the incoming voltage source down to a transistor junction level, about 0.6 to 0.8 volts, which will be read by the A/D input. Switching to input mode may give that chance for it to become high impedance, allowing you to read the actual voltage. Not sure what the transition time is for all the circuits connected to that pin.

I don't think you have to keep changing ANSELH, leave it as Analog input - digital out will still work when TRIS is "0". Look at figure 3-9 in datasheet, you can see the A/D input is not impacted by other settings, the pin is fed directly to the A/D converter. Only impact would be a digital "output". Good luck...
 
Last edited:

mauried

New Member
Theres nothing wrong with your code, but the idea wont work.
When a pin is defined as analog in , it becomes very hi impedance, so that it can measure whatever voltage there is that you are feeding in.
If you have a LCD connected to the analog in pin, then the voltage on that pin will become undefined, as the LCD will try and sink current into that pin as the LCD is looking for an output pin which typically
consists of a totem pole output which either will pull up the pin to VCC or pull it down to gnd.
You need to physically disconnect the LCD from RB0 when you have it configured as an input.
 

Pommie

Well-Known Member
Most Helpful Member
I see you have the week pullups turned on. This will skew your results towards a higher number. Try turning them off whilst reading. OPTION_REG.7 = 1

Edit, the warning in the datasheet refers to analogue pins always reading 0 when read as digital. This is not your problem.

Mike.
 

sagor1

Active Member
Theres nothing wrong with your code, but the idea wont work.
When a pin is defined as analog in , it becomes very hi impedance, so that it can measure whatever voltage there is that you are feeding in.
If you have a LCD connected to the analog in pin, then the voltage on that pin will become undefined, as the LCD will try and sink current into that pin as the LCD is looking for an output pin which typically
consists of a totem pole output which either will pull up the pin to VCC or pull it down to gnd.
You need to physically disconnect the LCD from RB0 when you have it configured as an input.
Mauried is basically correct. There is a slim chance the input to the LCD "could" be high impedance as well (CMOS input maybe) or maybe not, but that still complicates things. Also, if there is any other circuits externally or internally with the LCD display itself (such as pullups or pulldowns), that would affect the voltage on the RB0 line. Without a total circuit diagram and LCD specs, hard to say how much error there would be. You will not get reliable readings unless you can ensure the data line to the LCD is not affecting the voltage on the RB0 line (measure it with a good digital voltmeter and see, without the voltage divider for A/D connected)
 

Pommie

Well-Known Member
Most Helpful Member
From experience, I have never had an LCD with inputs that are not very high impedance. The problem (as stated above) is most likely the week pullups. These are typically 20k and so, if the resistive divider is not much lower impedance then the input (B0) will be skewed towards 5V.

Mike.
 

spec

Well-Known Member
Most Helpful Member
You may be suffering from two problems as described above;
(1) pull ups enabled
(2) input to display affecting the MCU pin.

(1) is easy to fix as already stated- just disable active pull ups in the MCU configuration data base.
(2) connect a CMOS logic (4000 series) gate between the MCU I/O pin and and the input pin on the display. If the I/O pin of the MCU will ever be open circuit, connect a 1M Ohm resistor from the MCU I/O pin to 0V (ground).

spec
 
Status
Not open for further replies.

Latest threads

EE World Online Articles

Loading
Top