奇怪的知识又增加了:ESP32下的Lisp编程=>ULisp--Lisp for microcontrollers

ESP32下有MicroPython,那么我就在想,有Lisp语言支持吗?答案是果然有!有ULisp,专门为MCU设计的Lisp!

网址:uLisp - Lisp for microcontrollers

介绍:用于微控制器的 Lisp

适用于 Arduino、Adafruit M0/M4、Micro:bit、ESP32、RISC-V 和 Teensy 4.x 板的 Lisp。

uLisp® 是 Lisp 编程语言的一个版本,专门设计用于在具有有限 RAM 的微控制器上运行,从基于 ATmega328 的 Arduino Uno 到 Teensy 4.0/4.1。无论平台如何,您都可以使用完全相同的 uLisp 程序。有关每个平台的性能,请参阅 性能

因为 uLisp 是一个解释器,所以你可以输入命令,并立即看到效果,而不必编译和上传你的程序。这使它成为学习编程或设置简单电子设备的理想环境。

Lisp 也是学习基本编程概念的理想语言。它结合了字符串处理、列表处理和垃圾回收,因此也是表达复杂想法的优秀语言,例如教机器人解决迷宫或在地图上找到最短的路线。除了支持一组核心的 Lisp 功能外,uLisp 还包括 Arduino 扩展,使其成为 Arduino 控制语言的理想选择。

下载软件

您可以从下载并安装 uLisp

下载ESP32的Ulist软件

这个链接:http://www.ulisp.com/list?50GW

或者:

GitHub - technoblogy/ulisp-esp: A version of the Lisp programming language for ESP32-based boards.

有两个版本,分别是ulisp-esp-comments.ino 和ulisp-esp.ino ,两个版本内容一样,只是ulisp-esp-comments.ino 代码里有注释。

直接把文件,也就是c源代码保存下来即可。ino后缀就是Arduino的文件。

下载Arduino的UList软件

AVR-Nano version

Download the AVR-Nano version for AVR platforms with 32 or 48 Kbytes of program memory, such as the Arduino Uno, Arduino Nano, and Arduino Nano Every:

AVR-Nano Release 4.7 - 9th November 2024

or get it from GitHub at GitHub - technoblogy/ulisp: A version of the Lisp programming language for ATmega-based Arduino boards..

同样把源代码保存即可。

编译安装

直接在Arduino中编译即可。当然ESP32可以在自己的ESP-IDF里面编译(没测试)。

在Arduino新创建的项目中黏贴文件代码。

在ESP32中编译安装

编译,上传

编译好慢啊

编译失败。最终是在文件里加上这段才编译成功,不明白为什么我的esp32c3的板子,被认成了esp32,可以修改ESP32的定义:

cpp 复制代码
​
#elif defined(ESP32)                             /* Generic ESP32 board */
  #define WORKSPACESIZE (9216-SDSIZE)            /* Objects (8*bytes) */
  #define MAX_STACK 8000
  #define LITTLEFS
  #include <LittleFS.h>
  #define SDCARD_SS_PIN 13
  #define LED_BUILTIN 13
  #define CPU_RISC_V

也可以这样处理,在文件开始加入一句:

cpp 复制代码
#define AIRM2M_CORE_ESP32C3

后面加入一段

cpp 复制代码
#elif defined(AIRM2M_CORE_ESP32C3)
  #define WORKSPACESIZE (9216-SDSIZE)            /* Objects (8*bytes) */
  #define MAX_STACK 8000
  #define LITTLEFS
  #include <LittleFS.h>
  #define SDCARD_SS_PIN 13
  #define LED_BUILTIN 13
  #define CPU_RISC_V

这样就能识别出开发板了。 这块AIRM2M_CORE_ESP32C3开发板需要这样做,测试发现手上的ESP32S3开发板不需要这样做。

编译好这样显示:

在Arduino Uno中安装

编译,上传

编译好快啊

Sketch uses 32232 bytes (99%) of program storage space. Maximum is 32256 bytes.

Global variables use 1552 bytes (75%) of dynamic memory, leaving 496 bytes for local variables. Maximum is 2048 bytes.

这样就上传好了

测试

在Arduino Uno中,将13端口接上一个LED灯,

在ESP32C3中,直接用开发板上自带的LED灯,

输入如下代码

Lisp 复制代码
(defun blink (&optional x)
  (pinmode :led-builtin :output)
  (digitalwrite :led-builtin x)
  (delay 1000)
  (blink (not x)))

然后输入

复制代码
(blink)

我们就看到13号接口的灯在闪了,把频率调高一点:

Lisp 复制代码
(defun blink (&optional x)
  (pinmode :led-builtin :output)
  (digitalwrite :led-builtin x)
  (delay 500)
  (blink (not x)))

好像没有变化啊,看来lisp跟python不太一样啊,python是冲掉原来的代码....

(后来明白了,是灯闪了之后就卡住了,不接收任何指令,除非用"~"中断)

中断之后,再去修改频率等,就有变化了。

