Добавить в корзинуПозвонить
Найти в Дзене
Pinki Raspberry

STM32 для ленивых. Часть 1.

Мигаем светодиодом: Теперь пришла моя очередь помучать этого зверька. Немного предисловия: Скажу честно - кодить я не умею, вообще никак, абсолютно ни капельки. Все мои попытки изучить МК разбились лет 10 назад о камни синтаксиса Си+ для AVR-ок. Долго и упорно я пытался что то накодить для Atmega 8, получалось так себе. Пострадав чуть больше годика решил что это не моё и забросил. И вот сейчас у нас 2025й год, кругом ИИ... И что то решил я спросить ИИ а не умеет ли он писать программы, и оказывается умеет. И МК тоже эволюционировали, старые добрые атмеги 8 уже считаются старичками. Помню как началась эта stm8\stm32 как все писали кипятком от их возможностей. Но я не обращал на это внимания ибо уже решил что это не моё. Но тут как то прикупил на Али платку в народе называемую синей таблеткой "Blue pill". Мелкая такая дешевая фиговина на которой распаян stm32f1xx. Прикупил к ней "свисток" ST link V2. Но чёто у меня ничего не пошло. STM озлобилась на Ру-пользователей и даже не даёт скач

Мигаем светодиодом:

Теперь пришла моя очередь помучать этого зверька. Немного предисловия: Скажу честно - кодить я не умею, вообще никак, абсолютно ни капельки. Все мои попытки изучить МК разбились лет 10 назад о камни синтаксиса Си+ для AVR-ок. Долго и упорно я пытался что то накодить для Atmega 8, получалось так себе. Пострадав чуть больше годика решил что это не моё и забросил. И вот сейчас у нас 2025й год, кругом ИИ... И что то решил я спросить ИИ а не умеет ли он писать программы, и оказывается умеет. И МК тоже эволюционировали, старые добрые атмеги 8 уже считаются старичками. Помню как началась эта stm8\stm32 как все писали кипятком от их возможностей. Но я не обращал на это внимания ибо уже решил что это не моё. Но тут как то прикупил на Али платку в народе называемую синей таблеткой "Blue pill". Мелкая такая дешевая фиговина на которой распаян stm32f1xx. Прикупил к ней "свисток" ST link V2. Но чёто у меня ничего не пошло. STM озлобилась на Ру-пользователей и даже не даёт скачать софт. Конечно кто хочет тот найдёт, скачает. Если честно я ничего не понял в этой STM32CubeIDE, вроде всё поставил должно работать а проект открывается голый только один файл. Потом там куча меню, куча опций и ничего не понятно, где та заветная кнопка для заливки написанной программы в МК ? Нагуглил что это STM продолжает вставлять палки в колёса пользователям из России, оказывается теперь у них всё онлайн и нужно иметь аккаунт в STM чтобы всё работало. В общем почти расстроился, но тут вспомнил что была какая то штука называемая Ардуино и типа она очень доброжелательная к пользователю и простая. Поставил, сразу всё заработало. Далее попросил ИИ написать простенькую программу для мигания светодиодом, ИИ написал, я её скопировал, вставил, запустил и всё заработало. Офигеть как просто. Да это же меняет дело, кардинально, ИИ не просто пишет программу но еще и пояснения добавляет и комментирует строки кода. В общем буду изучать МК методом тыка используя ИИ.

Итак урок первый, классика жанра мигаем светодиодом. Те кто пытался но так и ничего не понял, сейчас вы увидете насколько это всё просто.
Написал ИИ просто текст вот он дословно:
"Напиши программу в Arduino IDE для stm32f103 мигающую светодиодом на выводе PC13 с частотой 5 гц" и ИИ выдал

// Пин светодиода
const int ledPin = PC13;

// Период мигания в миллисекундах (200 мс = 5 Гц)
const int delayTime = 100; // 100 мс вкл + 100 мс выкл = 200 мс период

void setup() {
  // Настройка пина как выхода
  pinMode(ledPin, OUTPUT);
}

