1YHAIhwx

Arduino некорректное значение micros()

9 сообщений в этой теме

1YHAIhwx    3

Я пытаюсь захватить сигнал с ИК-пульта ДУ. Делаю это при помощи Arduino Micro 16 МГц с подключенным к нему фотоприёмником TSOP1736. Код следующий:

const int ARDUINO_MICRO_INTTERRUPT_ON_PIN3 = 0;
const int BUFFER_LEN = 40;

volatile unsigned long periods[BUFFER_LEN];
volatile int i = 0;
volatile unsigned long lastMicros = 0;
volatile unsigned long mcs;
volatile boolean isRunning = false;

void sensorInterruptHandler() {
  isRunning = true;
}

void setup() {
 attachInterrupt(ARDUINO_MICRO_INTTERRUPT_ON_PIN3, sensorInterruptHandler, CHANGE);  
 Serial.begin(9600);
}

void loop() {
  if(!isRunning) {
     return; 
  }
  if(i == BUFFER_LEN) {
    detachInterrupt(ARDUINO_MICRO_INTTERRUPT_ON_PIN3);
    for(int x = 1; x < BUFFER_LEN; ++x) {
       Serial.print(periods[x]);
       Serial.print(" ");
    }
    Serial.println();
    Serial.flush();
    lastMicros = 0;
    delay(1500);
    i = 0;
    attachInterrupt(ARDUINO_MICRO_INTTERRUPT_ON_PIN3, sensorInterruptHandler, CHANGE);
  } else {
    mcs = micros();
    if(lastMicros > mcs) {
       Serial.print("Overflow!"); 
    }
    periods[i] = mcs - lastMicros;
    lastMicros = mcs;
    ++i;
  }
  isRunning = false;
}

Работает следующим образом: всякое изменение уровня на ножке 3 вызывает установку флага isRunning. Главный цикл проверяет установку флага, если он установлен, записывает в буфер periods промежуток времени в микросекундах, в течении которого на ножке не менялся логический уровень. Если буфер заполнен, его содержимое выводится в UART и процесс повторяется. Считываю я всегда один и тот же сигнал, представленный на изображении. И проблема в том, что периодически я получаю совершенно некорректную величину временного промежутка, которую не могу объяснить даже переполнением (для проверки гипотезы о переполнении была вставлена проверка - сообщение о переполнении не печатается). Ниже массив данных, которые я получаю, звездочками отмечены проблемные считывания.

2680 900 496 808 520 376 512 368 468 856 908 428 460 424 468 424 464 416 464 424 464 420 468 420 468 420 460 424 464 424 464 420 468 420 908 416 460 904 876 
2684 896 484 820 520 368 520 368 *1344* *1312* 520 368 528 360 456 428 464 420 468 420 468 420 468 416 464 424 468 420 464 420 468 420 908 416 464 872 904 *83656* 2684 
2680 900 492 816 524 360 520 368 464 860 908 428 460 428 460 424 464 424 468 416 472 416 460 424 464 424 464 424 468 416 460 428 460 424 904 424 468 864 912 
2680 904 492 812 516 372 516 368 *1356* *1304* 516 372 516 368 468 428 460 420 460 424 464 420 468 420 468 420 460 432 456 424 464 424 912 412 468 864 904 *83656* 2684 
2676 904 488 816 524 364 524 364 *1348* *1308* 524 364 524 364 460 424 464 424 468 420 468 416 460 424 464 424 468 420 468 416 460 428 912 412 468 896 880 *83652* 2680 
2676 904 496 812 520 364 524 364 460 864 912 424 468 416 472 416 460 424 464 424 468 420 468 416 460 428 460 428 464 420 468 416 472 424 904 416 464 896 880

Почему я получаю эти странные значения? Эффект повторяется с возмутительным постоянством, и если значения 1344, 1312 ещё как-то можно объяснить, то появления промежутка в 83656 мкс мне совсем не понятно. Что же я делаю не так и откуда берутся столь странные значения? Притом они не случайны и придерживаются какого-то диапазона. Позиция проблемных периодов тоже остаётся неизменной. При подключении датчика вместо микрофона и захвата сигнала при помощи Audacity изменения периода импульсов не наблюдаю.

 

signal.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Быстрый заказ печатных плат

Полный цикл производства PCB по низким ценам!

  • x
    мм
Заказать Получить купон на $5.00
Yurkin2015    247

А почему такая странная проверка флага?

47 минут назад, 1YHAIhwx сказал:

if(!isRunning) { return; }

сразу после return программа выходит из loop(), что-то там ещё делает, потом возвращается обратно в loop(). Вот иногда вне этого loop() программа занята чем-то, и набегает лишнее время.

Лучше так проверять

while(!isRunning) { }

Да, ещё нужно внешний while(1){ текст процедуры} поставить, тогда программа точно не будет вылетать из этого loop() 

Изменено пользователем Yurkin2015

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1YHAIhwx    3
Quote

сразу после return программа выходит из loop(), что-то там ещё делает

Выходит куда и делает что? Напоминаю, функция loop() {} в ардуинодиалекте аналогична коду while(true){}. Таким образом, код

void loop() {
    if(!isRunning) {
        return;
    }
    // do something useful here
}

функционально идентичен
 

