Взаимодействие WavGat Pro mini с другим МК

WavGat
Offline
Зарегистрирован: 14.04.2020

Помогите разобраться новичку.

1) С прерываниями не знаком, что-то не получается найти толковой документации, поэтому пытался решить поставленную задачу в лоб. Тем более что мой МК (WavGat Pro mini) заведомо мощнее того, с которым его нужно подружить. Так же, возможно, неправильно выбрал на какие ноги что цеплять, но в результате даже с расширителями портов цифровые входы/выходы закончились.

2) Задача почти решена, но остались "артефакты", с которыми можно мериться, но лучше побороть, а тут уже не хватает знаний и опыта в этой сфере.

Подзадача:

Устройство посылает сигнал (LATCH), после которого нужно начать вывод данных. Здесь неплохо было бы использовать прерывания, но я не знаю как это делается. В принципе я этот сигнал отлавливаю в цикле и тут проблем нет. По сигналу CLOCK нужно выдать следующий пин (сигнал на линии CLOCK появляется только после LATCH) - обычная последовательная передача данных. Задача усложняется тем, что линии CLOCK две штуки (назовём их CLOCK1 и CLOCK2), соответственно линии, куда нужно посылать данные - тоже 2 штуки. LATCH - общий, а CLOCK1 и CLOCK2 приходят не синхронно, CLOCK2 немного запаздывает относительно CLOCK1 (примерно на пол цикла), но иногда приходят только CLOCK1, тогда на вторую линию ничего выводить не нужно. У меня к моменту появления сигнала LATCH уже подготовлены переменные для вывода в порт.

Проблема: иногда теряются сигналы CLOCK, из-за чего нарушается порядок вывода битов. При переключении микроконтроллера на 32 МГц ошибок стало меньше.

Часть кода (в среде Arduino), которая отвечает за вывод информации (взаимодействие с вторым МК):

  while(1) {
    if ( PINC & 1 ) { // ловим LATCH
      PORTB = (PINB | 24) & DataOut_7; // Начинаем вывод сразу на две линии (первый бит обеих линий)
      while ( PINC & 2 ) {}; // Ждём CLOCK 1

      // Меняем значение на следующее
      PORTB = (PINB | 8) & DataOut1_6; // Так как CLOCK-и приходят не синхронно, то выводим следующий бит (второй) только на первую линию
      byte ReadPortC = 255; // Переменная используется для определения CLOCK1 или CLOCK2 пришёл.
      delayMicroseconds(3); // Нужна небольшая задержка на восстановление линии CLOCK, чтобы не среагировать на один и тот же сигнал 2 раза
      while ( (ReadPortC & 6) == 6) {ReadPortC = PINC;}; // Ждём CLOCK2 или CLOCK1
      if (ReadPortC & 2) { // CLOCK1 в норме, значит пришёл CLOCK 2
       PORTB = (PINB | 16) & DataOut2_6; // Выводим следующий бит (второй) второй линии
       while ( PINC & 2 ) {}; // На этот раз ждём CLOCK1
      };

... // и так далее

 };

Вопросы:

1) Я читаю с аналоговых входов, используя их как цифровые. Если перекинуть провода на цифровые входы, будет ли происходить чтение быстрее или нет? Например while ( PIND & 2 ) {}; вместо while ( PINC & 2 ) {}; Провода уже запаяны и лишний раз лезть туда паяльником, чтобі проверить, не хочется.

2) Если я правильно понимаю, то можно использовать аналоговый компаратор (а их в LGT8F328P как раз 2), чтобы отлавливать CLOCK-и, так как он работает вне зависимости от рабочего цикла программы. Но тут мне не хватает знаний, а гугление не помогает (что-то я не так ищу).

Пните в нужном направлении... Для ясности добавлю еще пару картинок с логического анализатора

Джойстик DendyДжойстик Dendy

WavGat
Offline
Зарегистрирован: 14.04.2020

Пояснения: LATCH по умолчанию в LOW, остальные линии в HIGH

LATCH сейчас припаян к А0 (PC0), CLOCK1 к А1 (PC1), CLOCK2 к А2 (PC3).

Вывожу данные через 11 цифровой выход (PB3) и 12 цифр. выход (PB4).

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

А чем SPI не нравится? Специально создан для высокоскоростных интерфейсов. А про расширение портов посмотрите в сторону мультиплексоров.

WavGat
Offline
Зарегистрирован: 14.04.2020

