-1

Please I'm facing difficultly understanding how to calculate the sampling rate of the PIC16F688 for ADC. My clock frequency(MCU) is 8MHz. and I configured ADCON1 to the following:

ADCON1 &= 0b01000000;      //clear bits 6 through 0
ADCON1 |= 0b01000000;     //set bits 6 though 0.

I did that according to the datasheet of the PIC. Because it has internal oscillator, and that means it works on Fosc/4, and according to table 8-1. So I'm trying to find the sampling rate. What code related to it? I think ADCON1 is the one responsible for clock period. i.e. sampling rate.

I don't think delay_ms(1000) is matter in my infinite loop. So it is not my sampling rate. Or either UART1_Init(9600).

Would you mind to help me out with that, I would appreciate that. Thanks.

    char temp[5];

    unsigned int adc_value;

    char uart_rd;
     int i;
     unsigned int d[10]={0};
     int average = 0;
     int counter =0;

     void main()
         {

          temp[0]='1';
          temp[1]='2';
          temp[2]='3';
          temp[3]='4';
          temp[4]=' ';
          OSCCON     = 0x77;         
          //ANSEL = 0;             
          ANSEL = 0b00000100;      
          CMCON0 = 0X07;   
          TRISA = 0b00001100;
          // ADCON0 =0b1011;
         // ADCON1 &= 0b01000000;       
         //ADCON1 |= 0b01000000;      
         UART1_Init(9600);               
         Delay_ms(100);                   
          while (1)
                {
                average=0;
                for(i=0;i<10;i++)
                    {
                   average+= ADC_Read(2);
                }
         average/=10;
        temp[0] = average/1000+48;
        temp[1] = (average/100)%10+48;
        temp[2] = (average/10)%10+48;
        temp[3] = average%10+48;
        for (i=0;i<5; i++)
           {
           UART1_Write(temp[i]);
       }
   }
 }




 //Updated the code using Interrupt.// But have problem reading from ANS2.


enter code here
char temp[5];
unsigned int adc_value;
int i;
unsigned int d[10]={0};
int average = 0;
void interrupt(){
       if (INTCON.T0IF) {
          INTCON.T0IF = 0 ;// clear T0IF (Timer interrupt flag).
          average= ADC_Read(2);
          temp[0] = average/1000+48;
          temp[1] = (average/100)%10+48;
          temp[2] = (average/10)%10+48;
          temp[3] = average%10+48;
          for (i=0;i<5; i++)
              {
              UART1_Write(temp[i]);
              }
       }
     TMR0 = 178;

  }

 void main() {

      temp[0]='1';
      temp[1]='2';
      temp[2]='3';
      temp[3]='4';
      temp[4]=' ';
      OSCCON= 0x77;        //8MHz
      ANSEL = 0b00000100;  //ANS2  
      CMCON0 = 0X07;  //
      TRISA = 0b00001100;
      UART1_Init(9600);    
      TMR0 = 178 ;
      // CMCON0 = 0X04; // turn off compartor.
      OPTION_REG = 0x87;   //
      INTCON =0xA0;
      while(1);

    }
