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

news2024/12/20 2:24:58

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 UNO的UList软件

正好手里也有一块Arduino UNO的开发板,就顺便在它上面也安装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中编译安装

将ESP32开发板通过USB线接入主机,在Arduino IDE中设置好连线串口,然后点击左上角的箭头(对钩只编译),一键编译,上传。

编译好慢啊

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

​
#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

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

#define AIRM2M_CORE_ESP32C3

后面加入一段AIRM2M_CORE_ESP32C3的定义

#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中编译安装

另一块Arduino Uno的开发板也不闲着,选好开发板,设好连接的串口,跟ESP32开发板一样编译,上传。

编译好快啊

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灯,

在ULisp的交互界面,输入如下代码

(defun blink (&optional x)
  (pinmode :led-builtin :output)
  (digitalwrite :led-builtin x)
  (delay 1000)
  (blink (not x)))

然后输入

(blink)

我们就看到Arduino Uno开发板上13号接口的灯(或者ESP32C3开发板上自带的LED灯)在闪了,把频率调高一点:

(defun blink (&optional x)
  (pinmode :led-builtin :output)
  (digitalwrite :led-builtin x)
  (delay 500)
  (blink (not x)))

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

后来明白了,是灯闪了之后代码就一直执行卡在这里了,系统已经不接收任何指令,除非用“~”中断。这点跟MicroPython下不一样,MicroPython是将控制器返回给交互界面的。

使用“~”中断程序,这时候灯就不闪了,灯的状态停留在发出“~”指令的时候的状态。然后再去修改频率等,灯的闪烁状态就有新变化了。

安装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:

循环亮灯也可以指定LED灯接的管脚,比如13号(13号就是ULisp在Arduino Uno和ESP32开发板里设定为led-builtin的默认管脚),

