65.9K
CodeProject 正在变化。 阅读更多。
Home

摩托车信号灯

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (23投票s)

2018年6月26日

CPOL

7分钟阅读

viewsIcon

36797

downloadIcon

488

使用 Arduino Nano 控制一辆旧的 1978 年 Suzuki GS500 上的所有工作灯

引言

几年前,我开始在业余时间定制摩托车,作为工作后放松的方式。定制摩托车的特点是它们通常在外观上是独一无二的。前两辆摩托车确实有一个Arduino来控制转向灯并与刹车灯配合工作,但这辆我想有所不同。如果你好奇,可以在这里查看构建过程。

我希望所有的照明都由LED驱动,但网上的选择非常差,所以我不得不自己制作所有的示廓灯,以适应摩托车的外观。

尽管“完成”的产品仍在修改中,但它已经足够稳定,可以作为我的夏季日常驾驶工具。

背景

Arduino代码相当简单;它尚未优化,除了用于看门狗定时器的<avr/wdt.h>之外,没有使用任何外部库。

电路稍微复杂一些,但接线并不太难。之所以需要添加所有额外的零件,而不是直接使用Arduino来驱动灯光,是因为每个引脚只能处理大约20ma @ 5vdc的电流。我从Digi-key采购的超亮白色LED每个消耗大约20ma。即使串联连接以减少电流负载,每个LED仍然会降压约2vdc,将每个输出引脚限制为2个LED,这不足以满足我的需求。因此,添加了一些PNP达林顿晶体管,这样我就可以在需要时每个输出泵出总共1.0A的电流。

这是所构建内容的粗略草图。

上面的图纸最终看起来像下面的样子

这是完整电气图的片段(zip文件中有一个PDF和PNG格式的完整版本)

Using the Code

首先是按钮去抖动!实际上完成了两个版本的按钮去抖动:一个用于刹车开关,另一个用于转向灯和远/近光选择器。所有输入都设置为HIGH,通过将此引脚接地(通过车架底盘),这会告诉代码刹车已按下。

但是,当开关即将激活或摩托车经过颠簸时,您可能会收到很多误报。因此,需要有一个延迟时间,我们需要在此时间内看到输入处于恒定状态;我发现大约100毫秒的效果很好。

下面的代码将在每次输入切换状态时设置一个时间戳,如果时间戳超过100毫秒的阈值,我们知道这很可能是一个稳定状态。如果当前状态也与_on(LOW)匹配,那么刹车灯应该亮起。

bool isBrake() {
  static long lasttm;
  static int last;
  int state = digitalRead(switchB);
  bool ret = false;

  if (last != state) {
    lasttm = millis();
    last = state;
  }

  if ((millis() - lasttm) > 100) {
    ret = last == _on;
  }

  return ret;
}

松开刹车开关将翻转状态并返回false,没有时间延迟。

转向灯按钮略有不同。代码类似于刹车逻辑,但现在我只希望按钮最初按下时触发其他代码;这由triggered布尔值处理,代码每次按下只会返回一次true,有点像按下电脑的电源按钮。

bool isLeftTurn() {
  static long lasttm;
  static int last;
  static bool triggered;
  int state = digitalRead(switchL);
  bool ret = false;

  if (last != state) {
    lasttm = millis();
    last = state;
    triggered = true;
  }

  if ((millis() - lasttm) > 100 && triggered) {
    ret = last == _on;
    triggered = false;
  }
  return ret;
}

setHiLowBeam的工作方式大致相同,只不过它也处理输出端。

void setHiLowBeam() {
  static long lasttm;
  static int last;
  static bool triggered;
  int state = digitalRead(switchH);

  if (last != state ) {
    lasttm = millis();
    last = state;
    triggered = true;
  }

  if ((millis() - lasttm) > 100 && triggered && last==_on) {
    digitalWrite(HILOW, digitalRead(HILOW) == LOW ? HIGH : LOW);
    triggered = false;
  }
}

转向灯计时器

从主循环中,它将调用一个简单的函数,获取当前的毫秒时间戳并来回切换ison变量,调用代码只是想知道转向灯应该处于什么状态,即使转向灯当前没有运行。

int isBlinkTime(unsigned long curr){
  static unsigned long fut_time;
  static int ison;
   if (curr > fut_time) {
    ison = !ison;
    fut_time = curr+_blinktime;
  }
  return(ison ? _off : _on);
}

如果你曾经在路上骑过摩托车或跟在后面,你就会知道有时我们会忘记关闭那些烦人的转向灯,有时会持续数英里,所以下一个函数将检查3分钟的超时,以及是否开始转向灯序列。

LR(左和右)是通过从主循环中调用瞬时isLeftTurnisRightTurn函数获得的。如果转向灯超过fut_time时间戳,它将把currTS设置为none并取消信号。

如果左侧或右侧被按下,并且我们不在序列中,则开始一个新的带有下一个超时的序列。在序列中按下任一按钮也会取消序列。

void CheckTurnSignals(unsigned long curr, bool L, bool R){
  static unsigned long fut_time; 
  if (curr >= fut_time) {
    currTS = none;
  }

  if (L || R) {
    if (currTS == none) {
      //the turn signal needs to start
      //and run for a max of 3 minutes
      fut_time = curr + _timeout;
      currTS = L ? left : right;
    } else {
      //the turn signal is being cancelled, because of a
      //secondary push to either button
      currTS = none;
    }
  }
}

引起后面司机的注意