Ahmed A
  • 71
  • 1
  • 9
  • In the code above you are setting bit 6 in `ADCON1` and clearing bits 0-5 and 7, which is not what it says in the comments - is that what you intended to do ? – Paul R Sep 21 '15 at 21:46
  • Oh I'm sorry, but the comment is wrong. so don't look at the comment, I didn't update it. But my question is that how can I caculate the sampling rate? if you see page 67 in the link below if the datasheet. I'm trying to let the sampling rate to 100Hz only. So the way that as my understanding is that:- F=1/t. but not anyone of those in the table can gives me 100Hz. Again I configure my clock frequency to 8MHz.(But I don't know exactly 8MHz means) So how I calculate the sampling rate. I appreciate if you walk with me though it. – Ahmed A Sep 21 '15 at 22:33
  • OK - please hit the [edit] link above and fix your question to avoid further confusion. – Paul R Sep 21 '15 at 22:35
  • http://ww1.microchip.com/downloads/en/DeviceDoc/41203D.pdf – Ahmed A Sep 21 '15 at 22:37
  • 1
    `ADCON1` sets the conversion time, not the sampling rate. You can sample at any rate up to the conversion rate. You could set a timer to run at 100 Hz which triggers the ADC conversion. Or you could read the ADC continuously and filter the reading to be collected at 100 Hz ditto. Try to avoid delay loops. – Weather Vane Sep 22 '15 at 07:55

2 Answers2

2

If you read about the ADC in the PIC16F688 datasheet, you'll see that you can select the conversion clock f_AD by setting ADCON1<4:6>. From your question it seems you intend to set this to f_OSC/4, although it is not clear from the two lines of code you've added that this is what actually happen. Try this instead:

ADCON1 = 0b01000000; // set conversion clock to F_osc/4

But that was a little aside. Going back to the data sheet, you can see in section 8.1.4 Conversion Clock that the time to complete the conversion of a single bit is T_AD, and to convert a full 10-bit sample is 11 T_AD.

One thing I've Microchip's data sheets not very good at is to explicitly state relationships such as T_AD = 1/conversion_clock. You can, however, infer this from the data sheet, such as table 8-1, where you can see for instance that if f_OSC is 8 MHZ, and the conversion clock f_AD is f_OSC/4, i.e. 2 MHz, T_AD is 500 ns, i.e. 1/f_AD. Note also from table 8-1 that this is outside of the recommended range for the ADC. (See below)

As mentioned above, the conversion time T_S of a full 10-bit sample is 11 T_AD = 5.5 us. The sample rate f_S is then 1/T_S, or 181.818 kHZ (this can also be calculated as f_AD/11)

This is the theoretical maximum sample rate the ADC peripheral is capable of doing, but it isn't necessarily the sample rate of your system. If you sample multiple channels, you divide this theoretical maximum for the peripheral, so you if you have two channels that you alternate between, the theoretical maximum is ~90 kHz per channel. However, there will also be overheads for setting up the conversion and reading the result, and also for charging the hold capacitor, which will reduce your actual maximum sample rate below the theoretical. In addition to this, there's the other things your code does, which may contribute to further reducing your actual sample rate.

If you also use with recommended T_AD as defined in table 14-9, you'll have a minimum T_AD of 1.6 us, giving a theoretical maximum sample frequency for the ADC peripheral f_S of 56.8 kHz.

EDIT after seeing some extra comments on the question.

These calculation only relate to the minimum conversion time (and so also the maximum theoretical sample frequency), which is an upper bound on the actual sample frequency. It is not a problem to have an actual sample frequency that's much lower than the maximum, but you can't control that with only the ADC peripheral registers. For instance, you could configure a a timer that interrupts at your desired sample frequency of 100 Hz, and in the timer's ISR, you start a single conversion.

EDIT after comment on 1 December 2015: I think that's quite a different question. But in short, based on the code you posted as another answer, there's no delay in your main loop. The code basically does 10 samples as fast as it can (if you allow a little overhead for the loop and function calls, you probably do the 10 samples in 60 us, or ~166 kHz). Then the samples are averaged, converted to ASCII and transmitted. The transmission will take about 5ms (5 bytes @ 9600, assuming 8N1). So you get a burst of samples, then a longer pause etc. On average, the transmission time dominates, so you'd get a ~190 Hz sample rate. As a quick and dirty change, you could change the sampling loop as follows:

average=ADC_Read(2); // was 0, but we're doing one less iteration of the loop
for(i=0;i<9;i++)
{
    delay_ms(10); // 10ms delay to get 100 Hz sampling
    average+= ADC_Read(2);
}
delay_ms(5); // together with the UART transmission time, we get 10 ms here as well

Now, this doesn't take into account the time needed to do the divisions, and there will still be a difference in the interval between samples within a "burst" and the interval between the last sample in one burst and the first sample in the next. If you set a pin high when calling ADC_Read() and low when it returns, you can check the timing and interval on a scope.

To do it more thoroughly I'd set up an on-chip timer for a 100 Hz interrupt and check the interrupt flag in the main loop (not necessary with to use an ISR). When the timer interrupt flag is set, clear it, re-initialise the timer, acquire one sample and process it. On every tenth sample, do the average and transmission. When the flag is clear, simply do nothing.

Sigve Kolbeinson
  • 1,133
  • 1
  • 7
  • 16
  • That was so useful information and readable. But am still up to now trying to set my sampling frequency to 100 Hz. The previous theoretical method you explained had a very high frequency. I actually configured that code, but it doesn't make sense to me as a sampling. it was very high speed sampled. Could you please tell me how can I set the timer to let it make sampled every 100 Hz. this is my code for clearance. Thank you for your help in advance. – Ahmed A Dec 01 '15 at 07:42
0
enter code here       
char temp[5];

unsigned int adc_value;

char uart_rd;
int i;
unsigned int d[10]={0};
int average = 0;
int counter =0;

void main()
{

    temp[0]='1';
    temp[1]='2';
    temp[2]='3';
    temp[3]='4';
    temp[4]=' ';
    OSCCON     = 0x77;         
    //ANSEL = 0;             
    ANSEL = 0b00000100;      
    CMCON0 = 0X07;   
    TRISA = 0b00001100;
    // ADCON0 =0b1011;
    // ADCON1 &= 0b01000000;       
    //ADCON1 |= 0b01000000;      
    UART1_Init(9600);               
    Delay_ms(100);                   
    while (1)
    {
        average=0;
        for(i=0;i<10;i++)
        {
            average+= ADC_Read(2);
        }
        average/=10;
        temp[0] = average/1000+48;
        temp[1] = (average/100)%10+48;
        temp[2] = (average/10)%10+48;
        temp[3] = average%10+48;
        for (i=0;i<5; i++)
        {
            UART1_Write(temp[i]);
        }
    }
}
Sigve Kolbeinson
  • 1,133
  • 1
  • 7
  • 16
Ahmed A
  • 71
  • 1
  • 9
  • It'd be better to add the code to the question rather than as an answer. – Sigve Kolbeinson Dec 01 '15 at 18:11
  • ok sure, am sorry, am just beginner. - - so am looking right now at a tutorial for timer0 for PIC6F688. I have configured my CPU clock at 8MHz as in the code. but I didn't understand in good version how did he make the preloaded (39) to get 1 sec. delay. I know that he configured OPTION_REG = 0x07; to get max. resolution - Could you please help me out. I need to set my sampling frequency to 100Hz, i.e. 0.01 sec. This is the link I studied. http://embedded-lab.com/blog/lab-7-timers-and-counters-part-1/ – Ahmed A Dec 01 '15 at 20:19
  • So 8 MHz F_osc => 2 MHz instruction clock. With a prescaler of 1:256, the timer will increment, at 2MHz/256 = 7812.5 Hz, or every 0.128 ms. For an interrupt at 100 Hz/every 10 ms, you need to count 7812.5 Hz/100 Hz = 78 'ticks'. Because the interrupt is generated when the counter overflows (would transition from 255 to 256, but goes to 0 because of the overflow, you need to set the register to 256-78 = 178. Note in the linked tutorial that the 39 doesn't give 1s intervals, but (256-39)/3906 = 0.055s intervals. To get 1s, `interrupt()` toggles the LED every 18 interrupts only (18*0.055 = 1s) – Sigve Kolbeinson Dec 01 '15 at 21:40
  • Thank you. Basically am reading from pressure sensor analog voltage from 0 to 5v. and base on your pressure, it set converted to digital based on the logic I set which temp array (I mean temp[0], temp[1]). Since the PIC16 is 10bit, that means it should be able to convert the 0 - 5v to a string of number between 0000 to 1023, and I used a space between each 4 bit. However, am trying now to read and write from the interrupt. Is that possible? I mean would you mind If I put the code in here to take a look? it seems to me there is a problem in my logic. – Ahmed A Dec 03 '15 at 01:32
  • I have added my new configurations. thank you again. – Ahmed A Dec 03 '15 at 02:00