CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to support@ccsinfo.com

5 adc channels !!!!

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
exodia505



Joined: 07 Feb 2012
Posts: 34

View user's profile Send private message

5 adc channels !!!!
PostPosted: Tue Feb 07, 2012 6:26 am     Reply with quote

Hello !!
I would like to know where is problem in using (division). As you see I use Timer2 to read, each Timer2 interrupt a value from Adc (successively ch1....to ch5). When I display idiv.rem it does take value(0,1,2,3,4)!!!!
I just want to get some correction.
Thanks

Here's my code:
Code:

#include <16F877.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES XT                       //Crystal osc <= 4mhz
#FUSES NOPUT                    //No Power Up Timer
#FUSES PROTECT                  //Code protected from reads
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES NOWRT                    //Program memory not write protected
#FUSES NODEBUG                  //No Debug mode for ICD

#use delay(clock=4000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

#include <LCD.C>
#include <Stdlib.h>

int a=0;
int val,val1,val2,val3,val4;
div_t idiv;

#int_TIMER2
void  TIMER2_isr(void)
{
idiv=div(a,5);
switch(idiv.rem)
{
case 0:set_adc_channel(0);
delay_us(20);
val=read_adc();
output_high(pin_c0);
a=a+1;
break;
case 1:set_adc_channel(1);
delay_us(20);
val1=read_adc();
output_high(pin_c1);
a=a+1;
break;
case 2:set_adc_channel(2);
delay_us(20);
val2=read_adc();
output_high(pin_c2);
a=a+1;
break;
case 3:set_adc_channel(3);
delay_us(20);
val3=read_adc();
output_high(pin_c4);
a=a+1;
break;
case 4:set_adc_channel(4);
delay_us(20);
val4=read_adc();
output_high(pin_c5);
a=a+1;
break;
}
}

void main()
{
   lcd_init();
   setup_adc_ports(AN0_AN1_AN2_AN3_AN4);
   setup_adc(ADC_clock_div_8);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DIV_BY_1,99,16);
   enable_interrupts(INT_TIMER2);
   enable_interrupts(GLOBAL);

while(1){

if(input(pin_b1)==1)
{
lcd_putc("\f");
lcd_gotoxy(1,1);
printf(lcd_putc,"%2u",val);}
else{
lcd_putc("            ");
}

if(input(pin_b2)==1){
lcd_putc("\f");
lcd_gotoxy(10,1);
printf(lcd_putc,"%2u",val1);
}else{
lcd_putc("            ");
}

if(input(pin_b3)==1){
lcd_putc("\f");
lcd_gotoxy(10,1);
printf(lcd_putc,"%2u",val2);
}
else{
lcd_putc("            ");
}

if(input(pin_b4)==1){
lcd_putc("\f");
lcd_gotoxy(10,1);
printf(lcd_putc,"%2u",val3);
}
else{
lcd_putc("            ");
}

if(input(pin_b4)==1){
lcd_putc("\f");
lcd_gotoxy(10,1);
printf(lcd_putc,"%2u",val4);
}
else{
lcd_putc("            ");
}

   // TODO: USER CODE!!

}
}

_________________
NOway!!
temtronic



Joined: 01 Jul 2010
Posts: 9165
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Feb 07, 2012 6:53 am     Reply with quote

One problem is that ISRs -MUST- be short and quick !!
That means the ISR should just set a flag then exit.

Having ANY kind of math function inside, especially division, is very bad.
The same holds true for delays( delay_us(), delay_ms()).
And switch/case statements.

You've got all 3 in your ISR !!

One answer is to just have the ISR set a 'flag', then in 'main' read that flag and if set, increment a variable( channel),then read the adc,display data,if channel=>5 reset channel to 0, then clear flag to 0.

Also
You should always add 'errors' to the use RS232(options) to keep the UART from 'freezing' as it only has a 2 byte buffer.
exodia505



Joined: 07 Feb 2012
Posts: 34

View user's profile Send private message

PostPosted: Tue Feb 07, 2012 9:02 am     Reply with quote

thx anyway , it works now
_________________
NOway!!
Ttelmah



Joined: 11 Mar 2010
Posts: 19348

View user's profile Send private message

PostPosted: Tue Feb 07, 2012 9:18 am     Reply with quote

The other appraoch that works for this, is to use a state machine, and do just one job at a time in the ISR.
(I slightly disagree with temtronics comment about switch/case in an ISR - it can work very well, and is as quick as a single array access - it just _must_ be used with care...). The delays involved in the code though are even worse, since the normal use of the read_adc function, takes 12 cycles of the ADC clock - typically 24uSec - add the acquisition delays, and things are getting very slow....
So:
Code:

