Funpack - S4 - Teensy - 模拟赛车速度指示器(RacingSpeed)
该项目使用了Teensy 4.1,实现了模拟赛车时速仪表的设计,它的主要功能为:实时显示赛车的车速信息,便于车手更好的驾驶赛车。
标签
Funpack活动
模拟赛车
Teensy
仪表
arilink
更新2025-01-13
22

项目背景

首先由衷的祝愿电子森林发展的越来越好,自从参与Funpack项目以来,接触了越来越多的开发板,也拓宽了很多知识面,所以无需犹豫,果断参与第四期。

第四期的主题是点灯,但考虑到I.MXRT1062这么丰富的外设,只点个灯确实有点浪费,于是尝试用Teensy驱动屏幕,制作一个模拟赛车的仪表,显示赛车的实时速度信息。

项目详情

Teensy4.1(以下简称Teensy)使用I.MXRT1062作为主控,主控USB上位机进行通讯,上位机通过UDP协议获取赛车游戏中车辆的遥测数据,并通过USB HID发给Teensy,Teensy上的I.MXRT1062通过SPI驱动LCD,并根据遥测数据中的车辆速度更新屏幕上的内容,项目框图如下:

IMG-Funpack 4 - Teensy-20241203160826.png

设计思路

  1. 上位机通过UDP与游戏建立通讯,获取到游戏中赛车的实时遥测数据
  2. Teensy有USB接口,可以连接到上位机
  3. 上位机和Teensy之间通过USB HID通讯,并传递遥测数据
  4. Teensy的SPI接口可以驱动GC9A01A的圆形屏幕,并根据遥测数据更新屏幕上显示的内容

硬件介绍

芯片介绍

  • IMG-Funpack 4 - Teensy-20241203164333.png
  • ARM Cortex-M7 at 600 MHz
  • Float point math unit, 64 & 32 bits
  • 7936K Flash, 1024K RAM (512K tightly coupled), 4K EEPROM (emulated)
  • QSPI memory expansion, locations for 2 extra RAM or Flash chips
  • USB device 480 Mbit/sec & USB host 480 Mbit/sec
  • 55 digital input/output pins, 35 PWM output pins
  • 18 analog input pins
  • 8 serial, 3 SPI, 3 I2C ports
  • 2 I2S/TDM and 1 S/PDIF digital audio port
  • 3 CAN Bus (1 with CAN FD)
  • 1 SDIO (4 bit) native SD Card port
  • Ethernet 10/100 Mbit with DP83825 PHY
  • 32 general purpose DMA channels
  • Cryptographic Acceleration & Random Number Generator
  • RTC for date/time
  • Programmable FlexIO
  • Pixel Processing Pipeline
  • Peripheral cross triggering
  • Power On/Off management

开发板介绍

IMG-Funpack 4 - Teensy-20241203164146.png

IMG-Funpack 4 - Teensy-20241203164448.png

没有获取到PCB图纸,不过肉眼看起来布局规整,整体小巧

软件流程

IMG-Funpack 4 - Teensy-20241203173154.png

如上图所示,Teensy上电后首先启动USB,通过代码里配置的端点与上位机建立通讯,然后通过SPI驱动LCD显示,在while循环里等待上位机发送遥测数据,收到遥测数据后解析提取需要的速度信息,根据速度信息更新LCD的显示内容,从而完成模拟赛车仪表的开发。

开发流程

开发流程主要包括USB HID通讯建立、屏幕驱动显示、延迟优化三个部分,具体内容如下:

USB HID通讯建立

HID通讯的代码可以参考官方示例,示例配置足够本项目使用,因此不做调整
Teensy通过USB连接到电脑后,可以使用USBLyazer查找到具体的配置信息。

IMG-Funpack 4 - Teensy-20241129113717.png

使用BUSHound可以确定具体用来通讯的IN OUT端点

IMG-Funpack 4 - Teensy-20241129115041.png

官方示例中的USB是复合了RawHID和UART两个设备,其中UART用于终端信息显示

IMG-Funpack 4 - Teensy-20241129114022.png

使用BUS Hound对OUT端点发送数据,可以Capture到 并且Teensy终端可以正常输出收到的信息,上位机就通过此端点向Teensy传输遥测数据


IMG-Funpack 4 - Teensy-20241129114557.png

HID部分的代码官方已经帮我们配置好了 只需要在Arduino的IDE里面选择对应的配置即可

IMG-Funpack 4 - Teensy-20241207180212.png

HID消息接收部分的代码如下:

void loop() {
  int n;
  uint16_t speed;
  n = RawHID.recv(buffer, 0); // 0 timeout = do not wait
  if (n > 0) {
    // the computer sent a message.  Display the bits
    // of the first byte on pin 0 to 7.  Ignore the
    // other 63 bytes!
    // Serial.print(F("Received packet, first byte: "));
    // Serial.println(buffer[0],HEX);
    // Serial.print(F("Received packet, Second byte: "));
    // Serial.println(buffer[1],HEX);
    speed = (buffer[0]<<8)+(buffer[1]);
    if(last_speed!=speed)
    {
      updateSpeed(speed);
      last_speed = speed;
    }
    // Serial.print(F("Speed:"));
    // Serial.println(speed,DEC);
  }
}

