PDA

View Full Version : A2D question



dbodenheimer
- 23rd July 2010, 23:00
I am working on what I thought was a simple PIC project and I need some help.

What I need to do is an Analog to digital conversion on an 0-5v signal on RA0 and then put the result in a 4 byte array so that I can send the data to a serial port.

I have used the example for USART and have that working, so I can send data to the serial port ok. I just need to do the A2D conversion and convert to characters I can transmit
IM using an PIC 16F876.

Thanks in Advance!

Dave

kevlar129bp
- 24th July 2010, 00:44
Hey Dave,

I don't know if I can help, but my project is basically doing that now... I think the more experienced gurus on the forum are going to request your code so far. Post that, and you'll have tons of help.

Chris

dbodenheimer
- 24th July 2010, 01:28
Here is my code, feel free to offer suggestions or modifications.



' Read and write hardware USART

B1 var byte
Done var byte
TxBuf var byte[3]
STX var byte
ETX var byte
Period var byte
i var byte
tmpval var byte
' Initialize USART
TRISC = %10111111 ' Set TX (PortC.6) to out, rest in
SPBRG = 25 ' Set baud rate to 2400
RCSTA = %10010000 ' Enable serial port and continuous receive
TXSTA = %00100000 ' Enable transmit and asynchronous mode

' Define ADCIN parameters
Define ADC_BITS 10 ' Set number of bits in result
Define ADC_CLOCK 3 ' Set clock source (3=rc)
Define ADC_SAMPLEUS 50 ' Set sampling time in uS

adval var word ' Create adval to store result


TRISA = %11111111 ' Set PORTA to all input
ADCON1 = %10000010 ' Set PORTA analog and right justify result
Pause 500 ' Wait .5 second


' Echo received characters in infinite loop
STX = 2
ETX = 3
Period = 46
TxBuf[1] = 51
TxBuf[2] = 54
TxBuf[3] = 56
mainloop:
ADCIN 0, adval ' Read channel 0 to adval
'basicaly here i want to convert advar into 3 ascii numbers
'representing advar
'So a result like 936 would put
'57 in txbuf[1]
'51 in txbuf[2]
'54 in txbuf[3]
'Then call the serial output routine which adds a stx char, 2 digits
'a period then the last digit followed by an ETX char
Gosub StringOut ' Send character to serial output
pause 500 ' Wait half second
Goto mainloop ' Do it forever


' Subroutine to get a character from USART receiver
charin: B1 = 0 ' Preset to no character received

If PIR1.5 = 1 Then ' If receive flag then...
B1 = RCREG ' ...get received character to B1
Endif

ciret: Return ' Go back to caller


' Subroutine to send a character to USART transmitter
charout: If PIR1.4 = 0 Then charout ' Wait for transmit register empty

TXREG = B1 ' Send character to transmit register
done = 1
Return ' Go back to caller
' Subroutine to send a character string to USART transmitter
'First Send STX (02) then 2 digits, then period (46), then 1 digit, then ETX (03)
StringOut: If PIR1.4 = 0 Then StringOut ' Wait for transmit register empty
done = 1

TXREG = STX ' Send character to transmit register
while PIR1.4 = 0
wend
for i = 1 to 2
TXREG = TxBuf[i] ' Send character to transmit register
while PIR1.4 = 0
wend
next i
TXREG = Period ' Send character to transmit register
while PIR1.4 = 0
wend
TXREG = TxBuf[3] ' Send character to transmit register
while PIR1.4 = 0
wend
TXREG = ETX ' Send character to transmit register
while PIR1.4 = 0
wend
Return ' Go back to caller

mackrackit
- 24th July 2010, 01:33
Put your code in code tags and post it. Not going to risk or take the time to download and open a ZIP when a ZIP is not needed.


put the result in a 4 byte array
This chip only has a 10 bit resolution so you need to explain what you are after. The ADC result can be sent serially with out an array of ant type...

Here is a sample to read the ADC at an 8 bit resolution. Maybe it will get you started.

