异步函数在while循环中没有立即产生结果

6
我是新手,正在学习nodejs。我已经将LCD面板和4x4薄膜键盘连接到了树莓派,并使用Node.js进行了编程。我的目标是,每当按下一个键时,它都应该立即显示在LCD面板上;当我按下#时,输入应该停止。
为此,我使用了LCD包https://www.npmjs.com/package/lcd和RPIOhttps://github.com/jperkin/node-rpio。由于我需要不断地检查用户输入,所以我将获取输入的代码放在while循环中,在其中写入打印语句,这就是问题所在。当我按下键盘上的一个键时,LCD面板上没有显示任何字符,但是当我按下#键时,程序退出并且所有字符都显示在LCD面板上。
我编写的代码如下。
var rpio = require('rpio');
var Lcd = require('lcd'),//This is asynchronous function
    lcd = new Lcd({
        rs: 18,
        e: 23,
        data: [24, 17, 27, 22],
        cols: 8,
        rows: 2
    });
var matrix=[[1,2,3,'A'],
            [4,5,6,'B'],
            [7,8,9,'C'],
            ['*',0,'#','D']]
var row=[37,35,33,31];
var col=[29,23,40,38];
for (var i = 0; i < 4; i++) {
    rpio.open(col[i], rpio.OUTPUT, rpio.HIGH);
}
for (var i = 0; i < 4; i++) {
    rpio.open(row[i], rpio.INPUT, rpio.PULL_UP);
}
var code="";
var comeout=0;
lcd.on('ready', function() {
    lcd.setCursor(0, 0);
    //start of keypad code
    while(true){
        for (var j = 0; j < 4; j++) {
            rpio.write(col[j],rpio.LOW);
            for (var i = 0; i < 4; i++) {
                if(rpio.read(row[i])==0){
                    console.log(matrix[i][j]);
                    lcd.print(matrix[i][j]);
                    if(matrix[i][j]=='#'){
                        comeout=1;
                        break;
                    }
                    while(rpio.read(row[i])==0);
                }
            }
            if(comeout==1)
                break;
            else
                rpio.write(col[j],rpio.HIGH);
        }
        if(comeout==1)
            break;
    }
    //end of keypad code
});

// If ctrl+c is hit, free resources and exit.
process.on('SIGINT', function() {
    lcd.clear();
    lcd.close();
    process.exit();
});

任何帮助都将不胜感激。谢谢。

我建议将您的代码分解成小块,并专注于问题。 - niry
在JS中,您不能以那种方式使用while(true)循环。这就是为什么有事件可以让您注意到按键等操作。请查看rpio.poll()。由于我不完全理解您的两个for循环以及您在那里使用的rpio.write()rpio.read(),因此无法重写您的代码。 - Thomas
我猜 colrow 包含某种位掩码!? - Thomas
你同时使用了Pin 23作为LCD使能和col[1],这可能是问题所在。你的while(true)使用没有问题,因为你扫描键盘矩阵(行和列)时没有使用任何中断或其他事件源。 - Peter Paul Kiefer
@PeterPaulKiefer,是的。这就像在JS中使用while(true)来检查(键盘/鼠标)按钮状态一样。它基本上是一个无限循环,永远不会中断以使任何其他JS代码执行;就像lcd.print()后面的代码似乎堆积起来一样。直到此循环被终止(通过按下#)。然后刷新堆栈并执行所有(挂起的)异步代码。这正是他描述的问题。 - Thomas
@Thomas 我查看了LCD模块的代码 https://github.com/fivdi/lcd/blob/master/lcd.js。LCD的开发人员为每个打印调用排队异步操作。此队列中的操作将在事件循环的下一个“tick”中按顺序调用。我不明白他们为什么要这样做,因为在LCD上打印足够快以处理同步。现在我必须同意你的观点。;-) 但是Vikas还使用了Raspi的PIO 23作为LCD的使能引脚和键盘矩阵的一个列输入引脚。我想知道他如何将同一PIO连接到两个引脚? - Peter Paul Kiefer
1个回答

1
根据与@Thomas的讨论,我建议您使用setImmediate调用来模拟while(true)循环,在其中请求矩阵并执行LCD.print。这是因为LCD.print将操作添加到javascript事件队列中。但是,事件队列的操作被阻塞,直到实际操作完成。只要您在while(true)循环中,它永远不会完成。
所以,您必须结束活动操作并给事件循环控制权,从而执行打印命令。但与此同时,只要您没有按下“#”键,您必须确保再次扫描密钥矩阵。
以下是一个示例:
lcd.on('ready', function() 
{
  lcd.setCursor(0, 0);
  setImediate(
  function scanMatrix()
  {
    for (var j = 0; j < 4; j++) 
    {
      rpio.write(col[j],rpio.LOW);
      for (var i = 0; i < 4; i++) 
      {
        if(rpio.read(row[i])==0)
        {
          console.log(matrix[i][j]);
          lcd.print(matrix[i][j]);
          if(matrix[i][j]!='#')
          {
            setImmediate(scanMatrix);
          }
          while(rpio.read(row[i])==0);
        }
      }
      rpio.write(col[j],rpio.HIGH);
    }
  });
});

代码没有经过测试,因为我这里没有树莓派。它应该能够给你一个解决问题的思路。
一个非常酷的解决方案是在按键矩阵上添加一个微控制器(MSP430,...)。MC扫描矩阵并通过SPI或I2C将按键传输到树莓派。....好了,好了,别打我;-)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接