Пример Blink. Мигаем светодиодом

Ну а кому нужны бессмысленные блинки, с тупыми задержками нопами, например. Как я понимаю, блинк должен не занимать время и память, иначе на кой он тогда нужен! Разве, если только проверить работоспособность платы?

Я бы уточнил: “для полезных примеров”. В число которых, очевидно, входят как самые короткие, так и самые понятные. В отличие от того, что было у Гайвера. У последнего - не более чем “занимательные”. Хотя бы потому, что почти все либо на delay(), либо на аналогах.
В данной же ветке кроме самой первой ссылки все остальные “без delay()”, т.е. имеют хоть какой практический смысл.

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

const byte ledPin = LED_BUILTIN;  
const long interval = 500;  

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  static byte ledState = LOW;  
  static unsigned long previousMillis = 0;  
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    digitalWrite(ledPin, ledState = !ledState);
  }
}
const byte ledPin = LED_BUILTIN;  
const long interval = 500;  

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  static byte ledState = LOW;  
  static unsigned long previousMillis = 0;  
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis += interval;
    digitalWrite(ledPin, ledState = !ledState);
  }
}

Варианты различаются 14-й строкой.
Если нам нужно переключать пин не чаще чем раз в interval мс, следует пользоваться первым вариантом.
Если нужно, чтобы переключения происходили в среднем через interval мс, следует предпочесть второй. Во втором варианте можно убрать 11-ю строку, а millis() использовать непосредственно в 13-й.

Естественно, для светодиода это в большинстве случаев не очень критично, но если мы вместо digitalWrite() вызываем какую-то функцию, я которой период опроса играет существенную роль, следует задуматься, который из вариантов нам нужен.

Для критикал тайминг ещё и миллис стоит брать после окончания основного действия, чтобы не получить интервал M-N, где M - минимально необходимый интервала опроса датчика, а N - фактическая продолжительность опроса.

Ну это уже третий вариант - когда необходимо выдержать не период, а интервал между окончанием предыдущего и началом следующего.

“Виртуальный светодиод” позволяет менять количество, расположение, цвет свечения, форму и вид элемента :slight_smile:
светодиод

const unsigned char ris_1[]PROGMEM = {//РИСУНОК СВЕТОДИОДА
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XE0,0X00,0X00,0X1F,0XF8,0X00,
0X00,0X30,0X0C,0X00,0X00,0X60,0X36,0X00,0X00,0XC0,0X1B,0X00,0X00,0XC0,0X0D,0X00,
0X01,0X80,0X05,0X80,0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,
0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,
0X01,0X80,0X02,0X80,0X01,0X80,0X00,0X80,0X07,0XFF,0XFF,0XE0,0X04,0X00,0X00,0X20,
0X04,0X00,0X00,0X20,0X04,0X00,0X00,0X20,0X07,0XFF,0XFF,0XE0,0X07,0XFF,0XFF,0XE0,
0X00,0X00,0X00,0X00,0X00,0X30,0X0C,0X00,0X00,0X30,0X0C,0X00,0X00,0X30,0X0C,0X00,
0X00,0X30,0X0C,0X00,0X00,0X30,0X0C,0X00,0X00,0X00,0X0C,0X00,0X00,0X00,0X00,0X00  
};
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>

#include <SPI.h>
#define TFT_CS     10
#define TFT_RST    9
#define TFT_DC     8
// TFT_SCL 13   
// TFT_SDA 11 
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);