ADCON1 = %00001110
OUT_TEMP VAR BYTE

MAIN_LOOP:
GOSUB GET_AD
GOTO MAIN_LOOP

GET_AD:
ADCON0=00000001
GOSUB READ_AD
OUT_TEMP = ADRESH
RETURN

READ_AD:
PAUSE 50
ADCON0.2=1
WHILE ADCON0.2=1:WEND
RETURN

mackrackit
- 24th July 2010, 13:02
txbuf[1] = advar / 1000
txbuf[2] = (advar - txbuf[1] * 1000) /100
txbuf[3] = (advar-((txbuf[1] * 1000) + (txbuf[2] * 100)))/10
txbuf[4] = advar - ((txbuf[1] * 1000) + (txbuf[2] * 100)+ txbuf[3] * 10)

aratti
- 24th July 2010, 15:01
TxBuf var byte[3]

'basicaly here i want to convert advar into 3 ascii numbers
'representing advar
'So a result like 936 would put
'57 in txbuf[1]
'51 in txbuf[2]
'54 in txbuf[3]
'Then call the serial output routine which adds a stx char, 2 digits
'a period then the last digit followed by an ETX char



Dave, with referece to your code please note that when you declare an array, the number you state is the real number of elements of your array. you stated 3 elements, so you will have the following bytes available:

txbuf[0]
txbuf[1]
txbuf[2]

Now since you are decoding a 10bits adc then the maximum reading you can obtain in adval is 1023, which are four digits so you will need a 4 bytes array for your data transfer.
Hence, you have to declare:

txbuf var byte [4]

and you will have the following bytes available:

txbuf[0]
txbuf[1]
txbuf[2]
txbuf[3]

Please read page 33 of PBP manual, you will find the DIG instruction that could help you in moving the single digits into your array. Here a snippet as an example:




A var byte ' used for the FOR/NEXT loop

For A=0 to 3
txbuf[A]= adval DIG A
next A

'assuming adval contains the value 1023 then your array will end up as follows:

'txbuf[0]="3"
'txbuf[1]="2"
'txbuf[2]="0"
'txbuf[3]="1"

In case you want them the other way then

For A=0 to 3
txbuf[A]= adval DIG (3-A)
next A


'will yield:

'txbuf[0]="1"
'txbuf[1]="0"
'txbuf[2]="2"
'txbuf[3]="3"





Al.

dbodenheimer
- 25th July 2010, 02:16
Thanks Everyone for the input! I'm now able to read values from my sensor. I now have 2 additional questions.

1. Is there a better way to send serial; data than the way I'm doing it? Anyone have a routine that they think works better?

2. It seems like my returned values vary quite a bit. I don't have access to a scope, but it seems that the sensor is returning pretty constant voltage. it returns 0 - 10 v I am using a voltage divider made up of 2 2.2 K resistors to shift the sensor output to 0 - 5 volts. Any hints on making the system more stable? Slow down the conversion time? My requirements are pretty easy, i need to read the sensor no more than twice a second.

Thanks again for everyone's help!

Dave

aratti
- 25th July 2010, 08:57
1. Is there a better way to send serial; data than the way I'm doing it? Anyone have a routine that they think works better?

Yes, use HSEROUT.

HSEROUT[STR txbuf \ 4] , will send your array out at the baud rate choosen in your define.

See page 79 - 80 of PBP manual.



2. It seems like my returned values vary quite a bit. I don't have access to a scope, but it seems that the sensor is returning pretty constant voltage. it returns 0 - 10 v I am using a voltage divider made up of 2 2.2 K resistors to shift the sensor output to 0 - 5 volts. Any hints on making the system more stable? Slow down the conversion time? My requirements are pretty easy, i need to read the sensor no more than twice a second.


Read the thread at the given link, you will find the technique and snippets to keep your adc reading stable.

www.picbasic.co.uk/forum/showthread.php?t=6734


Al.

