Оглавление: 25/07/20




Форт подобен Дао. Это Путь, и осознается он, когда ему следуешь.

Опубликовано: 21/12/16

Микроконтроллер отличает ограниченность ресурсов, а значит базовыми языками программирования являются Ассемблер, Си.

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

Форт это и интерпретатор и компилятор одновременно. Работа в режиме интерпретатора осуществляется с консоли. Это позволяет выполнять подключение к конечному устройству по rs232, производить финальное программирование, устанавливать глобальные параметры, контролировать работу готового изделия в реальном времени и передавать сведения о состоянии устройств по сети на узел управления. Память программ энерго-независима, после перезапуска контроллера все новые форт слова (команды) и определения, выполненные с консоли, будут доступны для исполнения.

Язык программирования Форт - воплощение идеи о переносимости кода между микроконтроллерами разных типов. Достаточно реализовать ядро языка Forth для каждой платформы, унифицировать работу с портами и о языке СИ, c его жесткой привязкой к оборудованию можно забыть. Подобный подход был использован в реализации форт системы - FlashForth.

Программирование на языке Форт является существенно диалоговым. Работая за терминалом, программист вводит слова-команды, а загруженная в память микроконтроллера форт-система, немедленно выполняет обозначаемые этими словами действия. Для связи с компьютером по RS232 в схему добавлена MAX232A.

Загрузить проект в diptrace.



  • Работает ввод с клавиатуры через PCF8574 (8-Bit I/O Expander for I2C Bus). Выход прерывания с PCF8574, заведён в МК. В Принципиальную схему и разводку необходимо внести исправление. Добавить подтягивающий регистр на +5V

  • Работает периферия - светодиоды, порты ввода вывода.

  • Работает связь с компьютером через RS232.

  • LCD - дисплей, динамик, частотомер, подключение sd карты - пока не проверялись.

Необходимо отказаться от подобных реализаций - всё в одном. Не все устройства требуют LCD экрана.

Построение систем.

Опубликовано: 22/12/16

Экосистема Arduino. есть головное устройство, к нему подключаем необходимый датчик, пишем программу, включаем - работает. Понадобился ещё датчик, добавляем, переделываем программу, включаем - работает. В итоге сборка конечного устройства, например терморегулятора, выльется в сборку устройства из библиотек (скетчей) - дисплея, клавиатуры и термодатчиков. Весь этот программно-аппаратный клубок начинает путаться, обрывается, в итоге устройство не работает. Возникает устойчивое ощущение что необходимо собрать устройство на одной плате. (Для МК Atmga реализация Форт - AMForth )

Всё собрано на одной плате. Для обеспечения работы дисплея LCD 1602 требуется значительное количество портов микроконтроллера. Накладно. Переделываем схему, добавляем расширитель PCF8574P (I2C I/O Expander), на него заводим часть портов дисплея. Переделали схему, потратились на один-два чипа PCF8574P, ещё и столкнулись с необходимостью переделывать библиотеки. В итоге - перехитрил сам себя.

Как должно быть. Все конечные устройства, включая самый элементарный датчик, должны изначально проектироваться для работы в сети. Контроллеры имеют прекрасную поддержку на аппаратном уровне I2C, SPI - минимум программного кода, максимум скорости и эффективности в обмене данными.

Как должен выглядеть терморегулятор в сетевой парадигме? Клавиатура - самодостаточное, сетевое (I2C/SPI) устройство на микроконтроллере. Каждый термодатчик - самодостаточное, сетевое (I2C/SPI) устройство на микроконтроллере. Дисплей - самодостаточное, сетевое (I2C/SPI) устройство на микроконтроллере. Все модули подключаются на единую шину данных и взаимодействуют с головным устройством и между собой на сетевом уровне.

Разработчик получает неограниченный ресурс модернизации любого сетевого, программно-аппаратного модуля, не затрагивая функционирование всей системы в целом.

UART - MAX232 - COM/USB - Персональный компьютер.

Опубликовано: 31/12/16

Программа обмена для микроконтроллера реализована на ассемблере. Скачать проект можно по данной ссылке.

