【问题标题】:PIC16F18855 ADC registers aren't behaving the way I expect them to (C, CCS compiler)PIC16F18855 ADC 寄存器的行为不像我期望的那样(C,CCS 编译器)
【发布时间】:2018-06-01 07:47:35
【问题描述】:

我正在使用带有 CCS 作为编译器的 PIC16F18855 微控制器,并且我正在尝试让 ADC 工作。从CCS提供的功能开始,我写道:

#device ADC=10
...
setup_adc_ports(sAN21);
setup_adc(ADC_CLOCK_DIV_8);
set_adc_channel(21);
...
fprintf(HOST, "%ld", read_adc()); //I have RS232 implemented elsewhere

这很奇怪,读数完全独立于引脚上的实际电压(对于 10 位读数,随机在 9 到 18 之间)。

一位同事查看了 C 编译到的程序集,并告诉我当编译器尝试使用内置的 read_adc() 函数读取 adc 时,它正在写入错误的寄存器。具体来说,当它应该写入 ADGO 位以开始转换时,它会写入不存在的 ADCON0 之前的寄存器。

为了解决这个问题,我尝试实现自己的函数来设置和读取 ADC:

#byte ADC_CON_0 = getenv("SFR:ADCON0")
#byte ADC_CON_1 = getenv("SFR:ADCON1")
#byte ADC_CON_2 = getenv("SFR:ADCON2")
#byte ADC_CON_3 = getenv("SFR:ADCON3")
#byte ADC_CLK   = getenv("SFR:ADCLK")
#byte ADC_RES_H = getenv("SFR:ADRESH")
#byte ADC_RES_L = getenv("SFR:ADRESL")
#byte AN_SEL_C  = getenv("SFR:ANSELC")
#byte ADC_PCH   = getenv("SFR:ADPCH")

void adc_setup(void){
    //setting the mode and clock
    ADC_CON_0 = 0x84;   //turn on ADC and right justify it
    ADC_CON_1 = 0x00;
    ADC_CON_2 = 0x00;
    ADC_CON_3 = 0x00;
    ADC_CLK   = 0x03;   //gives Fosc/8, for 1us T_AD with 8MHz clock

    //setting the input channel and telling the pin to be analogue
    AN_SEL_C  = 0x20;   //set pin C5 to analogue input
    ADC_PCH   = 0x15;   //0x15 = 21, analogue channel 21 is pin C5
}

int16 read_adc_custom_implementation(void){
    ADC_CON_0 |= 0x01;                      //set ADGO bit to start conversion
    while(ADC_CON_0 & 0x01){}               //wait till conversion is finished (indicated by hardware reset of ADGO bit)
    return make16(ADC_RES_H, ADC_RES_L);    //read the result registers and return them combined into a 16bit integer
}

我的代码有两个主要问题:

如果我在调用adc_setup(); 后立即调用fprintf(HOST, "0x%x", ADC_CON_0);,我会在预期0x84 时得到0x80。这意味着 10 位 adc 值在 2 个 8 位寄存器中左对齐,而不是右对齐。我不知道为什么它写不正确。我检查过的所有其他寄存器(ADCON1-3 和 ADCLK)都是正确的。

当我调用 read_adc_custom_implementation(); 时,它会在 while 循环中永远等待,这表明 ADGO 位永远不会按照数据表的指示进行重置。

有谁知道为什么我的adc_setupread_adc_custom_implementation 的实现不起作用?或者,如果有人知道 CCS 提供的功能为何不起作用,我会很高兴我能改用这些功能。

PIC16F18855 datasheet,ADCON0 在第 357 页。

【问题讨论】:

  • read_adc() 返回什么类型?贴出read_adc()的定义。
  • 来自 CCS 手册:8 位或 16 位 int 取决于 #DEVICE ADC= 指令。因为我使用的是 #device adc=10 它的 int16。
  • long代替l,使用fprintf(HOST, "%d", (int) read_adc());
  • 刚试过这个。我得到完全相同的结果 - 9-18 中的数字与引脚上的电压无关。当我第一次尝试时,我使用的是 #device adc=8 并且所有内容都设置为 8 位整数。结果是相同的,除了我得到的不是 9-18 的值,而是 2-4 的值,计算 10 位值中增加的精度与左移 2 的读数相同
  • 如果右对齐,结果寄存器的其余部分是什么?试试(int)(read_adc() & (1<<10 - 1))。此外,右对齐是指“将数据存储在数据 LSB 及以上”还是意味着“将数据从第 n 位存储到第 15 位”?鉴于 PIC 是小端,这些是不同的东西。

标签: c embedded pic adc


【解决方案1】:

user from the CCS forums 的帮助下,我找到了问题的解决方案。

我遇到的错误是硅错误 - 如果您设置 ADGO 位,然后在下一个时钟周期读取它,ADC 永远不会重置 ADGO 位。这意味着我的 while 循环将永远等待。在errata 中有更多关于这个错误的细节。实际上我之前已经阅读过这篇文章,但被误解为不适用于我,因为我的项目发生的事情与文档中描述的不同。

解决方法是在设置 ADGO 位和检查它之间包含一个时钟周期的延迟,即:

int16 read_adc_custom_implementation(void){
    ADC_CON_0 |= 0x01;                      //set ADGO bit to start conversion
    delay_cycles(1);                        //workaround
    while(ADC_CON_0 & 0x01){}               //wait till conversion is finished (indicated by hardware reset of ADGO bit)
    return make16(ADC_RES_H, ADC_RES_L);    //read the result registers and return them combined into a 16bit integer
}

此解决方法包含在 CCS 5.069 及更高版本中内置的 read_adc() 函数中,但我使用的是 5.065,这是第一个支持 18855 的编译器版本。我首先尝试了解决方法,它解决了一个我的问题,但我仍然遇到许多其他错误。 我没有尝试解决它们,而是更新到 5.075 并使用了内置的 read_adc() 函数。一切都开始完美运行。

长话短说,如果您有同样的问题,请更新编译器。

【讨论】:

    猜你喜欢
    • 2017-07-05
    • 1970-01-01
    • 2016-12-04
    • 1970-01-01
    • 2016-08-31
    • 2015-01-11
    • 1970-01-01
    • 1970-01-01
    • 2021-12-10
    相关资源
    最近更新 更多