亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

汽车 HUD - 挡风玻璃显示速度和指南针

组件和用品

Arduino Nano R3
× 1

关于这个项目

说明:

我想要! 我一直很反感我朋友塔吉的大车在挡风玻璃上显示速度。我必须拥有它,我当然必须自己做!

软件观点:

投影电路最困难和最令人困惑的部分是将 7 段显示器连接到 BCD 解码器,因为数字是“反转的” (镜像)。

有 3 个按钮 :+ 和 - 用于增加/减少亮度和 S/H 以在 以公里/小时为单位的速度和以度为单位的航向之间切换;在 Heading 的情况下,红色 LED 也亮起,表示移动时罗盘的“度数”(1-360°)。它不是电子罗盘,而是 GPS,您需要移动才能获得正确的移动方向信息。 亮度保存在EEPROM中 一分钟后记忆。通过PWM引脚改变显示器和LED的亮度。

代码的一个重要部分是从 GPS 收集数据,主要是速度和方向,从每个 NMEA 语句中取出它们。即使使用String类,主要用于Serial NMEA语句操作,整个阐述流程也稳定扎实;它使用“serialEvent()”来每秒从 GPS 接收一次数据 , 然后调用“nmeaExtractData()”,最后用“nmea0183_checksum()”检查数据包以确保数据完整性。如果您使用其他品牌和型号的 GPS,请确保句子具有相同的结构,否则您必须在此处进行一些更改. 例如EM406A使用“$GPRMC”数据包ID,BT220使用“$GNRMC” 相反...只是一个小的名称更改...一个有用的链接可以帮助您进行校验和测试:https://nmeachecksum.eqth.net - 这是一个完整的 NMEA 句子的示例,它包含:id、时间、有效性、纬度、经度、速度、真实航向、日期、变化和校验和。

$GNRMC, 095836.000, A, 4551.9676, N, 01328.7118, E, 2.09, 341.84, 280519,, *08

该草图一次为每个显示 BCD 解码器启用锁存器,在二进制 4 位总线上设置代码编号,禁用锁存器,等等。左侧不重要的零被消隐(不显示)。

在加载新草图之前 单片机 记得取出跳线 :它连接到 Arduino 的 Rx 引脚,在加载过程中肯定会与 GPS Tx 冲突。软件加载后再次放置跳线以恢复正常功能。

组件列表:

  • 1 x MCU Arduino Nano
  • 3 x 5161as 七段显示器,共阴极,红色
  • 1 个北天 BN-220 串行 TTL GPS(1 Hz GNRMC 语句)
  • 1 个跳线
  • 3 x 按钮(常开)+ 3 x 大写
  • 22 个 1/4W 220 欧姆电阻器
  • 1 x 3 毫米 LED,红色
  • 2 个 100n 电容器
  • 3 x 14511 BCD 解码器 + 锁存器
  • 1 个 USB "B" 母头
  • 1 x 带状线 2x 公针(用于跳线)
  • 1 x 带状线 4x 公针弯曲至 90°(用于 GPS)
  • 1 x 尾纤电缆将 GPS 连接到板载 4 针
  • 22 个带状线公针,用于将两个 PCB 连接在一起
  • 1 x 双面胶,用于将 GPS 粘贴在 PCB 上
  • 6 x 旋转带状线 5 x 母插针(用于显示器)
  • 50 个 0.6 毫米铜铆钉
  • 8 个 M3 螺丝
  • 4 x M3 母塔,高 20 毫米
  • 1 x 塑料盒 + 盖子(看看我准备打印的 3D 文件,如下)

PCB(印刷电路板):