安装Arduino ESP_OTA库

Installing uLisp

The download is a single text file. To compile it in the Arduino IDE either save it as a text file and rename it to a .cpp file, or copy and paste the text into a new empty project file. You can download the latest Arduino IDE from arduino.cc.

Select the correct Board option for your platform on the Tools menu, select the USB port from the Port menu, and upload uLisp. You should then be able to select Serial Monitor from the Tools menu, and interact with uLisp as described in Using uLisp.

The following pages give specific installation instructions for particular platforms:

一些简单语法

比如循环亮灯,

Lisp 复制代码
(defun b ()
  (pinmode 13 t)
  (loop
   (digitalwrite 13 t)
   (delay 1000)
   (digitalwrite 13 nil) 
   (delay 1000)))

停住循环,用 "~"

  • Enter a "~" into the Serial Monitor entry field, and press Return.

在ESP32C3里学习一点lisp

用问号查询

首先可以查询函数和运算符,比如输入:(? assoc)

cpp 复制代码
9216>  (? assoc)
(assoc key list [:test function])
Looks up a key in an association list of (key . value) pairs, using eq or the specified test function,
and returns the matching pair, or nil if no pair is found.
cpp 复制代码
(+ number*)
Adds its arguments together.
If each argument is an integer, and the running total doesn't overflow, the result is an integer,
otherwise a floating-point number.

驱动LED

复制代码
在ESP32C3下用analogwrite:(analogwrite 10 128) 失败 ,这句话是点亮10脚的灯,但这是Arduino的。所以analogwrite这句指令不能用。

在ESP32C3下应该用:

cpp 复制代码
(defun blink (&optional x)
  (pinmode :led-builtin t)
  (digitalwrite :led-builtin x)
  (delay 1000)
  (blink (not x)))

(blink())

这个据说是点亮13脚的灯,我这个开发板没有13脚,但是开发板上板载的灯闪了,原来13脚被接了LED灯啊!

闪起来之后交互就卡住了,需要用"~"来中断程序。

调试

ESP32编译报错'dacWrite' was not declared in this scope; did you mean 'i2cWrite'?

C:\Users\Admin\AppData\Local\Temp\.arduinoIDE-unsaved20241112-15076-ocdvvn.kcpg\sketch_dec12e\sketch_dec12e.ino:5345:3: note: in expansion of macro 'analogWrite'

5345 | analogWrite(pin, checkinteger(value));

| ^~~~~~~~~~~

exit status 1

Compilation error: 'dacWrite' was not declared in this scope; did you mean 'i2cWrite'?

还有这句:

'dacWrite' was not declared in this scope; did you mean 'i2cWrite'?

213 | #define analogWrite(x,y) dacWrite((x),(y))

看了下,没看明白。但是确实是dacWrite((x),(y)) 没有

引入arduino试试:

#include <Arduino.h>

还是不行。

用ESP32S3板子试试。这个就能编译成功。

倒是找到dacWrite((x),(y))了,在这个文件里:

arduino-esp32/cores/esp32/esp32-hal-dac.h at master · espressif/arduino-esp32 · GitHub

文件内容为:

cpp 复制代码
/*
 * SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "esp32-hal-dac.h"

#if SOC_DAC_SUPPORTED
#include "esp32-hal.h"
#include "esp32-hal-periman.h"
#include "soc/dac_channel.h"
#include "driver/dac_oneshot.h"

static bool dacDetachBus(void *bus) {
  esp_err_t err = dac_oneshot_del_channel((dac_oneshot_handle_t)bus);
  if (err != ESP_OK) {
    log_e("dac_oneshot_del_channel failed with error: %d", err);
    return false;
  }
  return true;
}

bool __dacWrite(uint8_t pin, uint8_t value) {
  esp_err_t err = ESP_OK;
  if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) {
    log_e("pin %u is not a DAC pin", pin);
    return false;  //not dac pin
  }

  dac_oneshot_handle_t bus = (dac_oneshot_handle_t)perimanGetPinBus(pin, ESP32_BUS_TYPE_DAC_ONESHOT);
  if (bus == NULL) {
    perimanSetBusDeinit(ESP32_BUS_TYPE_DAC_ONESHOT, dacDetachBus);
    if (!perimanClearPinBus(pin)) {
      return false;
    }
    dac_channel_t channel = (pin == DAC_CHAN0_GPIO_NUM) ? DAC_CHAN_0 : DAC_CHAN_1;
    dac_oneshot_config_t config = {.chan_id = channel};
    err = dac_oneshot_new_channel(&config, &bus);
    if (err != ESP_OK) {
      log_e("dac_oneshot_new_channel failed with error: %d", err);
      return false;
    }
    if (!perimanSetPinBus(pin, ESP32_BUS_TYPE_DAC_ONESHOT, (void *)bus, -1, channel)) {
      dacDetachBus((void *)bus);
      return false;
    }
  }

  err = dac_oneshot_output_voltage(bus, value);
  if (err != ESP_OK) {
    log_e("dac_oneshot_output_voltage failed with error: %d", err);
    return false;
  }
  return true;
}

bool __dacDisable(uint8_t pin) {
  if (pin != DAC_CHAN0_GPIO_NUM && pin != DAC_CHAN1_GPIO_NUM) {
    log_e("pin %u is not a DAC pin", pin);
    return false;  //not dac pin
  }
  void *bus = perimanGetPinBus(pin, ESP32_BUS_TYPE_DAC_ONESHOT);
  if (bus != NULL) {
    // will call dacDetachBus
    return perimanClearPinBus(pin);
  } else {
    log_e("pin %u is not attached to DAC", pin);
  }
  return false;
}

extern bool dacWrite(uint8_t pin, uint8_t value) __attribute__((weak, alias("__dacWrite")));
extern bool dacDisable(uint8_t pin) __attribute__((weak, alias("__dacDisable")));

#endif

发现这个文件里arduino-esp32/cores/esp32/esp32-hal.h at master · espressif/arduino-esp32 · GitHub也有这个定义,void analogWrite(uint8_t pin, int value);

这样在Arduino里面是不是不用转义了?

本来想不转义了,于是去看代码,结果看到了这里:

cpp 复制代码
#elif defined(ARDUINO_ADAFRUIT_QTPY_ESP32C3)
  #define WORKSPACESIZE (9216-SDSIZE)            /* Objects (8*bytes) */
  #define MAX_STACK 8000
  #define LITTLEFS
  #include <LittleFS.h>
  #define SDCARD_SS_PIN 13
  #define LED_BUILTIN 13
  #define CPU_RISC_V