tenaja
- 25th July 2010, 14:30
1. Is there a better way to send serial; data than the way I'm doing it? Anyone have a routine that they think works better?

For certain, Al's method is the preferred way if you are going to send ascii. However, in most situations you can send the actual data instead of the ascii representation of it. Doing so makes it MUCH faster and MUCH smaller code, because you are sending half the data and doing do it without ascii conversions.

Instead of
dim TxBuf[4] as byte 'var declaration
HSEROUT[STR txbuf \ 4] 'sending data

dim TxBuf as word 'new var declaration
HSEROUT[TxBuf] 'new sending data

now your pic will send two bytes, low byte first.

Side Note: As a personal preference, I like to make the first letter of a variable the first letter of the type. So I would make it wTxBuf, since it is a word. This makes it a lot easier to find type mismatch errors.

dbodenheimer
- 26th July 2010, 03:04
Want to thank everyone for their help on this! I'm posting my code as of right now for review and comments.

Al, Even with the multiple collections, sorting and averaging routines put in, I'm still getting a wider variance of data than I think I should ]. Do I have the code right? If so, could my voltage divider be the wrong value?

Also, I tried to increase the baudrate to 9600 and it seemed to stop transmitting, does my serial setup code look OK? I am using a 4 mhz crystal.

Thanks again Everyone!

Dave


' Read and write hardware USART

B1 var byte
Done var byte
TxBuf var byte[3]
SendBuf var byte[6]
STX var byte
ETX var byte
Period var byte
i var byte
tmpval var byte
A var byte 'for for next loop
DatAvg var word
CounterA var Word
DataA var Word
RawData var Word [16]


' Initialize USART

TRISC = %10111111 ' Set TX (PortC.6) to out, rest in
' Set receive register to receiver enabled
DEFINE HSER_RCSTA 90h
' Set transmit register to transmitter enabled
DEFINE HSER_TXSTA 20h
' Set baud rate
DEFINE HSER_BAUD 2400
DEFINE HSER_SPBRG 25 'Hser spbrg init

' Define ADCIN parameters
Define ADC_BITS 10 ' Set number of bits in result
Define ADC_CLOCK 3 ' Set clock source (3=rc)
Define ADC_SAMPLEUS 50 ' Set sampling time in uS

adval var word ' Create adval to store result


TRISA = %11111111 ' Set PORTA to all input
ADCON1 = %10000010 ' Set PORTA analog and right justify result
Pause 500 ' Wait .5 second


' Echo received characters in infinite loop
STX = 2
ETX = 3
Period = 46
TxBuf[1] = 51
TxBuf[2] = 54
TxBuf[3] = 56
mainloop:
'Collect 17 AD samples
for A = 0 to 16
ADCIN 0, RawData[A] ' Read channel 0 to array
Next A
'Now Lets sort the array
Gosub SortArray
'Now Sum the middle 8 values
for A = 4 to 11 ' pick out middle 8 readings
DatAvg = DatAvg + RawData[A]
NEXT A
'Now Divide by 8 (by shifting) too get an average
DatAvg = DatAvg >>3
'Now put data in TxBuf
For A=0 to 3
txbuf[A]= DatAvg DIG (3-A)
next A
for A = 0 to 3
TxBuf[A] = TxBuf[A] + 48
next A
'Send it!
Gosub SendString ' Send character to serial output
pause 500 ' Wait half second
Goto mainloop ' Do it forever

'
' Sort Array
' ----------
SortArray:
CounterA=0
SortLoop:
If RawData(CounterA+1) < RawData(CounterA) then
DataA=RawData(CounterA)
RawData(CounterA)=RawData(CounterA+1)
RawData(CounterA+1+0)=DataA
If CounterA > 0 then CounterA=CounterA-2
endif
CounterA=CounterA+1
If CounterA < 15 then goto SortLoop
Return
' Subroutine to send a character string to USART transmitter
'First Send STX (02) then 2 digits, then period (46), then 1 digit, then ETX (03)
SendString:
'Build Buffer
SendBuf[0] = STX
SendBuf[1] = TxBuf[0]
SendBuf[2] = TxBuf[1]
SendBuf[3] = TxBuf[2]
SendBuf[4] = Period
SendBuf[5] = TxBuf[3]
SendBuf[6] = ETX
'Now Send it
HSEROUT[STR SendBuf \ 7]