while(true) {
    if(!isRunning) {
        continue;
    }
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Yurkin2015    247

При выходе из loop() программа занимается отправкой байтов по Serial интерфейсу

#include <Arduino.h>

int main(void)
{
 init();

#if defined(USBCON)
 USBDevice.attach();
#endif
 
 setup();
    
 for (;;) {
 loop();
 if (serialEventRun) serialEventRun();
 }
        
 return 0;
}

 

Поэтому предлагаю, пока не выполнилось условие

if(i == BUFFER_LEN)

оставаться в пределах loop(), а уже при выполнении условия посылать данные по UART и выходить из loop()

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1YHAIhwx    3
void loop() {
  while(true) {
    if(i == BUFFER_LEN) {
      detachInterrupt(ARDUINO_MICRO_INTTERRUPT_ON_PIN3);
      for(int x = 1; x < BUFFER_LEN; ++x) {
          Serial.print(periods[x]);
          Serial.print(" ");
       }
        Serial.println();
        Serial.flush();
        lastMicros = 0;
        delay(1500);
        i = 0;
        attachInterrupt(ARDUINO_MICRO_INTTERRUPT_ON_PIN3, sensorInterruptHandler, CHANGE);
    } else {
      if(!isRunning) {
        continue; 
      }
      byte oldSREG = SREG;
      noInterrupts ();
      mcs = micros();
      periods[i] = mcs - lastMicros;
      lastMicros = mcs;
      ++i;
      SREG = oldSREG;
    }
    isRunning = false;
  }
}

Нет, это не помогло. Также стал заполнять массив периодов с отключенными прерываниями. Притом даже если я просто буду печатать в UART текущее значение микросекунд, видно, что иногда между соседними значениями проскакиевает большой промежуток (более 3мс). Не понимаю, откуда они берутся.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Yurkin2015    247

Отвчечаю на бегу, последний вариант ещё не разглядел, но можно сделать по-простому:

оставить Ваш первый вариант, только увеличить и сделать массив достаточного размера BUFFER_LEN для записи всех шести входных пакетов. И только после записи всего, что нужно, уже тогда спокойно отправлять данные по UART.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1YHAIhwx    3

Уже пробовал, результат аналогичен. Обновление арудиноиде тоже не спасло. Инициализация UART уже после отправки данных не помогла тоже. Девятый и десятый отчёт всё равно периодически становится равным ~1300 мкс вместо положенных 400-900.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Yurkin2015    247

Ну, тогда старым казацким способом: записывать длительность прямо в прерывании

void sensorInterruptHandler() {
    mcs = micros();
    periods[i] = mcs - lastMicros;
    lastMicros = mcs;
    ++i;
}

а в loop() проверять i == BUFFER_LEN  и отправлять по UART когда наберётся нужное число байтов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1YHAIhwx    3

А причина-то очевидна - с micros() всё в порядке, ровно как и с кодом. Таки дело в пульте. Я подключил фотоприёмник вместо микрофона ещё раз и настрелял десятки последовательностей. Да, пульт иногда производит импульс 9 и 10 в ~1300 мкс вместо ~900. Просто в первый раз я очень удачно нащёлкал и в запись такие последовательности не попали. И последняя чиселка тоже вполне объяснима: пульт пакет передал, встал на паузу, затем снова передавать начал, я же прерывание на изменение логического уровня повесил, вот он длину пребывания на выходе лог. 0 и считал.

Таким образом:

- с ардуиной и функцией micros() всё в порядке;

- код работает удовлетворительно;

- неправильная постановка эксперимента ведёт к потере времени.

Убедился в этом, генеря сигнал другой ардуиной и ловя его аудаситей и скетчем выше. Замечательно трекает все последовательности с удовлетворительной погрешностью.

const int LED_PIN = 13;
const int LED_PIN_CARRIER = 12;

void setup() {
  pinMode(LED_PIN, OUTPUT);
  pinMode(LED_PIN_CARRIER, OUTPUT);
}

void loop() {
  tone(LED_PIN_CARRIER, 38000);
  while(true) {
    delay(10);
    digitalWrite(LED_PIN, 0);
    delay(10);
    digitalWrite(LED_PIN, 1);
  }
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ваша публикация должна быть проверена модератором

Гость
Вы не авторизованы. Если у вас есть аккаунт, пожалуйста, войдите.
Ответить в тему...

×   Вставлено в виде отформатированного текста.   Восстановить форматирование

  Разрешено не более 75 смайлов.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставить изображения напрямую. Загрузите или вставьте изображения по ссылке.

Загрузка...

  • Похожие публикации

    • Автор: carlogulliani
      Добрый день!
      Столкнулся с такой проблемой, пытаюсь взаимодействовать с чипом AFE4300 по SPI. В дотащите указано, что уровень сигнала 2 - 3 вольта. Подключаю к nrf51 (использует уровень 3.3 вольта), ресетю и пытаюсь прочитать дефолтное значение регистра (0x01C3), в итоге получаю 0xFFFF.
      Перепроверил на Arduino Mega 2560, где уровень сигнала 5 вольт (подключил напрямую без level shifter) и считал свои дефолтные значения. Попробовал также считать другие 4 регистра, все также отлично считалось.
      Вот теперь не пойму, в дотащите указано не верно или у меня что-то не то.
      Еще вопрос про согласовать уровней TXB0108. Пытался через нее прокинуть сигнал от nrf51 (3.3v) на afe4300. Питание А - 3.3в, каналы А к nrf51, питание Б 5в, каналы Б к afe4300, OE к 3.3в. Не завелось, даже анализатор говорит, что MOSI шлет 0xFF, вместо реальных данных. Земля везде общая.
      Правда есть оговорка, как данный уровень ко мне приехал я мог по ошибке запитать Б 3 вольтами, а А 5 вольтами. Но даташит на него говорит, что когда А > Б, это не повреждает чип, хотя А должно быть до 3.6 вольт. Еще мог OE подключить к 5 вольтам. В общем, могли ли мои неразумные операции вывести его из строя???
       
      Еще в даташите указано, что неиспользуемые каналы надо подключить либо к питанию, либо к земле. Это может влиять на то, что я сейчас получаю?
    • Автор: mefi73
      Эта статья является логическим продолжением  вот этой статьи про монохромный OLED дисплей. На этот раз мне в руки достался цветной OLED дисплей, разрешением 96*64 пикселя от магазина Banggood (ссылка на дисплей) Пока не забыл, на странице товара есть ссылка на архив с документацией на дисплей.
      Кроме того достаточно информации по дисплею встречается в сети, так же есть готовые библиотеки для нетерпеливых (от Adafruit, Seeed-Studio и конечно же монстр среди библиотек для дисплеев U8Glib). Я же покажу работу с дисплеем безо всяких библиотек, покажу в среде программирования ArduinoIDE, что бы было понятно новичкам (матерые программисты наверняка разберутся).
      Итак, дисплей может подключаться при помощи параллельных интерфейсов (6800, 8080) и последовательного интерфейса SPI. В модуле, который попал мне в руки, реализован SPI протокол.

      Распиновка слева-направо: 2 вывода для питания, SCL - предназначен для тактового сигнала, SDA - по этому входу в контроллер дисплея поступают данные, RES - предназначен для сброса дисплея, DC (data/command) - логический сигнал на этом входе сообщает дисплею что в данный момент передается, данные или команда (об этом чуть позже подробнее), CS - обычный chip select протокола SPI, низкий уровень на этом входе сообщает дисплею, что данные, поступающие по нему, предназначены именно для дисплея. Подробно вдаваться в суть протока SPI я не буду, стоит только уточнить, что дисплей работает в режиме SPI_MODE3 (CPOL=1, CPHA=1).
      Вас могут смутить обозначения SDA и SCL, ведь они применяются для обозначения выводов устройств, работающих по протоколу I2C, но всё на самом деле не так плохо. Поскольку по линии SDA идут данные от микроконтроллера к дисплею - он подключается к выводу MOSI микроконтроллера (D11 на ардуино). По SCL идут тактовые сигналы, а значит он подключается к выводу SCK микроконтроллера (D13 на ардуино).
      Для выводов RES, DC и CS можно выбрать любые выводы (у меня D10 для CS, D8 для DC и  D9 для RES). Библиотека SPI не будет управлять этими выводами, это придется делать вручную. Разберемся для чего нужен каждый из этих выводов.
      CS - самое простое, логический 0 говорит дисплею о том, что данные предназначены для него, логическая 1 - о том что передача данных завершена.
      RES - служит для сброса дисплея, для этого надо на некоторое время подать на этот вывод логический 0. Это необходимо сделать один раз в начале программы перед инициализацией дисплея.
      DC - логический 0, подаваемый на этот вывод, сообщает дисплею о том, что передаются команды, логическая 1 - передаются данные.
      На основании этого создаем две функции для отправки команды и данных соответственно. 
      #include <SPI.h> const int ss = 10; //slave select const int dc = 8; // data/command data=1 command=0 const int reset = 9; //oled reset=0 void oledCommand(uint8_t val) //общая функция отправки команды дисплею {   digitalWrite(ss, LOW); //slave select устанавливаем в 0, это активирует SPI   digitalWrite(dc, LOW); //DC равен 0, это значит что отправляется команда   SPI.transfer(val); //отправляем команду стандартной функцией библиотеки SPI   digitalWrite(ss, HIGH); //slave select устанавливаем в 1, это означает что работа с SPI завершена } void oledData(uint8_t val) //общая функция отправки данных дисплею {   digitalWrite(ss, LOW); //slave select устанавливаем в 0, это активирует SPI   digitalWrite(dc, HIGH); //DC равен 1, это значит что отправляются данные   SPI.transfer(val); //отправляем данные стандартной функцией библиотеки SPI   digitalWrite(ss, HIGH); //slave select устанавливаем в 1, это означает что работа с SPI завершена } void setup() {  pinMode(ss, OUTPUT);  pinMode(dc, OUTPUT);  pinMode(reset, OUTPUT);  SPI.begin();  SPI.setDataMode(SPI_MODE3);  oledInit(); } void setup() {  pinMode(ss, OUTPUT);  pinMode(dc, OUTPUT);  pinMode(reset, OUTPUT);  SPI.begin();  SPI.setDataMode(SPI_MODE3);  oledInit(); } Обратите внимание на функция oledInit() в предпоследней строке кода. Прежде чем дисплей сможет что-либо выводить на экран, его необходимо настроить (инициализировать). Для этого посылаем команды, приведенные в следующей диаграмме.

      init.PNG
      В программе это будет выглядеть так:
      void oledInit() //функция инициализации дисплея {   digitalWrite(reset, HIGH); //процедура сброса дисплея   delay(100);   digitalWrite(reset, LOW);   delay(100);   digitalWrite(reset, HIGH);   delay(100);   //процедура инициализации дисплея   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0xAE); //display OFF   SPI.transfer(0xA0); //remap & color depth setting   SPI.transfer(0x72);          //b01110010 расшифровка ниже   /*   b01 - 65k format, (00 -256 color, 10 - 65k color format 2)   1 - enable COM split odd even (0 - disable)   1 - scan COM95 to COM0 (0 - COM0 to COM95) отражение по короткой стороне   0 - disable left-right swaping (1 - enable swaping)   0 - RGB color (1 - BGR color)   1 - RAM column 0 to 95 (0 - 95 to 0)   0 - horizontal address increment (1 - vertical)   */   SPI.transfer(0xA1); //set display start line (0-63)   SPI.transfer(0x0);   SPI.transfer(0xA2); //set vertical offset (0-63)   SPI.transfer(0x0);   SPI.transfer(0xA4); //normal display (A5 - all pixel ON, A6 - all pixel OFF, A7 - inverse display)   SPI.transfer(0xA8); //set MUX ratio N+1 mux   SPI.transfer(0x3F); //default 0x3F   SPI.transfer(0xAD); //select internal Vcc supply   SPI.transfer(0x8E); //default 0x8E   SPI.transfer(0xB0); //set power saving mode   SPI.transfer(0x0B); //default 0x0B (disable power saving mode) 0X1A - enable   SPI.transfer(0xB1); //set reset, pre-charge period   SPI.transfer(0x31); //default 0x31   SPI.transfer(0xB3); //oscillator frequency   SPI.transfer(0xF0); //default 0xF0   SPI.transfer(0x8A); //set second pre-charge color A   SPI.transfer(0x64); //default 0x64   SPI.transfer(0x8B); //set second pre-charge color B   SPI.transfer(0x78); //default 0x78   SPI.transfer(0x8C); //set second pre-charge color C   SPI.transfer(0x64); //default 0x64   SPI.transfer(0xBB); //set pre-charge voltage level   SPI.transfer(0x3A); //default 0x3A   SPI.transfer(0xBE); //set COM deselect voltage level   SPI.transfer(0x3E); //default 0x3E   SPI.transfer(0x87); //set master current   SPI.transfer(0x06); //default 0x06   SPI.transfer(0x81); //set contrast for color A   SPI.transfer(0x91); //default 0x91   SPI.transfer(0x82); //set contrast for color B   SPI.transfer(0x50); //default 0x50   SPI.transfer(0x83); //set contrast for color C   SPI.transfer(0x7D); //default 0x7D   SPI.transfer(0xAF); //display ON, normal mode   digitalWrite(ss, HIGH); } Теперь дисплей готов к выводу изображения. Но стоит рассмотреть некоторые команды. В частности очень важны следующие строки:
      SPI.transfer(0xA0); //remap & color depth setting
      SPI.transfer(0x72); //b01110010 расшифровка ниже
      /* b01 - 65k format, (00 -256 color, 10 - 65k color format 2) - здесь мы выбираем в каком формате будут задаваться цвета и сколько цветов будет возможно использовать
      Поскольку выбираем 65 тысяч цветов, то значение цвета в один байт не поместится, только в два байта.
      1 - enable COM split odd even (0 - disable)
      1 - scan COM95 to COM0 (0 - COM0 to COM95) отражение по короткой стороне
      0 - disable left-right swaping (1 - enable swaping) 0 - RGB color (1 - BGR color) задаем привычный нам формат RGB
      1 - RAM column 0 to 95 (0 - 95 to 0)
      0 - horizontal address increment (1 - vertical) */ Выбираем как будут выводиться данные на дисплей, слева-направо сверху-вниз (привычный нам способ, потому что и пишем так и массивы задаем) или сверху-вниз слева-направо. Эти методы адресации рассмотрены в предыдущей статье, лишь отмечу что мы будем использовать горизонтальную адресацию.
      Поскольку для задания цвета у нас есть всего 2 байта, а формат RGB предполагает 3, то необходимо произвести преобразование. Для красного цвета отводятся первые 5 бит, затем 6 бит зеленого цвета, замыкают 5 бит синего, поэтому функцию преобразования цвета я назвал color565
       
       uint16_t c;   c = r >> 3;   c <<= 6;   c |= g >> 2;   c <<= 5;   c |= b >> 3;   return c;// получаем 16-битное значение цвета и возвращаем его } Поскольку данными необходимо передавать только цвет, то функцию передачи данных можно переделать в функцию передачи цвета, но 16-битное значение цвета необходимо будет разбить на два 8-битных и послать их одно за другим.
      void oledDataColor(uint16_t color) //измененная функция для отправки 16-битного значения цвета {   digitalWrite(ss, LOW);   digitalWrite(dc, HIGH);   SPI.transfer(color >> 8); //разбиваем 16-битное значение на 2 8-битных   SPI.transfer(color);   digitalWrite(ss, HIGH); } Теперь можно рисовать, и начну я с базового элемента любого растрового изображения - пикселя. Для отображения графических примитивов предусмотрены готовые функции, но не для пикселя и окружности, поэтому будем изобретать велосипед.
      Я упоминал про горизонтальную адресацию, команды и данные, и сейчас я свяжу это всё воедино и поведаю как вывести изображение на дисплей (но делать я этого конечно же не буду).
      Представим, что необходимо вывести изображение размером N на N пикселей, левый верхний угол изображения должен находиться в координатах х=X, у=Y. Для этого необходимо выбрать прямоугольную область на дисплее, а затем передать значения цвета пикселей по очереди обходя каждый пиксель изображения слева-направо сверху-вниз. Полученные дисплеем значения цвета так же будут выводиться слева-направо сверху-вниз в пределах выбранной области, и обход пикселей будет таким, как представлен на изображении ниже.

      Для выбора области на дисплее необходимо передать команду 0x15,значения Х и У левого верхнего угла области, затем команду 0x75 и значения Х и У правого нижнего угла области. Все эти значения передаются командами, то есть вывод DC подтянут к нулю. Затем подаем на DC логическую единицу и посылаем значения цвета каждого пикселя. Функции отправки команд, данных и цвета я уже привел. Далее необходимо включить фантазию и принять факт что один пиксель - это изображение состоящее из одного пикселя, и процедуру вывода изображения применить к одному единственному пикселю. В итоге получается вот такая функция:
      //функция задает цвет выбранной точке void oledPixel(uint8_t x, uint8_t y, uint16_t color) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x15);   SPI.transfer(x);   SPI.transfer(95);   SPI.transfer(0x75);   SPI.transfer(y);   SPI.transfer(63);   delay(1);   digitalWrite(dc, HIGH);   SPI.transfer(color >> 8);   SPI.transfer(color);   delay(1);   digitalWrite(ss, HIGH); } Далее пойдут уже готовые функции для вывода линии, прямоугольника и залитого прямоугольника. 
      //функция отрисовывает линию определенного цвета между двумя указанными координатами void oledLine (uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint16_t color) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x21);   SPI.transfer(x1); //x start   SPI.transfer(y1); //Y start   SPI.transfer(x2); //X end   SPI.transfer(y2); //Y end   delay(1);   //здесь синтезированный в формат 565 цвет разбирается отбратно   //я понимаю что это костыль, но во первых для задания цвета необходимо использовать один аргумент вместо трех   //а во вторых, я использовал именно такой способ для общего понимания работы с цветом при работе с данным дисплеем   SPI.transfer((color >> 11) & 0x1F); //R color   SPI.transfer((color >> 5) & 0x3F); //G color   SPI.transfer(color & 0x1F); //B color   delay(1);   digitalWrite(ss, HIGH); } //функция рисует прямоугольник заданной высоты ширины и цвета, левый верхний угол прямоугольника задается первыми двумя аргументами void oledRect (uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t colorFrame) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x26); //настройка заливки прямоугольника   SPI.transfer(0x0); //отключаем заливку прямоугольника   SPI.transfer(0x22);   SPI.transfer(x); //x start   SPI.transfer(y); //Y start   SPI.transfer(x + w); //X end   SPI.transfer(y + h); //Y end   delay(1);   SPI.transfer((colorFrame >> 11) & 0x1F); //R color frame   SPI.transfer((colorFrame >> 5) & 0x3F); //G color   SPI.transfer(colorFrame & 0x1F); //B color   delay(10);   digitalWrite(ss, HIGH); } //то же самое, но прямоугольникк залит определенным цветом (6 аргумент задает цвет заливки) void oledRectFill (uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t colorFrame, uint16_t colorFill) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x26); //настройка заливки прямоугольника   SPI.transfer(0x1);  //включаем заливку прямоугольника   SPI.transfer(0x22);   SPI.transfer(x); //x start   SPI.transfer(y); //Y start   SPI.transfer(x + w); //X end   SPI.transfer(y + h); //Y end   delay(1);   SPI.transfer((colorFrame >> 11) & 0x1F); //R color frame   SPI.transfer((colorFrame >> 5) & 0x3F); //G color   SPI.transfer(colorFrame & 0x1F); //B color   SPI.transfer((colorFill >> 11) & 0x1F); //R color fill   SPI.transfer((colorFill >> 5) & 0x3F); //G color   SPI.transfer(colorFill & 0x1F); //B color   delay(10);   digitalWrite(ss, HIGH); } Так же предусмотрена функция очистки прямоугольной области дисплея и она же используется для очистки всего дисплея.
      void oledClear(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x25);   SPI.transfer(x1);   SPI.transfer(y1);   SPI.transfer(x2);   SPI.transfer(y2);   delay(1);   digitalWrite(ss, HIGH); } void oledClearAll() {   oledClear(0, 0, 95, 63); } И ещё команды скролинга дисплея. В них я глубоко не вникал, заставил картинку двигаться вертикально, но не смог заставить двигаться горизонтально. На том и хватит, я вряд ли буду использовать эти команды.
       
      //настройка скролинга дисплея void oledScrollSetup (uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x27);   SPI.transfer(a);   SPI.transfer(b);   SPI.transfer(c);   SPI.transfer(d);   SPI.transfer(e);   delay(1);   digitalWrite(ss, HIGH); } void oledScrollOn() {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x2F);   digitalWrite(ss, HIGH); } void oledScrollOff() {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x2E);   digitalWrite(ss, HIGH); } Приведу весь код "скетча".
      #include <SPI.h> const int ss = 10; //slave select const int dc = 8; // data/command data=1 command=0 const int reset = 9; //oled reset=0 void oledCommand(uint8_t val) //общая функция отправки команды дисплею {   digitalWrite(ss, LOW); //slave select устанавливаем в 0, это активирует SPI   digitalWrite(dc, LOW); //DC равен 0, это значит что отправляется команда   SPI.transfer(val); //отправляем команду стандартной функцией библиотеки SPI   digitalWrite(ss, HIGH); //slave select устанавливаем в 1, это означает что работа с SPI завершена } void oledData(uint8_t val) //общая функция отправки данных дисплею {   digitalWrite(ss, LOW); //slave select устанавливаем в 0, это активирует SPI   digitalWrite(dc, HIGH); //DC равен 1, это значит что отправляются данные   SPI.transfer(val); //отправляем данные стандартной функцией библиотеки SPI   digitalWrite(ss, HIGH); //slave select устанавливаем в 1, это означает что работа с SPI завершена } void oledDataColor(uint16_t color) //измененная функция для отправки 16-битного значения цвета {   digitalWrite(ss, LOW);   digitalWrite(dc, HIGH);   SPI.transfer(color >> 8); //разбиваем 16-битное значение на 2 8-битных   SPI.transfer(color);   digitalWrite(ss, HIGH); } void oledInit() //функция инициализации дисплея {   digitalWrite(reset, HIGH); //процедура сброса дисплея   delay(100);   digitalWrite(reset, LOW);   delay(100);   digitalWrite(reset, HIGH);   delay(100);   //процедура инициализации дисплея   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0xAE); //display OFF   SPI.transfer(0xA0); //remap & color depth setting   SPI.transfer(0x72);          //b01110010 расшифровка ниже   /*   b01 - 65k format, (00 -256 color, 10 - 65k color format 2)   1 - enable COM split odd even (0 - disable)   1 - scan COM95 to COM0 (0 - COM0 to COM95) отражение по короткой стороне   0 - disable left-right swaping (1 - enable swaping)   0 - RGB color (1 - BGR color)   1 - RAM column 0 to 95 (0 - 95 to 0)   0 - horizontal address increment (1 - vertical)   */   SPI.transfer(0xA1); //set display start line (0-63)   SPI.transfer(0x0);   SPI.transfer(0xA2); //set vertical offset (0-63)   SPI.transfer(0x0);   SPI.transfer(0xA4); //normal display (A5 - all pixel ON, A6 - all pixel OFF, A7 - inverse display)   SPI.transfer(0xA8); //set MUX ratio N+1 mux   SPI.transfer(0x3F); //default 0x3F   SPI.transfer(0xAD); //select internal Vcc supply   SPI.transfer(0x8E); //default 0x8E   SPI.transfer(0xB0); //set power saving mode   SPI.transfer(0x0B); //default 0x0B (disable power saving mode) 0X1A - enable   SPI.transfer(0xB1); //set reset, pre-charge period   SPI.transfer(0x31); //default 0x31   SPI.transfer(0xB3); //oscillator frequency   SPI.transfer(0xF0); //default 0xF0   SPI.transfer(0x8A); //set second pre-charge color A   SPI.transfer(0x64); //default 0x64   SPI.transfer(0x8B); //set second pre-charge color B   SPI.transfer(0x78); //default 0x78   SPI.transfer(0x8C); //set second pre-charge color C   SPI.transfer(0x64); //default 0x64   SPI.transfer(0xBB); //set pre-charge voltage level   SPI.transfer(0x3A); //default 0x3A   SPI.transfer(0xBE); //set COM deselect voltage level   SPI.transfer(0x3E); //default 0x3E   SPI.transfer(0x87); //set master current   SPI.transfer(0x06); //default 0x06   SPI.transfer(0x81); //set contrast for color A   SPI.transfer(0x91); //default 0x91   SPI.transfer(0x82); //set contrast for color B   SPI.transfer(0x50); //default 0x50   SPI.transfer(0x83); //set contrast for color C   SPI.transfer(0x7D); //default 0x7D   SPI.transfer(0xAF); //display ON, normal mode   digitalWrite(ss, HIGH); } uint16_t color565(uint8_t r, uint8_t g, uint8_t b) //функция преобразования цвета R8G8B8bit в формат R5G6B5bit {   uint16_t c;   c = r >> 3;   c <<= 6;   c |= g >> 2;   c <<= 5;   c |= b >> 3;   return c;// получаем 16-битное значение цвета и возвращаем его } //функция задает цвет выбранной точке void oledPixel(uint8_t x, uint8_t y, uint16_t color) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x15);   SPI.transfer(x);   SPI.transfer(95);   SPI.transfer(0x75);   SPI.transfer(y);   SPI.transfer(63);   delay(1);   digitalWrite(dc, HIGH);   SPI.transfer(color >> 8);   SPI.transfer(color);   delay(1);   digitalWrite(ss, HIGH); } void oledSetArea(uint8_t x, uint8_t y, uint8_t w, uint8_t h) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x15);   SPI.transfer(x);   SPI.transfer(y);   SPI.transfer(0x75);   SPI.transfer(x + w);   SPI.transfer(y + h);   delay(1);   digitalWrite(ss, HIGH); } //функция отрисовывает линию определенного цвета между двумя указанными координатами void oledLine (uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint16_t color) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x21);   SPI.transfer(x1); //x start   SPI.transfer(y1); //Y start   SPI.transfer(x2); //X end   SPI.transfer(y2); //Y end   delay(1);   //здесь синтезированный в формат 565 цвет разбирается отбратно   //я понимаю что это костыль, но во первых для задания цвета необходимо использовать один аргумент вместо трех   //а во вторых, я использовал именно такой способ для общего понимания работы с цветом при работе с данным дисплеем   SPI.transfer((color >> 11) & 0x1F); //R color   SPI.transfer((color >> 5) & 0x3F); //G color   SPI.transfer(color & 0x1F); //B color   delay(1);   digitalWrite(ss, HIGH); } //функция рисует прямоугольник заданной высоты ширины и цвета, левый верхний угол прямоугольника задается первыми двумя аргументами void oledRect (uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t colorFrame) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x26); //настройка заливки прямоугольника   SPI.transfer(0x0); //отключаем заливку прямоугольника   SPI.transfer(0x22);   SPI.transfer(x); //x start   SPI.transfer(y); //Y start   SPI.transfer(x + w); //X end   SPI.transfer(y + h); //Y end   delay(1);   SPI.transfer((colorFrame >> 11) & 0x1F); //R color frame   SPI.transfer((colorFrame >> 5) & 0x3F); //G color   SPI.transfer(colorFrame & 0x1F); //B color   delay(10);   digitalWrite(ss, HIGH); } //то же самое, но прямоугольникк залит определенным цветом (6 аргумент задает цвет заливки) void oledRectFill (uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint16_t colorFrame, uint16_t colorFill) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x26); //настройка заливки прямоугольника   SPI.transfer(0x1);  //включаем заливку прямоугольника   SPI.transfer(0x22);   SPI.transfer(x); //x start   SPI.transfer(y); //Y start   SPI.transfer(x + w); //X end   SPI.transfer(y + h); //Y end   delay(1);   SPI.transfer((colorFrame >> 11) & 0x1F); //R color frame   SPI.transfer((colorFrame >> 5) & 0x3F); //G color   SPI.transfer(colorFrame & 0x1F); //B color   SPI.transfer((colorFill >> 11) & 0x1F); //R color fill   SPI.transfer((colorFill >> 5) & 0x3F); //G color   SPI.transfer(colorFill & 0x1F); //B color   delay(10);   digitalWrite(ss, HIGH); } void oledClear(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x25);   SPI.transfer(x1);   SPI.transfer(y1);   SPI.transfer(x2);   SPI.transfer(y2);   delay(1);   digitalWrite(ss, HIGH); } void oledClearAll() {   oledClear(0, 0, 95, 63); } //настройка скролинга дисплея void oledScrollSetup (uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e) {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x27);   SPI.transfer(a);   SPI.transfer(b);   SPI.transfer(c);   SPI.transfer(d);   SPI.transfer(e);   delay(1);   digitalWrite(ss, HIGH); } void oledScrollOn() {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x2F);   digitalWrite(ss, HIGH); } void oledScrollOff() {   digitalWrite(ss, LOW);   digitalWrite(dc, LOW);   SPI.transfer(0x2E);   digitalWrite(ss, HIGH); } void setup() {   pinMode(ss, OUTPUT);   pinMode(dc, OUTPUT);   pinMode(reset, OUTPUT);   SPI.begin();   SPI.setDataMode(SPI_MODE3);   oledInit();   oledClearAll();   randomSeed(millis); } void loop() {  oledClearAll();   oledPixel(10, 10, color565(255, 0, 0));   oledLine(45, 32, 40, 63, color565(0, 255, 0));   oledRectFill(60, 0, 10, 20, color565(0, 0, 255), color565(255, 255, 0));   oledRect(40, 0, 10, 20, color565(255, 255, 0));   int x = 20, y = 31, w = 10, h = 10;   oledSetArea(x, y, w, h);   for (int i = 0; i < (w*h); i++) {     oledDataColor(color565(random(0, 255), random(0, 255), random(0, 255)));   }      delay(5000); } Результат работы на фото (специально сдвинул экспозицию в минус): вывод точки, линии, залитого и пустого прямоугольника и массива из точек случайного цвета.

      Плюсы данного дисплея очевидны: малое потребление из-за отсутствия подсветки, малая толщина дисплея, большие углы обзора, ну и наконец он цветной. Чип SSD1331 позволяет обращаться к каждой точке напрямую ( в отличии от монохромного дисплея на чипе SSD1306), что намного упрощает работу с ним. Ну и библиотеки для этого дисплея уже написаны, а то я тут всё велосипеды изобретаю.
      P.S. В архиве скетч, библиотеки для ArduinoIDE и даташиты.
      SSD1331.rar
    • Автор: CasperReduct
      Заранее извиняюсь если вопрос для многих будет примитивным, но так как в радиотехнике 0  - прошу помощи.
      Итак: есть Arduino mini,  различные датчики, датчик MQ135(потребление ~150mA). Питание: к пинам +5,Gnd припаян Microusb для подключения зарядника от телефона (но также хочу дать возможность запитать от 3 батареек).
      вопрос:
      1. как сделать обвязку чтобы при подключении внешнего источника питания, питание от батареек не тратилось?
      2. MQ135 пишут везде нужно свое питание но если я запитаю от внешнего источника его и ардуино то никаких проблем не возникнет?
      3. надо датчик MQ135 включать периодически допустим раз в 10мин на 2 мин c помощю ардуино, какие элементы могут помочь в этом?(использовать реле мне кажеться как с ружья по воробьям) 
    • Автор: jams
      Привет всем. первый раз держу ардуинку. Поставили такую задачу: На руки дали Arduino Mega 2560 R3   и такую задачу. надо задействовать 8 входов, а остальное выходы оставшиеся (их там много). Надо сделать так что бы эти 8 входов были расширены таким способом.: Допустим на  вход 1 подаем единичку а на другом выходе тоже единичка и так до восьми входов., но далее мы подаем комбинацию вход 1 и 2 вместе подаем по единичке и единичка выходит еще на одном свободном выходе и так со всеми восемью входами. например имеем входы 1,2,3,4,5,6,7,8, и делаем такую комбинацию после того как все выходы исчерпаны по одиночному подключению вход и выход. продолжаем дальше вход1+2=выход9 и так далее: 1+3=10, 1+4=11, 1+5=12... потом 2+2=...2+3...2+4... и так далее пока не закончатся все выходы. Теперь вопрос от человека первый раз державшего на руках вышеописанный девайс: Как это нарисовать в скетче? 
      БлагоДарю!
    • Автор: Макcим
      Привет, прошу помощи что и как сделать.
      Кратко, есть пасека с пчелами небольшая.
      Нужно сделать
      1) Под улей поставить 4 тензодатчика каждый на 50кг (как у напольных электронных весов) через модуль АЦП HX711 
       
      2) Во внутрь улья, датчик влажности и температуры DHT11 (неважно какой) 
       
      Пока всё, есть ещё идея сделать "Частотный анализатор"  что-бы отслеживать состояние семьи ( возбужденное или спокойное) но как это сделать пока понятия не имею, в дальнейшем добавить датчик детонации, когда будет gsm модуль. 
      Не знаю как делать, чтоб Arduino опрашивал датчики с интервалом раз в 2 часа и отправлял результат на rasbery pi 3 а он чтоб составлял хронологию (график изменения) на apache сервере. А я мог подключиться к нему к примеру прямо там в поле через wi-fi с телефона и всё посмотреть.(в дальнейшем добавить gsm модуль и отправлял в инет всю информацию.) Или делать сразу всё на Rasbery pi 3. Но ведь мне так не хватит одного Rasbery pi 3 что бы за питать все датчики от всех ульев. Прошу помощи, совета что и как сделать лучше. Делаю для себя.
  • Сообщения

    • Если честно слабо понимаю какой ток будет на BD139\BD140,  он же будет меньше тока питания, так ведь, кроме них есть же силовые транзисторы. Но я знаю вот что, мне кто-то писал еще давно, что КПД этого фильтра примерно 78% по мощности. Тогда можно прикинуть, что при 14В 2.3А входе и 12.8 В выходе, получаем ток на выходе (максимальный) 1.96 А, грубо 2 А. Думаю это позволяет прикинуть и какая мощности рассеивается на компонентах фильтра - (100% - 78%)*14*2.3 = 7 Вт. Только вот какое будет тепловыделение на силовом транзисторе, а какое на BD139\BD140?
    • А какой алгоритм включения защиты?
    • Добавил код из статьи с habrahabr, вроде отправляет. Завтра еще посмотрю.
    • Мощность на транзисторе будет равна падению на нем, умноженному на ток. Заклн Ома и производные в помощь.
    • Шпиндель все время в холостую крутился. И что ротор, что статор после остановки всегда были холодными. Двиг крякнул из-за неисправности реле. Если шкив увеличить, то будет сильно шумно. Как начну работать на станке, а пока я только с приводом вяло колдую уже который месяц, тогда при необходимости и обдув организую Как по мне, то это мнение близко к заблуждению. СМА может часами молотить без остановки. Мотор установлен в небольшом  замкнутом объёме и охлаждения у него нет. И ещё есть режим стирка при 90С°. Температура внутри машинки ещё выше становится. Так как я все соки выжимать не собираюсь, то и греться у меня тоже не должен А на chipmaker Вы есть?
    • Спасибо насчет напряжения. А можно еще вопрос, в этом фильтре питания, какая мощность примерно выделится на BD139\BD140 (интересует на одном транзисторе), если я поставил силовые 2SC5200\2SA1943 но они стоят просто потому, что были. На самом деле мне надо питание +\- 14 В, а ток 2.3 А максимум. То есть это по входу. На выходе насколько я понимаю напряжение уменьшится на 1.2 В, а кстати насчет тока даже интересно, какой в результате можно на  выходе получить ток. Давайте задам вопрос иначе: На вход я подаю +\-14В 2.3 А, и вопросы: 1. На выходе какое будет примерно напряжение и ток максимальный? (напряжение я так понял будет +\-12.8 В примерно, да?) 2. Какая мощность выделится на BD139\BD140 при максимуме нагрузки?
    • Скажите, пож-та: если я хочу написать программу для частотомера на PIC16F628a, в его даташите указано, что минимальный период внешних импульсов для TMR1 - 60 нс, из чего следует, что максимально возможная измеряемая частота 16,6 МГц при использовании этого таймера? А если использовать TMR0 с предделителем, то мин. период 20 нс, что соответствует макс. 50 МГц, это вроде понятно, но в русском даташите, стр. 139, указано "Период T0CKI (мин.) (Тсy+40)/N, Tcy - время маш.ц. =4/20 МГц, N - коэфф.преддел., получается мин. период может быть 0,94 нс? Где-то ошибка...