void loop() {
  // Включаем светодиод
  digitalWrite(ledPin, HIGH);
  delay(delayTime); // Задержка 100 мс
  
  // Выключаем светодиод
  digitalWrite(ledPin, LOW);
  delay(delayTime); // Задержка 100 мс
}
// Пин светодиода const int ledPin = PC13; // Период мигания в миллисекундах (200 мс = 5 Гц) const int delayTime = 100; // 100 мс вкл + 100 мс выкл = 200 мс период void setup() { // Настройка пина как выхода pinMode(ledPin, OUTPUT); } void loop() { // Включаем светодиод digitalWrite(ledPin, HIGH); delay(delayTime); // Задержка 100 мс // Выключаем светодиод digitalWrite(ledPin, LOW); delay(delayTime); // Задержка 100 мс }

Всего 19 строк и светодиод на плате Blue Pill будет мигать. Вам даже ничего делать не нужно, написали запрос ИИ, скопировали ответ.
В Ардуино IDE нажимаем "Скетч" - "Верифи/компилите" и программа проверяет ваш код и выдает внизу заключение. Если белый текст как на этом скрине значит всё нормально, если есть ошибки то она их выводит красным текстом.

-2

и нажать на "Уплоад" то программа заливается в синюю таблетку подключенную к ST link V2 воткнутый в USB порт. Особо даже ничего настраивать не нужно. Иногда Ардуино ругается на что то то какой то библиотеки нет, то какой то еще фигни, но чаще всего проблема решается установкой недостающей библиотеки. Или переделкой программу также при помощи ИИ.
Отчёт о прошивке:

-3

Всё намного проще чем было 10 лет назад с Атмегой 8.
Прошивка заливается за 1,5 секунды, в Атмегу она вливалась секунд 30...40.
Для прошивки достаточно всего 4 провода, причём два их них это общий и питание два других это SWCLK и SWDIO. Этот интерфейс называется SWD. В атмегу 8 чтобы залить прошивку нужно 6 проводов - Питание, общий, ресет, SCK, MISO, MOSI. В общем в разы всё проще и удобнее.
А давайте сравним мега 8 и stm32f103 )
8 vs 72 Мгц в пользу stm32
8 vs 32 бит в пользу stm32
8 КБ / 1 КБ vs 64–128 КБ / 20 КБ снова stm32 лучше
У меги8 3 таймера, 2 8 битных и один 16 битный, у stm32 7 таймеров, причём 4 из них 16 битных
АЦП у меги на 10 бит 6 каналов, против 12 бит и до 16 каналов у stm32
Можно еще долго сравнивать периферию, но в целом stm32 кладёт мегу на лопатки. Пожалуй единственные плюсы атмеги это диапазон напряжений питания и ток потребления. Если нужно экономить электричество тот тут атмега 8 будет предпочтительнее. Во всём остальном stm32 лучше.
Я пару дней поковырял синюю таблетку и кажись спалил пару ножек. Дело было так - ну думаю если импульсы сделать крайне короткими то может ничего страшного, да и напряжение у нас тут не 5 вольт и 3,3. В общем решил подцепить светодиодный семисегментный дисплей без токоограничивающих резисторов. Почти получилось, но не долго )
Ладно, а как бы провеить много ли выводов я потерял ?
Смотрим на распиновку таблетки

-4

Напишем мигалку но на все ножки, получилось вот так:

// Массив всех доступных GPIO-выводов на STM32F103 (Blue Pill)
const uint8_t allPins[] = {
  PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA15,              // Порты A 
  PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15,  // Порты B 
  PC13, PC14, PC15                                                                       // Порты C (PC13 - встроенный светодиод на Blue Pill)
};

const int pinCount = sizeof(allPins) / sizeof(allPins[0]);

void setup() {
  // Инициализируем все выводы как OUTPUT
  for (int i = 0; i < pinCount; i++) {
    pinMode(allPins[i], OUTPUT);
  }
}

void loop() {
  // Включаем все светодиоды
  for (int i = 0; i < pinCount; i++) {
    digitalWrite(allPins[i], HIGH);
  }
  
  delay(125);  // Половина периода для 4 Гц (1000 мс / 4 / 2 = 125 мс)
  
  // Выключаем все светодиоды
  for (int i = 0; i < pinCount; i++) {
    digitalWrite(allPins[i], LOW);
  }
  
  delay(125);  // Вторая половина периода
}
// Массив всех доступных GPIO-выводов на STM32F103 (Blue Pill) const uint8_t allPins[] = { PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA15, // Порты A PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15, // Порты B PC13, PC14, PC15 // Порты C (PC13 - встроенный светодиод на Blue Pill) }; const int pinCount = sizeof(allPins) / sizeof(allPins[0]); void setup() { // Инициализируем все выводы как OUTPUT for (int i = 0; i < pinCount; i++) { pinMode(allPins[i], OUTPUT); } } void loop() { // Включаем все светодиоды for (int i = 0; i < pinCount; i++) { digitalWrite(allPins[i], HIGH); } delay(125); // Половина периода для 4 Гц (1000 мс / 4 / 2 = 125 мс) // Выключаем все светодиоды for (int i = 0; i < pinCount; i++) { digitalWrite(allPins[i], LOW); } delay(125); // Вторая половина периода }