Физически компьютер подключен к схеме микроконтроллера через преобразователь com-usb Prolific Technology Inc. Скорость передачи 9600 (8 бит, чётность не установлена, стоповый бит - 1, управление потоком выключено).

...

Терминальная программа подключения со стороны Windows может быть любой - putty,realterm. Но дальнейший алгоритм будет подстроен под терминальную программу Terminal. Удобство данной программы в том, что есть редактируемая строка ввода. По нажатию Enter строка передаётся в микроконтроллер для дальнейшей обработки.

Компьютер начинает передачу данных. Как только байт по протоколу UART считан в регистр RCREG, МК генерирует приоритетное прерывание. Обработчик прерывания считывает данный байт и помещает его в буфер. После этого инициирует обратную передачу считанного байта в компьютер через прерывания низкого приоритета. В результате на экране терминала отображается только что введённый символ или строка.

Форт. Анализ входного потока.

Опубликовано: 05/01/17

Форт слово – любая последовательность символов, разделённая пробелами. Чтобы не усложнять анализ, в текущей реализации к разделителям приравнивается любая последовательность управляющих символов. Таких как - возврат каретки (CR), новая строка (LF), и так далее. В ASCII таблице практически все они расположены ниже знака пробела.

Байты по протоколу RS232 поступают в буфер ввода (UABuffer). При получении управляющего символа CR (0x0D) ввод данных приостанавливается, управление передаётся подпрограмме (fwNext) выборки форт-слов в буфере ввода. Если слово найдено, ищем его в словаре-списке форт-слов.

Форт-слова располагаются в памяти программ контроллера. Пока структура словаря форт-слов выглядит следующим образом:

VocFRT db .5,0x00 Длина и флаги
dw 0x0000 Поиск закончен
db "forth" Название
return Исполнимый код
WRED db .3,0x00
dw VocFRT Предыдущее слово
db "red"
movlw b'00000100' Поле кода
xorwf LATE,1
return

Интерпретатор прост. Пройти по связанному списку форт-слов от конца к началу. Если имена слов в буфере ввода и в списке программ совпадают, передать управление на поле кода. При вводе слова-команды red с клавиатуры будет происходить включение и выключение красного светодиода.

Механизм выполнения процессорных команд найденного форт-слова следующий: в аппаратном стеке микроконтроллера размещается адрес начала исполнимого кода форт-слов и по команде return выполняется переход.

doCall incf STKPTR, F
movf TBLPTRL,W
movwf TOSL
movf TBLPTRH, W
movwf TOSH
movf TBLPTRU, W
movwf TOSU
return
Форт-Стек.

Опубликовано: 07/01/17

Реализован механизм поиска в словаре форт слов и их вызова. Не хватает механизма обмена данными между вызываемыми форт-программами. Этим механизмом является стек данных. Под работу со стеком отводится память, задаваемая константой STACK equ 0x0700. Указатель на вершину стека всегда хранится в регистре FSR1. При добавлении, байт записывается по указателю FSR1, затем значение регистра уменьшается на единицу. Использование регистра FSR1 снимает ограничение на размер обращений к сегменту памяти в 255 байт.

WPORTE db .5,0x00 ; ( -- а )
dw WB2C ; На стек адрес порта E
db "PortE"
lfsr 0,LATE
movff FSR0H,POSTDEC1
movff FSR0L,POSTDEC1
return
 
WIOR db .3,0x00 ; ( n а --)
dw WPORTE ; Логическая OR
db "or!" ; с числом по адресу
movff PREINC1,FSR0L
movff PREINC1,FSR0H
movf PREINC1,w
iorwf INDF0,1
return

На экране показан разбор и выполнения некоторых форт-слов. Хотелось бы сделать базовый минимум и не засорять исходник быстро-растущим количеством форт-слов.

...

Ввод и вывод по RS232 каждый живёт своей жизнью. Надо убрать вывод по прерываниям. Процесс должен быть непременно последовательным. Прочитали строку, разобрали, вывели.