屏幕显示

本项目使用的屏幕是GC9A01A,需要用SPI进行通讯,Arduino有丰富的插件库,经过测试筛选,发现Adafruit的GFX和GC9A01A库可以驱动该屏幕,Teensy和屏幕的连接方式如下:


/* Hardware Connect2

Teensy4.1 TFT-LCD3

7 ---- BLK4

NC ---- RES5

9 ---- DC6

10 ---- CS7

13 ---- SCL8

11 ---- SDA9

GND ---- GND10

3V ---- VCC

*/

LCD驱动代码如下:

#define TFT_CS 10 // Chip select
#define TFT_DC 9 // Data/command
#define TFT_BL 7 // Backlight control
Adafruit_GC9A01A tft(TFT_CS, TFT_DC);
void setup() {
  Serial.begin(9600);
  Serial.println(F("RawHID Example"));
  tft.begin();
#if defined(TFT_BL)
  pinMode(TFT_BL, OUTPUT);
  digitalWrite(TFT_BL, HIGH); // Backlight on
#endif // end TFT_BL


  Serial.println(F("Benchmark                Time (microseconds)"));
  delay(10);
  Serial.print(F("Screen fill              "));
  Serial.println(testFillScreen());
  delay(500);


  tft.setRotation(-90);
  Serial.print(F("Text                     "));
  racingSpeedInit();
  delay(3000);
}

延迟优化

当要显示的内容有变化时,因为没有移植类似LVGL的显示框架来实现只针对变化的区域更新,如果采用默认的方式进行整屏擦除再写入,整个显示内容的更新会非常缓慢,整体的刷新率很低,给用户的直观感觉就是卡顿和延迟。

// //V1版本 延迟很高
// unsigned long updateSpeed(unsigned int speed) {
//   //tft.fillScreen(GC9A01A_BLACK);
//   unsigned long start = micros();
//   tft.setCursor(80, 50);
//   tft.setTextColor(GC9A01A_YELLOW); tft.setTextSize(3);
//   tft.println("SPEED:");


//   tft.setCursor(30, 90);  //Clear Last Speed
//   tft.setTextColor(GC9A01A_BLACK); tft.setTextSize(10);
//   tft.println("   ");
//   if(speed<10)
//   {
//     tft.setCursor(100, 90);  //个位数时速x:30  两位数时速x:100
//   }
//   else if((speed>=10)&&(speed<100))
//   {
//     tft.setCursor(70, 90);  //十位数时速x:30  两位数时速x:70
//   }
//   else
//   {
//     tft.setCursor(30, 90);  //百位数时速x:30  两位数时速x:30
//   }
//   tft.setTextColor(GC9A01A_YELLOW); tft.setTextSize(10);
//   tft.println(speed);
//   return micros() - start;
// }

再次基础上进行优化,取消整屏擦除,只是对上一次更新的区域进行擦除,然后再进行新一轮内容的更新,例如上一次的速度是100Km/h,本次更新的时候先用黑色把上次的100覆盖掉,再写入本次速度,这样就能显著提升内容更新效率,显著降低延迟。

//V2版本 显著降低延迟
unsigned long updateSpeed(unsigned int speed) {
  unsigned long start = micros();
  if(disp_update_cnt>1)
  {
    if(last_disp_speed<10)
    {
      tft.setCursor(100, 90);  //个位数时速x:30  两位数时速x:100
    }
    else if((last_disp_speed>=10)&&(last_disp_speed<100))
    {
      tft.setCursor(70, 90);  //十位数时速x:30  两位数时速x:70
    }
    else
    {
      tft.setCursor(30, 90);  //百位数时速x:30  两位数时速x:30
    }
    tft.setTextColor(GC9A01A_BLACK); tft.setTextSize(10);
    tft.println(last_disp_speed);
  }


  if(speed<10)
  {
    tft.setCursor(100, 90);  //个位数时速x:30  两位数时速x:100
  }
  else if((speed>=10)&&(speed<100))
  {
    tft.setCursor(70, 90);  //十位数时速x:30  两位数时速x:70
  }
  else
  {
    tft.setCursor(30, 90);  //百位数时速x:30  两位数时速x:30
  }
  tft.setTextColor(GC9A01A_YELLOW); tft.setTextSize(10);
  tft.println(speed);
  last_disp_speed = speed;
  disp_update_cnt++;
  return micros() - start;
}

当然了,上面的代码可以进一步优化,比如上次速度是108本次速度是102,那只需对个位进行擦除重新写入即可,在这里就只提供思路了。

项目展示

项目演示视频已上传到B站,链接如下:
项目演示

附件下载
Racing_Speed.rar
RacingSpeed源码
团队介绍
self
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号