void setup(void) {
  tft.initR(INITR_BLACKTAB);
  tft.setRotation(3);
  tft.fillScreen(ST7735_BLACK);
}
void loop() {
 drawBitmapFast(65, 50, ris_1, 32, 32,tft.Color565(0,255,0),tft.Color565(0,0,0) );// ВКЛЮЧАЕМ СВЕТОДИОД
 delay(500);
 drawBitmapFast(65, 50, ris_1, 32, 32,tft.Color565(127,127,127),tft.Color565(0,0,0) );// ВЫКЛЮЧАЕМ СВЕТОДИОД
 delay(500);
}
///////////функция отрисовки картинки по заданным координатам,размерам и цветам
void drawBitmapFast(int16_t x, int16_t y, const uint8_t bitmap[],
                    int16_t w, int16_t h, uint16_t color, uint16_t bg) {
  uint8_t b(0);
  uint8_t bc(0);
  uint16_t tc(0);
  int16_t c =  w * h;
 // tft.startWrite();
  tft.setAddrWindow(x, y, x+w-1, y+h-1);
  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  digitalWrite(TFT_DC, HIGH);
  digitalWrite(TFT_CS, LOW); 
 asm volatile(
    "mloop_%=: \n"
    "   LPM %[b],%a[bm]+ \n"
    "   LDI %[bc],1 \n"
    "loop_%=: \n"
    "   SBRC %[b],7 \n"
    "   MOVW %[tc],%[color] \n"
    "   SBRS %[b],7 \n"
    "   MOVW %[tc],%[bg] \n"
    "   IN __tmp_reg__,%[spdr]-1 \n"
    "   OUT %[spdr],%B[tc] \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   IN __tmp_reg__,%[spdr]-1 \n"
    "   OUT %[spdr],%A[tc] \n"
    "   SBIW %[c],1 \n"
    "   BREQ exit_%= \n"
    "   NOP \n"
    "   LSL %[b] \n"
    "   LSL %[bc] \n"
    "   BRCS mloop_%= \n"
    "   RJMP .+0 \n"
    "   NOP \n"
    "   RJMP loop_%= \n"
    "exit_%=: \n"
  : [tc] "=&r" (tc)
  : [b] "l" (b), [bm] "z" (bitmap), [bc] "d" (bc), [color] "w" (color), [bg] "w" (bg),
    [spdr] "M" (_SFR_IO_ADDR(SPDR)), [c] "w" (c)
  : );
 // tft.endWrite();
  digitalWrite(TFT_CS, HIGH);
  SPI.endTransaction();
}

Вопроса два:

  1. Зачем это здесь вообще?
  2. Зачем в данном фрагменте нужен ассемблер?
2 лайка

Ассемблер для новичков самая тема - изучил и считай что можешь писать на любом языке !

1 лайк

Шоб блеснуть.

2.Ассемблер, как вариант мне предложенный, даёт самую быструю отрисовку - приближает виртуальные светодиоды к реальным.

  1. Мне показалось…, после реальных светодиодов можно попробовать блинк и на виртуальных. Всяких симуляторов тьма, а тут интерпретатор-толкователь блинка.

ИМХО, в “блинке” цель не светодиоТ™ освоить, а основополагающий алгоритм программирования МК

1 лайк

Он и есть его образ, как самый доступный и понятный из элем. РЭА. В моём примере можно нарисовать 20 светодиодов и их миллисить до умопомрачения, а макетка с 20 реальными будет ёжиком небритым.

Начинающего путает в мыслях обычный INPUT, а тут код, в котором сделать ошибку можно даже не стараться.

1 лайк

Ну это проба, а так в мыслях свести к двум командам led_1(red); led_1(off); через функцию и дефайны.

Это утверждение можно подкрепить цифрами?

Особенно с учетом последующего delay(500);

Да, в старой теме - выходило 46-48 миллисекунд на отрисовку полного экрана. 20 кадров за секунду, около.

Ты за спрайты чонить слышал? У этих экранчиков можно обновлять только необходимую, меняющуюся область памяти (правда с дискретностью по вертикали в 8 точек). Задаешь сначала адрес куда выводить (в терминах экрана) и выводишь только измененные данные, а не перерисовываешь весь экран.

Помница мне, когда я с этими экранами экспериментировал (давно было) ему можно было указать “окно вывода”, все записи в память не выходили за пределы ограниченной области, адресация была относительная, первая ячейка ограниченной области (не экрана), имела логический адрес 0.
Могу ашибацца, давно было.

В скетче так всё и написано. Весь экран это все 20 штук, для одного будет гораздо быстрее.

Если мы утверждаем “самая быстрая”, то одной цифры мало: цифру нельзя сравнивать саму с собой. Для сравнения нужно минимум две цифры.
Например, одна - для того, что написано на Ассемблере, а вторая - для того, что написано на Си.
Если мы говорим “самая быстрая”, то должны продемонстрировать другие варианты - которые медленнее.
И, кстати, какой здесь экран? 220х160? Тогда фрагмент 32х32 пикселя - это 3% площади и, соответственно, должен отрисовываться за 1.4 мс. По сравнению с delay(500); это, конечно, очень существенно.