Переменные.

Опубликовано: 09/01/17

Память микроконтроллера представляет собой совокупность памяти данных - ОЗУ, памяти программ - FLASH и энергонезависимой памяти данных. Память данных отличает минимализм - до 4КБ. Память программ - это 64кб. Но запись (программирование) памяти программ нетривиально, и выполняется в четыре этапа:

  call Read ; Чтение 64 байт памяти программ в буфер.
call Modify ; Изменение значений в буфере на актульные.
call Erase ; Очистка 64 байт памяти программ.
call Write ; Перезапись данных обратно в память.

И это ещё не всё. Например, данные для перезаписи находятся в памяти программ по адресу 0x00115C. Команда процессора очистки памяти игнорирует шесть младших бит адреса (0:5). 0x00115C - 1000101011100 - 1000101000000. Таким образом, шестнадцатеричными FF, будут заполнены 64 байта, начиная с адреса 0x001140, а не с адреса 0x00115C. К особенности работы с памятью можно отнести и то, что последней командой в циле записи шестидесяти четырёх байт не может быть автоинкремент tblrd *+. Значение регистра TBLPTR автоматически меняется на следующий банк памяти. Последующая команда записи будет ошибочна и выполнена для блока памяти - 0x1180. И на последок. Создаваемое слово может оказаться между банками в 64 байта. Тоесть работать необходимо со 128 байтами памяти программ.

Идея реализации форт-переменные состоит в том, чтобы в памяти программ хранить только указатель на ранее зарезервированный адрес памяти данных (HEAP). Работа форт-слова variable, которое отвечает за создание новых переменных состоит из двух частей.

Первый часть. Создание переменной. Обработчик команды variable считывает из входного потока очередное слово и вносит его в словарь как новую команду. Размещает в информационном поле только что созданной переменной адрес области памяти данных и некоторый программный код.

...

Вторая часть - исполнение. При вводе имени переменной, интерпретатор ищет в словаре слово с этим именем и передаёт управление в поле кода. Исполнимый код размещает на стеке двухбайтный адрес памяти данных , куда записываются и считываются значения. На вершине стека младший байт адреса, далее старший. Временная команда h2c преобразует значение на вершине стека в шестнадцатеричный формат. с! записывает байт по адресу. с@ считывает.

Есть и третиий, важный момент. В каждый момент времени текстовый интерпретатор находится в одном из двух состояний: в состоянии исполнения или в состоянии компиляции. В состоянии исполнения, найденное слово исполняется (т.е. выполняется действие, составляющее его семантику), а число кладется на стек. Если же интерпретатор находится в состоянии компиляции, то найденное слово не исполняется, а компилируется, т.е. включается в создаваемую последовательность действий для определяемого в данный момент слова.

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

Алгоритм реализации variable.

Опубликовано: 11/01/17

Доступ к памяти программ возможен только через табличный регистр (TBLPTR). Записываем адрес, считываем байт. Для доступа к константам в памяти программ был использован следующий алгоритм. При создания форт-слова, сразу за его именем, размещаются машинные коды инструкции сall doVariable. Код команды и адрес подпрограммы считает компилятор. Достаточно корректно скопировать данную инструкцию.

Найдя в списке команд переменную, форт-интерпретатор передаёт управление в поле кода. Первой командой будет выполнена инструкция вызова программы: сall doVariable.

Дальше просто. В аппаратном стеке микроконтроллера будет полный адрес памяти, следующего за командой сall doVariable. За этой командой следуют двух байт данных, которые при создания слова были заполнены адресом зарезервированных ячеек памяти данных. Переписываем адрес из стековых регистров, в регистры доступа к памяти TOS->TBLPTR. Считываем данные по адресу TBLPTR и размещаем их в стеке форт-системы. С аппаратного стека снимаем адрес возврата и выполняем return. Программа продолжит выполняться с точки,откуда было вызвано форт-слово. Данный механизм будет использован для хранения строковых констант.