Порты уже расширил при помощи CD4053. А SPI нужно сразу 2 штуки, причём в параллель. Не уверен, что стандартные библиотеки Arduino c этим справятся. В общем случае картина (с двумя линиями) примерно такая:

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

[quote=WavGat]

А SPI нужно сразу 2 штуки, причём в параллель. Не уверен, что стандартные библиотеки Arduino c этим справятся. 

 

/quote]

При чем здесь библиотеки? Это аппаратная вещь. И вообще - разговор ни о чем - какие скорости предполагаются? И зачем?

А вообще в параллель смотри в сторону многопроцессорных систем или к Б.Гейтсу. Один кристалл всегда выполняет одну задачу.

WavGat
Offline
Зарегистрирован: 14.04.2020

Ну тогда извините, глупость сморозил по незнанию.

Частота процессора, с которым нужно общаться 1.66 MHz. А у меня 32 MHz. Скорость я не знаю, так как описание протокола, которое я нашёл на просторах интернета, отличается от реальной ситуации. Логический анализатор между двумя соседними сигналами CLOCK показал 25,7 KHz, но в зависимости от загруженной программы в тот процессор частота может немного отличаться от этой. На некоторых программах у меня таких артефактов нет, видимо там длительность сигнала на линии CLOCK немного больше.

WavGat
Offline
Зарегистрирован: 14.04.2020

Т.е. нужно использоватьshiftOut(DATA, CLOCK, MSBFIRST, digits); ? И SPI вроде только один, а мне 2 нужно

WavGat
Offline
Зарегистрирован: 14.04.2020

mykaida пишет:

А вообще в параллель смотри в сторону многопроцессорных систем или к Б.Гейтсу. Один кристалл всегда выполняет одну задачу.

На одноядерных процессорах паралельные задачи тоже последовательно выполняются, причем не только и Билла. Да и у меня параллельный вывод в две линии почти получился (при минимальных знаниях).

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

WavGat пишет:

На одноядерных процессорах паралельные задачи тоже последовательно выполняются, причем не только и Билла. Да и у меня параллельный вывод в две линии почти получился (при минимальных знаниях).

Бредим?

Немного о SPI

b707
Offline
Зарегистрирован: 26.05.2017

WavGat - в дешевых Stm32 ака "блюпилл" как раз 2 SPI.

и программируются в Ардуино ИДЕ

b707
Offline
Зарегистрирован: 26.05.2017

mykaida пишет:

Бредим?

ну почему. Если через ДМА - вполне возможно делать вывод на SPI _асинхронно_ - то есть одновременно с другими действиями в программе

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

b707 пишет:

ну почему. Если через ДМА - вполне возможно делать вывод на SPI _асинхронно_ - то есть одновременно с другими действиями в программе

Прошу прощения - был неправ.

WavGat
Offline
Зарегистрирован: 14.04.2020

По приведённой Вами ссылке читаю: "Для Arduino написана специальная библиотека, которая реализует протокол SPI.". (это к моему 3 сообщению).

Может я неправильно выразился, попробую перефразировать: У меня задача считать параллельные данные и выдать их в последовательном виде. Фактически я одним микроконтроллером эмулирую два устройства, т.е. мне нужно чтобы микроконтроллер был не Master, а ДВУМЯ Slave! И да, у меня получилось написать работающую программу для WavGat Pro mini в среде Arduino, которая справляется с поставленной задачей, но при определённых условиях даёт небольшие сбои. Они не критичны, но хочется избавиться от них.

Нужно задействовать SPI? Хорошо, но как сразу в две линии писать?

Мне кажется, что нужно использовать прерывания, но тут я нуб, а за целый день поисков так и не нашёл ничего понятного. Есть конечно готовые примеры, но что за что там отвечает - не понятно. В моём случае в этом микроконтроллере есть 2 встроенных компаратора. Как их задействовать? Есть 4 встроенных опорных напряжения. Как их задействовать в коде? Даташиты скачал, читаю. Голова уже плавится, но пока не могу ничего понять.

Пните в документацию с примерами. Или помогите строчкой кода.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Давайте так - 28кГц для ардуинки - это ничто. Паралельно не считываем - считываем последовательно. Прерывания нужны если Вы работаете на скорости работы 1/10 скорости процессора. Или в остальных особенных случаях. Еще раз - 28кГц для ардуинки это не о чем. Выдаем паралельно :) Не получилось? Да хер с ним - последовательно. 28кГц

 

