用NodeJs来控制硬件(基于Raspberry Pi)(二)

用NodeJs来控制硬件(基于Raspberry Pi)(二)

最终效果在第一节 首先,我们需要对RaspberryPi有一个了解,Wiki上说得比较详细了,中文的介绍也有,我就不废话了,用到的OLED显示模块提供的是SPI接口,一般不能“即插即用”,而是需要自己写驱动,还好SPI是属于比较通用的接口,Linux上已经有了通用的驱动,但在经过若干次试验后,发现RaspberryPi 的 SPI接口速度太快,不能满足OLED模块(驱动芯片是SSD1306)的时序要求,于是,只能自己通过GPIO,用编程来模拟。 要想通过GPIO来模拟,首先得看看RaspberryPi都有哪些GPIO口。下图是Raspberry Pi的接口图: RaspberryPi GPIO 图片中以GPIO开头的接口都可以通过程序控制其电平的高低,从而实现模拟SPI时序。我使用的模块采用四线SPI模式,接口如下: DAT – 数据输入 CLK – 时钟 RST – 复位 DC – 数据/命令切换 CS – 使能信号

将OLED模块的各个数据引脚接到Raspberry Pi的引脚上,并记下各个信号所对应的GPIO号。 接下来,就是写程序来驱动显示模块了! Raspberry将IO口的控制映射到了/sys/class/gpio里相关文件中了,通过读写相关文件,就可以实现对应IO口的控制,参考这个例子

echo "4" > /sys/class/gpio/export //将4号IO口导出,完成后会创建gpio4这个文件夹
echo "out" > /sys/class/gpio/gpio4/direction //将四号IO口设置为输出
echo "1" > /sys/class/gpio/gpio4/value //往4号IO口写值1,即输出高点平
echo "4" > /sys/class/gpio/unexport //清除4号IO口,删除gpio4文件夹

可以看到,以上操作其实都是文件操作,使用Node自带的fs相关函数就能进行控制,也有老外写过RaspberryPi GPIO相关的库另一个),实现原理也是如此。经过试验,发现OLED虽然能够成功驱动,但是由于文件IO操作比较耗时,导致显示屏的刷新率非常低,而且CPU消耗非常大。于是只能另寻他径。 玩过硬件的大概都知道Arduino,它是面向电子发烧友和设计师的一款开源电路板,以其简单的API,在世界范围内大受欢迎。而且,也有国外的大牛把它移植到了RaspberryPi上,以类似的简单API,编写硬件控制程序。而且还封装了不同语言的扩展。不过可惜,唯独没有NodeJs的扩展。 受到Arduino简洁的API的吸引,于是决定自己封装NodeJs的函数库

用NodeJs来控制硬件(基于Raspberry Pi)(一):最终效果图 用NodeJs来控制硬件(基于Raspberry Pi)(三):NodeJs 扩展


7 回复

用NodeJs来控制硬件(基于Raspberry Pi)(二)

引言

在上一篇教程中,我们已经介绍了如何使用Node.js来控制Raspberry Pi的GPIO接口。本篇我们将进一步探讨如何通过GPIO接口来模拟SPI时序,从而控制一个OLED显示模块。

硬件准备

我们使用的OLED显示模块采用四线SPI模式,具体接口如下:

  • DAT – 数据输入
  • CLK – 时钟
  • RST – 复位
  • DC – 数据/命令切换
  • CS – 使能信号

配置GPIO接口

首先,我们需要配置Raspberry Pi上的GPIO接口。可以通过/sys/class/gpio目录下的文件来控制GPIO口。以下是一些基本的操作示例:

const fs = require('fs');