至少闪烁刹车10次,以提醒他们你正在停车,然后保持刹车灯常亮。这是另一个计时器/计数器函数,用于确定刹车灯的状态。该函数获取当前的毫秒时间戳,如果刹车被按下,并且有一个用于闪烁的开/关值的指针,并返回我们是否仍处于闪烁状态。

bool isBrakeFlashTime(unsigned long curr, bool B,int *state){
  static unsigned long fut_time;
  static int flshcntr;
  static int flserstate;
    
  if (curr >=fut_time){
    flserstate = !flserstate;
    if(B && flserstate)flshcntr++;
    fut_time=curr+_flshSleapTime;  
  }
 
  *state = flserstate;
 
  if (!B)
    flshcntr = 0; //no brake, reset the counter
  else if(flshcntr <= _flshCntrMax)
    return true; //has brake and still has flashes

  return false; //fall though, not in flash
}

多功能刹车灯

刹车灯分为4个部分:外环、内环、左半部分、右半部分(后左转向灯、后左刹车、后右刹车和后右转向灯),因此通过这4个部分,我可以实现

  • 全亮刹车(所有灯都亮)
  • 示廓灯(外环亮)
  • 脉冲刹车(内环和外环来回闪烁)
  • 动画(稍后详细介绍)
  • 左转(RLTS闪烁,RRTS常亮)
  • 左转带刹车(RLTS闪烁,RRB和RRTS常亮)
  • 右转(RRTS闪烁,RLTS常亮)
  • 右转带刹车(RRTS闪烁,RLB和RLTS常亮)

动画转向灯“走动”灯光在所有4组之间向左或向右移动(除非刹车被按下),这有点炫耀,但它确实能引起注意。

byte updateRearTurnBits(unsigned long curr){
  static unsigned long fut_time;
  static byte scrcnt;
  static byte leftscroll;
  static byte rightscroll;
  if(curr>=fut_time){
    fut_time = curr+_scrollTime;
    scrcnt =++scrcnt % 4;
    leftscroll = (leftscroll<<1) | (!scrcnt & 1);
    rightscroll = (rightscroll >>1) | (!scrcnt<<7);
  }
  if(currTS == left)
    return leftscroll;//scroll from right to left
  else
    return rightscroll; //scroll from left to right
}

代码更新两个字节,一个向左移动,一个向右移动。每次fut_time超过时,它会更新行

scrcnt =++scrcnt % 4;

这基本上在第四次计数时将计数重置为0。我将其用作标记来翻转位!scrcnt,以及我正在寻找的位(左侧为0x1或0x80并将其推入每个序列以使其字节滚动)。然后根据当前的转向灯状态返回左侧滚动或右侧滚动。

循环

主循环可能是所有代码中最简单的

void loop() {
  //====reset the watchdog on each loop===
  wdt_reset();

  //=====Get the statuses=====
  bool B = isBrake();
  bool L = isLeftTurn();
  bool R = isRightTurn();
  setHiLowBeam();
 
  //====update the timers====
  unsigned long curr = millis();
  int blnk = isBlinkTime(curr);
  int rearflashstate;
  bool brkflsh = isBrakeFlashTime(curr,B,&rearflashstate);
  byte rtb = updateRearTurnBits(curr);
  CheckTurnSignals(curr,L,R);

//update all lights depending on the current state.
}

顶部块将重置看门狗定时器并获取输入的状态。第二部分将获取当前的毫秒时间并计算出其余的当前状态。我不会费心展示数字输出触发,因为它非常基础,但如果你想查看其余部分,请下载代码并尝试一下。

关注点

我想分享这个的主要原因之一是,当将迷你电脑添加到一辆老旧的、电子噪音很大的摩托车上时,会发生一些隐藏的陷阱,从线圈到触点都会产生大量的电磁干扰

  • 如果输入和输出离开项目盒的保护范围,请对它们进行光电隔离
  • 项目盒应该是金属的,并接地到底盘。
  • 在实际世界中,输入去抖动至关重要,在路上行驶时会发生许多误报。
  • 不要单独依赖Arduino稳压器,它们很好,但不足以与大量外部电路配合使用。放置一个前置稳压器7805,它能真正处理摩托车充电系统疯狂的调制波动。
  • 始终使用看门狗定时器。你可能会认为你的代码是完美的,但外部因素,如不良的电磁干扰,可能会使小型计算机锁定。看门狗至少会重启计算机并使其恢复运行,以便行车灯仍然可以工作。

我稍微修改的看门狗定时器代码来自https://forum.arduino.cc/index.php?topic=63651.0,作者为za_nic。

如果我对这个版本的硬件满意,它将再次返工,以排除Arduino板并使用单个ATmega 168A-PU芯片作为下一个挑战,因为我刚买了一堆,现在它们需要好好利用。

如果有人感兴趣,我也可以添加从Digi-key和Amazon购买的零件清单。现在zip文件中包含一个文本文件,其中列出了从Digi-Key和Amazon购买的所有零件(尽管其中一些是我的工作室库存)。 我将尽快抽出时间发布详细的电气图的PDF版本。请告诉我你的想法现在zip文件中包含完整的电气图,为PDF和PNG文件。

历史

  • 1.0:首次发布
  • 1.1:修复了拼写错误,并将零件文本文件添加到Bike_lights_v3b.zip文件
  • 1.2:将最终电气图添加到zip文件,并在文本中包含其片段
© . All rights reserved.