我使用了两个双面 PCB 出于这个原因,大约 50 个铆钉或销钉用于解决整个电路的布线。还有5个对齐孔 先做。我在每块 PCB 上设计了这 5 个对齐点。在下载部分,您拥有镜像的所有 PCB 文件、组件和焊接面,可通过激光打印机在“黄色”或“蓝色”纸张上下载和打印;这次我用的是蓝色的,黄色的也不错,价格也低。我不得不说蓝纸更好...打印时记得禁用省墨设置,而是使用 1200 dpi 分辨率以获得深黑色效果。墨粉从魔术贴转移到 PCB 的过程是通过使用热铁完成的......在网上有一些教程展示了如何生产一个好的 PCB 但请记住这些要点:完美清洁并用厨房红海绵轻轻刷铜,熨烫5分钟,水热冲击,两个面通过5个孔对齐(我在一个大的白色LED表面上用了5个针看孔),在腐蚀过程中保护另一面。打印组件面也使项目“专业”:-)

注意: 图片是在最后一个版本之前拍摄的,这意味着一些细节可能有所不同:即最终版本中 PCB 周围奇怪的绿线消失了,或者跳线已从 PCB #1 移动到 PCB #2。您应该将 GPS 设置为 9600 bps 速度,1Hz GNRMC NMEA 语句;这可以使用它自己的设置软件来完成。最后,在焊接 USB 母连接器之前,在其下方粘贴一小块绝缘胶带,以避免与铜线发生不必要的接触,在组件侧,就在其下方。

第一次给电你会发现数字是“不可读的”,因为它是用来看看它们反映在挡风玻璃(镜子)上的。 安装在车内驾驶员座椅前部 ,找个舒服的地方修好。我建议连接USB电源线 在点火位置之后,因此它会在发动机开/关时打开/关闭。女士们,先生们!

新闻和改进:

  • 25.03.2021:为了完成这个项目,我准备了两个 3D .STL 模型 在此链接:https://grabcad.com/library/car-hud-1,您可以在其中下载有关该项目的形状盒子及其盖板的文件,以通过 3D 打印机进行打印。
  • 20.07.2021:有 V2(第二版) 在软件中,连同两根电线和一个电阻器,您将在显示屏上显示千米(即 0.89 =890m)的高度信息,只需再按一次 S/H 按钮!使用说明: 1)设置GPS通过自带软件的方式也输出$GNGGA nmea语句; 2)切断d5 Arduino pwm引脚与电路其余部分的实际连接,用220ohm电阻将其连接到中央显示十进制点引脚; 3) 将 d13 Arduino 数字引脚连接到连接 d5 的电路;看看下面的几张照片; 4) 在您的 Arduino Nano 上安装 V2 草图。
  • 01.11.2021:V2.2 版本已准备就绪 为你。有什么新的?关于高度(V2 已经存在),hud 显示的全值最高可达 999m,从 1000m 开始,它以千为单位显示值,即 1.24 (1240m) 或 2.02 (2020m)。我添加了一个 LDR 光敏电阻 GL5539、一个 10kOhm 电阻和 2 根电线。查看下面的图片了解 V2.2 mods 并在您的 Arduino 上安装 V2.2 草图。 LDR 传感器将根据光线水平(白天、阴天或夜晚)自动修改亮度(3 个级别)。按钮(+)和(-)仍在手动修改亮度,它们有优先权;要返回自动,只需同时按下 (+) 和 (-) 按钮一段时间。不再使用用于存储亮度级别的内部 EEPROM。先做 V2 模组吧!

开车时,就开车。安全驾驶!

代码

  • Car-HUD Arduino 草图