(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.

小结

ULisp支持多种硬件系统,每种系统只需要下载一个Arduin格式的cpp文件(以.ino结尾),然后在Arduino IDE里编译、上传即可。

Ulisp里默认指定了一个led-builtin接线,对应13插口,这个插口对应ESP32C3开发板上的板载LED灯,非常方便我们练习用。

在ESP32C3里学习一点lisp

用问号查询 

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

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.
(+ 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下应该用:

(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

文件内容为:

/*
 * 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里面是不是不用转义了?

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

#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 ,这就比较搞了...

修改代码,加上

#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了吗?

手工写上:

#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...

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

#define AIRM2M_CORE_ESP32C3

后面加入一段

#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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2262462.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

决策树的生成与剪枝

决策树的生成与剪枝 决策树的生成生成决策树的过程决策树的生成算法 决策树的剪枝决策树的损失函数决策树的剪枝算法 代码 决策树的生成 生成决策树的过程 为了方便分析描述&#xff0c;我们对上节课中的训练样本进行编号&#xff0c;每个样本加一个ID值&#xff0c;如图所示…

51c嵌入式~单片机~合集2

我自己的原文哦~ https://blog.51cto.com/whaosoft/12362395 一、不同的电平信号的MCU怎么通信&#xff1f; 下面这个“电平转换”电路&#xff0c;理解后令人心情愉快。电路设计其实也可以很有趣。 先说一说这个电路的用途&#xff1a;当两个MCU在不同的工作电压下工作&a…

Kerberos实验

kdc&#xff1a;192.168.72.163 客户端&#xff08;机器账户win10&#xff09;&#xff1a;192.168.72.159 用户&#xff1a;administrator 抓包&#xff1a;开机登录win10&#xff0c;使用administrator域用户凭据登录。 生成 Kerberos 解密文件 抓取 krbtgt 用户和 win1…

AI一键分析小红书对标账号‼️

宝子们&#xff0c;AI小助手近期发现了一款宝藏AI工具&#xff0c;拥有对标账号AI分析功能&#xff0c;只需10秒就能全面掌握对标账号的运营情况&#xff0c;并且可以根据分析结果提供创作方向和灵感&#xff0c;轻松助力1:1复刻起号&#xff01; 功能亮点&#xff1a; &…

大腾智能CAD:国产云原生三维设计新选择

在快速发展的工业设计领域&#xff0c;CAD软件已成为不可或缺的核心工具。它通过强大的建模、分析、优化等功能&#xff0c;不仅显著提升了设计效率与精度&#xff0c;还促进了设计思维的创新与拓展&#xff0c;为产品从概念构想到实体制造的全过程提供了强有力的技术支持。然而…

VMware虚拟机 Ubuntu没有共享文件夹的问题

在虚拟机的Ubuntu系统中&#xff0c;共享文件目录存放在 mnt/hgfs 下面&#xff0c;但是我安装完系统并添加共享文件后发现&#xff0c;在mnt下连/hgfs目录都没有。 注意&#xff1a;使用共享文件目录需要已安装VMtools工具。 添加共享文件目录 一&#xff1a;在超级用户下 可…

OpenGL ES 01 渲染一个四边形

项目架构 着色器封装 vertex #version 300 es // 接收顶点数据 layout (location 0) in vec3 aPos; // 位置变量的属性位置值为0 layout (location 1) in vec4 aColors; // 位置变量的属性位置值为1 out vec4 vertexColor; // 为片段着色器指定一个颜色输出void main() {gl…

leetcode二叉搜索树部分笔记

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 二叉搜索树 1. 二叉搜索树的最小绝对差2. 二叉搜索树中第 K 小的元素3. 验证二叉搜索树 1. 二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中…

推送本地仓库到远程git仓库

目录 推送本地仓库到远程git仓库1.1修改本地仓库用户名1.2 push 命令1.3远程分支查看 推送本地仓库到远程git仓库 删除之前的仓库中的所有内容&#xff0c;从新建库&#xff0c;同时创建一个 A.txt 文件 清空原有的远程仓库内容&#xff0c;重新创建一个新的仓库&#xff0c;…

暂停一下,给Next.js项目配置一下ESLint(Next+tailwind项目)

前提 之前开自己的GitHub项目&#xff0c;想着不是团队项目&#xff0c;偷懒没有配置eslint&#xff0c;后面发现还是不行。eslint的存在可以帮助我们规范代码格式&#xff0c;同时 ctrl s保存立即调整代码格式是真的很爽。 除此之外&#xff0c;团队使用eslint也是好处颇多…

基于微信小程序的小区疫情防控ssm+论文源码调试讲解

第2章 程序开发技术 2.1 Mysql数据库 为了更容易理解Mysql数据库&#xff0c;接下来就对其具备的主要特征进行描述。 &#xff08;1&#xff09;首选Mysql数据库也是为了节省开发资金&#xff0c;因为网络上对Mysql的源码都已进行了公开展示&#xff0c;开发者根据程序开发需…

Win11安装安卓子系统WSA

文章目录 简介一、启用Hyper-V二、安装WSA三、安装APKAPK商店参考文献 简介 WSA&#xff1a;Windows Subsystem For Android 一、启用Hyper-V 控制面板 → 程序和功能 → 启用或关闭 Windows 功能 → 勾选 Hyper-V 二、安装WSA 进入 Microsoft Store&#xff0c;下拉框改为 …

[面试题]--索引用了什么数据结构?有什么特点?

答&#xff1a;使用了B树&#xff1a; 时间复杂度&#xff1a;O(logN),可以有效控制树高 B树特点&#xff1a; 1.叶子节点之间有相互链接的作用&#xff0c;会指向下一个相近的兄弟节点。 MySQL在组织叶子节点使用的是双向链表 2.非叶子节点的值都保存在叶子节点当中 MySQL非叶…

Element plus 下拉框组件选中一个选项后显示的是 value 而不是 label

最近刚进行 Vue3 Element plus 项目实践&#xff0c;在进行表单二次封装的时候&#xff0c;表单元素 select 下拉框组件选中一个选项后显示的是 value 而不是 label&#xff0c;下面上代码&#xff1a; 原来的写法&#xff1a; <el-selectv-if"v.type select"…

bean创建源码

去字节面试&#xff0c;直接让人出门左拐&#xff1a;Bean 生命周期都不知道&#xff01; spring启动创建bean流程 下面就接上了 bean生命周期 doGetBean Object sharedInstance this.getSingleton(beanName); sharedInstance this.getSingleton(beanName, new ObjectF…

【C++】- 掌握STL List类:带你探索双向链表的魅力

文章目录 前言&#xff1a;一.list的介绍及使用1. list的介绍2. list的使用2.1 list的构造2.2 list iterator的使用2.3 list capacity2.4 list element access2.5 list modifiers2.6 list的迭代器失效 二.list的模拟实现1. list的节点2. list的成员变量3.list迭代器相关问题3.1…

泷羽sec学习打卡-brupsuite8伪造IP和爬虫审计

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于brupsuite的那些事儿-Brup-FaskIP 伪造IP配置环境brupsuite导入配置1、扩展中先配置python环境2、安…

挑战一个月基本掌握C++(第五天)了解运算符,循环,判断

一 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 内置了丰富的运算符&#xff0c;并提供了以下类型的运算符&#xff1a; 算术运算符关系运算符逻辑运算符位运算符赋值运算符杂项运算符 1.1 算术运算符 假设变量 A 的值为 10&#xff0c;变量 B 的值为…

JAVA没有搞头了吗?

前言 今年的Java程序员群体似乎承受着前所未有的焦虑。投递简历无人问津&#xff0c;难得的面试机会也难以把握&#xff0c;即便成功入职&#xff0c;也往往难以长久。于是&#xff0c;不少程序员感叹&#xff1a;互联网的寒冬似乎又一次卷土重来&#xff0c;环境如此恶劣&…