Tiny Basic для Arduino

Tiny Basic для Arduino

Музыка, I2C часы и другие забавные программы

Tiny Basic был создан в середине 1970х годов для первых компьютеров с очень ограниченным объёмом памяти. В Tiny Basic имена переменных состоят только из одной буквы, поэтому в программе может быть не более 26 переменных (хотя можно декларировать массивы). Замечательную версию Tiny Basic для Arduino с открытым исходным кодом можно найти по адресу:

https://github.com/slviajero/tinybasic

Tiny Basic для Arduino обладает весьма большим быстродействием в сравнении с компьютерами 70х годов, но размер BASIC-программ ограничен примерно 1 килобайтом (!). Это делает его не очень полезным для разработок, но он очень удобен для обучения и простейших экспериментов с Arduino. Он поддерживает возможность работы с I2C интерфейсом, радио платами, дисплеями, клавиатурами и многим другим. В этой статье описываются основы работы с Tiny Basic в наиболее простых примерах.

Скачайте архив с Tiny Basic и примерами программ с этой страницы. В файле manual.md дано подробное руководство по языку. Файлы .ino и .h - исходный текст программы. Загрузите скетч в Arduino IDE. Программу можно настроить операторами #define на разные конфигурации. В этой версии - целочисленный BASIC с поддержкой PLAY (функция tone()). Загрузите скётч в Arduino (у меня - Arduino Uno). Запустите терминал IDE Установите скорость обмена 9600 Бод и завершение строки символом NL. Теперь можно набрать простую программу, например:


10 A$=9:PRINT "Pythagorean table"
20 FOR J=1 TO 9
30 FOR I=1 TO 9
40 PRINT I*J;a$;
50 NEXT I
60 PRINT
70 NEXT J

Запустите программу командой RUN Неплохо, правда ? Но как теперь сохранить программу, не набирать же её каждый раз заново ? Наберите SAVE - программа сохранится в EEPROM Arduino. Теперь можно нажать на Reset или закрыть и снова открыть терминал, и даже отключить Arduino от питания. Программа исчезнет, но она может быть снова загружена командой LOAD

Для того, чтобы загружать .bas файлы в Arduino, я написал простую программу !load.exe на языке PureBasic Откройте файл !load.ini и введите имя COM порта, к которому подключается Arduino. Запустите !load.exe Попробуйте набрать теперь load blink (обратите внимание - строчными буквами). Если всё нормально, должна загрузиться программа. Запустите её командой run и Вы увидите мигание встроенного на плату светодиода. Ну, а если потребуется загрузить программу из EEPROM ? Тогда используйте LOAD (большие буквы).

Для сохранения программы на диске используется команда save filename или просто save Завершить наш маленький терминал можно командой exit (строчные буквы).

Полезные команды Tiny Basic и !load
КомандаОписание
NEWНовая программа
PRINT SIZEРазмер свободной памяти
SAVEЗаписать программу в EEPROM
LOADЗагрузить программу из EEPROM
save filenameЗаписать программу в файл
load filenameЗагрузить программу из файла
LISTНапечатать текст программы
RUNЗапустить программу
exitВыйти из !load

Я также написал версию терминала на VisualBasic.NET Эта версия программы называется SerialSmall.exe Перед запуском программы следует записать в файл SerialSmall.ini (с помощью notepad) номер COM порта, к которому подключается Arduino. Если записать в .ini файл COM0, то имя COM порта Arduino будет определяться автоматически (не до конца проверено). SerialSmall.exe выполняет те же команды, что и !load.exe. Для работы требуется установленная библиотека .NET

Для дальнейших опытов понадобится пищалка (баззер), подключённая к выходу 8 Arduino и к земле (обязательно отключите плату Arduino от компьютера при монтаже). Подключите Arduino и запустите !load.exe Наберите к примеру load nokia. Наберите run - программа проиграет незабвенный рингтон Nokia. Обратите внимание, как закодированы ноты. (Команда list) В качестве первого параметра идёт частота звука, в качестве второго - достаточно большая задержка в миллисекундах, делённая на число (напомним, что BASIC - целочисленный). Это сделано для того, чтобы уменьшить объём памяти, занимаемый строками DATA. При желании можно изменить пин подключения баззера, отредактировав строку B=8

Я сконвертировал на Tiny Basic несколько мелодий, все программы построены сходным образом, за исключением minuet.bas Эта мелодия оказалась слишком длинной и не вмещалась в 1 килобайт. Поэтому я преобразовал строки "DATA число1,число2,..." в строки по 60 символов (20 нот). Один символ кодирует 5 бит: "@" - 0, "A" - 1, и далее до "^" - 31. Таким образом ноту кодируют 3 символа: 2 - частота (от 0 до 1023) и 1 - длительность (от 0 до 31).

Попробуем написать на Tiny Basic более сложную программу - управление часами реального времени DS1307. Я использовал готовую плату - "Тройка модуль" от amperka.ru с литиевой батарейкой, кварцем и резисторами. Вы можете попробовать любую другую микросхему с интерфейсом I2C. На примере для часов я покажу, как действительно надо программировать обмен по I2C.