WavGat
Offline
Зарегистрирован: 14.04.2020

Конечно же последовательно.
Паралельно - это я в том смысле, что два байта нужно паралельно выдать в 2 порта (1 байт в 1 порт, другой байт в другой порт).

WavGat
Offline
Зарегистрирован: 14.04.2020

Проблема у меня в том, что вот этот цикл

       while(PINC & 2) {}; // Ждём CLOCK 1

иногда пропускает момент тактирования. Закономерности нет. Может первый бит растянуть на 2 такта, а может пятый... Длительность импульса, котрый "теряется" при чтении с порта, на картинке:
 

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

WavGat пишет:

Конечно же последовательно.
Паралельно - это я в том смысле, что два байта нужно паралельно выдать в 2 порта (1 байт в 1 порт, другой байт в другой порт).

А последовательно - религия не позволяет? Сначала туда спросил, потом сюда. Скорости никакие. Это аппаратка

WavGat
Offline
Зарегистрирован: 14.04.2020

Так я же и говорю, что последовательно.

Вот весь алгоритм выдачи двух байт ( для второго байта некоторые биты ничего не значат, поэтому они просто пропущены)
 

  while(1) {
    if(PINC & 1) { // LATCH
      LATCH:
      PORTB = (PINB | 24) & DataOut_7; // B (J1 + J2)
      Turbo++;
      while(PINC & 2) {}; // Ждём CLOCK 1

      // Меняем значение на следующее
      PORTB = (PINB | 8) & DataOut1_6; // A (J1)
      CanRead = 0;
      byte ReadPortC = 255;
      delayMicroseconds(3);
      while((ReadPortC & 6) == 6) {ReadPortC = PINC;}; // Ждём CLOCK 2 или CLOCK 1
      if(ReadPortC & 2) { // пришёл CLOCK 2
       PORTB = (PINB | 16) & DataOut2_6; // A (J2)
       while(PINC & 2) {}; // Ждём CLOCK 1
      };

      // Меняем значение на следующее
      PORTB = (PINB | 8) & DataOut1_5; // SEL (J1)
      ReadPortC = 255;
      delayMicroseconds(3);
       while(PINC & 2) {}; // Ждём CLOCK 1

      // Меняем значение на следующее
      PORTB = (PINB | 24) & DataOut1_4; // START (J1)
      delayMicroseconds(3);
      while((PINC & 2) == 2) {}; // Ждём CLOCK 1

      // Меняем значение на следующее
      PORTB = (PINB | 8) & DataOut1_3; // UP (J1)
      ReadPortC = 255;
      delayMicroseconds(3);
      while((ReadPortC & 6) == 6) {ReadPortC = PINC;}; // Ждём CLOCK 2 или CLOCK 1
      if(ReadPortC & 2) { // пришёл CLOCK 2
       PORTB = (PINB | 16) & DataOut2_3; // UP (J2)
       while(PINC & 2) {}; // Ждём CLOCK 1
      };

      // Меняем значение на следующее
      PORTB = (PINB | 8) & DataOut1_2; // DOWN (J1)
      ReadPortC = 255;
      delayMicroseconds(3);
      while((ReadPortC & 6) == 6) {ReadPortC = PINC;}; // Ждём CLOCK 2 или CLOCK 1
      if(ReadPortC & 2) { // пришёл CLOCK 2
       PORTB = (PINB | 16) & DataOut2_2; // DOWN (J2)
       while(PINC & 2) {}; // Ждём CLOCK 1
      };

      // Меняем значение на следующее
      PORTB = (PINB | 8) & DataOut1_1; // LEFT (J1)
      ReadPortC = 255;
      delayMicroseconds(3);
      while((ReadPortC & 6) == 6) {ReadPortC = PINC;}; // Ждём CLOCK 2 или CLOCK 1
      if(ReadPortC & 2) { // пришёл CLOCK 2
       PORTB = (PINB | 16) & DataOut2_1; // LEFT (J2)
       while(PINC & 2) {}; // Ждём CLOCK 1
      };

      // Меняем значение на следующее
      PORTB = (PINB | 8) & DataOut1_0; // RIGHT (J1)
      delayMicroseconds(3);
      byte TestPortC = 2;
      if(ReadPortC & 2) { // пришёл CLOCK 2
       ReadPortC = 255;
       while(ReadPortC & 4) {ReadPortC = PINC;}; // Ждём CLOCK 2
       PORTB = (PINB | 16) & DataOut2_0; // RIGHT (J2)
       TestPortC = 4;
      };
      ReadPortC = 254;
      while((ReadPortC & (TestPortC | 1)) == TestPortC) {ReadPortC = PINC;}; // Ждём CLOCK 1 или CLOCK 2
      if(ReadPortC & 1) goto LATCH;

      // Восстанавливаем состояние портов
      delayMicroseconds(2);
      PORTB = PINB | 24;
    };

//Тут чтение данных от других устройств

  };