Return ' Go back to caller

aratti
- 26th July 2010, 07:03
' Read and write hardware USART

B1 var byte
Done var byte
TxBuf var byte[3]
SendBuf var byte[6]
STX var byte
ETX var byte
Period var byte
i var byte
tmpval var byte
A var byte 'for for next loop
DatAvg var word
CounterA var Word
DataA var Word
RawData var Word [16]



' Initialize USART

TRISC = %10111111 ' Set TX (PortC.6) to out, rest in
' Set receive register to receiver enabled
DEFINE HSER_RCSTA 90h
' Set transmit register to transmitter enabled
DEFINE HSER_TXSTA 20h
' Set baud rate
DEFINE HSER_BAUD 2400
DEFINE HSER_SPBRG 25 'Hser spbrg init

' Define ADCIN parameters
Define ADC_BITS 10 ' Set number of bits in result
Define ADC_CLOCK 1 ' Set clock source to 1.6 us Tad @ Fosc/8 (xtal = 4 MHz)
Define ADC_SAMPLEUS 50 ' Set sampling time in uS

adval var word ' Create adval to store result


TRISA = %11111111 ' Set PORTA to all input
ADCON1 = %10000010 ' Set PORTA analog and right justify result
Pause 500 ' Wait .5 second


' Echo received characters in infinite loop
STX = 2
ETX = 3
Period = 46
TxBuf[1] = 51
TxBuf[2] = 54
TxBuf[3] = 56
mainloop:
'Collect 17 AD samples
for A = 0 to 15
ADCIN 0, RawData[A] ' Read channel 0 to array
Next A
'Now Lets sort the array
Gosub SortArray
'Now Sum the middle 8 values

DatAvg = 0 ' set DatAvg to zero

for A = 4 to 11 ' pick out middle 8 readings
DatAvg = DatAvg + RawData[A]
NEXT A
'Now Divide by 8 (by shifting) too get an average
DatAvg = DatAvg >>3
'Now put data in TxBuf
For A=0 to 3
txbuf[A]= DatAvg DIG (3-A)
next A
for A = 0 to 3
TxBuf[A] = TxBuf[A] + 48
next A
'Send it!
Gosub SendString ' Send character to serial output
pause 500 ' Wait half second
Goto mainloop ' Do it forever

'
' Sort Array
' ----------
SortArray:
CounterA=0
SortLoop:
If RawData(CounterA+1) < RawData(CounterA) then
DataA=RawData(CounterA)
RawData(CounterA)=RawData(CounterA+1)
RawData(CounterA+1+0)=DataA
If CounterA > 0 then CounterA=CounterA-2
endif
CounterA=CounterA+1
If CounterA < 15 then goto SortLoop
Return
' Subroutine to send a character string to USART transmitter
'First Send STX (02) then 2 digits, then period (46), then 1 digit, then ETX (03)
SendString:
'Build Buffer
SendBuf[0] = STX
SendBuf[1] = TxBuf[0]
SendBuf[2] = TxBuf[1]
SendBuf[3] = TxBuf[2]
SendBuf[4] = Period
SendBuf[5] = TxBuf[3]
SendBuf[6] = ETX
'Now Send it
HSEROUT[STR SendBuf \ 7]

Return ' Go back to caller


I found three errors in your code (corrected in red) .

The ADC clock source must be set the closesest possible to 1.6 us Tad.

Very likely the fact you didn't reset DatAvg to zero was responsible for the non stable reading.

As far as the baud rate is concerned you can use 4800 as the maximum baud rate with your 4 MHz oscillator.

Tray and see you should have rock stable reading now, good luck.

Al.