// 导出GPIO端口
function exportGpio(pin) {
    return new Promise((resolve, reject) => {
        fs.writeFile(`/sys/class/gpio/export`, pin.toString(), (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

// 设置GPIO方向
function setGpioDirection(pin, direction) {
    return new Promise((resolve, reject) => {
        fs.writeFile(`/sys/class/gpio/gpio${pin}/direction`, direction, (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

// 设置GPIO值
function setGpioValue(pin, value) {
    return new Promise((resolve, reject) => {
        fs.writeFile(`/sys/class/gpio/gpio${pin}/value`, value.toString(), (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

// 清除GPIO端口
function unexportGpio(pin) {
    return new Promise((resolve, reject) => {
        fs.writeFile(`/sys/class/gpio/unexport`, pin.toString(), (err) => {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

模拟SPI时序

为了模拟SPI时序,我们需要手动控制GPIO引脚的电平变化。以下是模拟SPI时序的一个简单示例:

async function sendSpiData(pinDat, pinSclk, data) {
    for (let i = 7; i >= 0; i--) {
        await setGpioValue(pinDat, (data >> i) & 1);
        await setGpioValue(pinSclk, 1);
        await setGpioValue(pinSclk, 0);
    }
}

async function resetOled(pinReset) {
    await setGpioValue(pinReset, 0);
    await new Promise(resolve => setTimeout(resolve, 10));
    await setGpioValue(pinReset, 1);
}

async function initOled() {
    const pinDat = 17;
    const pinSclk = 27;
    const pinReset = 22;

    await exportGpio(pinDat);
    await exportGpio(pinSclk);
    await exportGpio(pinReset);

    await setGpioDirection(pinDat, 'out');
    await setGpioDirection(pinSclk, 'out');
    await setGpioDirection(pinReset, 'out');

    await resetOled(pinReset);

    // 发送初始化命令
    await sendSpiData(pinDat, pinSclk, 0xAE); // 显示关闭
    await sendSpiData(pinDat, pinSclk, 0xA1); // 段重定向
    await sendSpiData(pinDat, pinSclk, 0xC8); // COM重定向
    await sendSpiData(pinDat, pinSclk, 0xDA, 0x12); // 设置COM引脚配置
    await sendSpiData(pinDat, pinSclk, 0x81, 0xCF); // 设置对比度控制寄存器
    await sendSpiData(pinDat, pinSclk, 0xA4); // 全局显示
    await sendSpiData(pinDat, pinSclk, 0xA6); // 正常显示
    await sendSpiData(pinDat, pinSclk, 0xAF); // 显示开启
}

initOled().then(() => console.log("OLED 初始化完成")).catch(console.error);

结论

通过上述代码,我们可以看到如何通过Node.js来控制Raspberry Pi的GPIO接口,进而模拟SPI时序来控制OLED显示模块。这种方式虽然比直接使用硬件驱动更复杂,但提供了更大的灵活性和可定制性。

希望这篇教程对你有所帮助!在下一篇教程中,我们将继续探讨如何封装一个更高级别的Node.js库来简化这些操作。


刚看一下, 感觉 RaspberryPi 是一个 all in one 的板卡. 链上显示, 键鼠 可以当电脑用了

要赞一个啊,虽然看不懂

汗… 完全不懂

很感兴趣 支持分享。。。

正好用到,可是我买的是树莓派…

在这一篇中,我们将详细介绍如何使用Node.js通过GPIO来模拟SPI接口,以驱动OLED显示模块。以下是具体的步骤及代码示例:

步骤 1: 安装 onoff 模块

首先,你需要安装 onoff 模块,这是一个轻量级的库,用于读取GPIO引脚的状态和控制引脚。

npm install onoff

步骤 2: 初始化GPIO引脚

我们假设已经连接了OLED模块的数据、时钟、复位和数据/命令切换引脚到Raspberry Pi的GPIO引脚。这里是一个初始化这些引脚的示例代码:

const Gpio = require('onoff').Gpio;

// 初始化GPIO引脚
let DAT_PIN = new Gpio(17, 'out'); // DAT引脚
let CLK_PIN = new Gpio(27, 'out'); // CLK引脚
let RST_PIN = new Gpio(22, 'out'); // RST引脚
let DC_PIN = new Gpio(10, 'out');  // DC引脚

步骤 3: 编写SPI模拟函数

这里是一个简单的函数,用于模拟SPI通信。注意,这只是一个基础示例,实际应用中可能需要更复杂的逻辑来处理不同的OLED型号和具体参数。

function sendSPI(data) {
    for (let i = 7; i >= 0; i--) { // 发送8位数据
        let bit = (data >> i) & 1;
        DAT_PIN.writeSync(bit); // 写入数据位
        CLK_PIN.writeSync(1);   // 时钟上升沿
        CLK_PIN.writeSync(0);   // 时钟下降沿
    }
}

步骤 4: 使用封装的库

通过上述步骤,你可以开始使用这些函数来控制你的OLED模块了。例如,发送一个简单的命令或数据。

sendSPI(0x00); // 发送一个简单的数据字节

总结

通过这种方式,你可以利用Node.js轻松地控制基于Raspberry Pi的硬件设备,如OLED显示模块。虽然这种方法可能不如直接使用硬件专用库高效,但它展示了如何灵活地利用现有的GPIO接口来完成特定任务。

希望这个示例能帮助你更好地理解和使用Node.js控制硬件。如果你有任何问题或需要进一步的指导,请随时提问!

回到顶部