// Legacy boards ***************************************************************
  
#elif defined(ESP32)                             /* Generic ESP32 board */
  #define WORKSPACESIZE (9216-SDSIZE)            /* Objects (8*bytes) */
  #define MAX_STACK 7000
  #define LITTLEFS
  #include <LittleFS.h>
  #define analogWrite(x,y) dacWrite((x),(y))
  #define SDCARD_SS_PIN 13
  #define LED_BUILTIN 13
  #define CPU_LX6

根据报错信息,主板没有选对ESP32C3,而是选成了ESP32 ,这就比较搞了...

修改代码,加上

cpp 复制代码
#elif defined(ARDUINO_AIRM2M_CORE_ESP32C3)
  #define WORKSPACESIZE (9216-SDSIZE)            /* Objects (8*bytes) */
  #define MAX_STACK 8000
  #define LITTLEFS
  #include <LittleFS.h>
  #define SDCARD_SS_PIN 13
  #define LED_BUILTIN 13
  #define CPU_RISC_V

编译,还是报错,不明白为什么还是没认出板子来?

它就认成esp32了?这不bug了吗?

手工写上:

cpp 复制代码
#elif defined(ESP32)                             /* Generic ESP32 board */
  #define WORKSPACESIZE (9216-SDSIZE)            /* Objects (8*bytes) */
  #define MAX_STACK 8000
  #define LITTLEFS
  #include <LittleFS.h>
  #define SDCARD_SS_PIN 13
  #define LED_BUILTIN 13
  #define CPU_RISC_V

编译,终于过了!

显示:

Wrote 1128144 bytes (683960 compressed) at 0x00010000 in 13.9 seconds (effective 649.0 kbit/s)...

Hash of data verified.

Leaving...

Hard resetting via RTS pin...

除了这样处理,还可以在文件开始加入一句:

cpp 复制代码
#define AIRM2M_CORE_ESP32C3

后面加入一段

cpp 复制代码
#elif defined(AIRM2M_CORE_ESP32C3)
  #define WORKSPACESIZE (9216-SDSIZE)            /* Objects (8*bytes) */
  #define MAX_STACK 8000
  #define LITTLEFS
  #include <LittleFS.h>
  #define SDCARD_SS_PIN 13
  #define LED_BUILTIN 13
  #define CPU_RISC_V
相关推荐
王二空间2 分钟前
数据可视化的Python实现
开发语言·python·信息可视化
qq_4597300320 分钟前
STM32 IIC协议实现
stm32·单片机·嵌入式硬件
雯0609~40 分钟前
PHP:上传图片的图片压缩
android·开发语言·php
Amo 67291 小时前
取消网络请求
开发语言·前端·javascript
未来之窗软件服务1 小时前
软件架构设计——通用表单UI—未来之窗行业应用跨平台架构
开发语言·javascript·ui
编程零零七1 小时前
【Python】tensorflow中的argmax()函数
开发语言·python·信息可视化·数据分析·arm·python学习·python数据可视化
嘉琪0011 小时前
js相关面试题
java·开发语言
iiiiiankor1 小时前
【C语言实现:用队列模拟栈与用栈模拟队列(LeetCode 225 & 232)】
c语言·开发语言·leetcode··队列
精神病不行计算机不上班2 小时前
[C++]纯虚函数与虚函数
开发语言·c++
oVo5672 小时前
Rust 宏
开发语言·算法·rust