Не удалось прочитать идентификатор через SWD с помощью Arduino.

Итак, я пытаюсь прочитать немного данных через SWD. В качестве целевых я попробовал две микросхемы - LPC1110 и STM32F030F4 (первую из них я использовал для другой схемы, но я перезагрузил флэш-память, чтобы убедиться, что контакты не настроены неправильно).

Я использую Arduino в качестве хоста. Я подключаю D3 к SWCLK и D4 к SWDIO (с подтягиванием до линии 3,3 В). Я терплю неудачу с обоими чипами - то есть, когда я ожидаю, что какой-то ACK будет прочитан - он все время показывает только ВЫСОКОЕ состояние на линии.

Вот мой код. Может ли кто-нибудь посмотреть и сказать, где я ошибся:

int swdck = 3;
int swdio = 4;

void setup() {                
  pinMode(swdck, OUTPUT);
  digitalWrite(swdck, HIGH);
  pinMode(swdio, INPUT);
  digitalWrite(swdio, HIGH);
  Serial.begin(9600);
}

// записываем несколько битов из X (сначала наименее значимый) в SWD
void clockOut(int x, int bits) {
  pinMode(swdio, OUTPUT);
  while (bits > 0) {
    digitalWrite(swdck, LOW);
    digitalWrite(swdio, (x & 1) ? HIGH : LOW);
    delay(1);
    digitalWrite(swdck, HIGH);
    delay(1);
    bits--;
    x >>= 1;
  }
  pinMode(swdio, INPUT);
  digitalWrite(swdio, HIGH);
}

// читаем некоторые биты из SWD, сначала наименее значимые
int clockIn(int bits) {
  pinMode(swdio, INPUT);
  digitalWrite(swdio, HIGH);
  int i, res;
  res = 0;
  for (i = 0; i < bits; i++) {
    digitalWrite(swdck, LOW);
    delay(1);
    if (digitalRead(swdio) == HIGH) {
      res += (1 << i);
    }
    digitalWrite(swdck, HIGH);
    delay(1);
  }
  return res;
}

void loop() {
  int x;
  // последовательность разблокировки, хотя, вероятно, она не нужна
  clockIn(26); clockIn(26); // более 50 часов
  clockOut(0xE79E, 16); //команда переключения на SWD
  clockIn(26); clockIn(26);

  clockOut(0x25, 7); // 0b0100101 - запрос на чтение Debug-port-0, т.е. ID
  x = clockIn(5); // где-то должны быть биты ACK... но я получаю 0x1111
  clockIn(26); clockIn(26); // выполняем дополнительные чтения на случай, если что-нибудь будет возвращено
  Serial.println(x);
  delay(1000);
}

, 👍0

Обсуждение

Пожалуйста, опубликуйте объяснение того, как контакты ARM-MCU питаются только до 3,3 В, а не 5 В. Я предполагаю, что это правильно, и в своем ответе я рассмотрел только программное обеспечение. Тем не менее, было бы полезно быть уверенным в булавках., @gbulmer

Ваше беспокойство верно, но похоже, что оба чипа ARM, которые я использовал (LPC и STM32), имеют входы, устойчивые к 5 В. С другой стороны, когда логический сигнал 3,3 В подается на вход TTL, он также превышает пороговое значение, поэтому все работает нормально. Похоже, производители чипов принимают такие меры предосторожности, чтобы избавить людей от изобретения интерфейсных схем..., @Alumashka

Я не читал внимательно спецификации LPC, поэтому очень полезно знать, что у них есть контакты SWD, устойчивые к 5 В (возможно, даже стоит обновить ваш вопрос, чтобы упомянуть об этом). Мне интересны ваши результаты. У вас это работает?, @gbulmer


1 ответ


1

Я не эксперт по SWD; прошло несколько лет с тех пор, как я читал спецификацию. Так что прошу прощения, если вы меня опередили.

