Malloc, куча, стек

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

Режим всех пинов 0-31 (ввод-вывод) можно включать общим регистром на 32 бита. А тонкая настройка - подтяжки и альтернативные функции - на каждый пин свой регистр. Это общий подход, у большинства других контроллеров также, есп не одинок.
Все-таки смена тонких настроек - штучная операция, перевод кучи пинов в режим , например, СПИ, разом не делается :slight_smile:

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

Режим чтения с подтяжкой включить заранее?
И потом читать пины параллельно (хоть все 32 ) можно за один раз, как и на старых МК

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

ИМХО, момент переключения такой шины с приема на передачу обычно совсем не “моментальный”, можно и побитно.

я, честно, ЕСП вообще не знаю, для чего он подходит - подтвердить или опровергнуть не могу.

Но что могу сказать - для таких веще отлично подходят ПИО-машины в РП2040/2350 :slight_smile:

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

Плюс только один - вифи и блупуп, в остальном это тот-же рп2040, только без пио.

вот пио это вещь, пусть и скорость ниже, пусть без вифи, но зато всратой операционки не надо, от которой в есп нельзя отказаться.

Какая красивая штука!

Да, именно в этом и заключается главная прелесть Forth. Вы можете запустить отправку UDP-пакетов в фоновом режиме, и консоль (REPL) останется полностью доступной для интерактивной работы.

В ESP32forth встроен кооперативный многозадачник (multitasker). Чтобы перевести программу в фоновый режим, нужно объявить задачу (task), связать её с кодом и использовать слово pause внутри цикла. Слово pause временно передает управление консоли и другим задачам. [1]

Ниже обновленный код. Вставьте его целиком в консоль:

\ --- Настройки сети (замените на свои) ---
z" Твоя_Имя_Сети_WiFi"  constant MY_SSID
z" Твой_Пароль_WiFi"    constant MY_PASS
44444 constant UDP_PORT
z" 255.255.255.255"     constant BROADCAST_IP

WiFiUDP new constant my_udp

\ Переменная-флаг для управления циклом из консоли
variable udp-run
1 udp-run !  \ по умолчанию включено

\ Подключение к Wi-Fi (однократное при старте)
: connect-wifi
    MY_SSID MY_PASS WiFi.begin
    begin WiFi.status 3 = not while 500 ms repeat
;

\ Функция отправки одного пакета
: send-broadcast { message_str -- }
    BROADCAST_IP UDP_PORT my_udp .beginPacket drop
    message_str my_udp .print drop
    my_udp .endPacket drop
;

\ --- Код фонового процесса ---
: udp-background-worker
    connect-wifi
    begin
        udp-run @ if
            z" Background Hello from ESP32 Forth!" send-broadcast
        then
        
        \ КРИТИЧЕСКИ ВАЖНО: 
        \ Используем 2000 ms, так как в ESP32forth слово 'ms' 
        \ автоматически вызывает внутри себя 'pause' для многозадачности!
        2000 ms 
    again
;

\ --- Создание и запуск фоновой задачи ---
\ Выделяем память под задачу (стеки данных и возвратов)
0 0 0 task udp_task