А вот картина того, что мы имеем на выходе ( в случае сбоя) для двух линий:

WavGat
Offline
Зарегистрирован: 14.04.2020

if(ReadPortC & 1) goto LATCH; - это костыль, который позволяет заново переслать байт, если пришёл новый сигнал на линии LATCH, когда из-за сбоя передача данных затянулась...

WavGat
Offline
Зарегистрирован: 14.04.2020

В моём случае SPI не подходит по нескольким причинам:

1) В моем микроконтроллере их 2, но 2-ой на USART0, т.е. я потеряю возможность его потом перепрошить и отслеживать в мониторе что не так. Или я опять чего-то не знаю?

2) Смотрим первые две картинки в начале темы (а также читаем, что я писал): сигнал LATCH всё время находится в LOW, и только перед тем как читать данные поднимается в HIGH на короткое время.

 

Копаю в сторону прерываний, но тут пока для меня дремучий лес с кустарниками.

WavGat
Offline
Зарегистрирован: 14.04.2020

Попробовал включить прерывания по изменению уровня на порту. LATCH отлавливается, а вот CLOCK-и - нет. Что я не так сделал?

volatile uint8_t testInt = 6;

#define IN_LATCH   A0 // PC0 LATCH
#define IN_CLOCK1  A1 // PC1 CLOCK1
#define IN_CLOCK2  A2 // PC2 CLOCK2

void setup() {
// 32 MHz
  CLKPR = 1<<PMCE;//разрешить изменение
  CLKPR = B00000000;
  Serial.begin(115200); // Скорость работы COM-порта
// Установим нужные порты на вход
  pinMode(IN_LATCH, INPUT);
  pinMode(IN_CLOCK1, INPUT);
  pinMode(IN_CLOCK2, INPUT);
  PCICR |= 2; // Разрешаем прерывание по изменению уровня на порту PCx
  PCMSK1 = 7; // Указываем по каким портам реагировать на изменение уровня ( PC0, PC1, PC2, что соответствует A0, A1, A2)
  interrupts();              // Разрешить прерывания глобально 
}

ISR(PCINT1_vect) {
  testInt = PINC & 7;
}

void loop() {
//  if(testInt == 4) {
//  if(testInt == 2) {
  if(testInt != 6) {
    noInterrupts();
    Serial.print("Int OK ");
    Serial.println(testInt);
    testInt = 6;
    interrupts();
  }
}

В консоли только 7-ки.

WavGat
Offline
Зарегистрирован: 14.04.2020

Изменил код и нашёл ответ на предыдущий вопрос.

volatile uint8_t testInt = 255;

#define IN_LATCH   A0 // PC0 LATCH
#define IN_CLOCK1  A1 // PC1 CLOCK1
#define IN_CLOCK2  A2 // PC2 CLOCK2

void setup() {
// 32 MHz
  CLKPR = 1<<PMCE;//разрешить изменение
  CLKPR = B00000000;
  Serial.begin(115200); // Скорость работы COM-порта
// Установим нужные порты на вход
  pinMode(IN_LATCH, INPUT);
  pinMode(IN_CLOCK1, INPUT);
  pinMode(IN_CLOCK2, INPUT);
  PCICR |= 2; // Разрешаем прерывание по изменению уровня на порту PCx
  PCMSK1 = 7; // Указываем по каким портам реагировать на изменение уровня ( PC0, PC1, PC2, что соответствует A0, A1, A2)
  interrupts();              // Разрешить прерывания глобально 
}

ISR(PCINT1_vect) {
  testInt = testInt ^ 8;
  PORTB = (PINB | 8) & testInt; // A (J1)
}

void loop() {
}

В момент, когда подпрограмма прерываний доходит до чтения порта, сигнал уже поменялся обратно.

Вывод: прерывание по LATCH можно оставить, но проблему с потерей CLOCK-ов они не решают, так как понять по какой из двух линий пришел CLOCK просто не получится!