Подключите модуль RTC к контактам A4 (D-SDA) и A5 (C-SCL), а также подключите питание на разъём питания. Контакты A4 и A5 являются аппаратными для шины I2C, но я предложу чисто программный вариант обмена I2C Master. Запустите !load.exe и в ней команды load i2cscan, run. Если всё работает нормально, появится сообщение "Device found at address 114". Теперь можно загрузить load i2clock и посмотреть, как она работает.

Программа i2clock приближается по длине с максимуму для Tiny Basic и содержит следующие подпрограммы. Обратите особое внимание на переключение режимов линии SDA. PINM D,1 - запись, PINM D,2 - чтение с подтягивающим резистором.

Строка подпрограммыОписание
100Start условие
200Stop условие
300Запись адреса V и бита Read/Write R
310Запись байта Q и чтение бита подтверждения A
400Чтение байта E и запись бита подтверждения Y
600Сброс шины I2C в исходное состояние

Так как же программировать шину I2C ?

В руководствах по чипам с I2C всё вроде бы просто: "выдайте Start-условие, затем 7-битный адрес slave и бит чтение-запись, затем по алгоритму пишите или читайте байты, в конце выдайте Stop-условие". При этом создатели I2C отмечают, что протокол синхронный, и его можно чуть ли не по шагам отлаживать. На самом деле I2C - это настоящий Ящик Пандоры. С линией SCL всё понятно - микроконтроллер переводит пин в режим записи и выдаёт через неё стробы. Но линия SDA - двунаправленная, поэтому при отладке (даже с помощью осциллографа) непонятно, кто генерирует сигнал - master или slave.

Представим себе, что master читает, а slave передаёт байты, например байт со значением 0. И внезапно (например при отладке) мы остановили программу (где-то на середине приёма байта). Как правильно возобновить обмен с slave с самого начала ? Послать Start-условие ? Но если slave передаёт бит 0, он давит линию SDA низким уровнем и не отреагирует на Start. Послать Stop-условие ? Та же проблема. К тому же, особенно если slave программный, он может и не ждать Stop в этот момент, а просто сидеть в цикле, надеясь передать остатки байта. Здесь даже reset микроконтроллера не поможет, поскольку он не подействует на slave, поможет только полное отключение питания от схемы.

Опытным путём я нашёл следующее решение: чтобы начать работу по шине I2C (особенно, если на ней подключено несколько slave) надо в цикле посылать Stop-условие, а затем читать линию SDA до тех пор, пока не придут подряд 18 уровней "1" (18=9*2, где 9 бит соответствует приёму или передаче одного байта с битом подтверждения). Следующая подпрограмма иллюстрирует этот способ.


590 rem "Reset I2C bus"
600 Z=0
605 rem "200 - subroutine of Stop"
610 gosub 200:A=dread(D)
620 if A=0 then goto 600
620 Z=Z+1:IF Z<=18 THEN GOTO 610
630 RETURN

Ещё одна интересная микросхема с интерфейсом I2C - Atmel AT24C512 - FLASH память объёмом 64 KB. Этот чип относительно легко подключается и программируется, но имеет свои трюки. Для подключения AT24C512 достаточно 4 линий: земля, +5V, SCL, SDA. Если других микросхем на шине I2C нет, нужно исспользовать 2 подтягивающих резистора по 2 КОм на линиях SCL и SDA.

По умолчанию AT24C512 имеет 7битный адрес устройства 50h (8битный - 0A0h). Чтение чипа возможно с любого адреса памяти на любую длину (в пределах ёмкости). Запись чипа возможна только в пределах адресов памяти одной страницы (для этой модели - 128 байт). Записав страницу, следует выдать Stop-условие и дождаться, пока страница запишется. Время записи - порядка десятков миллисекунд, что значительно больше, чем при обычном обмене с чипом по I2C (порадка нескольких микросекунд при передаче байта). В программе на Tiny Basic это не столь заметно, т к программа выполняется медленно, но при программировании на Си длинные задержки записи FLASH следует учитывать.

Я написал 3 программы работы с AT24C512, используя уже описанные подпрограммы:

ПрограммаОписание
i2flashrЧтение 256 байт
i2flashwЗапись 256 байт
i2flasheraseСтирание 256 байт

Подключите AT24C512 согласно схеме. Запустите !load.exe или serialsmall.exe Наберите


load i2flashr
run

Вы увидите содержимое первых 256 байт FLASH памяти. (если AT24C512 новая, то это будут байты 0FFh) Теперь наберите:

load i2flashw
run
load i2flashr
run

Содержимое таблицы байт изменится на 00,01,02...FD,FE,FF - FLASH память запрограммирована. Чтобы вернуть память к исходному состоянию, наберите:

load i2flasherase
run
load i2flashr
run

Первые 256 байт FLASH памяти будут заполнены байтами 0FFh.

Загрузить скетч Tiny Basic, терминал !Load и .bas примеры программ

Обновлено 7 июля 2024 Программа !load.exe заменена версией на PureBasic (см. исходный текст !load.pb). Добавлено несколько мелодий, программы i2clon.bas и i2clon2.bas теперь печатают дату и время. Добавлены программы i2flash*.bas для работы с FLASH памятью AT24C512