doVariable:
movf TOSL,w
movwf TBLPTRL
movf TOSH,w
movwf TBLPTRH
movf TOSU,w
movwf TBLPTRU
decf STKPTR,f ;Отмена возврата в точку вызова.
 
tblrd *+ ;Младший байт адреса памяти
movf TABLAT,w
movwf POSTDEC1 ;Добавляем в форт-стек.
 
tblrd * ;Старший байт адреса памяти
movf TABLAT,w
movwf POSTDEC1 ;Добавляем в форт-стек.
return
Вызовы форт-слов, стек возвратов.

Опубликовано: 12/01/17

Стандарты Форт-систем предписывают реализацию стека возвратов. Перед тем как передать управление, на стек возвратов необходимо поместить адрес исполнимого кода следующего форт слова. После, команда возврата снимают со стека этот адрес и передаёт по нему управление.

Разработчиков микроконтроллеров pic можно упрекнуть в чём угодно, но только не в любви к командам call/return. Аппаратный стек возвратов рассчитан на глубину в 32 вызова, СИ отказывается компилировать рекурсию. В случае с форт это будет означать, что максимально-допустимая глубина вложений форт-слов при компиляции в 32 уровня. Но это лучше, чем программный аналог стека в архитектуре, где нет косвенных вызовов. Общий случай как делать нельзя.

: вывод cr ;

Переопределили слово cr и впустую потеряли один уровень стека возвратов.

Разберём команду call: 1110 110s kkkk kkkk 1111 kkkk kkkk kkkk Командой производится вызов подпрограммы во всем диапазоне адресуемой памяти (2 мегабайта). Все немедленно обращают внимание на то, что максимальный адрес 20-ти битного значения k - это FFFFF (1048575). Но это один мегабайт. И задают вопрос. Как 20-ю биатами можно адресовать два мегабайта? И тут же отвечают на него.

Команд процессора длинной в один байт в данной архитектуре нет. Это значит, что все ячейки памяти в которых расположены команды процессора могут быть либо только чётными или только нечётными. Проще говоря. Необходимо весь адрес разделить на два и сформировать поле команды call по инструкции - вначале байт данных, затем байт кода команды.

RRNCF TBLPTRU,F ; Делим адрес на два
RRCF TBLPTRH,F ; сдвигом вправо на один бит
RRCF TBLPTRL,F
;
movff TBLPTRL,POSTINC0 ; 0:7 биты адреса
movlw 0xEC ; Код команды call
movff TBLPTRH,POSTINC0 ; 8:15 биты адреса
movlw 0xF0 ; 16:20 - биты адреса
iorwf TBLPTRU,W
movwf POSTINC0

В регистре fsr0 указатель на адрес куда компилируется команда, В регистре TBLPTR адрес найденного только исполнимого кода.

В большинстве случаев проще сгенерировать машинную команду и это будет эффективнее, чем если строить стек возвратов, вводить идентификаторы слов, искать их в таблицах и передавать управление по записанным там адресам. Как? Эффективно это не сделать. Пример косвенного вызова в архитектуре микроконтроллеров PIC был показан выше.

Определение новых слов.

Опубликовано: 14/01/17

Замечательное свойство языка Форт - это возможность вводить в него новые слова, расширяя тем самым набор его команд в нужном для программиста направлении. Для введения новых слов используется : двоеточие - определение нового слова через уже известные. Такое определение начинается словом : (двоеточие) и заканчивается словом ; (точка с запятой). Сразу после двоеточия идет определяемое слово, а за ним - последовательность слов, через которые оно определяется.

...

Проследим за работой текстового интерпретатора по обработке последовательности определения нового слова "адрес".

: адрес swap h2c h2c cr ;

Первым словом является : (двоеточие), которое исполняется. Его семантика с учётом архитекторы состоит в следующем.

Подпрограмма GetWORD читает из входного потока следующее слово, которое и будет являться именем новой команды.