Я полагаю, что в документации SWD от ARM, например, «ARM® Debug Interface v5 Architecture Specification» (ARM IHI 0031A), говорится, что передачи имеют фиксированную длину, начиная с 8-битного запроса.

Код:

void clockOut(int x, int bits) {
  pinMode(swdio, OUTPUT);
  while (bits > 0) {
    ...
    bits--;
    ...
  }
  ...
}

вызывается с помощью clockOut(0xE79E, 16)

Запрос пакета — это 8 бит, за которыми следует ответ от цели. Так что это, похоже, не соответствует протоколу.

Аналогично clockOut(0x25, 7), похоже, отправляет 7 бит, что также выглядит неправильно и может объяснять, почему чтение не удается.

Кроме того, после отправки пакета должен быть «бит возврата».

Этот код:

void clockOut(int x, int bits) {
  pinMode(swdio, OUTPUT);
  ...
  pinMode(swdio, INPUT);
  digitalWrite(swdio, HIGH);
}

Похоже, он готовится к этому, однако ему также необходимо подать сигнал о другом тактовом импульсе, например:

void clockOut(int x, int bits) {
  pinMode(swdio, OUTPUT);
  ...
  pinMode(swdio, INPUT);
  digitalWrite(swdio, HIGH);
  /* signal a clock pulse */
  digitalWrite(swdck, LOW);
  delay(1);
  digitalWrite(swdck, HIGH);
  delay(1);
 }

Чтение SWD должно возвращать 33 бита, поэтому x = clockIn(5); выглядит немного хрупким.

Даже в этом случае он должен получить три бита ACK. Он должен оставить еще одну кучу битов непрочитанными, что я и имею в виду под хрупкостью.

Однако x = clockIn(5); может быть неверным из-за короткого clockOut(0x25, 7) и отсутствия бита возврата.

Небольшие замечания:

  • Я бы изменил этот цикл clockOut while на цикл for, например, clockIn, чтобы было ясно, что он выполняется для определенного «количества» (бит) бит, которые необходимо передать.
  • delay(1) кажется, это долгое время между битами. Я не помню никаких Ограничения по времени. Однако я бы ожидал, что переводы будут выполняться В 10–100 раз быстрее.
  • ИМХО clockIn(26); clockIn(26); кажется излишне запутанным способом для создания 50-цикловой последовательности соединения/сброса. Я бы, наверное, немного более простая функция, чем clockIn, которая только генерирует последовательность подключения/сброса.

Редактировать:
Оператор res += (1 << i); в clockIn работает.

Однако мне пришлось прочитать его пару раз, чтобы понять, что он собирает двоичное значение, а не выполняет какую-то сложную арифметику. Чаще всего для сборки двоичного значения используются побитовые операторы. Это всего лишь небольшая деталь стиля, однако res |= (1 << i); может более четко создавать двоичное значение, чем res += (1 << i);, потому что он может повлиять только на один бит и никогда на много битов.

,

Спасибо за развернутый ответ! Постараюсь ответить пошагово. Запрос SWD — это 8 бит, да, но последний бит «не должен управляться хостом на swdio», т. е. в этот момент хост должен отключить вывод и просто отправить еще один такт. Я предполагаю, что это выполняется внутри clockIn(5). Вот почему у меня здесь 5 — я хочу отправить несколько тактов, чтобы любой бит парковки, бит поворота и ack тактировались одновременно. Если придет любое другое значение, кроме 0x1F, я надеюсь, что смогу угадать, где в нем ACK..., @Alumashka

Что касается 0xE79E - я считаю, что это вообще не часть протокола SWD, просто какая-то магическая последовательность для переключения с JTAG на SWD (и есть обратная последовательность переключения). Я понятия не имею, нужна ли она этим чипам, поэтому я тестировал с ней и без нее. В любом случае, видите ли, я сам почти ничего не знаю о SWD - я только пару дней назад начал читать руководства... Так что, конечно, я могу ошибаться в чем угодно или во всем. Также спасибо за ценные рекомендации по стилю/дизайну!, @Alumashka