Порты я с распиновки добавил, уж не знаю может и не должны они в таком случае все работать. Но попробую проверить. Светодиод на этот раз включил через резистор 330 Ом и пошёл тыкать по всем ножкам. Сначала нижний ряд
В12, В13, В14, В15, А8, А9, А10 живы, светодиод мигает.
А11 не мигает,
А12 постоянно светится,
А15, В3, В4, не светится
В5 - В9 мигает
Теперь верхний ряд, тут всё в порядке начиная с крайнего В10 и заканчивая последним С13

Итого потеря: А11, А12, A15, B3, B4. проверял подключив светодиод к Gnd. Попробовал также к +3,3в ну вдруг где то жив один из ключей, нет не повезло. 5 ножек подряд выгорели, 4 полностью и на A12 видимо пробило верхний ключ.
Ладно, может закажу на Али новый МК и перепаяю, а этот пущу на какую нибудь поделку благо ножек еще полно живых. Хотя у меня ощущение что сгореть должны были только В3 и В4. Видимо забыл уже что и остальные подключал.

Ну а теперь подурачимся мигалкой, вот эта штука мигает светодиодом на ножке РС13 и частота мигания плавно увеличивается и уменьшается от 0,5 Гц до 20 Гц

#define LED_PIN PC13  // Встроенный светодиод на STM32 Blue Pill

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

void loop() {
  // Плавное увеличение частоты от 0.5 Гц до 20 Гц
  for (float freq = 0.5; freq <= 20.0; freq += 0.1) {
    blinkLED(freq);
  }
  
  // Плавное уменьшение частоты от 20 Гц до 0.5 Гц
  for (float freq = 20.0; freq >= 0.5; freq -= 0.1) {
    blinkLED(freq);
  }
}

void blinkLED(float frequency) {
  // Рассчет периода в миллисекундах
  unsigned long period = 1000 / frequency;  // Полный период (вкл + выкл)
  unsigned long halfPeriod = period / 2;
  
  // Ограничение минимальной задержки
  if (halfPeriod < 1) halfPeriod = 1;
  
  digitalWrite(LED_PIN, !digitalRead(LED_PIN));
  delay(halfPeriod);
}
#define LED_PIN PC13 // Встроенный светодиод на STM32 Blue Pill void setup() { pinMode(LED_PIN, OUTPUT); } void loop() { // Плавное увеличение частоты от 0.5 Гц до 20 Гц for (float freq = 0.5; freq <= 20.0; freq += 0.1) { blinkLED(freq); } // Плавное уменьшение частоты от 20 Гц до 0.5 Гц for (float freq = 20.0; freq >= 0.5; freq -= 0.1) { blinkLED(freq); } } void blinkLED(float frequency) { // Рассчет периода в миллисекундах unsigned long period = 1000 / frequency; // Полный период (вкл + выкл) unsigned long halfPeriod = period / 2; // Ограничение минимальной задержки if (halfPeriod < 1) halfPeriod = 1; digitalWrite(LED_PIN, !digitalRead(LED_PIN)); delay(halfPeriod); }

А вот оптимизированная версия с более плавным изменением частоты

-7

Но она не работает, ардуино ругается, но ИИ быстренько всё подправил и вот рабочий вариант

#define LED_PIN PC13

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