На запись двух байт в памяти программ может потребоваться перепрограммирование участка памяти в 128 байт. Для оперативной работы с памятью программ организован промежуточный буффер FLBuffer(L/H) в памяти данных. Адрес вершины свободной памяти программ хранится в переменной HereCODE(L/H/U). В буфер копируется участок +- 64 байта памяти программ начиная с адреса в переменной HereCODE. Адрес памяти программ и промежуточный буфер имеют разные адреса. Подпрограмма чтения ReadCodeHERE, рассчитывает корректное смещение в промежуточном буфере адреса HereCODE и начиная с этого адреса (FlBufPTR) идёт формирование структуры форт-слова.

После того как структура слова сформирована, слово ":" переводит систему в режим компиляции bcf ForthConfiguration, COMPILATOR и управление возвращается интерпретатору команд.

В режиме компиляции интерпретатор, после того как найдёт очередное слово (swap) в словаре, управление ему не передаёт, а вызывает подпрограмму CompileCALL. Подпрограмма формирует машиный код команды call на найденный адрес и записывет команду в FLBuffer.

Если интерпретатор не находит слова в словаре, он пытается определить введённую последовательность как число. Если введено число, интерпретатор помещается число на стек и вызывает обработчик CompileNUMBER. Обработчик формирует две машинных команды: movlw (число на вершине стека) и movwf POSTDEC1. В будущем, после передачи управления, данный код поместит число-константу на стек данных.

Процесс выделения и компиляции слов будет продолжаться до тех пор, пока не встретится ; (точка запятой). Это слово должно иметь признак немедленного исполнения". Пока обработчик данного слова вызывается в режиме компиляции. На это указывает флаг - COMPILE в признаках слова. Семантика точки с запятой заключается в том, что построение определения, начатого двоеточием, завершается.

Точка с запятой формирует в промежуточном буфере (FLBuffer) машинную команду return. После этого переписывает буфер (128 байт) в память программ. После записи, изменяются значения переменной адреса последнего слова в словаре LastWORD, которое становится равным значению текущего HereCODE. А значение HereCODE увеличивается на длину созданного слова. Определение нового слова завершено, режим компиляции выключается.

В системе предусмотрено хранение глобальных переменных указывающих на свободную память программ (HereCODE) и адрес последнего слова (LastWORD) в памяти программ. Память программ энергонезависима, после перезапуска контроллера все новые форт слова будут доступны для исполнения.

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

Условный оператор.

Опубликовано: 16/01/17

Определим два слова. Истина - выводит на экран цифру один. Ложь - выводит на экран цифру ноль. Слово проверка снимает со стека данных байт. Если на стеке значение отличное от нуля, то выполняется группа команд между операторами if и else, после этого управление передаётся на команду следующую за оператором end. Если на стеке значение равное нулю, if передаёт управление на команду, первую за оператором else.

...

Форт-слово это последовательность машинных команд. Команда if вызывает обработчик CompileIF. Обработчик помещает в буфер компиляции группу машинных команд, которая снимает со стека значение и сравнивает с нулём. На этапе обработки оператора if компилятору не известен адрес команды (else) на которую необходимо передать управление, если значение равно нулю. Компилятор размещает на стеке данных адрес зарезервировано места под команду, которая должна выполнить этот переход. Слова else или end снимают со стека этот адрес и формируют оператор перехода goto с реальным адресом в памяти.

Описанные выше функции в стандарте выполняют слова >mark - отметить текущий адрес для ссылки вперёд и >resolv - разрешить ссылку вперёд. Так же стандарт настоятельно рекомендует сделать контроль парности if/end. Очень важно, но позже.

Циклы.

Опубликовано: 17/01/17

Цикл begin-until повторяется только если на стеке, перед выполнением команды until, нуль, в противном случае цикл заканчивается. Поэтому при наличии на стеке счётчика, значение которого больше нуля, необходима последовательность команд "dup if 0 else 1 end", которая проверяет байт на стеке и меняет его логическое значение на противоположное.

...

Последовательность слов "dup b2c cr" дублирует байт на стеке (dup), преобразует его в бинарную ASCII последовательность в буфере вывода (b2c) и отображает на экране (cr).

