Регистры АЦП PIC16F18855 ведут себя не так, как я ожидал (компилятор C, CCS)

Я использую микроконтроллер PIC16F18855 с CCS в качестве компилятора и пытаюсь заставить работать АЦП. Начиная с функций, предоставляемых 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

Это вело себя странно, с показаниями, которые были полностью независимы от фактического напряжения на выводе (случайным образом между 9 и 18 для 10-битного чтения).

Сотрудник просмотрел сборку, в которую был скомпилирован C, и сказал мне, что компилятор записывает в неправильный регистр, когда пытается прочитать adc, используя встроенную функцию read_adc(). В частности, когда он должен записать бит ADGO для начала преобразования, он записывает в регистр до ADCON0, которого не существует.

Чтобы обойти это, я попытался реализовать свои собственные функции для настройки и чтения АЦП:

#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
}

В моем коде есть две основные проблемы:

Если я позвоню fprintf(HOST, "0x%x", ADC_CON_0); сразу после звонка adc_setup();, я получу 0x80, когда я ожидал 0x84. Это означает, что 10-битное значение АЦП выравнивается по левому краю в 2 8-битных регистрах, а не по правому краю. Понятия не имею, почему он пишет неправильно. Все остальные регистры, которые я проверил (ADCON1-3 и ADCLK), верны.

Когда я вызываю read_adc_custom_implementation();, он вечно ждет в цикле while, показывая, что бит ADGO никогда не сбрасывается, как указано в таблице данных.

Кто-нибудь знает, почему мои реализации adc_setup и read_adc_custom_implementation не работают? В качестве альтернативы, если кто-нибудь знает, почему не работают поставляемые функции CCS, я был бы счастлив, если бы мог использовать их вместо них.

Таблица данных PIC16F18855, ADCON0 находится на странице 357.


person Escape    schedule 18.12.2017    source источник
comment
Какой тип возвращает read_adc()? Разместите определение read_adc().   -  person chux - Reinstate Monica    schedule 19.12.2017
comment
из руководства CCS: 8- или 16-битное int в зависимости от директивы #DEVICE ADC =. потому что я использую #device adc = 10 его int16.   -  person Escape    schedule 19.12.2017
comment
Вместо l для long, используйте fprintf(HOST, "%d", (int) read_adc());   -  person chux - Reinstate Monica    schedule 19.12.2017
comment
Просто попробовал это. Я получаю точно такой же результат - числа от 9 до 18, которые не имеют никакого отношения к напряжению на контакте. Когда я впервые попробовал, я использовал #device adc = 8, и все было настроено для 8-битных целых чисел. Результаты были идентичны, за исключением того, что вместо значений из 9-18 я получал значения из 2-4, которые, учитывая повышенную точность в 10-битном значении, совпадают с показаниями со сдвигом влево на 2.   -  person Escape    schedule 19.12.2017
comment
И каков остальной регистр результатов, если вы выравниваете по правому краю? Попробуйте (int)(read_adc() & (1<<10 - 1)). Кроме того, оправдано ли правильное выравнивание означает хранить данные в младшем разряде данных и выше или это означает хранить данные от бита n до бита 15? Учитывая, что PIC имеет прямой порядок байтов, это разные вещи.   -  person Lundin    schedule 19.12.2017
comment
В регистре PIC бит 0 (LSB) находится справа, а бит 7 (MSB) - слева. Регистры результатов при выравнивании по левому или правому краю лучше всего видны на рис. 23-3 на странице 341 спецификации. Все нереализованные биты читаются как 0, поэтому при выравнивании по правому краю чтение двух регистров как одного int16 даст значение от 0 до 1023. при выравнивании по левому краю чтение регистров таким же образом даст тот же результат, умноженный на 2 в степени 6, или сдвиг влево 6. Однако read_adc все это следует учитывать. Все примеры кода показывают то, что было в моем первом фрагменте кода.   -  person Escape    schedule 19.12.2017
comment
Попробуйте задержку 1-2 мс после установки регистра, прежде чем проверять его. AD обычно немного медленнее, чем обычные цифровые переключатели.   -  person cup    schedule 01.01.2018


Ответы (1)


Я нашел решение проблемы с помощью пользователя с форумов CCS.

Ошибка, с которой я столкнулся, - это ошибка кремния: если вы установите бит ADGO, а затем прочитаете его в следующем тактовом цикле, АЦП никогда не сбросит бит 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
}

Этот обходной путь включен в функцию read_adc(), встроенную в CCS в версиях 5.069 и более поздних, но я использовал 5.065, которая была первой версией компилятора, поддерживающей 18855. Я сначала попробовал обходной путь, и он решил одну из моих проблем, но Я все еще сталкивался с рядом других ошибок. Вместо того, чтобы пытаться их обойти, я обновился до версии 5.075 и использовал встроенную функцию read_adc(). Все заработало отлично.

Короче говоря, если у вас такая же проблема, обновите компилятор.

person Escape    schedule 07.01.2018