From 271e9a4ea7670bd266c020dc752b14d273249c9c Mon Sep 17 00:00:00 2001 From: Joerg Siebenmorgen Date: Fri, 18 Sep 2026 22:29:35 +0200 Subject: [PATCH 1/3] TEENSY: Add flash and SD card file system --- src/platform/teensy/CMakeLists.txt | 6 + src/platform/teensy/Makefile.am | 5 + src/platform/teensy/README.md | 14 +- src/platform/teensy/libs/CMakeLists.txt | 6 + .../teensy/samples/SerialTerminal.bas | 220 ++++---- .../teensy/samples/{bt.bas => bluetooth.bas} | 0 .../{hc-06.bas => bluetooth_hc-06.bas} | 0 src/platform/teensy/samples/console.bas | 493 ++++++++++++++++++ src/platform/teensy/samples/fs_copy_file.bas | 52 ++ .../teensy/samples/fs_flash_basic_io.bas | 58 +++ .../teensy/samples/fs_flash_list_files.bas | 22 + .../teensy/samples/fs_sd_basic_io.bas | 55 ++ src/platform/teensy/samples/hd44780.bas | 218 ++++++++ src/platform/teensy/samples/i2c_bh1750.bas | 14 + src/platform/teensy/samples/i2c_bmp180.bas | 14 + src/platform/teensy/samples/i2c_mpu6050.bas | 201 +++++++ .../{pwm_led.bas => io_analog_pwm_led.bas} | 0 ...{pwm_led_2.bas => io_analog_pwm_led_2.bas} | 0 .../teensy/samples/io_analog_read.bas | 23 + .../samples/{led.bas => io_digital_led.bas} | 0 .../teensy/samples/io_digital_read.bas | 19 + .../{serialprint.bas => serial_print.bas} | 0 ...ive_bytes.bas => serial_receive_bytes.bas} | 14 +- ...e_string.bas => serial_receive_string.bas} | 7 + src/platform/teensy/setup.sh | 33 +- src/platform/teensy/src/fs.cpp | 362 +++++++++++++ src/platform/teensy/src/fs.h | 15 + src/platform/teensy/src/i2c.cpp | 146 ++++++ src/platform/teensy/src/i2c.h | 14 + src/platform/teensy/src/main.cpp | 76 ++- src/platform/teensy/src/module.h | 1 + src/platform/teensy/src/pinio.cpp | 150 ++++++ src/platform/teensy/src/pinio.h | 17 + src/platform/teensy/src/serial.cpp | 164 ++++++ src/platform/teensy/src/serial.h | 3 + src/platform/teensy/src/ssd1306.cpp | 3 +- src/platform/teensy/src/teensy.cpp | 454 +--------------- 37 files changed, 2279 insertions(+), 600 deletions(-) rename src/platform/teensy/samples/{bt.bas => bluetooth.bas} (100%) rename src/platform/teensy/samples/{hc-06.bas => bluetooth_hc-06.bas} (100%) create mode 100644 src/platform/teensy/samples/console.bas create mode 100644 src/platform/teensy/samples/fs_copy_file.bas create mode 100644 src/platform/teensy/samples/fs_flash_basic_io.bas create mode 100644 src/platform/teensy/samples/fs_flash_list_files.bas create mode 100644 src/platform/teensy/samples/fs_sd_basic_io.bas create mode 100644 src/platform/teensy/samples/hd44780.bas create mode 100644 src/platform/teensy/samples/i2c_mpu6050.bas rename src/platform/teensy/samples/{pwm_led.bas => io_analog_pwm_led.bas} (100%) rename src/platform/teensy/samples/{pwm_led_2.bas => io_analog_pwm_led_2.bas} (100%) create mode 100644 src/platform/teensy/samples/io_analog_read.bas rename src/platform/teensy/samples/{led.bas => io_digital_led.bas} (100%) create mode 100644 src/platform/teensy/samples/io_digital_read.bas rename src/platform/teensy/samples/{serialprint.bas => serial_print.bas} (100%) rename src/platform/teensy/samples/{serialUSB_receive_bytes.bas => serial_receive_bytes.bas} (69%) rename src/platform/teensy/samples/{serialUSB_receive_string.bas => serial_receive_string.bas} (75%) create mode 100644 src/platform/teensy/src/fs.cpp create mode 100644 src/platform/teensy/src/fs.h create mode 100644 src/platform/teensy/src/i2c.cpp create mode 100644 src/platform/teensy/src/i2c.h create mode 100644 src/platform/teensy/src/pinio.cpp create mode 100644 src/platform/teensy/src/pinio.h create mode 100644 src/platform/teensy/src/serial.cpp diff --git a/src/platform/teensy/CMakeLists.txt b/src/platform/teensy/CMakeLists.txt index 8304ea0a..aa259832 100644 --- a/src/platform/teensy/CMakeLists.txt +++ b/src/platform/teensy/CMakeLists.txt @@ -42,6 +42,7 @@ set(CMAKE_EXE_LINKER_FLAGS "-Os -Wl,--gc-sections,--relax ${SPECS} ${CPU_OPTIONS set(CMAKE_CXX_COMPILER arm-none-eabi-g++) set(CMAKE_C_COMPILER arm-none-eabi-gcc) set(CMAKE_SIZE arm-none-eabi-size) +set(CMAKE_OBJCOPY arm-none-eabi-objcopy) # Include the module directories add_subdirectory(libs) @@ -58,6 +59,7 @@ include_directories(${MODULES}/USBHost_t36) include_directories(${MODULES}/Wire) include_directories(${MODULES}/SD/src) include_directories(${MODULES}/SdFat/src) +include_directories(${MODULES}/LittleFS/src) # include config.h and common headers include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../..) @@ -71,6 +73,10 @@ set(SOURCES src/module.cpp src/teensy.cpp src/ssd1306.cpp + src/i2c.cpp + src/serial.cpp + src/pinio.cpp + src/fs.cpp ) # Add executable with the list of source files diff --git a/src/platform/teensy/Makefile.am b/src/platform/teensy/Makefile.am index 68b2c758..ed4091ad 100644 --- a/src/platform/teensy/Makefile.am +++ b/src/platform/teensy/Makefile.am @@ -25,6 +25,11 @@ build/smallbasic.elf : \ src/device.cpp \ src/teensy.cpp \ src/ssd1306.cpp \ + src/fs.cpp \ + src/i2c.cpp \ + src/serial.cpp \ + src/pinio.cpp \ + src/module.cpp \ src/main_bas.h @cd build && make -j 32 diff --git a/src/platform/teensy/README.md b/src/platform/teensy/README.md index 317af58d..126acf4d 100644 --- a/src/platform/teensy/README.md +++ b/src/platform/teensy/README.md @@ -93,16 +93,16 @@ build/modules/teensy_loader_cli/teensy_loader_cli --mcu=TEENSY40 -w -v -s build/ SMALLBASIC for Teensy offers three ways to upload and run a program. 1. Format a SD card using FAT32. Rename your program to `MAIN.BAS` and copy it to the SD card. -2. Include your program in the firmware. Replace `main.bas` in `src/platform/teensy` by your program and build +2. Upload your program as `MAIN.BAS` to the flash memory file system (i.e. use `console.bas`) +3. Include your program in the firmware. Replace `main.bas` in `src/platform/teensy` by your program and build the firmware. -3. Send your program via USB-serial connection to the Teensy. In Linux use ` cat YourProgram.bas > /dev/ttyACM0` +5. Send your program via USB-serial connection to the Teensy. In Linux use ` cat YourProgram.bas > /dev/ttyACM0` Change `/dev/ttyACM0` to the USB-serial port of your Teensy. -4. Goto 3. When the Teensy starts up, it will check in the above indicated order for your program. If it finds -a SD card and the SD card contains a file `MAIN.BAS`, it will execute it. Otherwise it will check, if a program -was included in the firmware. If no program was included, the Teensy will switch to interactive mode and waits -for a program upload via USB-serial. +a SD card and the SD card contains a file `MAIN.BAS`, it will execute it. Otherwise it will check flash for +the file `MAIN.bas` and executes it. Otherwise it will check, if a program was included in the firmware. If no +program was included, the Teensy will switch to interactive mode and waits for a program upload via USB-serial. While your program is running, the Teensy will check continuously if data is available at the USB-serial port. If data is available for longer than one second your running program will be terminated and the queued data of the @@ -114,7 +114,7 @@ If an error occurred, for example a syntax error, execution will stop and you ha the USB-serial port. If the execution of your program comes to an end, for example when reaching the end of the program or when `STOP` is -called, the program is terminated and the next section in the above list is performed. +called, the program is terminated and the teensy switches to interactive mode. # Read output from your running program diff --git a/src/platform/teensy/libs/CMakeLists.txt b/src/platform/teensy/libs/CMakeLists.txt index 05452b15..1439e8b1 100644 --- a/src/platform/teensy/libs/CMakeLists.txt +++ b/src/platform/teensy/libs/CMakeLists.txt @@ -16,6 +16,7 @@ set(USB_DIR ${MODULES_DIR}/USBHost_t36) set(WIRE_DIR ${MODULES_DIR}/Wire) set(SD_DIR ${MODULES_DIR}/SD/src) set(SDFAT_DIR ${MODULES_DIR}/SdFat/src) +set(LITTLEFS_DIR ${MODULES_DIR}/LittleFS/src) set(SOURCES ${ADC_DIR}/ADC.cpp @@ -162,6 +163,10 @@ set(SOURCES ${SDFAT_DIR}/common/FsUtf.cpp ${SDFAT_DIR}/common/PrintBasic.cpp ${SDFAT_DIR}/common/upcase.cpp + ${LITTLEFS_DIR}/LittleFS.cpp + ${LITTLEFS_DIR}/LittleFS_NAND.cpp + ${LITTLEFS_DIR}/littlefs/lfs.c + ${LITTLEFS_DIR}/littlefs/lfs_util.c ) add_library(libs STATIC ${SOURCES}) @@ -176,4 +181,5 @@ target_include_directories(libs PRIVATE ${TEENSY_DIR}) target_include_directories(libs PRIVATE ${WIRE_DIR}) target_include_directories(libs PRIVATE ${SD_DIR}) target_include_directories(libs PRIVATE ${SDFAT_DIR}) +target_include_directories(libs PRIVATE ${LITTLEFS_DIR}) diff --git a/src/platform/teensy/samples/SerialTerminal.bas b/src/platform/teensy/samples/SerialTerminal.bas index 0eee365d..cfa54078 100644 --- a/src/platform/teensy/samples/SerialTerminal.bas +++ b/src/platform/teensy/samples/SerialTerminal.bas @@ -3,7 +3,7 @@ option base 1 ReceiveBuffer = "" ComPort = "/dev/ttyACM0" ComSpeed = "115200" -FileName = "serialUSB_receive_string.bas" +FileName = "samples/console.bas" SendString = "" NumberLines = floor(YMAX / TextHeight("Qly")) - 1 NumberRows = floor(YMAX / TextWidth("W")) @@ -33,93 +33,96 @@ const KeyF6 = ESC + Chr(0xF0 + 6) const KeyF9 = ESC + Chr(0xF0 + 9) const KeyF10 = ESC + Chr(0xF0 + 10) + +GetStartupParameter() Connect() PrintGUI() while(1) - 'CheckIsConnected() - if(!isConnected) then Connect() - k = inkey - if(len(k)) - select case k - case KeyReturn - Send(SendString) - SendHistory << SendString - SendHistoryIndex = ubound(SendHistory) - SendString = "" - case KeyEsc - SendString = "" - case KeyLeft - case KeyRight - case KeyDown - SendHistoryIndex = iff(SendHistoryIndex < ubound(SendHistory), SendHistoryIndex + 1, SendHistoryIndex) - SendString = SendHistory[SendHistoryIndex] - case KeyUp - SendHistoryIndex = iff(SendHistoryIndex > 0, SendHistoryIndex - 1, 0) - SendString = SendHistory[SendHistoryIndex] - case KeyBackspace - SendString = chop(SendString) - case KeyF1 - color 15,0 - locate NumberLines - 1,0 - print "\e[K"; - Input "Enter COM port: ", ComPort - if(isConnected) then close #1 - isConnected = 0 - case KeyF2 - color 15,0 - locate NumberLines - 1,0 - print "\e[K"; - Input "Enter COM speed: ", ComSpeed - if(isConnected) then close #1 - isConnected = 0 - case KeyF3 - 'color 15,0 - 'locate NumberLines - 1,0 - 'print "\e[K"; - 'Input "Enter file name: ", FileName - showpage(1) - FileName = FileSelectDialog(0, 50, 50, XMAX - 100, YMAX - 100, 0) - case KeyF4 - tsave "teensylog_" + ticks() + ".txt", ReceiveArray - case KeyF6 - UploadFile() - case KeyF9 - if(isConnected) - close #1 - isConnected = 0 - endif - ReceiveBuffer = "" - ReceiveArray = [] - case KeyF10 - if(isConnected) then close #1 - stop - case else - SendString = SendString + k - end select - PrintGUI() - endif - - if(isConnected) - l = lof(1) - if(l) - ReceiveBuffer = ReceiveBuffer + INPUT(l, 1) - split ReceiveBuffer, "\n", ReceiveArray - if(len(ReceiveArray) > NumberLines - 1) - n = len(ReceiveArray) - NumberLines + 1 - delete ReceiveArray, 1, n + if(!isConnected) then Connect() + k = inkey + if(len(k)) + select case k + case KeyReturn + Send(SendString) + SendHistory << SendString + SendHistoryIndex = ubound(SendHistory) + SendString = "" + case KeyEsc + SendString = "" + case KeyLeft + case KeyRight + case KeyDown + if(SendHistoryIndex > 0) + SendHistoryIndex = iff(SendHistoryIndex < ubound(SendHistory), SendHistoryIndex + 1, SendHistoryIndex) + SendString = SendHistory[SendHistoryIndex] + endif + case KeyUp + if(SendHistoryIndex > 0) + SendString = SendHistory[SendHistoryIndex] + SendHistoryIndex = iff(SendHistoryIndex > 1, SendHistoryIndex - 1, 1) endif - PrintGUI() + case KeyBackspace + SendString = chop(SendString) + case KeyF1 + color 15,0 + locate NumberLines - 1,0 + print "\e[K"; + Input "Enter COM port: ", ComPort + Input "Enter COM speed: ", ComSpeed + if(isConnected) then close #1 + isConnected = 0 + case KeyF3 + if(instr(sbver(), "SDL")) + showpage(1) + FileName_temp = FileSelectDialog(0, 50, 50, XMAX - 100, YMAX - 100, 0) + if(FileName_temp != -1) then FileName = FileName_temp + else + color 15,0 + repeat + locate NumberLines - 1,0 + print "\e[K"; + Input "Enter file name for upload: ", FileName + until(exist(FileName)) + endif + case KeyF4 + tsave "teensylog_" + ticks() + ".txt", ReceiveArray + case KeyF6 + UploadFile() + case KeyF9 + ReceiveBuffer = "" + ReceiveArray = [] + case KeyF10 + if(isConnected) then close #1 + stop + case else + if(asc(k) > 31 AND asc(k) < 127) + SendString = SendString + k + endif + end select + PrintGUI() + endif + + if(isConnected) + l = lof(1) + if(l) + ReceiveBuffer = ReceiveBuffer + INPUT(l, 1) + split ReceiveBuffer, "\n", ReceiveArray + if(len(ReceiveArray) > NumberLines - 1) + n = len(ReceiveArray) - NumberLines + 1 + delete ReceiveArray, 1, n endif + PrintGUI() endif - - delay(20) - showpage + endif + + delay(20) + showpage wend sub CheckIsConnected() local result - + if(!isConnected) then return result = lof(1) @@ -141,11 +144,12 @@ sub Connect() isConnected = 0 end try PrintGUI() + print #1, "PRINT SBVER" end sub Send(s) - ReceiveBuffer = ReceiveBuffer + "\e[33m" + "> " + s + "\e[30m\n" + ' ReceiveBuffer = ReceiveBuffer + "\e[33m" + "> " + s + "\e[30m\n" print #1, s end @@ -160,21 +164,17 @@ sub UploadFile() end sub PrintGUI() - color 7,1 + color 15,0 cls - + for s in ReceiveArray - print "\e[0m"; - color 14,1 - print s + print s + "\e[0m" next - - print "\e[0m" - + color 0, 7 locate 0, 0 - print "TEENSY Serial Terminal v1.0 |\e[35mF1\e[30m Port|\e[35mF2\e[30m Speed|\e[35mF3\e[30m File|\e[35mF4\e[30m Save|\e[35mF6\e[30m Upload|\e[35mF9\e[30m Reset|\e[35mF10\e[30m Quit\e[K" - + print "TEENSY Terminal v1.1 |\e[35mF1\e[30m Setup|\e[35mF3\e[30m File|\e[35mF4\e[30m Save|\e[35mF6\e[30m Upload|\e[35mF9\e[30m Clear|\e[35mF10\e[30m Quit\e[K" + color 15, 0 locate NumberLines - 1, 0 print "Send: "; SendString; "\e[K";"\e[7m \e[27m" @@ -189,6 +189,16 @@ sub PrintGUI() endif end +sub GetStartupParameter() + local p + p = command() + split p, " ", parameters + + if(ubound(parameters) > 0) + FileName = parameters[1] + endif +end + func FileSelectDialog(Type, x, y, w, h, ButtonSize) ' Displays a file select dialog. If type is 0 (LOAD) then the function @@ -220,29 +230,29 @@ func FileSelectDialog(Type, x, y, w, h, ButtonSize) color 15,0 cls color TextColor, BGColor - - + + Directory = cwd() - + if(ButtonSize == 0) then ButtonSize = 3 * Textwidth("W") - + func cmpfunc_strings(x, y) - x = lower(x) - y = lower(y) - - if x == y - return 0 - elseif x > y - return 1 - else - return -1 - endif + x = lower(x) + y = lower(y) + + if x == y + return 0 + elseif x > y + return 1 + else + return -1 + endif end - + func GetFileList() local FileList FileList = files("*") - + for ii = 1 to ubound(FileList) if(isdir(FileList[ii])) then FileList[ii] = enclose(FileList[ii], "[]") next @@ -413,3 +423,5 @@ func FileSelectDialog(Type, x, y, w, h, ButtonSize) end + + diff --git a/src/platform/teensy/samples/bt.bas b/src/platform/teensy/samples/bluetooth.bas similarity index 100% rename from src/platform/teensy/samples/bt.bas rename to src/platform/teensy/samples/bluetooth.bas diff --git a/src/platform/teensy/samples/hc-06.bas b/src/platform/teensy/samples/bluetooth_hc-06.bas similarity index 100% rename from src/platform/teensy/samples/hc-06.bas rename to src/platform/teensy/samples/bluetooth_hc-06.bas diff --git a/src/platform/teensy/samples/console.bas b/src/platform/teensy/samples/console.bas new file mode 100644 index 00000000..fd986335 --- /dev/null +++ b/src/platform/teensy/samples/console.bas @@ -0,0 +1,493 @@ +' Console.bas provides a simple interface for +' accessing flash (teensy 4.0 and 4.1), SD card +' (teensy 4.1) and manipulating input and output +' pins. After uploading this file to the teensy +' type "help" for more information. + +import teensy + +const FS_READ = 0 +const FS_WRITE = 1 + +const fs = teensy.fs +const usbSerial = teensy.openSerial() + +teensy.SetInteractive(1) + +CurrentDirectory = "" + +print +print "\e[1mSmallBASIC console v1.0\e[0m" +print "Type 'help' for a list of commands" + +while(1) + if(usbSerial.ready()) + s = usbSerial.receive() + if(len(s) > 0) + s = trim(s) + SPLIT s, " ", v + cmd = lcase(v[0]) + + par_error = 1 + + select case cmd + case "ls" + if(ubound(v) > 0) + cmd_ls(v[1]) + else + cmd_ls(-1) + endif + par_error = 0 + case "cd" + if(ubound(v) > 0) + cmd_cd(v[1]) + par_error = 0 + endif + case "mv" + if(ubound(v) > 1) + cmd_mv(v[1], v[2]) + par_error = 0 + endif + case "rm" + if(ubound(v) > 0) + cmd_rm(v[1]) + par_error = 0 + endif + case "cp" + if(ubound(v) > 1) + cmd_cp(v[1], v[2]) + par_error = 0 + endif + case "mkdir" + if(ubound(v) > 0) + cmd_mkdir(v[1]) + par_error = 0 + endif + case "print" + if(ubound(v) > 0) + cmd_print(v[1]) + par_error = 0 + endif + case "exit" + print "See you soon." + exit + case "run" + if(ubound(v) > 0) + cmd_run(v[1]) + par_error = 0 + endif + case "getfile" + if(ubound(v) > 0) + cmd_getfile(v[1]) + par_error = 0 + endif + case "help" + cmd_help() + case "setpin" + if(ubound(v) > 1) + cmd_setpin(v[1], v[2]) + par_error = 0 + endif + case "getpin" + if(ubound(v) > 0) + cmd_getpin(v[1]) + par_error = 0 + endif + case "analogwrite" + if(ubound(v) > 1) + cmd_analogwrite(v[1], v[2]) + par_error = 0 + endif + case "analogread" + if(ubound(v) > 0) + cmd_analogread(v[1]) + par_error = 0 + endif + case "info" + cmd_info() + case "format" + cmd_format() + end select + if(par_error) then print_error("Parameter missing") + endif + else + delay(10) + endif +wend + +func GetFullFile(f) + local Directory + + if(f == -1) then return -1 + if(f == "/") then return "" + if(right(f) == "/") then f = chop(f) + + ' Absolute path starting with / + if(left(f, 4) == "/sd/") then return("sd:" + mid(f, 4)) + if(f == "/sd") then return("sd:") + if(left(f, 7) == "/flash/") then return("flash:" + mid(f, 7)) + if(f == "/flash") then return("flash:") + if(left(f) == "/") then return(-1) + + ' path starts with one or more ../ + Directory = CurrentDirectory + if(left(f, 3) == "../") + while(1) + if(left(f, 3) == "../") + Directory = leftoflast(Directory, "/") + f = mid(f, 4) + else + return GetFullFile(Directory + "/" + f) + endif + wend + endif + + if(f == "..") + if(Directory == "/sd" or Directory == "/flash") then return "" + return GetFullFile(leftoflast(Directory, "/")) + endif + + ' Relative path + if(CurrentDirectory == "/") then return(-1) + if(left(f, 2) == "./") then return(GetFullFile(CurrentDirectory + "/" + mid(f, 3))) + return(GetFullFile(CurrentDirectory + "/" + f)) +end + +sub cmd_ls(dir) + local file, path, file2, name + + if(dir == -1 or dir == "/") + if(CurrentDirectory == "" or dir == "/") + print "\n\e[32m\e[1mList directory: /\e[0m" + print "\e[34m[flash]\n\e[34m[sd]\e[0m" + exit sub + endif + path = CurrentDirectory + "/" + else + path = dir + endif + + path = GetFullFile(path) + "/" + if(path == -1) + print_error("Path not valid") + exit sub + endif + + file = fs.open(path) + if(!file) + print_error("Path not found") + exit sub + endif + if(!file.isDirectory()) + file.close() + exit sub + endif + + print "\n\e[32m\e[1mList directory "; path; "\e[0m" + + while(1) + name = file.getNextFilename() + if(name == 0) + exit loop + endif + + file2 = fs.open(path + name) + if(file2.isDirectory()) + print "\e[34m[";name;"]\e[0m" + else + if(rightof(name,".") == "bas") + print "\e[35m\e[1m";name; "\t\t\e[0m "; file2.size(); " Bytes" + else + print name; " "; "\t\t"; file2.size(); " Bytes" + endif + endif + file2.close() + wend + file.close() +end + +sub cmd_cd(directory) + local file, path + + path = GetFullFile(directory) + if(path == -1) + print_error("Path not valid") + exit sub + endif + if(path == "") + CurrentDirectory = "" + print "\nchange to directory /" + exit sub + endif + + file = fs.open(path) + if(!file) + print_error("Not a directory") + exit sub + endif + + if(file.isDirectory()) + if(left(path, 3) == "sd:") + CurrentDirectory = "/sd" + mid(path, 4) + else if(left(path, 6) == "flash:") + CurrentDirectory = "/flash" + mid(path, 7) + endif + if(right(CurrentDirectory) == "/") then CurrentDirectory = chop(CurrentDirectory) + print "\nchange to directory "; CurrentDirectory + else + print_error("Not a directory") + endif + file.close() +end + +sub cmd_mv(a, b) + a = GetFullFile(a) + b = GetFullFile(b) + + if(a == -1 or b == -1) + print_error("File not valid") + exit sub + endif + + if(left(a) != left(b)) + print_error("files can't be moved between different file systems") + exit sub + endif + + print "\nmove "; a; " to "; b + if(!fs.rename(a,b)) then print_error("could not move file") +end + +sub cmd_rm(f) + f = GetFullFile(f) + if(f == -1) + print_error("File not valid") + exit sub + endif + + print "\nremove "; f + if(!fs.remove(f)) then print_error("could not remove file") +end + +sub cmd_cp(f1, f2) + local file1, file2, s, path + + f1 = GetFullFile(f1) + f2 = GetFullFile(f2) + + if(a == -1 or b == -1) + print_error("File not valid") + exit sub + endif + + print "\ncopy "; f1; " to "; f2 + + file1 = OpenFile(f1) + if(!file1) + print_error("could not open source file") + exit sub + endif + + file2 = fs.open(f2, FS_WRITE) + if(!file2) + file1.close() + print_error("could not open destination file") + exit sub + endif + file2.truncate() + + while(file1.available()) + s = file1.read(1) + file2.write(s) + wend + + file1.close() + file2.close() +end + +sub cmd_mkdir(dir) + dir = GetFullFile(dir) + + if(dir == -1) + print_error("Directory not valid") + exit sub + endif + + print "\ncreate directoy "; dir + if(!fs.mkdir(dir)) + print_error("could not create directory") + endif +end + +sub cmd_print(f) + local file, s + + f = GetFullFile(f) + if(f == -1) + print_error("File not valid") + exit sub + endif + + file = OpenFile(f) + if(!file) + exit sub + endif + + while(file.available()) + s = file.read(1) + print chr(s); + wend + + file.close() +end + +sub print_error(s) + print "\e[31mERROR: ";s;"\e[0m" +end + +func OpenFile(name) + local file + + file = fs.open(name, FS_READ) + if(!file) + print_error("not a file") + return 0 + endif + if(file.isDirectory()) + print_error("is directory") + file.close() + return 0 + endif + return file +end + +sub cmd_getfile(name) + local file, bytes, c + + name = GetFullFile(name) + if(name == -1) + print_error("File not valid") + exit sub + endif + + file = fs.open(name, FS_WRITE) + if(!file) + print_error("could not write to file") + exit sub + endif + file.truncate() + + print "\nWaiting for upload." + teensy.SetInteractive(0) + + while(!usbSerial.ready()) + delay(200) + wend + + bytes = 0 + while(usbSerial.ready()) + c = usbSerial.receive(1) + file.write(c, 1) + bytes++ + wend + + file.close() + Print bytes; " Bytes received" + teensy.SetInteractive(1) +end + +sub cmd_run(name) + local file, bytes, s + + name = GetFullFile(name) + if(name == -1) + print_error("File not valid") + exit sub + endif + + file = fs.open(name, FS_READ) + if(!file) + print_error("could not open file") + exit sub + endif + + dim s + while(file.available()) + s << file.read() + wend + chain s +end + +sub cmd_format() + if(!fs.Quickformat()) then print_error("could not format flash") +end + +sub cmd_help() + print + print "ls | List files: ls | ls /test | ls .." + print "cd | Change directory: cd / | cd /dir1 | cd .." + print "cp | Copy file: cp a.txt b.txt | cp a.txt /test/a.txt" + print "mv | Move file: mv a.txt b.txt | mv a.txt /test/a.txt" + print "rm | Delete file or direcrtory: rm a.txt | rm /test/a.txt" + print "mkdir | Create directory: mkdir dir1 | mkdir dir1/dir2" + print "print | Print file: print a.txt" + print "run | Run file: run a.bas" + print "getfile | Get file: getfile a.txt" + print "exit | Exit console" + print "format | Quickformat of flash file system" + print "info | Print system information" + print "setpin | Set pin to high or low: setpin 13 0 | setpin 13 1 + print "getpin | Get pin level. Returns high or low: getpin 10 + print "analogwrite | Set pin to 0 to 255: analogwrite 13 128 + print "analogread | Get ADC value of pin: analogread 10 + print + print "Absolute paths start with /" + print "Relative paths start without /" + print +end + +sub cmd_setpin(PinNumber, level) + local Pin + PinNumber = val(PinNumber) + level = val(level) + Pin = teensy.openDigitalOutput(PinNumber) + Pin.write(level) +end + +sub cmd_getpin(PinNumber) + local Pin + PinNumber = val(PinNumber) + Pin = teensy.openDigitalInput(PinNumber) + print Pin.read() +end + +sub cmd_analogwrite(PinNumber, level) + local Pin + PinNumber = val(PinNumber) + level = val(level) + Pin = teensy.openAnalogOutput(PinNumber) + Pin.write(level) +end + +sub cmd_analogread(PinNumber) + local Pin + PinNumber = val(PinNumber) + Pin = teensy.openAnalogInput(PinNumber) + print Pin.read() +end + +sub cmd_info() + local RAM, FileSystem + RAM = teensy.free() + FileSystem = fs.free() + print "\n\e[1mMemory usage\e[0m" + print "STACK free : "; RAM[0]; " Bytes" + print "HEAP free : "; RAM[1]; " Bytes" + print "FLASH used : "; FileSystem[0]; " Bytes" + print "FLASH total: "; FileSystem[1]; " Bytes" + print "SD used : "; FileSystem[2]; " Bytes" + print "SD total: "; FileSystem[3]; " Bytes" + print "\e[1mCPU\e[0m" + print "TEMP : "; teensy.GetTemp(); " °C" + print "SPEED : "; teensy.GetCPUSpeed(); " MHz" +end \ No newline at end of file diff --git a/src/platform/teensy/samples/fs_copy_file.bas b/src/platform/teensy/samples/fs_copy_file.bas new file mode 100644 index 00000000..a36be04e --- /dev/null +++ b/src/platform/teensy/samples/fs_copy_file.bas @@ -0,0 +1,52 @@ +' Example for copying a file from flash to SD card + +import teensy + +const FS_READ = 0 +const FS_WRITE = 1 + +fs = teensy.fs + +print "1. Create a test-file" +print " Open file for writing" +file1 = fs.open("flash:/test.txt", FS_WRITE) +print " Remove file content (make file empty)" +file1.truncate() +print " Write strings to file" +file1.write("test1\n") +print " "; "test1" +file1.write("test2\n") +print " "; "test2" +file1.write("test3\n") +print " "; "test3" +print " Flush buffer (force writing)" +file1.flush() +print " Close test file" +file1.close() + +print "2. Copy test-file from flash to sd" +print " Open new file on sd" +file_sd = fs.open("sd:/test.txt", FS_WRITE) +print " Remove file content (make file empty)" +file_sd.truncate() +print " Open test-file on flash" +file_flash = fs.open("flash:/test.txt", FS_READ) +print " Read data from flash and write to sd" +while(file_flash.available()) + s = file_flash.read(1) ' read one byte + file_sd.write(s) +wend +print " Close files" +file_flash.close() +file_sd.close() + +print "3. Print content of copied file" +print " Open file on sd" +file_sd = fs.open("sd:/test.txt", FS_READ) +print " Read data:" +while(file_sd.available()) + s = file_sd.read() ' read one line + print " "; s +wend +print " Close file" +file_sd.close() diff --git a/src/platform/teensy/samples/fs_flash_basic_io.bas b/src/platform/teensy/samples/fs_flash_basic_io.bas new file mode 100644 index 00000000..541e8842 --- /dev/null +++ b/src/platform/teensy/samples/fs_flash_basic_io.bas @@ -0,0 +1,58 @@ +' Example for flash file access + +import teensy + +const FS_READ = 0 +const FS_WRITE = 1 + +fs = teensy.fs + +'print "format flash" +'fs.quickformat() + +print "1. Free space" +r = fs.free() +print " Flash used size: ", r[0] +print " Flash total size: ", r[1] + +print "2. Create directory" +fs.mkdir("flash:/TEST") +print " flash:/Test exists: "; fs.exists("flash:/TEST") + +print "3. Open file for writing" +file1 = fs.open("flash:/test.txt", FS_WRITE) +print " Remove file content (make file empty)" +file1.truncate() +print " Write string to file" +file1.write("test1\n") +file1.write("test2\n") +file1.write("test3\n") +print " Flush buffer (force writing)" +file1.flush() +print " Close file" +file1.close() + +print "4. Read from file" +file2 = fs.open("flash:/test.txt", FS_READ) +size = file2.size() +print " Size of test.txt: "; size +print " Content of test.txt: " +while(file2.available()) + s = file2.read() + print " "; s +wend + +print " Goto position 1 in file" +file2.seek(1) +print " Get current position" +print " "; file2.position() +print " Read one line from current position" +print " "; file2.read() + +file2.close() + +print "5. Move file" +fs.rename("flash:/test.txt", "flash:/test2.txt") + +print "6. Remove file" +fs.remove("flash:/test2.txt") diff --git a/src/platform/teensy/samples/fs_flash_list_files.bas b/src/platform/teensy/samples/fs_flash_list_files.bas new file mode 100644 index 00000000..9cd8312a --- /dev/null +++ b/src/platform/teensy/samples/fs_flash_list_files.bas @@ -0,0 +1,22 @@ +' Example for listing files in a directory of flash memory + +import teensy + +const FS_READ = 0 +const FS_WRITE = 1 + +fs = teensy.fs + +file = fs.open("flash:/") + +while(1) + s = "" + name = file.getNextFilename() + if(name == 0) then exit + + file2 = fs.open(name) + if(file2.isDirectory()) then s = "/" + file2.close() + print name; s +wend + diff --git a/src/platform/teensy/samples/fs_sd_basic_io.bas b/src/platform/teensy/samples/fs_sd_basic_io.bas new file mode 100644 index 00000000..fa3d7ddd --- /dev/null +++ b/src/platform/teensy/samples/fs_sd_basic_io.bas @@ -0,0 +1,55 @@ +' Example for SD card file access + +import teensy + +const FS_READ = 0 +const FS_WRITE = 1 + +fs = teensy.fs + +print "1. Free space" +r = fs.free() +print " SD used size: ", r[2] +print " SD total size: ", r[3] + +print "2. Create directory" +fs.mkdir("sd:/TEST") +print " sd:/Test exists: "; fs.exists("sd:/TEST") + +print "3. Open file for writing" +file1 = fs.open("sd:/test.txt", FS_WRITE) +print " Remove file content (make file empty)" +file1.truncate() +print " Write string to file" +file1.write("sdtest1\n") +file1.write("sdtest2\n") +file1.write("sdtest3\n") +print " Flush buffer (force writing)" +file1.flush() +print " Close file" +file1.close() + +print "4. Read from file" +file2 = fs.open("sd:/test.txt", FS_READ) +size = file2.size() +print " Size of test.txt: "; size +print " Content of test.txt: " +while(file2.available()) + s = file2.read() + print " "; s +wend + +print " Goto position 1 in file" +file2.seek(1) +print " Get current position" +print " "; file2.position() +print " Read one line from current position" +print " "; file2.read() + +file2.close() + +print "5. Move file" +fs.rename("sd:/test.txt", "sd:/test2.txt") + +print "6. Remove file" +fs.remove("sd:/test2.txt") diff --git a/src/platform/teensy/samples/hd44780.bas b/src/platform/teensy/samples/hd44780.bas new file mode 100644 index 00000000..e4bb8965 --- /dev/null +++ b/src/platform/teensy/samples/hd44780.bas @@ -0,0 +1,218 @@ +' HD44780 - Text LCD +' ================== +' +' This example demonstrates how to use the alphanumeric +' dot matrix liquid crystal display HD44780. +' +' Connect the LCD to the Teensy board: +' +' -------- -------- 1 (GND) +' TEENSY | |HD44780 | +' 5 |-------|11 (DB4) --- +' 4 |-------|12 (DB5) | | +' 3 |-------|13 (DB6) |10K|<---3 (VEE) +' 2 |-------|14 (DB7) | | +' GND |-------| 5 (RW) --- +' 1 |-------| 6 (E) | +' 0 |-------| 4 (RS) 2 (VIN) +' Vin |-------| 2 (VIN) +' GND |-------| 1 (GND) +' -------- -------- +' +' A potentiometer needs to be connected to the display +' to control the contrast of the display. If the display +' has a background light, connect power according to the +' data sheet to the pins 15 and 16. +' +' Edit Init() according to your display. Initial setting are: +' - Display with 2 lines +' - Cursor off +' - Blinking off +' - 5x7 font +' +' Code based on https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung + +'######################################################### +'# Constant definition # +'######################################################### + +const LCD_CLEAR_DISPLAY = 0x01 +const LCD_CURSOR_HOME = 0x02 + +const LCD_SET_ENTRY = 0x04 +const LCD_ENTRY_DECREASE = 0x00 +const LCD_ENTRY_INCREASE = 0x02 +const LCD_ENTRY_NOSHIFT = 0x00 +const LCD_ENTRY_SHIFT = 0x01 + +const LCD_SET_DISPLAY = 0x08 +const LCD_DISPLAY_OFF = 0x00 +const LCD_DISPLAY_ON = 0x04 +const LCD_CURSOR_OFF = 0x00 +const LCD_CURSOR_ON = 0x02 +const LCD_BLINKING_OFF = 0x00 +const LCD_BLINKING_ON = 0x01 + +const LCD_SET_SHIFT = 0x10 +const LCD_CURSOR_MOVE = 0x00 +const LCD_DISPLAY_SHIFT = 0x08 +const LCD_SHIFT_LEFT = 0x00 +const LCD_SHIFT_RIGHT = 0x04 + +const LCD_SET_FUNCTION = 0x20 +const LCD_FUNCTION_4BIT = 0x00 +const LCD_FUNCTION_8BIT = 0x10 +const LCD_FUNCTION_1LINE = 0x00 +const LCD_FUNCTION_2LINE = 0x08 +const LCD_FUNCTION_5X7 = 0x00 +const LCD_FUNCTION_5X10 = 0x04 + +const LCD_SOFT_RESET = 0x30 + +const LCD_SET_CGADR = 0x40 +const LCD_SET_DDADR = 0x80 +const LCD_DDADR_LINE1 = 0x00 +const LCD_DDADR_LINE2 = 0x40 +const LCD_DDADR_LINE3 = 0x10 +const LCD_DDADR_LINE4 = 0x50 + +'######################################################### +'# Main program # +'######################################################### + +import teensy + +LCD_Init(0,1,2,3,4,5) + +LCD_Write(" Hello World") +LCD_Locate(1,2) +LCD_Write("-= SmallBASIC =-") +print "Done" + +end + + +'######################################################### +'# Functions and subs # +'######################################################### + +sub LCD_Init(PinRS, PinE, PinDB4, PinDB5, PinDB6, PinDB7) + Print "Connect to HD44780" + RS = teensy.openDigitalOutput(PinRS) + E = teensy.openDigitalOutput(PinE) + DB4 = teensy.openDigitalOutput(PinDB4) + DB5 = teensy.openDigitalOutput(PinDB5) + DB6 = teensy.openDigitalOutput(PinDB6) + DB7 = teensy.openDigitalOutput(PinDB7) + Print "Connection established" + + RS.write(0) + E.write(0) + DB4.write(0) + DB5.write(0) + DB6.write(0) + DB7.write(0) + + delay(50) + + ' Send soft-reset 3 time to initialize LCD + Send4Bit(LCD_SOFT_RESET) + delay(5) + SendEnable() + delay(1) + SendEnable() + delay(1) + + ' Set 4-bit mode + Send4Bit(LCD_SET_FUNCTION BOR LCD_FUNCTION_4BIT) + delay(5) + + ' 2 lines and 5x7 pixel in 4 bit mode + SendCommand(LCD_SET_FUNCTION BOR LCD_FUNCTION_4BIT BOR LCD_FUNCTION_2LINE BOR LCD_FUNCTION_5X7) + ' Display on, cursor off and blinking off + SendCommand(LCD_SET_DISPLAY BOR LCD_DISPLAY_ON BOR LCD_CURSOR_OFF BOR LCD_BLINKING_OFF) + ' Cursor increment no scrolling + SendCommand(LCD_SET_ENTRY BOR LCD_ENTRY_INCREASE BOR LCD_ENTRY_NOSHIFT) + + LCD_Cls() +end + +sub LCD_Write(Text) + ' Write characters to lcd + local length, t + length = len(Text) + + for i = 1 to length + t = asc(mid(text, i, 1)) + SendData(t) + next +end + +sub LCD_Cls() + SendCommand(LCD_CLEAR_DISPLAY) + SendCommand(LCD_CURSOR_HOME) +end + +sub LCD_Off() + SendCommand(LCD_SET_DISPLAY BOR LCD_DISPLAY_OFF) +end + +sub LCD_On() + ' Display on, cursor off and blinking off + SendCommand(LCD_SET_DISPLAY BOR LCD_DISPLAY_ON BOR LCD_CURSOR_OFF BOR LCD_BLINKING_OFF) +end + +sub LCD_Locate(x, y) + local dat + + if(x < 1) then x == 1 + + select case y + case 1 ' 1. line + dat = LCD_SET_DDADR + LCD_DDADR_LINE1 + x - 1 + case 2 ' 2. line + dat = LCD_SET_DDADR + LCD_DDADR_LINE2 + x - 1 + case 3 ' 3. line + dat = LCD_SET_DDADR + LCD_DDADR_LINE3 + x - 1 + case 4 ' 4. line + dat = LCD_SET_DDADR + LCD_DDADR_LINE4 + x - 1 + case else + return + end select + + SendCommand(dat) +end + +sub SendCommand(cmd) + RS.write(0) + SendByte(cmd) +end + +sub SendData(dat) + RS.write(1) + SendByte(dat) +end + +sub SendEnable() + E.write(1) + delay(1) + E.write(0) + delay(1) +end + +sub SendByte(byte) + Send4Bit(byte) ' Send high bits first + Send4Bit(byte lshift 4) ' Send low bits +end + +sub Send4Bit(byte) + DB7.write(GetBit(byte, 7)) + DB6.write(GetBit(byte, 6)) + DB5.write(GetBit(byte, 5)) + DB4.write(GetBit(byte, 4)) + SendEnable() +end + +func GetBit(value, bit) + return (value rshift bit) BAND 1 +end \ No newline at end of file diff --git a/src/platform/teensy/samples/i2c_bh1750.bas b/src/platform/teensy/samples/i2c_bh1750.bas index 5dd2d24d..ffa23590 100644 --- a/src/platform/teensy/samples/i2c_bh1750.bas +++ b/src/platform/teensy/samples/i2c_bh1750.bas @@ -1,3 +1,17 @@ +' I2C: BH1750 - Ambient Light Sensor +' ===================================================== +' +' This example demonstrates how to use a BH1750 ambient +' light sensor with the I2C interface. +' +' --------------- ---------- +' Teensy | | BH1750 +' 3.3V |-------| VCC +' PIN 17 (SDA1) |-------| SDA +' PIN 16 (SCL1) |-------| SCL +' GND |-------| GND +'---------------- --------- + import teensy const ADDRESS = 0x23 diff --git a/src/platform/teensy/samples/i2c_bmp180.bas b/src/platform/teensy/samples/i2c_bmp180.bas index 095a5c93..67ffc80d 100644 --- a/src/platform/teensy/samples/i2c_bmp180.bas +++ b/src/platform/teensy/samples/i2c_bmp180.bas @@ -1,3 +1,17 @@ +' I2C: BMP180 - Pressure and temperature sensor +' ===================================================== +' +' This example demonstrates how to use a BMP180 +' pressure and temperature sensor with the I2C interface. +' +' --------------- ---------- +' Teensy | | BMP180 +' 3.3V |-------| VIN +' PIN 17 (SDA1) |-------| SDA +' PIN 16 (SCL1) |-------| SCL +' GND |-------| GND +'---------------- --------- + import teensy const ADDRESS = 0x77 diff --git a/src/platform/teensy/samples/i2c_mpu6050.bas b/src/platform/teensy/samples/i2c_mpu6050.bas new file mode 100644 index 00000000..aaaa79ca --- /dev/null +++ b/src/platform/teensy/samples/i2c_mpu6050.bas @@ -0,0 +1,201 @@ +' MPU6050 - I2C accelerometer and gyroscope +' +' Pins: +' TEENSY MPU6050 +' 16 SCL1 --> SCL +' 17 SDA1 --> SDA +' 3.3V --> VCC +' GND --> GND + +import teensy + +const ADDRESS = 0x68 + +Init() + +currentTime = ticks() +lastUpdate = ticks() + +while(1) + ' Read accelerometer data + Accel = GetAcceleration() + AccX = Accel[0] + AccY = Accel[1] + AccZ = Accel[2] + + ' Get delta time + previousTime = currentTime + currentTime = ticks() + dt = (currentTime - previousTime) / 1000 + + ' Read gyroscope data + Gyro = GetGyroscope() + GyroX = Gyro[0] + GyroY = Gyro[1] + GyroZ = Gyro[2] + + ' Calc roll and pitch using acceleration sensor + AccRoll = atan2(AccY, AccZ) * 180 / PI + AccPitch = atan2(-AccX, sqr(AccY^2 + AccZ^2)) * 180/PI + + ' Calc roll and pitch unsing gyroscope and accelerometer to reduce noise and cancel drift + roll = 0.98 * (roll + GyroX * dt) + 0.02 * AccRoll + pitch = 0.98 * (pitch + GyroY * dt) + 0.02 * AccPitch + + ' Display every 500ms + if(ticks() - lastUpdate > 500) + print "Roll : "; round(roll, 2), " Pitch : "; round(pitch, 2), " Temperature: "; round(GetTemperature(),1); "°C" + ' uncomment to see roll and pitch measured with accelerometer only + ' print "AccRoll : "; round(AccRoll, 2), " AccPitch: "; round(AccPitch, 2) + lastUpdate = ticks() + endif +wend + +' #### Functions ############################################# + +sub Init() + local buffer, Who_am_I + + Print "Open I2C..."; + const I2C = teensy.OpenI2C(1) ' use I2C pins 16 and 17 + Print "Opened" + + ' Test if sensor is present + I2C.write(ADDRESS, 0x75) + Who_am_I = I2C.read(ADDRESS, 1) + if(Who_am_I != 0x68 AND Who_am_I != 0x69) + print "Error: MPU6050 WHO_AM_I value wrong" + stop + endif + + ' Set SMPLRT_DIV to 0 + ' Sample Rate = Gyroscope Output Rate / (1 + SMPLRT_DIV) + buffer = [0x19, 0] + I2C.write(ADDRESS, buffer) + + ' MPU config + ' external Frame Synchronization disabled + buffer = [0x1A, 0] + I2C.write(ADDRESS, buffer) + + GyroscopeConfig(250) + AccelerationConfig(2) + + ' Turn on + buffer = [0x6B, 0x01] + I2C.write(ADDRESS, buffer) +end + +func short(dat) + if dat > 32767 then + return dat - 65536 + else + return dat + endif +end + +func GetAcceleration() + local buffer, AccX, AccY, AccZ + dim buffer[5] + + I2C.write(ADDRESS, 0x3B) + buffer = I2C.read(ADDRESS, 6) + + AccX = short((buffer[0] lshift 8) BOR buffer[1]) / AccelerationLSBSensitivity + AccY = short((buffer[2] lshift 8) BOR buffer[3]) / AccelerationLSBSensitivity + AccZ = short((buffer[4] lshift 8) BOR buffer[5]) / AccelerationLSBSensitivity + + return [AccX, AccY, AccZ] +end + +func GetGyroscope() + local buffer, GyroX, GyroY, GyroZ + dim buffer[5] + + I2C.write(ADDRESS, 0x43) + buffer = I2C.read(ADDRESS, 6) + + ' Correct the outputs with the error values + GyroX = short((buffer[0] lshift 8) BOR buffer[1]) / GyroscopeLSBSensitivity + GyroY = short((buffer[2] lshift 8) BOR buffer[3]) / GyroscopeLSBSensitivity + GyroZ = short((buffer[4] lshift 8) BOR buffer[5]) / GyroscopeLSBSensitivity + + return [GyroX, GyroY, GyroZ] +end + +func GetTemperature() + local buffer, Temp + dim buffer[1] + + I2C.write(ADDRESS, 0x41) + buffer = I2C.read(ADDRESS, 2) + + Temp = short((buffer[0] lshift 8) BOR buffer[1]) / 340.0 + 36.53 + + return Temp +end + +' GyroscopeConfig(range) +' Sets the full scale range of the Gyroscope. Range can have the +' values 250, 500, 1000, or 2000 in °/s. +sub GyroscopeConfig(range) + local Setting, buffer + dim buffer[1] + + select case range + case 250 + Setting = 0x00 + GyroscopeLSBSensitivity = 131.0 + case 500 + Setting = 0x08 + GyroscopeLSBSensitivity = 65.5 + case 1000 + Setting = 0x10 + GyroscopeLSBSensitivity = 32.8 + case 2000 + Setting = 0x18 + GyroscopeLSBSensitivity = 16.4 + case else + Setting = 0x00 + GyroscopeLSBSensitivity = 131.0 + end select + + buffer[0] = 0x1B + buffer[1] = Setting + I2C.write(ADDRESS, buffer) +end + +' Accelerometer Configuration +' AccelerationConfig(range) +' Sets the full scale range of the Accelerometer. Range can have the +' values 2, 4, 8, or 16, which corresponds to the following g-force +' 2 -> 2g +' 4 -> 4g +' 8 -> 8g +' 16 -> 16g +sub AccelerationConfig(range) + local setting, buffer + dim buffer[1] + + select case range + case 2 + Setting = 0x00 + AccelerationLSBSensitivity = 16384 + case 4 + Setting = 0x08 + AccelerationLSBSensitivity = 8192 + case 8 + Setting = 0x10 + AccelerationLSBSensitivity = 4096 + case 16 + Setting = 0x18 + AccelerationLSBSensitivity = 2048 + case else + Setting = 0x00 + AccelerationLSBSensitivity = 16384 + end select + + buffer[0] = 0x1C + buffer[1] = Setting + I2C.write(ADDRESS, buffer) +end \ No newline at end of file diff --git a/src/platform/teensy/samples/pwm_led.bas b/src/platform/teensy/samples/io_analog_pwm_led.bas similarity index 100% rename from src/platform/teensy/samples/pwm_led.bas rename to src/platform/teensy/samples/io_analog_pwm_led.bas diff --git a/src/platform/teensy/samples/pwm_led_2.bas b/src/platform/teensy/samples/io_analog_pwm_led_2.bas similarity index 100% rename from src/platform/teensy/samples/pwm_led_2.bas rename to src/platform/teensy/samples/io_analog_pwm_led_2.bas diff --git a/src/platform/teensy/samples/io_analog_read.bas b/src/platform/teensy/samples/io_analog_read.bas new file mode 100644 index 00000000..616bcbd6 --- /dev/null +++ b/src/platform/teensy/samples/io_analog_read.bas @@ -0,0 +1,23 @@ +' Analog IO - Potentiometer +' ===================================================== +' +' This example demonstrates how to read an analog +' voltage using a potentiometer. +' +' ---------- +' TEENSY | +' 3.3V |-------- 1 ---/\/\/\--- 3 -- +' | | | +' PIN 23 |---------------- 2 | +' | | +' GND |---------------------------- +' ---------- + +import teensy + +const Potentiometer = teensy.openAnalogInput(23) + +for ii = 1 to 50 + print ii, Potentiometer.read() + delay(500) +next \ No newline at end of file diff --git a/src/platform/teensy/samples/led.bas b/src/platform/teensy/samples/io_digital_led.bas similarity index 100% rename from src/platform/teensy/samples/led.bas rename to src/platform/teensy/samples/io_digital_led.bas diff --git a/src/platform/teensy/samples/io_digital_read.bas b/src/platform/teensy/samples/io_digital_read.bas new file mode 100644 index 00000000..a4efe246 --- /dev/null +++ b/src/platform/teensy/samples/io_digital_read.bas @@ -0,0 +1,19 @@ +' Digital IO - Push Button +' ===================================================== +' +' This example demonstrates how to use a push button. +' +' ---------- ---------- +' TEENSY | | Button +' PIN 0 |-------| Pin 1 +' GND |-------| Pin 2 +' ---------- --------- + +import teensy + +const Pushbutton = teensy.openDigitalInput(0) + +for ii = 1 to 20 + print ii, Pushbutton.read() + delay(500) +next diff --git a/src/platform/teensy/samples/serialprint.bas b/src/platform/teensy/samples/serial_print.bas similarity index 100% rename from src/platform/teensy/samples/serialprint.bas rename to src/platform/teensy/samples/serial_print.bas diff --git a/src/platform/teensy/samples/serialUSB_receive_bytes.bas b/src/platform/teensy/samples/serial_receive_bytes.bas similarity index 69% rename from src/platform/teensy/samples/serialUSB_receive_bytes.bas rename to src/platform/teensy/samples/serial_receive_bytes.bas index 783dd860..9851daf0 100644 --- a/src/platform/teensy/samples/serialUSB_receive_bytes.bas +++ b/src/platform/teensy/samples/serial_receive_bytes.bas @@ -2,18 +2,22 @@ import teensy const usbSerial = teensy.openSerial() const BuiltInLED = teensy.openDigitalOutput(13) -value = 0 teensy.SetInteractive(1) +print +print "Receive two bytes over serial connection" +print "Send Q1 to exit program" +print + while(1) if(usbSerial.ready()) then - s = usbSerial.read(2) + s = usbSerial.receive(2) print s if(len(s) == 2) then if(s[0] == 81) then ' if first Byte is a Q - print "Quit program." - stop + print "Quit program." + stop endif endif else @@ -25,5 +29,3 @@ while(1) BuiltInLED.write(0) delay(25) wend - - diff --git a/src/platform/teensy/samples/serialUSB_receive_string.bas b/src/platform/teensy/samples/serial_receive_string.bas similarity index 75% rename from src/platform/teensy/samples/serialUSB_receive_string.bas rename to src/platform/teensy/samples/serial_receive_string.bas index 11e9e906..5e76f772 100644 --- a/src/platform/teensy/samples/serialUSB_receive_string.bas +++ b/src/platform/teensy/samples/serial_receive_string.bas @@ -6,6 +6,13 @@ value = 0 teensy.SetInteractive(1) +print +print "Receive a string over serial connection" +print "Send following strings: " +print "led -> turn onboard LED on or off" +print "quit -> exit program" +print + while(1) if(usbSerial.ready()) then s = usbSerial.receive() diff --git a/src/platform/teensy/setup.sh b/src/platform/teensy/setup.sh index 294867ff..87f772d8 100755 --- a/src/platform/teensy/setup.sh +++ b/src/platform/teensy/setup.sh @@ -3,25 +3,26 @@ DIR=`pwd` (mkdir -p build/modules && cd build/modules && \ - git clone https://github.com/PaulStoffregen/cores.git && \ - git clone https://github.com/PaulStoffregen/SPI.git && \ - git clone https://github.com/PaulStoffregen/Wire.git && \ - git clone https://github.com/PaulStoffregen/USBHost_t36.git && \ + git clone https://github.com/PaulStoffregen/cores.git && \ + git clone https://github.com/PaulStoffregen/SPI.git && \ + git clone https://github.com/PaulStoffregen/Wire.git && \ + git clone https://github.com/PaulStoffregen/USBHost_t36.git && \ git clone https://github.com/PaulStoffregen/SD.git && \ - git clone https://github.com/PaulStoffregen/SdFat.git && \ - git clone https://github.com/PaulStoffregen/teensy_loader_cli.git && \ - git clone https://github.com/pedvide/ADC && \ - git clone https://github.com/ARM-software/CMSIS_5.git && \ - git clone https://github.com/ARM-software/CMSIS-DSP.git && \ - git clone https://github.com/adafruit/Adafruit_SSD1306.git && \ - git clone https://github.com/adafruit/Adafruit-GFX-Library && \ - git clone https://github.com/adafruit/Adafruit_BusIO) + git clone https://github.com/PaulStoffregen/SdFat.git && \ + git clone https://github.com/PaulStoffregen/teensy_loader_cli.git && \ + git clone https://github.com/pedvide/ADC && \ + git clone https://github.com/ARM-software/CMSIS_5.git && \ + git clone https://github.com/ARM-software/CMSIS-DSP.git && \ + git clone https://github.com/adafruit/Adafruit_SSD1306.git && \ + git clone https://github.com/adafruit/Adafruit-GFX-Library && \ + git clone https://github.com/adafruit/Adafruit_BusIO && \ + git clone https://github.com/PaulStoffregen/LittleFS) (cd build/modules/CMSIS-DSP && \ - mkdir -p build && \ - cd build && \ - cmake .. -Wno-dev -DARM_CPU=cortex-m7 -DUSE_FPU=ON -DFLOAT_ABI=hard -DCMAKE_C_FLAGS="-I${DIR}/build/modules/CMSIS_5/CMSIS/Core/Include" && \ - cmake --build .) + mkdir -p build && \ + cd build && \ + cmake .. -Wno-dev -DARM_CPU=cortex-m7 -DUSE_FPU=ON -DFLOAT_ABI=hard -DCMAKE_C_FLAGS="-I${DIR}/build/modules/CMSIS_5/CMSIS/Core/Include" && \ + cmake --build .) (cd build/modules/teensy_loader_cli && make) diff --git a/src/platform/teensy/src/fs.cpp b/src/platform/teensy/src/fs.cpp new file mode 100644 index 00000000..5953278f --- /dev/null +++ b/src/platform/teensy/src/fs.cpp @@ -0,0 +1,362 @@ +#include +#include "common/device.h" +#include "common/pproc.h" +#include "config.h" +#include "languages/messages.en.h" +#include "include/var_map.h" +#include "module.h" +#include +#include +#include + +extern LittleFS_Program flashfs; + +std::vector fileStack; + +/* + * close file + */ +static int cmd_close(var_s *self, int argc, slib_par_t *args, var_s *retval) { + fileStack[self->v.m.id].flush(); + fileStack[self->v.m.id].close(); + return 1; +} + +/* + * if current file is a directory, getNextFilename() returns + * the next filename in the directory + */ +static int cmd_getNextFilename(var_s *self, int argc, slib_par_t *args, var_s *retval) { + File file = fileStack[self->v.m.id].openNextFile(); + if (!file) { + v_setint(retval, 0); + return 1; + } + + v_setstr(retval, file.name()); + + file.close(); + return 1; +} + +/* + * check if file is a directory + * isDirectory() returns true or false + */ +static int cmd_isDirectory(var_s *self, int argc, slib_par_t *args, var_s *retval) { + v_setint(retval, fileStack[self->v.m.id].isDirectory()); + return 1; +} + +/* + * return file size + */ +static int cmd_size(var_s *self, int argc, slib_par_t *args, var_s *retval) { + v_setint(retval, fileStack[self->v.m.id].size()); + return 1; +} + +/* + * return current read/write position + */ +static int cmd_position(var_s *self, int argc, slib_par_t *args, var_s *retval) { + v_setint(retval, fileStack[self->v.m.id].position()); + return 1; +} + +/* + * set read/write position + * seek(pos) -> 'pos' psition in file + */ +static int cmd_seek(var_s *self, int argc, slib_par_t *args, var_s *retval) { + uint64_t pos = get_param_int(argc, args, 0, 0); + fileStack[self->v.m.id].seek(pos); + return 1; +} + +/* + * read from file + * 1. read() or read(0) -> read until '\n' and return as string + * 2. read(1) -> read one byte and return as integer + * 2. read(n) -> read n bytes and return as integer array + */ +static int cmd_read(var_s *self, int argc, slib_par_t *args, var_s *retval) { + uint32_t bytes = get_param_int(argc, args, 0, 0); + int size; + + if (bytes == 0) { + // Read until '\n' and return as string + bytes = CDC_RX_SIZE_480; + char buffer[bytes]; + size = fileStack[self->v.m.id].readBytesUntil('\n', buffer, bytes - 1); + buffer[size] = '\0'; + v_setstr(retval, buffer); + } else if (bytes > 1) { + // Read number of bytes and return as array + v_toarray1(retval, bytes); + for (uint32_t ii = 0; ii < bytes; ii++) { + v_setint(v_elem(retval, ii), fileStack[self->v.m.id].read()); + } + } else { + v_setint(retval, fileStack[self->v.m.id].read()); + } + return 1; +} + +/* + * write to file + * wrtie(buffer) -> 'buffer' can be integer, string or array + */ +static int cmd_write(var_s *self, int argc, slib_par_t *args, var_s *retval) { + switch (args[0].var_p->type) { + case V_INT:{ + uint8_t value = get_param_int(argc, args, 0, 0); + fileStack[self->v.m.id].write(value); + } + break; + case V_STR:{ + const char *buffer = get_param_str(argc, args, 0, ""); + fileStack[self->v.m.id].print(buffer); + } + break; + case V_ARRAY:{ + var_p_t array = args[1].var_p; // Get array + if (array->maxdim > 1) { + v_setstr(retval, "ERROR: FS: Write requires 1D-array"); + return 0; + } + uint32_t bytes = v_ubound(array, 0) - v_lbound(array, 0) + 1; + uint8_t *buffer = new uint8_t[bytes]; + for (uint32_t ii = 0; ii < bytes; ii++) { + buffer[ii] = get_array_elem_int(array, ii); + } + fileStack[self->v.m.id].write(buffer, bytes); + delete[]buffer; + } + break; + } + + return 1; +} + +/* + * flush buffer and sync file + */ +static int cmd_flush(var_s *self, int argc, slib_par_t *args, var_s *retval) { + fileStack[self->v.m.id].flush(); + return 1; +} + +/* + * convert current file, which might contain data, to an empty file + */ +static int cmd_truncate(var_s *self, int argc, slib_par_t *args, var_s *retval) { + fileStack[self->v.m.id].truncate(); + fileStack[self->v.m.id].seek(0); + return 1; +} + +/* + * returns bytes available for reading + */ +static int cmd_available(var_s *self, int argc, slib_par_t *args, var_s *retval) { + v_setint(retval, fileStack[self->v.m.id].available()); + return 1; +} + +/* + * open a file or directory + * open(name, mode) -> open file 'name' with rwmode 'mode'; mode = 0 -> read; mode = 1 -> write + * open() returns a map + */ +static int cmd_open(var_s *self, int argc, slib_par_t *args, var_s *retval) { + const char *name = get_param_str(argc, args, 0, "flash:"); + uint8_t mode = get_param_int(argc, args, 1, FILE_READ); + + File file; + char *namePtr; + + if (strncmp(name, "flash:", 6) == 0) { + namePtr = (char *)name + 6; + file = flashfs.open(namePtr, mode); + } else if (strncmp(name, "sd:", 3) == 0) { + // root directory of SD must be "/", "" will crash + if (strlen(name) == 3) { + file = SD.open("/", mode); + } else { + namePtr = (char *)name + 3; + file = SD.open(namePtr, mode); + } + } else { + file = flashfs.open(name, mode); + } + + if (!file) { + v_setint(retval, 0); + return 1; + } + + fileStack.push_back(file); + + map_init(retval); + retval->v.m.id = fileStack.size() - 1; + if (file.isDirectory()) { + v_create_callback(retval, "getNextFilename", cmd_getNextFilename); + } else { + if (mode == FILE_READ) { + v_create_callback(retval, "read", cmd_read); + v_create_callback(retval, "available", cmd_available); + } else if (mode == FILE_WRITE) { + v_create_callback(retval, "write", cmd_write); + v_create_callback(retval, "truncate", cmd_truncate); + v_create_callback(retval, "flush", cmd_flush); + } + v_create_callback(retval, "size", cmd_size); + v_create_callback(retval, "position", cmd_position); + v_create_callback(retval, "seek", cmd_seek); + } + v_create_callback(retval, "isDirectory", cmd_isDirectory); + v_create_callback(retval, "close", cmd_close); + + return 1; +} + +/* + * create directory + * mkdir(name) -> 'name' directory name + */ + +static int cmd_mkdir(var_s *self, int argc, slib_par_t *args, var_s *retval) { + const char *name = get_param_str(argc, args, 0, ""); + char *namePtr; + + if (strncmp(name, "flash:", 6) == 0) { + namePtr = (char *)name + 6; + v_setint(retval, flashfs.mkdir(namePtr)); + return 1; + } + if (strncmp(name, "sd:", 3) == 0) { + namePtr = (char *)name + 3; + v_setint(retval, SD.mkdir(namePtr)); + return 1; + } + + v_setint(retval, flashfs.mkdir(name)); + return 1; +} + +/* + * Quickformate flash memory + */ +static int cmd_quickFormat(var_s *self, int argc, slib_par_t *args, var_s *retval) { + v_setint(retval, flashfs.quickFormat()); + return 1; +} + +/* + * check if file or directory exists + * returns true or false + */ +static int cmd_exists(var_s *self, int argc, slib_par_t *args, var_s *retval) { + const char *name = get_param_str(argc, args, 0, ""); + char *namePtr; + + if (strncmp(name, "flash:", 6) == 0) { + namePtr = (char *)name + 6; + v_setint(retval, flashfs.exists(namePtr)); + return 1; + } + if (strncmp(name, "sd:", 3) == 0) { + namePtr = (char *)name + 3; + v_setint(retval, SD.exists(namePtr)); + return 1; + } + + v_setint(retval, flashfs.exists(name)); + return 1; +} + +/* + * rename a file or directory + * rename(name_old, name_new) -> 'name_old' current name; 'name_new' new name + */ +static int cmd_rename(var_s *self, int argc, slib_par_t *args, var_s *retval) { + const char *nameOld = get_param_str(argc, args, 0, ""); + const char *nameNew = get_param_str(argc, args, 1, ""); + char *nameOldPtr; + char *nameNewPtr; + + if (strncmp(nameOld, "flash:", 6) == 0 && strncmp(nameNew, "flash:", 6) == 0) { + nameOldPtr = (char *)nameOld + 6; + nameNewPtr = (char *)nameNew + 6; + v_setint(retval, flashfs.rename(nameOldPtr, nameNewPtr)); + return 1; + } + if (strncmp(nameOld, "sd:", 3) == 0 && strncmp(nameNew, "sd:", 3) == 0) { + nameOldPtr = (char *)nameOld + 3; + nameNewPtr = (char *)nameNew + 3; + v_setint(retval, SD.rename(nameOldPtr, nameNewPtr)); + return 1; + } + + v_setint(retval, flashfs.rename(nameOld, nameNew)); + return 1; +} + +/* + * remove file or directory + * remove(name) -> 'name' file or directory + */ +static int cmd_remove(var_s *self, int argc, slib_par_t *args, var_s *retval) { + const char *name = get_param_str(argc, args, 0, ""); + char *namePtr; + + if (strncmp(name, "flash:", 6) == 0) { + namePtr = (char *)name + 6; + v_setint(retval, flashfs.remove(namePtr)); + return 1; + } + if (strncmp(name, "sd:", 3) == 0) { + namePtr = (char *)name + 3; + v_setint(retval, SD.remove(namePtr)); + return 1; + } + + v_setint(retval, flashfs.remove(name)); + return 1; +} + +/* + * show file system usage + * returns an array [FlashUsedSize, FlashTotalSize, SDUsedSize, SDTotalSize] + */ +static int cmd_free(var_s *self, int argc, slib_par_t *args, var_s *retval) { + v_toarray1(retval, 4); + v_setint(v_elem(retval, 0), flashfs.usedSize()); + v_setint(v_elem(retval, 1), flashfs.totalSize()); + v_setint(v_elem(retval, 2), SD.usedSize()); + v_setint(v_elem(retval, 3), SD.totalSize()); + return 1; +} + +int cmd_fs(int argc, slib_par_t *args, var_t *retval) { + map_init(retval); + v_create_callback(retval, "OPEN", cmd_open); + v_create_callback(retval, "QUICKFORMAT", cmd_quickFormat); + v_create_callback(retval, "EXISTS", cmd_exists); + v_create_callback(retval, "MKDIR", cmd_mkdir); + v_create_callback(retval, "RENAME", cmd_rename); + v_create_callback(retval, "REMOVE", cmd_remove); + v_create_callback(retval, "FREE", cmd_free); + + return 1; +} + +int fs_close(void) { + for(File& f : fileStack) { + if (f) { + f.close(); + } + } + return 1; +} diff --git a/src/platform/teensy/src/fs.h b/src/platform/teensy/src/fs.h new file mode 100644 index 00000000..86245fd1 --- /dev/null +++ b/src/platform/teensy/src/fs.h @@ -0,0 +1,15 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2025 Chris Warren-Smith. +// Copyright(C) 2000 Nicholas Christopoulos +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#pragma once + +#include "common/var.h" + +int cmd_fs(int argc, slib_par_t *args, var_t *retval); +int fs_close(void); diff --git a/src/platform/teensy/src/i2c.cpp b/src/platform/teensy/src/i2c.cpp new file mode 100644 index 00000000..ae67f1bd --- /dev/null +++ b/src/platform/teensy/src/i2c.cpp @@ -0,0 +1,146 @@ +#include +#include "config.h" +#include "languages/messages.en.h" +#include "include/var_map.h" +#include "module.h" +#include + +static TwoWire *getI2C(int interfaceNumber) { + TwoWire *result; + switch (interfaceNumber) { + case 0: + result = &Wire; + break; + case 1: + result = &Wire1; + break; + case 2: + result = &Wire2; + break; + default: + result = &Wire; + break; + } + return result; +} + +static int cmd_i2c_write(var_s *self, int argc, slib_par_t *args, var_s *retval) { + uint8_t address = get_param_int(argc, args, 0, 0); + uint8_t stop = get_param_int(argc, args, 2, 1); + + if (address == 0 || argc < 2) { + v_setstr(retval, ERR_PARAM); + return 0; + } + + int interfaceNumber = self->v.m.id; + TwoWire *ptrWire; + ptrWire = getI2C(interfaceNumber); + + ptrWire->beginTransmission(address); + + switch (args[1].var_p->type) { + case V_INT:{ + int value = get_param_int(argc, args, 1, 0); + ptrWire->write(value); + } + break; + case V_STR:{ + const char *buffer = v_getstr(args[1].var_p); + int length = v_strlen(args[1].var_p); + ptrWire->write(buffer, length); + } + break; + case V_ARRAY:{ + var_p_t array = args[1].var_p; // Get array + if (array->maxdim > 1) { + v_setstr(retval, "ERROR: I2C: Write requires 1D-array"); + return 0; + } + uint32_t bytes = v_ubound(array, 0) - v_lbound(array, 0) + 1; + uint8_t *buffer = new uint8_t[bytes]; + for (uint32_t ii = 0; ii < bytes; ii++) { + buffer[ii] = get_array_elem_int(array, ii); + } + ptrWire->write(buffer, bytes); + delete[]buffer; + } + break; + } + + if (ptrWire->endTransmission(stop)) { + v_setstr(retval, "ERROR I2C: Transmission failed"); + return 0; + } + + return 1; +} + +static int cmd_i2c_read(var_s *self, int argc, slib_par_t *args, var_s *retval) { + uint8_t address = get_param_int(argc, args, 0, 0); + uint32_t bytes = get_param_int(argc, args, 1, 1); + uint8_t stop = get_param_int(argc, args, 2, 1); + + if (address == 0) { + v_setstr(retval, ERR_PARAM); + return 0; + } + + int interfaceNumber = self->v.m.id; + TwoWire *ptrWire; + ptrWire = getI2C(interfaceNumber); + ptrWire->requestFrom(address, bytes, stop); + + if (bytes > 1) { + v_toarray1(retval, bytes); + for (uint32_t ii = 0; ii < bytes; ii++) { + v_setint(v_elem(retval, ii), ptrWire->read()); + } + } else { + v_setint(retval, ptrWire->read()); + } + return 1; +} + +static int cmd_i2c_setClock(var_s *self, int argc, slib_par_t *args, var_s *retval) { + uint32_t clockFrequency = get_param_int(argc, args, 0, 100000); + + if (clockFrequency != 100000 && clockFrequency != 400000 && clockFrequency != 1000000) { + v_setstr(retval, "ERROR I2C: Clock freuqency not supported"); + return 0; + } + + int interfaceNumber = self->v.m.id; + getI2C(interfaceNumber)->setClock(clockFrequency); + + return 1; +} + +int cmd_openi2c(int argc, slib_par_t *args, var_t *retval) { + uint8_t interfaceNumber = get_param_int(argc, args, 0, 0); + uint8_t pinSDA = get_param_int(argc, args, 1, 0); + uint8_t pinSCL = get_param_int(argc, args, 2, 0); + + if (interfaceNumber > 2) { + v_setstr(retval, ERR_PARAM); + return 0; + } + + map_init(retval); + v_create_callback(retval, "write", cmd_i2c_write); + v_create_callback(retval, "read", cmd_i2c_read); + v_create_callback(retval, "setClock", cmd_i2c_setClock); + retval->v.m.id = interfaceNumber; + + TwoWire *ptrWire; + ptrWire = getI2C(interfaceNumber); + + if (pinSDA > 0 && pinSCL > 0) { + ptrWire->setSDA(pinSDA); + ptrWire->setSCL(pinSCL); + } + + ptrWire->begin(); + + return 1; +} diff --git a/src/platform/teensy/src/i2c.h b/src/platform/teensy/src/i2c.h new file mode 100644 index 00000000..64734cfc --- /dev/null +++ b/src/platform/teensy/src/i2c.h @@ -0,0 +1,14 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2025 Chris Warren-Smith. +// Copyright(C) 2000 Nicholas Christopoulos +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#pragma once + +#include "common/var.h" + +int cmd_openi2c(int argc, slib_par_t *args, var_t *retval); diff --git a/src/platform/teensy/src/main.cpp b/src/platform/teensy/src/main.cpp index dbc86413..30f5ae1e 100644 --- a/src/platform/teensy/src/main.cpp +++ b/src/platform/teensy/src/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "common/device.h" #include "config.h" #include "common/sbapp.h" @@ -19,8 +20,10 @@ #define MAIN_BAS "__main_bas__" #define SERIAL_SD_BAS "__serial_sd_bas__" +#define PROG_FLASH_SIZE 65536*16 strlib::String buffer; +LittleFS_Program flashfs; void *plugin_lib_open(const char *name) { void *result = nullptr; @@ -50,6 +53,8 @@ void *plugin_lib_address(void *handle, const char *name) { result = (void *)pModule->_func_count; } else if (strcmp(name, "sblib_func_getname") == 0) { result = (void *)pModule->_func_getname; + } else if (strcmp(name, "sblib_close") == 0) { + result = (void *)pModule->_close; } return result; } @@ -93,6 +98,13 @@ void setup() { opt_verbose = 0; opt_graphics = 0; dev_init(0, 0); + + if (!flashfs.begin(PROG_FLASH_SIZE)) { + while (1) { + dev_print("Error initializing FLASH file system\n"); + delay(1000); + } + } } void serial_read() { @@ -171,7 +183,7 @@ void print_error(char *source) { void interactive_main() { while (true) { - dev_print("\r\n\033[30;47mInteractive mode - waiting for data...\033[0m\r\n"); + dev_print("\r\nWaiting for upload... "); serial_read(); if (!sbasic_main(SERIAL_SD_BAS)) { print_error((char *)buffer.c_str()); @@ -179,34 +191,53 @@ void interactive_main() { } } +void executeFile(File *f) { + uint32_t fileSize = f->available(); + char *fileBuffer = new char[fileSize + 1]; + + f->read(fileBuffer, fileSize); + fileBuffer[fileSize] = '\0'; + buffer.clear(); + buffer.append(fileBuffer); + + delete[]fileBuffer; + + if (!sbasic_main(SERIAL_SD_BAS)) { + while (!Serial) { + delay(250); + } + dev_print("Error executing MAIN.BAS:\n"); + print_error((char *)buffer.c_str()); + } else { + dev_print("MAIN.BAS ended\n"); + } +} + extern "C" int main(void) { setup(); + // Start from SD card if (SD.begin(BUILTIN_SDCARD)) { File sdFile = SD.open("/MAIN.BAS", FILE_READ); if (sdFile) { - uint32_t fileSize = sdFile.available(); - char *bufferSD = new char[fileSize + 1]; - - sdFile.read(bufferSD, fileSize); - bufferSD[fileSize] = '\0'; - buffer.clear(); - buffer.append(bufferSD); - - delete[]bufferSD; + executeFile(&sdFile); sdFile.close(); + goto interactive; + } + } - if (!sbasic_main(SERIAL_SD_BAS)) { - while (!Serial) { - delay(250); - } - dev_print("Error executing main.bas from SD card:\n"); - print_error((char *)buffer.c_str()); - } else { - dev_print("main.bas from SD card ended\n"); - } + // Start from LittleFS + if (flashfs.exists("/MAIN.BAS")) { + File flashFile = flashfs.open("/MAIN.BAS", FILE_READ); + if (flashFile) { + executeFile(&flashFile); + flashFile.close(); + goto interactive; } - } else if (main_bas_len > 0) { + } + + // Start from memory + if (main_bas_len > 0) { if (!sbasic_main(MAIN_BAS)) { while (!Serial) { delay(250); @@ -216,7 +247,8 @@ extern "C" int main(void) { } else { dev_print("main.bas ended"); } - } else { - interactive_main(); } + +interactive: + interactive_main(); } diff --git a/src/platform/teensy/src/module.h b/src/platform/teensy/src/module.h index 07812fa7..f5a95f40 100644 --- a/src/platform/teensy/src/module.h +++ b/src/platform/teensy/src/module.h @@ -45,6 +45,7 @@ typedef struct { sblib_count_fn _proc_count; sblib_getname_fn _proc_getname; sblib_free_fn _free; + sblib_close_fn _close; } ModuleConfig; ModuleConfig *get_teensy_module(); diff --git a/src/platform/teensy/src/pinio.cpp b/src/platform/teensy/src/pinio.cpp new file mode 100644 index 00000000..9588bc0e --- /dev/null +++ b/src/platform/teensy/src/pinio.cpp @@ -0,0 +1,150 @@ +#include +#include "config.h" +#include "languages/messages.en.h" +#include "include/var_map.h" +#include "module.h" + +static void set_pin(var_p_t var, uint8_t pin, uint8_t mode) { + map_init(var); + var->v.m.id = pin; + pinMode(pin, mode); +} + +static bool is_pin(int id) { + return id >= 0 && id < CORE_NUM_TOTAL_PINS; +} + +static bool is_pin_object(var_p_t var) { + return var != nullptr && v_is_type(var, V_MAP) && is_pin(var->v.m.id); +} + +static int cmd_analoginput_read(var_s *self, int argc, slib_par_t *arg, var_s *retval) { + int result = 0; + if (argc != 0 || !is_pin_object(self)) { + error(retval, "AnalogInput.read", 0); + } else { + int pin = self->v.m.id; + v_setint(retval, analogRead(pin)); + result = 1; + } + return result; +} + +int cmd_openanaloginput(int argc, slib_par_t *args, var_t *retval) { + int result = 1; + int pin = get_param_int(argc, args, 0, -1); + if (is_pin(pin)) { + set_pin(retval, pin, INPUT); + v_create_callback(retval, "read", cmd_analoginput_read); + } else { + result = 0; + } + return result; +} + +static int cmd_digitalinput_read(var_s *self, int argc, slib_par_t *arg, var_s *retval) { + int result = 0; + if (argc != 0 || !is_pin_object(self)) { + error(retval, "DigitalInput.read", 0); + } else { + int pin = self->v.m.id; + v_setint(retval, digitalRead(pin)); + result = 1; + } + return result; +} + +int cmd_opendigitalinput(int argc, slib_par_t *args, var_t *retval) { + int result = 1; + int pin = get_param_int(argc, args, 0, -1); + uint8_t mode = get_param_int(argc, args, 1, 1); + if (mode) { + mode = INPUT_PULLUP; + } else { + mode = INPUT; + } + if (is_pin(pin)) { + set_pin(retval, pin, mode); + v_create_callback(retval, "read", cmd_digitalinput_read); + result = 1; + } else { + result = 0; + } + return result; +} + +static int cmd_digitaloutput_write(var_s *self, int argc, slib_par_t *arg, var_s *retval) { + int result = 0; + if (argc != 1 || !is_pin_object(self)) { + error(retval, "DigitalOutput.write", 1); + } else { + int pin = self->v.m.id; + int value = get_param_int(argc, arg, 0, 0); + digitalWrite(pin, value); + result = 1; + } + return result; +} + +int cmd_opendigitaloutput(int argc, slib_par_t *args, var_t *retval) { + int result; + int pin = get_param_int(argc, args, 0, -1); + if (is_pin(pin)) { + set_pin(retval, pin, OUTPUT); + v_create_callback(retval, "write", cmd_digitaloutput_write); + result = 1; + } else { + result = 0; + } + return result; +} + +static int cmd_analogoutput_write(var_s *self, int argc, slib_par_t *arg, var_s *retval) { + if (argc != 1 || !is_pin_object(self)) { + error(retval, "AnalogOutput.write", 1); + return 0; + } else { + int pin = self->v.m.id; + int value = get_param_int(argc, arg, 0, 0); + analogWrite(pin, value); + } + return 1; +} + +static int cmd_analogoutput_frequency(var_s *self, int argc, slib_par_t *arg, var_s *retval) { + if (argc != 1 || !is_pin_object(self)) { + error(retval, "AnalogOutput.frequency", 1); + return 0; + } else { + int pin = self->v.m.id; + double value = get_param_num(argc, arg, 0, 0.0); + analogWriteFrequency(pin, value); + } + return 1; +} + +static int cmd_analogoutput_resolution(var_s *self, int argc, slib_par_t *arg, var_s *retval) { + if (argc != 1 || !is_pin_object(self)) { + error(retval, "AnalogOutput.resolution", 1); + return 0; + } else { + int value = get_param_int(argc, arg, 0, 0); + analogWriteResolution(value); + } + return 1; +} + +int cmd_openanalogoutput(int argc, slib_par_t *args, var_t *retval) { + int result; + int pin = get_param_int(argc, args, 0, -1); + if (is_pin(pin)) { + set_pin(retval, pin, OUTPUT); + v_create_callback(retval, "write", cmd_analogoutput_write); + v_create_callback(retval, "frequency", cmd_analogoutput_frequency); + v_create_callback(retval, "resolution", cmd_analogoutput_resolution); + result = 1; + } else { + result = 0; + } + return result; +} diff --git a/src/platform/teensy/src/pinio.h b/src/platform/teensy/src/pinio.h new file mode 100644 index 00000000..882607d2 --- /dev/null +++ b/src/platform/teensy/src/pinio.h @@ -0,0 +1,17 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2001-2025 Chris Warren-Smith. +// Copyright(C) 2000 Nicholas Christopoulos +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + +#pragma once + +#include "common/var.h" + +int cmd_opendigitalinput(int argc, slib_par_t *args, var_t *retval); +int cmd_opendigitaloutput(int argc, slib_par_t *args, var_t *retval); +int cmd_openanaloginput(int argc, slib_par_t *args, var_t *retval); +int cmd_openanalogoutput(int argc, slib_par_t *args, var_t *retval); diff --git a/src/platform/teensy/src/serial.cpp b/src/platform/teensy/src/serial.cpp new file mode 100644 index 00000000..8aa1f2a7 --- /dev/null +++ b/src/platform/teensy/src/serial.cpp @@ -0,0 +1,164 @@ +#include +#include "config.h" +#include "languages/messages.en.h" +#include "include/var_map.h" +#include "module.h" +#include "serial.h" + +#define USB_CLASS_ID 1002 +#define MAX_HW_SERIAL 7 +#define SERIAL_DEFAULT_BAUD 9600 + +static HardwareSerialIMXRT *getSerial(int serialNo) { + HardwareSerialIMXRT *result; + switch (serialNo) { + case 1: + result = &Serial1; + break; + case 2: + result = &Serial2; + break; + case 3: + result = &Serial3; + break; + case 4: + result = &Serial4; + break; + case 5: + result = &Serial5; + break; + case 6: + result = &Serial6; + break; + case 7: + result = &Serial7; + break; + default: + result = &Serial1; + break; + } + return result; +} + +static bool is_serial(int id) { + return id >= 0 && id <= MAX_HW_SERIAL; +} + +static bool is_serial_var(var_p_t var) { + return var != nullptr && v_is_type(var, V_INT) && is_serial(var->v.i); +} + +static bool is_serial_object(var_p_t var) { + return var != nullptr && v_is_type(var, V_MAP) && is_serial(var->v.m.id); +} + +static int cmd_serial_ready(var_s *self, int argc, slib_par_t *args, var_s *retval) { + int result; + if (argc != 0 || !is_serial_object(self)) { + v_setstr(retval, ERR_PARAM); + result = 0; + } else { + int serialNo = self->v.m.id; + if (serialNo == 0) { + v_setint(retval, Serial.available()); + } else { + v_setint(retval, getSerial(serialNo)->available()); + } + result = 1; + } + return result; +} + +static int cmd_serial_receive(var_s *self, int argc, slib_par_t *args, var_s *retval) { + int result; + if (argc > 1 || !is_serial_object(self)) { + v_setstr(retval, ERR_PARAM); + result = 0; + } else { + int bufferSize = get_param_int(argc, args, 0, 0); + int size; + int serialNo = self->v.m.id; + + if (bufferSize == 0) { + // Read until '\n' and return as string + bufferSize = CDC_RX_SIZE_480; + char buffer[bufferSize]; + + if (serialNo == 0) { + size = Serial.readBytesUntil('\n', buffer, bufferSize - 1); + } else { + size = getSerial(serialNo)->readBytesUntil('\n', buffer, bufferSize - 1); + } + buffer[size] = '\0'; + v_setstr(retval, buffer); + result = 1; + } else { + // Read number of bytes and return as array + char buffer[bufferSize]; + if (serialNo == 0) { + size = Serial.readBytes(buffer, bufferSize); + } else { + size = getSerial(serialNo)->readBytes(buffer, bufferSize); + } + if (bufferSize > 1) { + v_toarray1(retval, bufferSize); + for (int32_t ii = 0; ii < size; ii++) { + v_setint(v_elem(retval, ii), buffer[ii]); + } + } else { + v_setint(retval, buffer[0]); + } + result = 1; + } + } + return result; +} + +static int cmd_serial_send(var_s *self, int argc, slib_par_t *args, var_s *retval) { + int result; + if (argc != 1 || !is_serial_object(self) || !v_is_type(args[0].var_p, V_STR)) { + v_setstr(retval, ERR_PARAM); + result = 0; + } else { + const char *buffer = v_getstr(args[0].var_p); + int length = v_strlen(args[0].var_p); + int serialNo = self->v.m.id; + switch (serialNo) { + case 0: + Serial.write(buffer, length); + break; + default: + getSerial(serialNo)->write(buffer, length); + break; + } + result = 1; + } + return result; +} + +int cmd_openserial(int argc, slib_par_t *args, var_t *retval) { + int result; + if (!(argc == 0 || (argc == 1 && is_serial_var(args[0].var_p)))) { + v_setstr(retval, ERR_PARAM); + result = 0; + } else { + int serialNo = argc == 0 ? 0 : args[0].var_p->v.i; + map_init(retval); + retval->v.m.id = serialNo; + retval->v.m.cls_id = USB_CLASS_ID; + v_create_callback(retval, "ready", cmd_serial_ready); + v_create_callback(retval, "receive", cmd_serial_receive); + v_create_callback(retval, "send", cmd_serial_send); + switch (serialNo) { + case 0: + serial_init(); + break; + default: + int serialSpeed = get_param_int(argc, args, 1, SERIAL_DEFAULT_BAUD); + getSerial(serialNo)->begin(serialSpeed); + break; + } + result = 1; + } + return result; +} \ No newline at end of file diff --git a/src/platform/teensy/src/serial.h b/src/platform/teensy/src/serial.h index 0c854b87..d887ab01 100644 --- a/src/platform/teensy/src/serial.h +++ b/src/platform/teensy/src/serial.h @@ -9,4 +9,7 @@ #pragma once +#include "common/var.h" + void serial_init(); +int cmd_openserial(int argc, slib_par_t *args, var_t *retval); diff --git a/src/platform/teensy/src/ssd1306.cpp b/src/platform/teensy/src/ssd1306.cpp index 28639418..4c21f335 100644 --- a/src/platform/teensy/src/ssd1306.cpp +++ b/src/platform/teensy/src/ssd1306.cpp @@ -393,7 +393,8 @@ static ModuleConfig ssd1306Module = { ._proc_exec = ssd1306_proc_exec, ._proc_count = ssd1306_proc_count, ._proc_getname = ssd1306_proc_getname, - ._free = nullptr + ._free = nullptr, + ._close = nullptr }; ModuleConfig *get_ssd1306_module() { diff --git a/src/platform/teensy/src/teensy.cpp b/src/platform/teensy/src/teensy.cpp index 72b0d13b..5a026a68 100644 --- a/src/platform/teensy/src/teensy.cpp +++ b/src/platform/teensy/src/teensy.cpp @@ -16,220 +16,12 @@ #include "device.h" #include "module.h" #include "serial.h" +#include "i2c.h" +#include "pinio.h" +#include "fs.h" #include #include -#define USB_CLASS_ID 1002 -#define MAX_HW_SERIAL 7 -#define BT_BAUD 9600 - -static HardwareSerialIMXRT *getSerial(int serialNo) { - HardwareSerialIMXRT *result; - switch (serialNo) { - case 1: - result = &Serial1; - break; - case 2: - result = &Serial2; - break; - case 3: - result = &Serial3; - break; - case 4: - result = &Serial4; - break; - case 5: - result = &Serial5; - break; - case 6: - result = &Serial6; - break; - case 7: - result = &Serial7; - break; - default: - result = &Serial1; - break; - } - return result; -} - -static TwoWire *getI2C(int interfaceNumber) { - TwoWire *result; - switch (interfaceNumber) { - case 0: - result = &Wire; - break; - case 1: - result = &Wire1; - break; - case 2: - result = &Wire2; - break; - default: - result = &Wire; - break; - } - return result; -} - -static void set_pin(var_p_t var, uint8_t pin, uint8_t mode) { - map_init(var); - var->v.m.id = pin; - pinMode(pin, mode); -} - -static bool is_pin(int id) { - return id >= 0 && id < CORE_NUM_TOTAL_PINS; -} - -static bool is_pin_object(var_p_t var) { - return var != nullptr && v_is_type(var, V_MAP) && is_pin(var->v.m.id); -} - -static bool is_serial(int id) { - return id >= 0 && id <= MAX_HW_SERIAL; -} - -static bool is_serial_var(var_p_t var) { - return var != nullptr && v_is_type(var, V_INT) && is_serial(var->v.i); -} - -static bool is_serial_object(var_p_t var) { - return var != nullptr && v_is_type(var, V_MAP) && is_serial(var->v.m.id); -} - -static int cmd_analoginput_read(var_s *self, int argc, slib_par_t *arg, var_s *retval) { - int result = 0; - if (argc != 0 || !is_pin_object(self)) { - error(retval, "AnalogInput.read", 0); - } else { - int pin = self->v.m.id; - v_setint(retval, analogRead(pin)); - result = 1; - } - return result; -} - -static int cmd_openanaloginput(int argc, slib_par_t *args, var_t *retval) { - int result = 1; - int pin = get_param_int(argc, args, 0, -1); - if (is_pin(pin)) { - set_pin(retval, pin, INPUT); - v_create_callback(retval, "read", cmd_analoginput_read); - } else { - result = 0; - } - return result; -} - -static int cmd_digitalinput_read(var_s *self, int argc, slib_par_t *arg, var_s *retval) { - int result = 0; - if (argc != 0 || !is_pin_object(self)) { - error(retval, "DigitalInput.read", 0); - } else { - int pin = self->v.m.id; - v_setint(retval, digitalRead(pin)); - result = 1; - } - return result; -} - -static int cmd_opendigitalinput(int argc, slib_par_t *args, var_t *retval) { - int result = 1; - int pin = get_param_int(argc, args, 0, -1); - uint8_t mode = get_param_int(argc, args, 1, 1); - if (mode) { - mode = INPUT_PULLUP; - } else { - mode = INPUT; - } - if (is_pin(pin)) { - set_pin(retval, pin, mode); - v_create_callback(retval, "read", cmd_digitalinput_read); - result = 1; - } else { - result = 0; - } - return result; -} - -static int cmd_digitaloutput_write(var_s *self, int argc, slib_par_t *arg, var_s *retval) { - int result = 0; - if (argc != 1 || !is_pin_object(self)) { - error(retval, "DigitalOutput.write", 1); - } else { - int pin = self->v.m.id; - int value = get_param_int(argc, arg, 0, 0); - digitalWrite(pin, value); - result = 1; - } - return result; -} - -static int cmd_opendigitaloutput(int argc, slib_par_t *args, var_t *retval) { - int result; - int pin = get_param_int(argc, args, 0, -1); - if (is_pin(pin)) { - set_pin(retval, pin, OUTPUT); - v_create_callback(retval, "write", cmd_digitaloutput_write); - result = 1; - } else { - result = 0; - } - return result; -} - -static int cmd_analogoutput_write(var_s *self, int argc, slib_par_t *arg, var_s *retval) { - if (argc != 1 || !is_pin_object(self)) { - error(retval, "AnalogOutput.write", 1); - return 0; - } else { - int pin = self->v.m.id; - int value = get_param_int(argc, arg, 0, 0); - analogWrite(pin, value); - } - return 1; -} - -static int cmd_analogoutput_frequency(var_s *self, int argc, slib_par_t *arg, var_s *retval) { - if (argc != 1 || !is_pin_object(self)) { - error(retval, "AnalogOutput.frequency", 1); - return 0; - } else { - int pin = self->v.m.id; - double value = get_param_num(argc, arg, 0, 0.0); - analogWriteFrequency(pin, value); - } - return 1; -} - -static int cmd_analogoutput_resolution(var_s *self, int argc, slib_par_t *arg, var_s *retval) { - if (argc != 1 || !is_pin_object(self)) { - error(retval, "AnalogOutput.resolution", 1); - return 0; - } else { - int value = get_param_int(argc, arg, 0, 0); - analogWriteResolution(value); - } - return 1; -} - -static int cmd_openanalogoutput(int argc, slib_par_t *args, var_t *retval) { - int result; - int pin = get_param_int(argc, args, 0, -1); - if (is_pin(pin)) { - set_pin(retval, pin, OUTPUT); - v_create_callback(retval, "write", cmd_analogoutput_write); - v_create_callback(retval, "frequency", cmd_analogoutput_frequency); - v_create_callback(retval, "resolution", cmd_analogoutput_resolution); - result = 1; - } else { - result = 0; - } - return result; -} - static int cmd_get_temperature(int argc, slib_par_t *args, var_t *retval) { v_setint(retval, tempmonGetTemp()); return 1; @@ -240,238 +32,6 @@ static int cmd_get_cpu_speed(int argc, slib_par_t *args, var_t *retval) { return 1; } -static int cmd_serial_ready(var_s *self, int argc, slib_par_t *args, var_s *retval) { - int result; - if (argc != 0 || !is_serial_object(self)) { - v_setstr(retval, ERR_PARAM); - result = 0; - } else { - int serialNo = self->v.m.id; - if (serialNo == 0) { - v_setint(retval, Serial.available()); - } else { - v_setint(retval, getSerial(serialNo)->available()); - } - result = 1; - } - return result; -} - -static int cmd_serial_receive(var_s *self, int argc, slib_par_t *args, var_s *retval) { - int result; - if (argc > 1 || !is_serial_object(self)) { - v_setstr(retval, ERR_PARAM); - result = 0; - } else { - int bufferSize = get_param_int(argc, args, 0, 0); - int size; - int serialNo = self->v.m.id; - - if (bufferSize == 0) { - // Read until '\n' and return as string - bufferSize = CDC_RX_SIZE_480; - char buffer[bufferSize]; - - if (serialNo == 0) { - size = Serial.readBytesUntil('\n', buffer, bufferSize - 1); - } else { - size = getSerial(serialNo)->readBytesUntil('\n', buffer, bufferSize - 1); - } - buffer[size] = '\0'; - v_setstr(retval, buffer); - result = 1; - } else { - // Read number of bytes and return as array - char buffer[bufferSize]; - if (serialNo == 0) { - size = Serial.readBytes(buffer, bufferSize); - } else { - size = getSerial(serialNo)->readBytes(buffer, bufferSize); - } - if (bufferSize > 1) { - v_toarray1(retval, bufferSize); - for (int32_t ii = 0; ii < size; ii++) { - v_setint(v_elem(retval, ii), buffer[ii]); - } - } else { - v_setint(retval, buffer[0]); - } - result = 1; - } - } - return result; -} - -static int cmd_serial_send(var_s *self, int argc, slib_par_t *args, var_s *retval) { - int result; - if (argc != 1 || !is_serial_object(self) || !v_is_type(args[0].var_p, V_STR)) { - v_setstr(retval, ERR_PARAM); - result = 0; - } else { - const char *buffer = v_getstr(args[0].var_p); - int length = v_strlen(args[0].var_p); - int serialNo = self->v.m.id; - switch (serialNo) { - case 0: - Serial.write(buffer, length); - break; - default: - getSerial(serialNo)->write(buffer, length); - break; - } - result = 1; - } - return result; -} - -static int cmd_openserial(int argc, slib_par_t *args, var_t *retval) { - int result; - if (!(argc == 0 || (argc == 1 && is_serial_var(args[0].var_p)))) { - v_setstr(retval, ERR_PARAM); - result = 0; - } else { - int serialNo = argc == 0 ? 0 : args[0].var_p->v.i; - map_init(retval); - retval->v.m.id = serialNo; - retval->v.m.cls_id = USB_CLASS_ID; - v_create_callback(retval, "ready", cmd_serial_ready); - v_create_callback(retval, "receive", cmd_serial_receive); - v_create_callback(retval, "send", cmd_serial_send); - switch (serialNo) { - case 0: - serial_init(); - break; - default: - int serialSpeed = get_param_int(argc, args, 1, BT_BAUD); - getSerial(serialNo)->begin(serialSpeed); - break; - } - result = 1; - } - return result; -} - -static int cmd_i2c_write(var_s *self, int argc, slib_par_t *args, var_s *retval) { - uint8_t address = get_param_int(argc, args, 0, 0); - uint8_t stop = get_param_int(argc, args, 2, 1); - - if (address == 0 || argc < 2) { - v_setstr(retval, ERR_PARAM); - return 0; - } - - int interfaceNumber = self->v.m.id; - TwoWire *ptrWire; - ptrWire = getI2C(interfaceNumber); - - ptrWire->beginTransmission(address); - - switch (args[1].var_p->type) { - case V_INT:{ - int value = get_param_int(argc, args, 1, 0); - ptrWire->write(value); - } - break; - case V_STR:{ - const char *buffer = v_getstr(args[1].var_p); - int length = v_strlen(args[1].var_p); - ptrWire->write(buffer, length); - } - break; - case V_ARRAY:{ - var_p_t array = args[1].var_p; // Get array - if (array->maxdim > 1) { - v_setstr(retval, "ERROR: I2C: Write requires 1D-array"); - return 0; - } - uint32_t bytes = v_ubound(array, 0) - v_lbound(array, 0) + 1; - uint8_t *buffer = new uint8_t[bytes]; - for (uint32_t ii = 0; ii < bytes; ii++) { - buffer[ii] = get_array_elem_int(array, ii); - } - ptrWire->write(buffer, bytes); - delete[]buffer; - } - break; - } - - if (ptrWire->endTransmission(stop)) { - v_setstr(retval, "ERROR I2C: Transmission failed"); - return 0; - } - - return 1; -} - -static int cmd_i2c_read(var_s *self, int argc, slib_par_t *args, var_s *retval) { - uint8_t address = get_param_int(argc, args, 0, 0); - uint32_t bytes = get_param_int(argc, args, 1, 1); - uint8_t stop = get_param_int(argc, args, 2, 1); - - if (address == 0) { - v_setstr(retval, ERR_PARAM); - return 0; - } - - int interfaceNumber = self->v.m.id; - TwoWire *ptrWire; - ptrWire = getI2C(interfaceNumber); - ptrWire->requestFrom(address, bytes, stop); - - if (bytes > 1) { - v_toarray1(retval, bytes); - for (uint32_t ii = 0; ii < bytes; ii++) { - v_setint(v_elem(retval, ii), ptrWire->read()); - } - } else { - v_setint(retval, ptrWire->read()); - } - return 1; -} - -static int cmd_i2c_setClock(var_s *self, int argc, slib_par_t *args, var_s *retval) { - uint32_t clockFrequency = get_param_int(argc, args, 0, 100000); - - if (clockFrequency != 100000 && clockFrequency != 400000 && clockFrequency != 1000000) { - v_setstr(retval, "ERROR I2C: Clock freuqency not supported"); - return 0; - } - - int interfaceNumber = self->v.m.id; - getI2C(interfaceNumber)->setClock(clockFrequency); - - return 1; -} - -static int cmd_openi2c(int argc, slib_par_t *args, var_t *retval) { - uint8_t interfaceNumber = get_param_int(argc, args, 0, 0); - uint8_t pinSDA = get_param_int(argc, args, 1, 0); - uint8_t pinSCL = get_param_int(argc, args, 2, 0); - - if (interfaceNumber > 2) { - v_setstr(retval, ERR_PARAM); - return 0; - } - - map_init(retval); - v_create_callback(retval, "write", cmd_i2c_write); - v_create_callback(retval, "read", cmd_i2c_read); - v_create_callback(retval, "setClock", cmd_i2c_setClock); - retval->v.m.id = interfaceNumber; - - TwoWire *ptrWire; - ptrWire = getI2C(interfaceNumber); - - if (pinSDA > 0 && pinSCL > 0) { - ptrWire->setSDA(pinSDA); - ptrWire->setSCL(pinSCL); - } - - ptrWire->begin(); - - return 1; -} - static int cmd_set_interactive(int argc, slib_par_t *args, var_t *retval) { uint8_t mode = get_param_int(argc, args, 0, 1); @@ -505,6 +65,7 @@ static FuncSpec lib_func[] = { {1, 1, "OPENDIGITALOUTPUT", cmd_opendigitaloutput}, {0, 1, "OPENSERIAL", cmd_openserial}, {0, 3, "OPENI2C", cmd_openi2c}, + {0, 0, "FS", cmd_fs}, {0, 0, "FREE", cmd_free} }; @@ -582,6 +143,10 @@ static int teensy_proc_exec(int index, int argc, slib_par_t *args, var_t *retval return result; } +void teensy_close(void) { + fs_close(); +} + static ModuleConfig teensyModule = { ._func_exec = teensy_func_exec, ._func_count = teensy_func_count, @@ -589,7 +154,8 @@ static ModuleConfig teensyModule = { ._proc_exec = teensy_proc_exec, ._proc_count = teensy_proc_count, ._proc_getname = teensy_proc_getname, - ._free = nullptr + ._free = nullptr, + ._close = teensy_close }; ModuleConfig *get_teensy_module() { From d48b0d363214ee6e83094558a7dc4f944c769784 Mon Sep 17 00:00:00 2001 From: Joerg Siebenmorgen Date: Sun, 20 Sep 2026 14:04:23 +0200 Subject: [PATCH 2/3] WINDOWS: Remove __p__environ warning when builing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building for windows (cross-compiling or MSYS2) the warning `warning: »__p__environ« ohne Attribut »dllimport« redeklariert: vorheriges »dllimport« ignoriert [-Wattributes]` is shown. Removing `extern char **environ` for Windows build solves the issue. --- src/common/system.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/system.c b/src/common/system.c index 16d32584..7ad9357d 100644 --- a/src/common/system.c +++ b/src/common/system.c @@ -23,8 +23,6 @@ #include #endif -extern char **environ; - #define BUFSIZE 1024 #if defined(_Win32) @@ -126,6 +124,8 @@ int dev_run(const char *cmd, var_t *r, int wait) { } #else +extern char **environ; + int dev_run(const char *cmd, var_t *r, int wait) { int result = 1; if (r != NULL) { From 35615ed9b2b51788e79480d80d7d4410f755f5e8 Mon Sep 17 00:00:00 2001 From: Joerg Siebenmorgen Date: Sun, 20 Sep 2026 14:23:53 +0200 Subject: [PATCH 3/3] TEENSY: Add license header to src files --- ChangeLog | 4 ++++ src/platform/teensy/src/fs.cpp | 8 ++++++++ src/platform/teensy/src/i2c.cpp | 8 ++++++++ src/platform/teensy/src/pinio.cpp | 8 ++++++++ src/platform/teensy/src/serial.cpp | 8 ++++++++ 5 files changed, 36 insertions(+) diff --git a/ChangeLog b/ChangeLog index 53c62804..e9742c99 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2026-09-26 (12.34) + TEENSY: Add flash and SD card file system + WINDOWS: Remove __p__environ warning when building + 2026-04-28 (12.34) COMMON: implement plugin_refresh_id for variable lifecycle handling COMMON: fix bug with comma in string assigned as local #283 diff --git a/src/platform/teensy/src/fs.cpp b/src/platform/teensy/src/fs.cpp index 5953278f..99f59523 100644 --- a/src/platform/teensy/src/fs.cpp +++ b/src/platform/teensy/src/fs.cpp @@ -1,3 +1,11 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2026 Joerg Siebenmorgen +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + #include #include "common/device.h" #include "common/pproc.h" diff --git a/src/platform/teensy/src/i2c.cpp b/src/platform/teensy/src/i2c.cpp index ae67f1bd..5bea6242 100644 --- a/src/platform/teensy/src/i2c.cpp +++ b/src/platform/teensy/src/i2c.cpp @@ -1,3 +1,11 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2026 Joerg Siebenmorgen +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + #include #include "config.h" #include "languages/messages.en.h" diff --git a/src/platform/teensy/src/pinio.cpp b/src/platform/teensy/src/pinio.cpp index 9588bc0e..1135bee7 100644 --- a/src/platform/teensy/src/pinio.cpp +++ b/src/platform/teensy/src/pinio.cpp @@ -1,3 +1,11 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2026 Joerg Siebenmorgen +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + #include #include "config.h" #include "languages/messages.en.h" diff --git a/src/platform/teensy/src/serial.cpp b/src/platform/teensy/src/serial.cpp index 8aa1f2a7..277738ee 100644 --- a/src/platform/teensy/src/serial.cpp +++ b/src/platform/teensy/src/serial.cpp @@ -1,3 +1,11 @@ +// This file is part of SmallBASIC +// +// Copyright(C) 2026 Joerg Siebenmorgen +// +// This program is distributed under the terms of the GPL v2.0 or later +// Download the GNU Public License (GPL) from www.gnu.org +// + #include #include "config.h" #include "languages/messages.en.h"