Цикл begin-while-repeat гораздо более интересен и удобен в работе. Я его понимаю и сделал следующим образом. Группа команд после begin осуществляет подготовку данных на стеке для оператора while. Оператор while сравнивает значение на стеке с нулём. Если значение не нулевое, начинает выполняться группа команд после while. Команда repeat передаёт управление на начало цикла (begin). Если, перед выполнением оператора while на стеке нуль, цикл заканчивается, управление передаётся команде следующий за repeat.

...

: цикл begin 1- dup while dup h2c cr repeat cr ;

Как реализованы циклы.

Опубликовано: 19/01/17

Первой командой цикла является begin. По данной команде интерпретатор находит и передаёт управление обработчику - CompileBEGIN. Ничем примечательным данная команда не отличается. На стек помещается текущий адрес в буфере компиляции. Чтобы оставить за собой возможность реализовать аналоги команд continue/break, в обращение введён ещё один стек. Пока он называется стеком аргументов и указатель на него постоянно находится в регистре FSR2.

Все слова за командой begin компилируются обычным образом. По команде while вызывается обработчик CompileWHILE. В буфер компиляции копируются машинные коды следующих команд до команды goto. Адрес goto в буфере ввода помещается на стек FSR2 и резервируются четыре байта под команду goto которая будет создана оператором repeat.

movlw .0
xorwf PREINC1,w
bnz WHBNZ
WHGOTO goto 0x0000
WHBNZ nop

Оператор цикла заканчивается repeat. По данной команде вызывается обработчик CompileREPEAT. У него две задачи. Первое - сделать безусловный переход на команду начала цикла. И второе, создать команду goto для оператора while, которая выполнит переход на первую команду за оператором repeat. Все необходимые адреса расположения команд begin и while/goto находятся на стеке FSR2.

К сожалению, не удаётся избежать пересчётов смещений в буфере компиляции в реальные адреса памяти программ. Но алгоритм прост. Компиляция начинается с адреса FlCodeBEG(H/L). Этот адрес из буфера компиляции ляжет точно по адресу памяти HereCODE(U/H/L). Отнимаем от текущего значения в буфере адрес FlCodeBEG. Получаем смещение которое прибавляем к значению в HereCODE(U/H/L). Это даётточный, реальный адрес памяти, который подставляется в операторах goto.

Сравнение скорости СИ/ФОРТ.

Опубликовано: 19/01/17

На СИ и ФОРТ реализованы программы изменения бита порта командой XOR в бесконечном цикле. На первой фотографии скорость работы данного генератора сигналов на СИ, далее ФОРТ. Количество выполняемых команд примерно равны.





Две новости - плохая и хорошая. Форт медленнее си, хорошая - почему только в два раза? Форт выполняет множество операций со стеком. В каждом цикле форт помещает на стек одни и те же константы и затем их снимает. СИ этого не делает. Но что такое 200 кГц для СИ с таким примитивным набором команд? Это его непригодность для задач реального времени обработки и генерации сигналов.

Примеры. Выход из цикла.

Опубликовано: 14/02/17

Для удобства работы реализован оператор leave (досрочный выход из цикла).

...

...

...

1-Wire.

Опубликовано: 19/02/17

Базовые конструкции работают. Первый этап работы можно считать законченным и развивать систему в одной из предметных областей. Данной областью будет реализация протокола 1-Wire для работы с температурными датчиками. Всё начинается с написания программ сброса, передать бит, передать байт, считать бит,считать байт.

...

Реализован опрос шины и чтение адресов группы 1-wire устройств. Была попытка сделать это на Форт, но итоговая реализация пришлась на ассемблер: ow_init - инициализация; ow_next - считать следующий адрес устройства; ow_rc - коды возврата.

...

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

Но так делать не будем. Списки и алгоритм поиска уже есть - это словарь форт-слов, который хранится в энергонезависимом ОЗУ (память программ). Алгоритм должен найти это слово в словаре и передать управление.

...

...

Копилка.

Опубликовано: 20/02/17

Мищук Андрей.
Tinkoff: 5536 9141 6900 6230
energy4all@inbox.ru
Буду признателен за поддержку!