Встроенный литерал индексации массива C и проблема с переменной

Я занимаюсь своим первым встроенным проектом C на работе, и я столкнулся с поведением, которое меня озадачивает.

Я использую KEIL uVision 5 IDE и нашел, как сопоставить пространство памяти специальных регистров функций (SFR) по следующей ссылке http://www.keil.com/support/docs/2998.htm

Что я и сделал со следующими двумя фрагментами файла

dataStructures.h

typedef unsigned char byte;
typedef struct DAC {        
    byte loc[15]; 
} DAC;
extern DAC DAC_MAP; 

DAC_MAP.a51

PUBLIC DAC_MAP

DAC_MAP     DATA  0xB9

END

Затем у меня есть код C, который работает только тогда, когда используется буквальное значение 1.

byte i = 1;
DAC_MAP.loc[i] = value; // Does not write to the SFR
DAC_MAP.loc[1] = value; // Writes to the SFR

Я действительно хотел бы иметь возможность писать в местоположение по индексу и не писать большие случаи переключения для решения этой проблемы.

Любые мысли о том, почему это происходит?

DAC_MAP.a51

PUBLIC DAC_MAP

DAC_MAP     DATA  0xB9

END

driver.c

#include <DP8051XP.H> 

typedef unsigned char byte;

typedef struct DAC {        
    byte loc[15]; 
} DAC;

extern DAC DAC_MAP; 

int main(void) {
    byte i = 1;

    DAC_MAP.loc[i] = 0xAA; // Does not write to the SFR
    DAC_MAP.loc[1] = 0xAA; // Writes to the SFR

    return 0;
}

Когда я запускаю этот код в отладчике KEIL uVision 5, вы можете видеть на карте памяти, что когда индексация выполняется переменной, изменений нет, но когда используется литерал, значение изменяется, как и ожидалось.


person josefflores    schedule 05.06.2017    source источник
comment
Я поменял байт на size_t для индекса, и такое же поведение присутствует...   -  person josefflores    schedule 05.06.2017
comment
Извините, я пропустил запрос, но я опубликовал обновление.   -  person josefflores    schedule 05.06.2017
comment
Я никогда не работал ни с одной из этих технологий, но пользуюсь темой форума, ссылка на которую приведена в в статье поддержки арифметика указателей, подобная неявной в DAC_MAP.loc[i] или даже DAC_MAP.loc[1], не имеет смысла с SFR, и попытка вообще использовать массив, вероятно, плохая идея.   -  person user2357112 supports Monica    schedule 05.06.2017
comment
Это минимально, проверяемо и полно. Я начал новый проект и довел его до этого момента. Единственным отличием является использование переменной i и литерала 1 во время индексации. Почему этого не должно быть в примере, ведь это часть проблемы? Когда я запускаю этот код в отладчике KEIL uVision 5, вы можете видеть на карте памяти, что когда индексация выполняется переменной, изменений нет, но когда используется литерал, значение изменяется, как и ожидалось. Без этого значения все, что вы показываете, это то, что индексация не работает, что не является проблемой.   -  person josefflores    schedule 05.06.2017
comment
так что я думаю, что я застрял с переключателем, чтобы получить функциональность, которую я хочу   -  person josefflores    schedule 05.06.2017
comment
Включены ли оптимизации во время компиляции? Поскольку оба оператора делают одно и то же, компилятор/оптимизатор может избавиться от первого.   -  person Harald    schedule 05.06.2017
comment
Когда я запускаю приведенный выше код с закомментированной строкой, нет никакой разницы в поведении. Так что это не связано с оптимизацией.   -  person josefflores    schedule 05.06.2017
comment
Кстати, а зачем вам такой массив? После просмотра документа это явно не цель этой памяти. Так что, возможно, истинный вопрос заключается в том, как... вопреки почему..., проблема XY.   -  person Stargateur    schedule 06.06.2017
comment
Я хотел использовать массив, чтобы иметь возможность перебирать местоположения SFR, уменьшая повторения в моем src, я также хотел уменьшить размер скомпилированного кода. Я надеялся узнать, почему это происходит, и, возможно, получить удовлетворительное решение проблемы.   -  person josefflores    schedule 06.06.2017


Ответы (1)


Проблема в том, что вы пытаетесь получить доступ к регистрам специальных функций 8051 с косвенной адресацией, которая не поддерживается. Вместо этого вам придется заставить компилятор использовать прямую адресацию.

Из ОБЩЕГО НАЗНАЧЕНИЯ ИНТЕРФЕЙСА SFR:

SFR в 8051 имеют только прямую адресацию. Это означает, что адрес должен быть частью программы. Косвенно обратиться к SFR невозможно. Таким образом, указатели не будут работать. Взгляните на документацию Intel по внутренней памяти данных, и должно стать ясно.

Один из способов «отчасти» сделать это — определить SFR для каждого адреса SFR и использовать большой оператор switch следующим образом:

