PIC16F688 的采样率

Sampling rate for PIC16F688

我很难理解如何计算 PIC16F688 的 ADC 采样率。我的时钟频率(MCU)是 8MHz。我将 ADCON1 配置为以下内容:

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

我是根据PIC的数据表做的。因为它有内部振荡器,这意味着它可以在 Fosc/4 上工作,并且根据 table 8-1。 所以我试图找到采样率。与它相关的代码是什么?我认为 ADCON1 是负责时钟周期的。即采样率。

我认为 delay_ms(1000) 在我的无限循环中无关紧要。所以这不是我的采样率。 或者 UART1_Init(9600)。

你能帮我解决这个问题吗,我将不胜感激。 谢谢。

    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);

    }

如果您阅读了 PIC16F688 datasheet 中的 ADC,您会发现可以通过设置 ADCON1<4:6> select 转换时钟 f_AD。从你的问题来看,你似乎打算将其设置为 f_OSC/4,尽管从你添加的两行代码中不清楚这是实际发生的事情。试试这个:

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

但这有点离题了。回到数据sheet,可以看到在8.1.4 Conversion Clock小节中,完成一个bit的转换时间是T_AD,转换一个完整的10-bit sample是11 T_AD.

我有 Microchip 的数据 sheet 不太擅长的一件事是明确说明关系,例如 T_AD = 1/conversion_clock。但是,您可以从数据 sheet 中推断出这一点,例如 table 8-1,例如,您可以在其中看到如果 f_OSC 是 8 MHZ,并且转换时钟 f_AD是f_OSC/4,即2MHz,T_AD是500ns,即1/f_AD。 另请注意 table 8-1,这超出了 ADC 的推荐范围。 (见下文)

如上所述,一个完整的10位样本的转换时间T_S是11T_AD = 5.5 us。采样率 f_S 则为 1/T_S,即 181.818 kHZ(这也可以计算为 f_AD/11)

这是 ADC 外围设备能够执行的理论最大采样率,但不一定是您系统的采样率。如果您对多个通道进行采样,则将这个理论最大值除以外围设备,因此,如果您有两个通道在它们之间交替,则理论最大值为每个通道约 90 kHz。但是,设置转换和读取结果以及为保持电容器充电也会产生开销,这会使您的实际最大采样率降低到理论值以下。除此之外,您的代码还会执行其他操作,这可能会进一步降低您的实际采样率。

如果您还使用 table 14-9 中定义的推荐 T_AD,您的最小 T_AD 为 1.6 us,给出ADC 外设的理论最大采样频率 f_S 为 56.8 kHz。

在看到关于该问题的一些额外评论后进行编辑。

这些计算仅涉及最小 conversion 时间(以及最大理论采样频率),这是实际采样频率的上限。实际采样频率远低于最大值不是问题,但您无法仅使用 ADC 外设寄存器来控制它。例如,您可以配置一个定时器以您想要的 100 Hz 采样频率中断,并在定时器的 ISR 中启动单次转换。

2015 年 12 月 1 日评论后编辑: 我认为这是一个完全不同的问题。但简而言之,根据您作为另一个答案发布的代码,您的主 loop 没有延迟。该代码基本上尽可能快地执行 10 个样本(如果允许循环和函数调用有一点开销,您可能在 60 us 或 ~166 kHz 内完成 10 个样本)。然后对样本进行平均,转换为 ASCII 并传输。传输大约需要 5 毫秒(5 字节 @ 9600,假设 8N1)。所以你会得到一连串的样本,然后是更长的暂停等。平均而言,传输时间占主导地位,所以你会得到大约 190 Hz 的采样率。 作为快速而肮脏的更改,您可以按如下方式更改采样循环:

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

现在,这没有考虑进行除法所需的时间,"burst" 中样本之间的间隔与一个样本中最后一个样本之间的间隔仍然存在差异突发和下一个中的第一个样本。如果您在调用 ADC_Read() 时将引脚设置为高电平,而在调用 returns 时将引脚设置为低电平,则可以在示波器上检查时间和间隔。

为了做得更彻底,我会为 100 Hz 中断设置一个片上定时器,并检查主循环中的中断标志(使用 ISR 不是必需的)。当定时器中断标志置位时,清除它,重新初始化定时器,获取一个样本并处理它。在每十个样本上,进行平均和传输。当 flag 清除时,什么都不做。

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]);
        }
    }
}