void loop() {
  // Плавное увеличение частоты от 0.5 Гц до 20 Гц
  for (float freq = 0.5; freq <= 20.0; freq += 0.1) {
    unsigned long halfPeriod = 500000UL / freq;
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    delayMicroseconds(halfPeriod);
  }
  
  // Плавное уменьшение частоты от 20 Гц до 0.5 Гц
  for (float freq = 20.0; freq >= 0.5; freq -= 0.1) {
    unsigned long halfPeriod = 500000UL / freq;
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
    delayMicroseconds(halfPeriod);
  }
}
#define LED_PIN PC13 void setup() { pinMode(LED_PIN, OUTPUT); } void loop() { // Плавное увеличение частоты от 0.5 Гц до 20 Гц for (float freq = 0.5; freq <= 20.0; freq += 0.1) { unsigned long halfPeriod = 500000UL / freq; digitalWrite(LED_PIN, !digitalRead(LED_PIN)); delayMicroseconds(halfPeriod); } // Плавное уменьшение частоты от 20 Гц до 0.5 Гц for (float freq = 20.0; freq >= 0.5; freq -= 0.1) { unsigned long halfPeriod = 500000UL / freq; digitalWrite(LED_PIN, !digitalRead(LED_PIN)); delayMicroseconds(halfPeriod); } }

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

// Используем Timer 2 для генерации прерываний
HardwareTimer timer(2); // TIM2

#define LED_PIN PC13
volatile bool ledState = false;

void setup() {
  pinMode(LED_PIN, OUTPUT);
  
  // Настройка таймера
  timer.pause(); // Приостанавливаем таймер перед настройкой
  timer.setPrescaleFactor(7200); // Тактовая 72 МГц / 7200 = 10 кГц
  timer.setOverflow(2500); // 10 кГц / 2500 = 4 Гц (прерывание 2 раза за период)
  
  timer.attachInterrupt(TIMER_UPDATE_INTERRUPT, []() {
    ledState = !ledState;
    digitalWrite(LED_PIN, ledState);
  });
  
  timer.refresh(); // Сбрасываем таймер
  timer.resume(); // Запускаем таймер
}

void loop() {
  // Основной цикл не используется, все работает в прерывании
}
// Используем Timer 2 для генерации прерываний HardwareTimer timer(2); // TIM2 #define LED_PIN PC13 volatile bool ledState = false; void setup() { pinMode(LED_PIN, OUTPUT); // Настройка таймера timer.pause(); // Приостанавливаем таймер перед настройкой timer.setPrescaleFactor(7200); // Тактовая 72 МГц / 7200 = 10 кГц timer.setOverflow(2500); // 10 кГц / 2500 = 4 Гц (прерывание 2 раза за период) timer.attachInterrupt(TIMER_UPDATE_INTERRUPT, []() { ledState = !ledState; digitalWrite(LED_PIN, ledState); }); timer.refresh(); // Сбрасываем таймер timer.resume(); // Запускаем таймер } void loop() { // Основной цикл не используется, все работает в прерывании }

Вроде тоже всё наглядно и очевидно, можно заменить 2500 на 250 и тогда частота станет в 10 раз больше. Можно заменить 2500 на 5000 и тогда соответственно в 2 раза реже.
Эта программа кстати использует вот сколько памяти
"Sketch uses 13188 bytes (40%) of program storage space. Maximum is 32768 bytes."
А предыдущая которая без таймера:
"Sketch uses 14636 bytes (44%) of program storage space. Maximum is 32768 bytes."

А та версия которая мигает всеми ножками сразу:
"Sketch uses 12852 bytes (39%) of program storage space. Maximum is 32768 bytes."
Поэтому я решил что памяти много не бывает и нужно для экспериментов приобрести МК с памятью пожирнее.
Ну и на последок еще парочка мигалок, эта мигает случайным образом

// Программа для STM32F103 для мигания светодиодом со случайной частотой
// Диапазон частот: 0.5 Гц (период 2000 мс) - 10 Гц (период 100 мс)

#define LED_PIN PC13  // Встроенный светодиод на STM32F103 обычно на PC13

void setup() {
  pinMode(LED_PIN, OUTPUT);  // Настраиваем пин светодиода как выход
  randomSeed(analogRead(0)); // Инициализация генератора случайных чисел
}