sfr SFR_0x80 = 0x80;
sfr SFR_0x81 = 0x81;
sfr SFR_0x82 = 0x82;
.
.
.
void write_sfr (
  unsigned char sfr_address,
  unsigned char value)
{
switch (sfr_address)
  {
  case 0x80: SFR_0x80 = value; break;
  case 0x81: SFR_0x81 = value; break;
  case 0x82: SFR_0x82 = value; break;
  }

Поскольку похоже, что ваш компилятор достаточно умен, чтобы преобразовать DAC_MAP.loc[1] в прямой адрес, этот driver.c, вероятно, подойдет вам:

#include <DP8051XP.H> 

typedef unsigned char byte;
typedef struct DAC {        
    byte loc[15]; 
} DAC;
extern DAC DAC_MAP;

static void write_dac_map(byte i, byte d) {
    switch (i) {
        case 0:  DAC_MAP.loc[0]  = d; break;
        case 1:  DAC_MAP.loc[1]  = d; break;
        case 2:  DAC_MAP.loc[2]  = d; break;
        case 3:  DAC_MAP.loc[3]  = d; break;
        case 4:  DAC_MAP.loc[4]  = d; break;
        case 5:  DAC_MAP.loc[5]  = d; break;
        case 6:  DAC_MAP.loc[6]  = d; break;
        case 7:  DAC_MAP.loc[7]  = d; break;
        case 8:  DAC_MAP.loc[8]  = d; break;
        case 9:  DAC_MAP.loc[9]  = d; break;
        case 10: DAC_MAP.loc[10] = d; break;
        case 11: DAC_MAP.loc[11] = d; break;
        case 12: DAC_MAP.loc[12] = d; break;
        case 13: DAC_MAP.loc[13] = d; break;
        case 14: DAC_MAP.loc[14] = d; break;
        default: //error
    }
}

int main(void) {
    byte i = 1;
    write_dac_map(i, 0xAA);
    return 0;
}

Если вы посмотрите на сборку, которую генерирует ваш код (предоставлено stargateur), проблема находится в C:0x0806:

     9: int main(void) { 
    10:     byte i = 1; 
    11:  
C:0x0800    7F01     MOV      R7,#0x01
    12:     DAC_MAP.loc[i] = 0xAA; // Does not write to the SFR 
C:0x0802    74B9     MOV      A,#DAC_MAP(0xB9)
C:0x0804    2F       ADD      A,R7
C:0x0805    F8       MOV      R0,A
C:0x0806    76AA     MOV      @R0,#0xAA
    13:     DAC_MAP.loc[1] = 0xAA; // Writes to the SFR 
    14:  
C:0x0808    75BAAA   MOV      0xBA,#0xAA
    15:     return 0; 
C:0x080B    E4       CLR      A
C:0x080C    FE       MOV      R6,A
C:0x080D    1F       DEC      R7
    16: }
C:0x080E    22       RET      
C:0x080F    787F     MOV      R0,#0x7F
C:0x0811    E4       CLR      A
C:0x0812    F6       MOV      @R0,A
C:0x0813    D8FD     DJNZ     R0,C:0812
C:0x0815    758107   MOV      SP(0x81),#0x07
C:0x0818    020800   LJMP     main(C:0800)

MOV @R0,#0xAA использует косвенную адресацию и будет записывать 0xAA во внутреннюю оперативную память по адресу 0xBA (R0 установлен на 0xB9 + 1).

Инструкция MOV 0xBA,#0xAA по адресу C:0x0808 использует прямую адресацию и запишет 0xAA в SFR по адресу 0xBA. (При использовании прямой адресации адреса между 0x00 и 0x7F относятся к SFR, а не к ячейкам в ОЗУ).

На этом веб-сайте содержится дополнительная информация о различных режимах адресации 8051: http://www.8052.com/tutaddr.phtml

person Tim    schedule 05.06.2017
comment
@Stargateur И указатели, и массивы полагаются на косвенную адресацию, которую 8051 не поддерживает для доступа к SFR. - person Tim; 05.06.2017
comment
То, что вы написали, это то, что у меня было до того, как я свернул это для примера. Необходимость писать что-то подобное ужасна как во времени, так и в пространстве, когда это может быть что-то такое простое, как DAC_MAP.loc[i] = d;. - person josefflores; 05.06.2017
comment
@josefflores Я знаю. Обход аппаратных ограничений — одна из многих радостей встроенного программирования :) - person Tim; 05.06.2017
comment
@Stargateur Не на 8051, см. Раздел 2.1 в Подробностях набора инструкций: keil.com/dd/docs/datashts/dcd/dp805x_instr.pdf . Но да, вывод сборки поможет это подтвердить. - person Tim; 05.06.2017
comment
сегодня позже выложу сборку - person josefflores; 05.06.2017
comment
@Stargateur MOV @R0,#0xAA - это проблема. Это использует косвенную адресацию, которая будет обращаться к внутренней оперативной памяти вместо SFR. См. 8052.com/tutaddr.phtml . Доступ к SFR возможен только с прямой адресацией. - person Tim; 05.06.2017
comment
@Tim Действительно, и компилятор обрабатывает этот адрес, не заботясь о местоположении. Это может быть сообщение об ошибке, компилятор может выдать ошибку. Очень поучительный вопрос и ответ. - person Stargateur; 06.06.2017