#int_TIMER2
void TIMER2_isr(void) {
   static int8 state=0;
   switch(state++) {
   case 0:
      set_adc_channel(0); //This only used on the first pass
      break;
   case 1:
      read_adc(ADC_START_ONLY); //Normal re-entry point for the loop
      break;
   case 2:
      val=read_adc(ADC_READ_ONLY);
      output_high(pin_c0);
      set_adc_channel(1);
      break;
   case 3:
      read_adc(ADC_START_ONLY);
      break;
   case 4:
      val1=read_adc(ADC_READ_ONLY);
      output_high(pin_c1);
      set_adc_channel(2);
      break;
   case 5:
      read_adc(ADC_START_ONLY);
      break;
   case 6:
      val2=read_adc(ADC_READ_ONLY);
      output_high(pin_c2);
      set_adc_channel(3);
      break;
   case 7:
      read_adc(ADC_START_ONLY);
      break;
   case 8:
      val3=read_adc(ADC_READ_ONLY);
      output_high(pin_c4);
      set_adc_channel(4);
      break;
   case 9:
      read_adc(ADC_START_ONLY);
      break;
   case 10:
      val4=read_adc(ADC_READ_ONLY);
      output_high(pin_c5);
      set_adc_channel(0);
      state=1; //start again _at the second state_.
      break;
   }
}

This takes 11 calls to the timer ISR to sample all five channels, but is _fast_ for each route. The switch takes about 18 machine cycles (note no 'default' - this is vital, since it forces a jump table to be made, rather than successive tests). Each call just sets a channel (a couple of machine cycles only), starts an ADC conversion (again just a couple of cycles), or reads a conversion (about 8 cycles).
No divisions - just set the state back to 0 on the last call.
No waiting for the ADC, start it on one ISR, and read it on the next.
Compiling this the _slowest_ route through the code takes under 40 machine cycles on a PIC16.

Best Wishes
exodia505



Joined: 07 Feb 2012
Posts: 34

View user's profile Send private message

PostPosted: Tue Feb 07, 2012 10:47 am     Reply with quote

yes, that's true i just changed two things and it worked well
_________________
NOway!!
temtronic



Joined: 01 Jul 2010
Posts: 9165
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Tue Feb 07, 2012 10:52 am     Reply with quote

The only issue I have with Case inside the ISR, is in timing. Depending on how the 'case' is made it might take too long to be effective inside the ISR. Looking at the listing will see how much code is created.
As we all have found out, code always seems to 'grow'....and ISRs should
always be fast !

The bottom line is that if it works, great ! BUT if you run into 'issues', reduce ISR code to the minimum.
Ttelmah



Joined: 11 Mar 2010
Posts: 19348

View user's profile Send private message

PostPosted: Tue Feb 07, 2012 3:53 pm     Reply with quote

temtronic wrote:
The only issue I have with Case inside the ISR, is in timing. Depending on how the 'case' is made it might take too long to be effective inside the ISR. Looking at the listing will see how much code is created.
As we all have found out, code always seems to 'grow'....and ISRs should
always be fast !

The bottom line is that if it works, great ! BUT if you run into 'issues', reduce ISR code to the minimum.

Yes.
This is where I am with 'must be used with care'. Setup by someone who has worked out CCS's rather 'non plain' rules for how switches are decoded, so a jump table is used, it is quite fast. However the point Temtronic is making here is totally valid you _need_ to actually study the timings to be sure that it is coding the way you expect.

Best Wishes
exodia505



Joined: 07 Feb 2012
Posts: 34

View user's profile Send private message

PostPosted: Fri Feb 10, 2012 4:45 am     Reply with quote

if i want to send adc values to pc via serial port and i use this :
printf("%2u %2u %2u %2u %2u\n\r",val,val1,val2,val3,val4);
will be fine? i mean is it easy to separare between data in vb.net ?
what you advice me to do ?
thanks
_________________
NOway!!
temtronic



Joined: 01 Jul 2010
Posts: 9165
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Feb 10, 2012 6:45 am     Reply with quote

How easy VB.NET works is up to you( I don't use it)
If you insert commas you creat 'CSV' data, Excel will display perfectly

original code...
printf("%2u %2u %2u %2u %2u\n\r",val,val1,val2,val3,val4);

modified code...
printf("%2u , %2u , %2u , %2u , %2u\n\r",val,val1,val2,val3,val4);

Also, you should read up on VB to see if \n\r is the 'end of line' delimiter or if \r\n is the 'end of line' delimiter.

I know for CSV files it's different than QB45.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group