void loop() {
  // Генерируем случайный период в миллисекундах
  // Частота 0.5 Гц = период 2000 мс
  // Частота 10 Гц = период 100 мс
  long randomPeriod = random(100, 2001); // Случайный период от 100 до 2000 мс
  
  digitalWrite(LED_PIN, HIGH);  // Включаем светодиод
  delay(randomPeriod / 2);      // Половина периода - время включенного состояния
  digitalWrite(LED_PIN, LOW);   // Выключаем светодиод
  delay(randomPeriod / 2);      // Половина периода - время выключенного состояния
}
// Программа для STM32F103 для мигания светодиодом со случайной частотой // Диапазон частот: 0.5 Гц (период 2000 мс) - 10 Гц (период 100 мс) #define LED_PIN PC13 // Встроенный светодиод на STM32F103 обычно на PC13 void setup() { pinMode(LED_PIN, OUTPUT); // Настраиваем пин светодиода как выход randomSeed(analogRead(0)); // Инициализация генератора случайных чисел } void loop() { // Генерируем случайный период в миллисекундах // Частота 0.5 Гц = период 2000 мс // Частота 10 Гц = период 100 мс long randomPeriod = random(100, 2001); // Случайный период от 100 до 2000 мс digitalWrite(LED_PIN, HIGH); // Включаем светодиод delay(randomPeriod / 2); // Половина периода - время включенного состояния digitalWrite(LED_PIN, LOW); // Выключаем светодиод delay(randomPeriod / 2); // Половина периода - время выключенного состояния }

А эта плавно увеличивает частоту от 0,5Гц до 10 Гц затем плавно уменьшает, длительность цикла 30 секунд

// Программа для STM32F103 для мигания светодиодом с плавно изменяющейся частотой
// Диапазон частот: 0.5 Гц (период 2000 мс) - 10 Гц (период 100 мс)
// Длительность цикла: 30 секунд (15 секунд на увеличение, 15 секунд на уменьшение)

#define LED_PIN PC13  // Встроенный светодиод на STM32F103 обычно на PC13
const float MIN_FREQ = 0.5;    // Минимальная частота (Гц)
const float MAX_FREQ = 10.0;   // Максимальная частота (Гц)
const long CYCLE_DURATION = 30000; // Длительность цикла (30 секунд в мс)
const long HALF_CYCLE = CYCLE_DURATION / 2; // Половина цикла (15 секунд)

void setup() {
  pinMode(LED_PIN, OUTPUT);  // Настраиваем пин светодиода как выход
}

void loop() {
  static unsigned long cycleStart = 0; // Время начала текущего цикла
  unsigned long currentTime = millis(); // Текущее время
  unsigned long elapsedTime = currentTime - cycleStart; // Время с начала цикла

  // Определяем, в какой фазе цикла мы находимся (увеличение или уменьшение)
  float freq;
  if (elapsedTime < HALF_CYCLE) {
    // Увеличение частоты от 0.5 до 10 Гц за 15 секунд
    float progress = (float)elapsedTime / HALF_CYCLE; // Прогресс от 0 до 1
    freq = MIN_FREQ + (MAX_FREQ - MIN_FREQ) * progress; // Линейная интерполяция
  } else {
    // Уменьшение частоты от 10 до 0.5 Гц за 15 секунд
    float progress = (float)(elapsedTime - HALF_CYCLE) / HALF_CYCLE; // Прогресс от 0 до 1
    freq = MAX_FREQ - (MAX_FREQ - MIN_FREQ) * progress; // Линейная интерполяция
  }

  // Вычисляем период в миллисекундах (период = 1 / частота * 1000)
  long period = (long)(1000.0 / freq);
  
  // Мигание светодиодом
  digitalWrite(LED_PIN, HIGH);  // Включаем светодиод
  delay(period / 2);            // Половина периода - время включенного состояния
  digitalWrite(LED_PIN, LOW);   // Выключаем светодиод
  delay(period / 2);            // Половина периода - время выключенного состояния

  // Сбрасываем цикл, если прошло 30 секунд
  if (elapsedTime >= CYCLE_DURATION) {
    cycleStart = currentTime; // Начинаем
// Программа для STM32F103 для мигания светодиодом с плавно изменяющейся частотой // Диапазон частот: 0.5 Гц (период 2000 мс) - 10 Гц (период 100 мс) // Длительность цикла: 30 секунд (15 секунд на увеличение, 15 секунд на уменьшение) #define LED_PIN PC13 // Встроенный светодиод на STM32F103 обычно на PC13 const float MIN_FREQ = 0.5; // Минимальная частота (Гц) const float MAX_FREQ = 10.0; // Максимальная частота (Гц) const long CYCLE_DURATION = 30000; // Длительность цикла (30 секунд в мс) const long HALF_CYCLE = CYCLE_DURATION / 2; // Половина цикла (15 секунд) void setup() { pinMode(LED_PIN, OUTPUT); // Настраиваем пин светодиода как выход } void loop() { static unsigned long cycleStart = 0; // Время начала текущего цикла unsigned long currentTime = millis(); // Текущее время unsigned long elapsedTime = currentTime - cycleStart; // Время с начала цикла // Определяем, в какой фазе цикла мы находимся (увеличение или уменьшение) float freq; if (elapsedTime < HALF_CYCLE) { // Увеличение частоты от 0.5 до 10 Гц за 15 секунд float progress = (float)elapsedTime / HALF_CYCLE; // Прогресс от 0 до 1 freq = MIN_FREQ + (MAX_FREQ - MIN_FREQ) * progress; // Линейная интерполяция } else { // Уменьшение частоты от 10 до 0.5 Гц за 15 секунд float progress = (float)(elapsedTime - HALF_CYCLE) / HALF_CYCLE; // Прогресс от 0 до 1 freq = MAX_FREQ - (MAX_FREQ - MIN_FREQ) * progress; // Линейная интерполяция } // Вычисляем период в миллисекундах (период = 1 / частота * 1000) long period = (long)(1000.0 / freq); // Мигание светодиодом digitalWrite(LED_PIN, HIGH); // Включаем светодиод delay(period / 2); // Половина периода - время включенного состояния digitalWrite(LED_PIN, LOW); // Выключаем светодиод delay(period / 2); // Половина периода - время выключенного состояния // Сбрасываем цикл, если прошло 30 секунд if (elapsedTime >= CYCLE_DURATION) { cycleStart = currentTime; // Начинаем

if (elapsedTime >= CYCLE_DURATION) {

cycleStart = currentTime; // Начинаем новый цикл

}

}
Не влезло в описание )
В 6, 7 и 8 строках можно настроить параметры мигания. И ничего этого я не печатал, это всё написал ИИ и оно всё работает, я проверил. Иногда ИИ даёт сбой, часто может исправить свои ошибки, иногда не может, но в целом это неплохой способ изучить программирование методом тыка.
Кстати последняя программа:
"ketch uses 16140 bytes (49%) of program storage space. Maximum is 32768 bytes."
С оптимизацией конечно всё плохо, но зато оно работает.
А вот кстати тоже самое но чуточку лаконичнее