: start-udp-bg
    \ Привязываем слово udp-background-worker к задаче udp_task
    ['] udp-background-worker udp_task construct
    \ Запускаем задачу в фоновое исполнение
    udp_task start-task
    cr ." UDP broadcast background task started!" cr
;

Как теперь этим управлять прямо из консоли:

  1. Запуск фона:
    Введите команду start-udp-bg. Пакеты начнут уходить в сеть, но консоль мгновенно вернет вам ответ ok и продолжит принимать команды.
  2. Проверка работоспособности:
    Пока плата шлет UDP в фоне, вы можете вводить любые команды, например:
    • 2 2 + . (выведет 4)
    • v.words (покажет список доступных слов)
  3. Приостановка отправки:
    Вы можете на лету поменять переменную флага:
    • 0 udp-run ! — отправка UDP прекратится, но задача продолжит «дышать» в фоне.
    • 1 udp-run ! — отправка возобновится.

Примечание: Если вы захотите посмотреть список всех запущенных задач, в ESP32forth можно ввести команду .tasks. [2]

Хотите ли вы, чтобы этот скрипт автоматически запускался в фоне каждый раз при включении или перезагрузке ESP32 (без необходимости вводить команду вручную)?

Я могу подсказать, как записать этот код в энергонезависимый файл автозапуска autoexec.fs внутри платы. [3]

[1] https://github.com

[2] https://wokwi.com

[3] https://esp32forth.wordpress.com

Наличие FreeRTOS - главное достоинство ESP32 и семейства, конкурентное преимущество даже. Поэтому это МК №1 в IoT. Даже выбор платформы не испортил репутацию этих МК.
Ты начинаешь походить на NMi с ассемблером, о котором он пишет ни хрена не понимая :wink:
Также и ты про контроллер, надеюсь ошибиться.
В надежде, что не нарвусь на демагогию, я прошу показать мне ситуацию, в которой тебе помешало наличие FreeRTOS.

Не, я прямой как угол дома. операционная система - это сторонний программный продукт, который не всегда нужен для поставленной задачи. Все производители выпускают голое железо, хочешь ставь ОС, хочешь нет. Тут же, в силу того, что у них есть вифи, кой собственно под лицензией и контролем, они заранее упаковали этот участок кода в бинарники, чтобы вася типа меня не делал на нем всякие жучки и пульты, и теперь без ос эта хрень не стартует. Вот в стм и рп2040 нет ос, но ее за две минуты можно прикрутить. Или у всех на домашних ПК винда или линукс гвоздями к процессору прибита? Вот из за вифи он и главный в iot, без нее он никому бы не всрался.

и кстати, xtensa тоже не идеал. ща они s31 выпустили на risc-v, посмотрим че умеет.

Примера не будет? Я так и думал. Ок. Удачи!

ниче не понял. какого примера?

Чиво?
:face_with_raised_eyebrow:

отож, исполнительный код для PIC в 500 байт выливает в текстовик в 30 килобайт

По большей части, да.

PS: Для любителей Rust я всегда предлагаю прстую задачу - “Покажите мне двусвязанный список на Rust”. Тем, кому интересно, как это выглядит (безопасно!!!111 my ass):slight_smile:

use std::cell::RefCell;
use std::rc::{Rc, Weak};

type Link<T> = Option<Rc<RefCell<Node<T>>>>;
type Prev<T> = Option<Weak<RefCell<Node<T>>>>;

struct Node<T> {
    value: T,
    prev: Prev<T>,
    next: Link<T>,
}

pub struct LinkedList<T> {
    head: Link<T>,
    tail: Link<T>,
    len: usize,
}

impl<T> LinkedList<T> {
    pub fn new() -> Self {
        Self {
            head: None,
            tail: None,
            len: 0,
        }
    }

    pub fn push_front(&mut self, value: T) {
        let node = Rc::new(RefCell::new(Node {
            value,
            prev: None,
            next: self.head.clone(),
        }));

        match self.head.take() {
            Some(old_head) => {
                old_head.borrow_mut().prev = Some(Rc::downgrade(&node));
                self.head = Some(node);
            }

            None => {
                self.tail = Some(node.clone());
                self.head = Some(node);
            }
        }

        self.len += 1;
    }

    pub fn push_back(&mut self, value: T) {
        let node = Rc::new(RefCell::new(Node {
            value,
            prev: self.tail.as_ref().map(Rc::downgrade),
            next: None,
        }));

        match self.tail.take() {
            Some(old_tail) => {
                old_tail.borrow_mut().next = Some(node.clone());
                self.tail = Some(node);
            }

            None => {
                self.head = Some(node.clone());
                self.tail = Some(node);
            }
        }

        self.len += 1;
    }

    pub fn remove(&mut self, node: &Rc<RefCell<Node<T>>>) {
        let prev = node.borrow().prev.clone();
        let next = node.borrow().next.clone();

        match prev.as_ref().and_then(Weak::upgrade) {
            Some(prev) => {
                prev.borrow_mut().next = next.clone();
            }

            None => {
                self.head = next.clone();
            }
        }

        match next {
            Some(next) => {
                next.borrow_mut().prev = prev;
            }

            None => {
                self.tail = prev.and_then(|p| p.upgrade());
            }
        }

        self.len -= 1;
    }

    pub fn len(&self) -> usize {
        self.len
    }
}

Неохота спросонья разбираться в коде, не скажете кратко, словами, что там не так со списком на расте?

Сложность и непонятность кода.

а потом тоже самое на АСМе