Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

PIC ADC Channel Cycling in C

Status
Not open for further replies.

Xen

Newbie level 1
Newbie level 1
Joined
Apr 12, 2013
Messages
1
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,281
Visit site
Activity points
1,293
Hi, I'm fairly new to PIC programming in C, I've completed a few projects but I've got a bug in this one and I can't understand the problem. I've looked through the datasheet and Google with no inspiration.

Its a PIC18F4520 and I'm using MPLAB with the HI-TECH C compiler.

Basically, all I have are 4 pots on channels 0 to 3. I need to read each one of these continuously and then change the outcome of the ADC for each to 0-127. I'm doing this using a timer interrupt to start the ADC process and an ADC interrupt to analyse the outcome. Each time, the channel number for ADC is increased by 1 and reset to 0 after 3 so it is cycling through the channels.

My code reads the first pot perfectly with full range 0-127. The second pot seems to be stuck in the range of around 71-74 (after scaling to 0-127), the third and fourth pots have a wider range but the value seem to vary depending on the position of the first pot!

Here is the code:

(main.c)
Code:
#include <htc.h>
#include <stdio.h>
#include "lcd.h"

//Chip Settings
__CONFIG(1,0x0200);
__CONFIG(2,0X1E1F);
__CONFIG(3,0X8100 & PBADDIS); 	// Disable Port B Analogue use.
__CONFIG(4,0X00C1);
__CONFIG(5,0XC00F);

//Globally accessible variables
volatile char PotVal[4]; 	//Pot Values, Scaled to 0-127

void init(void)
{
	//Port Settings
	TRISA = 0b00011111;		//Input mode for the 4 Pots and the RA4 Button.
	TRISB = 0b00000001; 	//Input mode for the RB0 Button.
	TRISD = 0b00000000;		//LCD Port

	//LCD Initialisation and Splash Screen
	lcd_init();

	//Timer Settings
	T0CON = 0b10000000;		// Pot Read Timer Settings
	TMR0IF=0; 				// Clear Interrupt Flag
	TMR0IE=1;				// Enable Interrupt
	TMR0IP=0;				// Low Priority

	//ADC Settings
	ADCON0=2; 				// Make sure the GO/DONE bit is set prior to activating the ADC
        ADFM=1; 				// Make sure Right Justification is on
	ADIF=0;					// Clear interrupt flag
	ADIE=1;					// Interrupt driven 
	ADIP=1;					// High Priority
	ADON=1;					// Switch on the ADC module 

	ei();					//Enable Interrupts!

}


void main(void)
{
	init();
        char outStr[16];

	while(1)
	{

		sprintf(outStr, "%d %d %d %d         ", PotVal[0], PotVal[1], PotVal[2], PotVal[3]);
		lcd_goto(0);			// select first line
		lcd_puts(outStr);

	}
}

(isr.c)
Code:
// Interrupt Service Routine

#include <htc.h>
#include <stdio.h>

extern volatile char PotVal[4]; 	//Pot Values, Scaled to 0-127

char ADC_Chan=-1; //Start at -1 because first timer interrupt will set to 0.

//ADC Variables
unsigned int total, last_value[4]={1024,1024,1024,1024}; //'Last' values out of range so variables WILL be updated on first run through


void interrupt isr(void){

	if((TMR0IE)&&(TMR0IF)){
		//If Timer Interrupt

		TMR0IE=0; //Disable interrupt to prevent sending a request whilst conversion still in progress.
		ADC_Chan++; //Move to next channel for next conversion
		if(ADC_Chan>3) ADC_Chan=0;
		
		ADCON0 = ADCON0 & 0b00000011; 	// Clear the top 6 bits. By and-ing a mask (00000011), bits with a 0 are cleared, bits with a 1 are unchanged.
		ADCON0 = ADCON0 | (ADC_Chan<<2);  //0b00001100 – Puts the channel bits in ADCON0 bits 2 and 3 without changing other values;
		NOP(); // Wait 4 uS NOP() is detailed in the HiTech C Manual
		NOP(); // along with other interesting features.	
		NOP(); // We need to wait a minimum of 4uS between switching channels
		NOP();
		GODONE=1; // Start a conversion on channel
		while(GODONE) continue;
		TMR0IF=0; //Reset Interrupt Flag

	}

	if((ADIE)&&(ADIF)){
		//If ADC Interrupt

		total = ((256*ADRESH)+ADRESL);

		if((total >= (last_value[ADC_Chan]+8)) || (total <= (last_value[ADC_Chan]-8)) || total == 0 || total == 1023) //Prevents value flicking between numbers if its near a boundary, but still allows max and min.
                {
			PotVal[ADC_Chan]=total/8;
		}
		last_value[ADC_Chan]=total;

		TMR0IE=1; //Ready for another ADC request		
		ADIF=0; //Reset Interrupt Flag
	}

}
 

outstr[16] is an array of 16 bytes. Will sprintf(outStr, "%d %d %d %d ", PotVal[0], PotVal[1], PotVal[2], PotVal[3]); put four strings into one outstr[16] array and make it a single string?
 

I can't test your code but the symptoms suggest you need more settling time between changing channels. There is only one ADC inside the PIC, when when you change to a different channel you are actually routing the measurement pin to the same ADC and it takes a while for voltages from the previous channel to change to the new one before measuring it. Four NOP instructions sounds too short but it depends on your actual clock speed (how long a NOP takes to execute) and also which ADC clock source you are using. I don't have the data sheet with me to look it up but the formula for minimum delay before measurement is given in the ADC section.
Unless you need extremely accurate timing, I sugest you add a little to the calculated time, it isn't something that suddently 'goes right', the longer you leave the channel to settle the more accurate it will be.

Brian.
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top