Car-HUD Arduino 草图Arduino
/* 此草图充当汽车挡风玻璃 HUD(平视显示器),作者 Marco Zonca,10/2020 Arduino Nano 作为 CPU,GPS BT-220 nmea 每 1 秒,3 个按钮,3 x 七段显示器共阴极, 3 x 14511 BCD 锁存解码器,MPU EEPROM 存储器(1 字节)和许多电阻器;警告:========在更新软件之前,通过 JUMPER*/#include #include String inputString ="" 断开 Arduino 上的 RX 引脚(TX 与 GPS);String nm_time ="00:00:00";String nm_validity ="V";String nm_latitude ="dd°mm.mmmm'N";String nm_longitude ="ddd°mm.mmmm'E";String nm_knots ="0.0 kn";float nmf_knots =0.0;float nmf_kmh =0.0;int nmi_kmh =0;String nm_truecourse ="360";float nmf_truecourse =360;String nm_date ="dd/mm/yyyy";int nmi_truecourse =0;byte kCent =0;字节kDeci =0;字节kUnit =0;字节tCent =0;字节tDeci =0;字节tUnit =0;字节亮度=120;字节latch_off =HIGH;字节latch_on =LOW;int n=0;unsigned long lastmemcheck =0;unsigned long memcheck =60000; // 每 60 秒检查一次将“亮度”值保存在 EEPROM 中bool stringComplete =false;bool isKMH=true;bool ret =false;const int disp001 =2; //单位显示latchconst int disp010 =8; // 十位显示latchconst int disp100 =12; // unreds 显示latchconst int disp001dim =9; // 单位显示调光器/关闭 pinconst int disp010dim =10; // 十位显示调光器/关闭 pinconst int disp100dim =11; // unreds 显示调光器/关闭 pinconst int button_kt =14; // kmh/truecourse buttonconst int button_more =15; // 亮度 + buttonconst int button_less =16; // 亮度 - buttonconst int degreeLED =3; // 度数 LEDconst int bit_3 =7; // 位 3const int bit_2 =6; // 位 2const int bit_1 =5; // 位 1const int bit_0 =4; // 位 0const int dly =10; // 延迟锁存 m/secconst 字节 off =0; // 等同于brightness=0const int addr =0; // 亮度值的 EEPROM 地址const 字节数[10] [4] ={{0,0,0,0},{1,0,0,0},{0,1,0,0},{1, 1,0,0},{0,0,1,0}, {1,0,1,0},{0,1,1,0},{1,1,1,0},{0, 0,0,1},{1,0,0,1}}; // 位 0,1,2,3void setup() { Serial.begin(9600); Wire.begin(); inputString.reserve(200);亮度 =EEPROM.read(addr);如果(亮度> 250 || 亮度<10){ 亮度=120; } // 避免第一次 EEPROM 读取的疯狂值 pinMode(disp001, OUTPUT); pinMode(disp010, 输出); pinMode(disp100, 输出); pinMode(度数LED,输出); pinMode(button_kt,INPUT_PULLUP); pinMode(button_less, INPUT_PULLUP); pinMode(button_more, INPUT_PULLUP); pinMode(bit_3, 输出); pinMode(bit_2, 输出); pinMode(bit_1, 输出); pinMode(bit_0, 输出);模拟写入(disp001dim,关闭); //关闭和零显示analogWrite(disp010dim, off);模拟写入(disp100dim,关闭);模拟写入(度LED,关闭); setBusNr(0);数字写入(disp001,latch_on);数字写入(disp010,latch_on);数字写入(disp100,latch_on);延迟(dly);数字写入(disp001,latch_off);数字写入(disp010,latch_off);数字写入(disp100,latch_off);模拟写入(disp001dim,亮度); // 显示模拟写入(disp010dim,亮度);模拟写入(disp100dim,亮度);} // setup()void loop() { // GPS NMEA ------------------ if (stringComplete ==true) { //串口接收到 nmea 语句 RX ret =nmeaExtractData(); inputString =""; stringComplete =假; if (ret ==true) { kCent=nmi_kmh/100; n=nmi_kmh-(kCent*100); kDeci=n/10; n=nmi_kmh-(kCent*100)-(kDeci*10); kUnit=n; tCent=nmi_truecourse/100; n=nmi_truecourse-(tCent*100); tDeci=n/10; n=nmi_truecourse-(tCent*100)-(tDeci*10); tUnit=n;展示(); } } if (millis()> (lastmemcheck+memcheck)) { // 放入内存亮度值(如果修改) EEPROM.update(addr,brightness); lastmemcheck=millis(); } checkButtons();}void display() { if (isKMH ==true) { // 以公里/小时为单位的速度 (isKMH=true)analogWrite(degreesLED, off); setBusNr(kUnit);数字写入(disp001,latch_on);延迟(dly);数字写入(disp001,latch_off); if (kDeci> 0 || kCent> 0) { // 如果tens=0(以及undreds=0)setBusNr(kDeci);数字写入(disp010,latch_on);延迟(dly);数字写入(disp010,latch_off);模拟写入(disp010dim,亮度); } else {analogWrite(disp010dim, off); } if (kCent> 0) { // 设置 undreds off if=0 setBusNr(kCent);数字写入(disp100,latch_on);延迟(dly);数字写入(disp100,latch_off);模拟写入(显示100dim,亮度); } else {analogWrite(disp100dim, off); } } else { // 以度为单位的真实方位(isKMH=false)analogWrite(degreesLED,brightness); setBusNr(tUnit);数字写入(disp001,latch_on);延迟(dly);数字写入(disp001,latch_off); if (tDeci> 0 || tCent> 0) { // 如果tens=0(并且undreds=0)setBusNr(tDeci);数字写入(disp010,latch_on);延迟(dly);数字写入(disp010,latch_off);模拟写入(disp010dim,亮度); } else {analogWrite(disp010dim, off); } if (tCent> 0) { // 设置 undreds off if=0 setBusNr(tCent);数字写入(disp100,latch_on);延迟(dly);数字写入(disp100,latch_off);模拟写入(显示100dim,亮度); } else {analogWrite(disp100dim, off); } }} // display()void checkButtons(){ if (digitalRead(button_kt) ==LOW) { if (isKMH ==true) { isKMH=false; } else { isKMH=true; } 延迟(250); } if (digitalRead(button_more) ==LOW) { if (brightness <=240) { 亮度=brightness+10;模拟写入(disp001dim,亮度);模拟写入(disp010dim,亮度);模拟写入(显示100dim,亮度);延迟(100); } if (digitalRead(button_less) ==LOW) { if (brightness>=20) { Brightness=brightness-10;模拟写入(disp001dim,亮度);模拟写入(disp010dim,亮度);模拟写入(显示100dim,亮度);延迟(100); }} // checkButtons()void setBusNr(int number) { // 设置 4 位总线 for (byte b=0; b<=3; b++) { if (numbers[number][b]==0) { if (b==0) {digitalWrite(bit_0, LOW);} if (b==1) {digitalWrite(bit_1, LOW);} if (b==2) {digitalWrite(bit_2, LOW);} if (b ==3) {digitalWrite(bit_3, LOW);} } else { if (b==0) {digitalWrite(bit_0, HIGH);} if (b==1) {digitalWrite(bit_1, HIGH);} if ( b==2) {digitalWrite(bit_2, HIGH);} if (b==3) {digitalWrite(bit_3, HIGH);} } }} // setBusNr()//从nmea中提取数据 inputStringbool nmeaExtractData() { int d=0;整数 s=0; int y=0;整数 z=0;浮动 t=0; bool ret =假; //true if nmea sentence =$GNRMC 和有效的CHKSUM if ((inputString.substring(0,6) =="$GNRMC") &&(inputString.substring(inputString.length()-4,inputString.length()- 2) ==nmea0183_checksum(inputString))) { y=0; for (s =1; s <11; s ++) { y=inputString.indexOf(",",y); switch (s) { case 1://----------------------time z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_time=inputString.substring(y+1,y+2+1)+":"+inputString.substring(y+1+2,y+4+1)+" :"+inputString.substring(y+1+4,y+6+1); y=z;休息; case 2://---------------------- 有效性 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_validity=inputString.substring(y+1,y+1+1); y=z;休息; case 3://------------------------纬度 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_latitude=inputString.substring(y+1,y+2+1)+"°"+inputString.substring(y+1+2,y+10+1)+" '"; y=z;休息;情况 4://--------------北/南 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_latitude=nm_latitude + inputString.substring(y+1,y+1+1); y=z;休息; case 5://----------------------longitude z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_longitude=inputString.substring(y+1,y+3+1)+"°"+inputString.substring(y+1+3,y+11+1)+" '"; y=z;休息; case 6://----------------------东/西 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_longitude=nm_longitude + inputString.substring(y+1,y+1+1); y=z;休息; case 7://------------------------速度节 z=inputString.indexOf(",",y+1); if (z>(y+1)) { nmf_knots=inputString.substring(y+1,z).toFloat(); t=roundOneDec(nmf_knots); nm_knots=String(t,1)+"kn"; nmf_kmh=roundTwoDec(nmf_knots * 1.852); nmi_kmh=roundZeroDec(nmf_knots * 1.852); y=z;休息; case 8://----------------------true course z=inputString.indexOf(",",y+1); if (z>(y+1)) { nmf_truecourse=inputString.substring(y+1,z).toFloat(); t=roundZeroDec(nmf_truecourse); nmi_truecourse=t; d=t; nm_truecourse=d; y=z;休息; case 9://----------------------date z=inputString.indexOf(",",y+1); if (z>(y+1)) { nm_date=inputString.substring(y+1,y+2+1)+"/"+inputString.substring(y+1+2,y+4+1)+" /20"+inputString.substring(y+1+4,y+6+1); y=z;休息; case 10:// 语句 n.u.休息;默认值:// 语句 n.u.休息; } } ret=true; } return ret;} // nmeaExtractData()/* SerialEvent 每当有新数据进入硬件串行 RX 时就会发生。该例程在每次 loop() 运行之间运行,因此在循环内使用延迟可以延迟响应。可能有多个字节的数据可用。*/void serialEvent() { while (Serial.available()) { char inChar =(char)Serial.read(); inputString +=inChar; // 如果传入的字符是换行符,则设置一个标志,以便主循环可以 // 对此进行处理 if (inChar =='\n') { stringComplete =true; } }} // serialEvent()//计算nmea的校验和语句String nmea0183_checksum(String nmea_data) { int crc =0; String chSumString ="";国际我; // 忽略第一个 $ 符号,句子中的校验和 for (i =1; i <(nmea_data.length()-5); i ++) { // 如果没有 "*" + cksum + cr +,则删除 - 5如果存在 crc ^=nmea_data[i]; } chSumString =String(crc,HEX); if (chSumString.length()==1) { chSumString="0"+chSumString.substring(0,1); chSumString.toUpperCase(); return chSumString;} // nmea0183_checksum(String nmea_data)// 舍入零小数float roundZeroDec(float f) { float y, d; y =f*1; d =y - (int)y; y =(float)(int)(f*1)/1;如果 (d>=0.5) { y +=1; } else { if (d <-0.5) { y -=1; } } return y;}// 舍入一位十进制float roundOneDec(float f) { float y, d; y =f*10; d =y - (int)y; y =(float)(int)(f*10)/10;如果 (d>=0.5) { y +=0.1; } else { if (d <-0.5) { y -=0.1; } } return y;}// 舍入两位小数float roundTwoDec(float f) { float y, d; y =f*100; d =y - (int)y; y =(float)(int)(f*100)/100;如果 (d>=0.5) { y +=0.01; } else { if (d <-0.5) { y -=0.01; } } 返回y;}

示意图

car-hud_m5RwPQqpxH.fzz ard-carhud-v2_3N5756haNI.ino ard-carhud-v2-2_XhXHFJu0T8.ino

制造工艺

  1. 为残疾司机拆解半自动驾驶汽车
  2. 驴车垃圾收集器的自动驾驶 AI
  3. 模拟城市环境下自动驾驶汽车在多种情况下的实时运动规划
  4. 5G 可以为联网汽车提供什么?
  5. 安全:未来汽车的重中之重
  6. 用于变速驱动器的电机电缆
  7. 用于抛光和去毛刺的增强型车床
  8. 专为精度和速度而设计的车床
  9. 产品数据交换:汽车厂商的痛点
  10. 如何为您的汽车选择涡轮增压器?
  11. 我的车装箱后漏油是什么原因?
  12. 第一次买车险怎么办?