#define LED_PIN PC13

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

void loop() {
  static unsigned long cycleStart = millis();
  static unsigned long lastToggle = 0;
  static bool rising = true;
  
  // Прогресс в 30-секундном цикле (0.0-1.0)
  float progress = (millis() - cycleStart) / 30000.0f;
  
  // Сброс цикла каждые 30 секунд
  if (progress >= 1.0f) {
    cycleStart = millis();
    rising = !rising;
    progress = 0.0f;
  }
  
  // Расчет текущей частоты
  float freq;
  if (rising) {
    freq = 0.5f + 9.5f * progress;
  } else {
    freq = 10.0f - 9.5f * progress;
  }
  
  // Расчет периода
  unsigned long halfPeriod = 500 / freq; // Полупериод в миллисекундах
  
  // Переключение светодиода
  if (millis() - lastToggle >= halfPeriod) {
    lastToggle = millis();
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));
  }
}
#define LED_PIN PC13 void setup() { pinMode(LED_PIN, OUTPUT); } void loop() { static unsigned long cycleStart = millis(); static unsigned long lastToggle = 0; static bool rising = true; // Прогресс в 30-секундном цикле (0.0-1.0) float progress = (millis() - cycleStart) / 30000.0f; // Сброс цикла каждые 30 секунд if (progress >= 1.0f) { cycleStart = millis(); rising = !rising; progress = 0.0f; } // Расчет текущей частоты float freq; if (rising) { freq = 0.5f + 9.5f * progress; } else { freq = 10.0f - 9.5f * progress; } // Расчет периода unsigned long halfPeriod = 500 / freq; // Полупериод в миллисекундах // Переключение светодиода if (millis() - lastToggle >= halfPeriod) { lastToggle = millis(); digitalWrite(LED_PIN, !digitalRead(LED_PIN)); } }

Эта версия занимает памяти:
"Sketch uses 14580 bytes (44%) of program storage space. Maximum is 32768 bytes."
На этом хватит мигалок, в следующий раз сделаем что то более полезное )

P.S. После сохранения статьи почему то портиться описание.
P.S.S Лаконичная версия что то не работает нормально, поэтому пользуется предыдущей )