35、车辆遥测平台开发详解

车辆遥测平台开发详解

1. LCD 功能实现

在开发中,LiquidCrystal 库对于驱动并行 LCD 模块很方便,但在内存紧张时,可能需要自定义功能。以下是相关要点:

1.1 光标定位函数 lcd_gotoXY()

该函数用于将光标移动到指定位置,其实现代码如下:

void lcd_gotoXY(byte x, byte y)
{
  byte dr = 0x80 + x;
  if (y&1) dr+= 0x40;
  if (y&2) dr+= 0x14;
  lcd_commandWrite( dr );
}
  • 原理
    • 首先,将显示寄存器的基地址 0x80 与 X 位置相加,得到初始的显示寄存器地址。
    • 然后,根据 Y 值使用位运算符添加行偏移量。例如,若 Y 为 1,则添加 0x40 ;若 Y 为 2,则添加 0x14 ;若 Y 为 3,则两者都添加。
    • 最后,将计算得到的地址发送给 LCD。
  • 局限性 :该函数仅适用于不超过 20 列的显示器。
1.2 LCD 初始化函数 lcd_init()

初始化 LCD 需要一系列步骤,包括设置模式、定义自定义字符等,代码如下:

void lcd_init()
{
  delay(16);
  for(byte i=0; i<3; i++)
  {
    lcd_pushNibble(B00110000);
    lcd_commandWriteSet();
    delay(5);
  }
  lcd_pushNibble(B00100000);
  lcd_commandWriteSet();
  delay(1);
  lcd_commandWrite(B00101000);
  lcd_commandWrite(B00001100);
  lcd_commandWrite(B00000110);
  #define NB_CHAR  7
  lcd_commandWrite(B01001000);
  static prog_uchar chars[] PROGMEM ={
    B110000,B00000,B110000,B000110,B001111,B111111,B000110,
    B110000,B00000,B1101100,B001100,B0011011,B11011011,B001100,
    B1110011,B00000,B111000,B011000,B001111,B11011011,B011000,
    B000110,B00000,B1101100,B110000,B00000,B00000,B110000,
    B001100,B00000,B00000,B001100,B00000,B001100,B001111,
    B0110011,B1110111,B111111,B001100,B00000,B00000,B001100,
    B000011,B1110111,B11011011,B001111,B00000,B001100,B0011011,
    B000011,B1110111,B11011011,B0011011,B00000,B001100,B001111,
  };
  for(byte x=0;x<NB_CHAR;x++)
    for(byte y=0;y<8;y++)
      lcd_dataWrite(pgm_read_byte(&chars[y*NB_CHAR+x]));
  lcd_cls();
  lcd_commandWrite(B10000000);
}
  • 步骤
    1. 延迟 16 毫秒,让 LCD 有时间启动。
    2. 多次发送 B0011 到 LCD 数据线 7 到 4,设置 LCD 为 4 位模式。
    3. 最后一次发送 B0010
    4. 设置 LCD 为 4 位模式,使用 5×8 字体。
    5. 打开显示控制,隐藏光标,无闪烁。
    6. 设置输入模式为自动位置递增,无显示移位。
    7. 定义自定义字符,如 L/100 km/h 等。
    8. 将字符数据写入 LCD 控制器的字符生成 RAM。
    9. 发送“清屏”命令,并将 RAM 地址设置为 0。
2. GPS 功能实现

GPS 功能主要由 TinyGPS 库处理,相关代码位于 GPS.pde 文件中。

2.1 GPS 初始化函数 initGps()

该函数用于初始化 GPS 模块,代码如下:

void initGps()
{
  hostPrint(" * Initialising GPS             ");
  GPS.begin(GPS_BAUD_RATE);
  hostPrintLn("[OK]");
}
  • 步骤
    1. 向连接的主机发送初始化 GPS 的消息。
    2. 设置 GPS 模块的波特率。
    3. 发送初始化成功的消息。
2.2 处理 GPS 缓冲区函数 processGpsBuffer()

该函数用于从 GPS 串口缓冲区提取数据并喂给 TinyGPS 对象,代码如下:

bool processGpsBuffer()
{
  while (GPS.available())
  {
    if (gps.encode(GPS.read()))
      return true;
  }
  return false;
}
  • 步骤
    1. 检查 GPS 串口缓冲区是否有数据。
    2. 若有数据,则读取并使用 gps.encode() 进行解码。
    3. 若解码成功,则返回 true ;否则返回 false
2.3 GPS 数据转储函数 gpsdump()

该函数主要来自 TinyGPS 的示例代码,在开发过程中可用于查看 GPS 数据,正常运行时一般不调用。

3. VDIP 功能实现

VDIP 模块提供了 USB 大容量存储功能,相关代码位于 VDIP.pde 文件中。

3.1 VDIP 初始化函数 initVdip()

初始化 VDIP 模块需要设置引脚、复位模块并设置通信模式,代码如下:

void initVdip()
{
  hostPrint(" * Initialising VDIP            ");
  pinMode(VDIP_STATUS_LED, OUTPUT);
  digitalWrite(VDIP_STATUS_LED, HIGH);
  pinMode(VDIP_WRITE_LED, OUTPUT);
  digitalWrite(VDIP_WRITE_LED, LOW);
  pinMode(VDIP_RTS_PIN, INPUT);
  pinMode(VDIP_RESET, OUTPUT);
  digitalWrite(VDIP_RESET, LOW);
  digitalWrite(VDIP_STATUS_LED, HIGH);
  digitalWrite(VDIP_WRITE_LED, HIGH);
  delay( 100 );
  digitalWrite(VDIP_RESET, HIGH);
  delay( 100 );
  VDIP.begin(VDIP_BAUD_RATE);
  VDIP.print("IPA\r");
  VDIP.print(13, BYTE);
  digitalWrite(VDIP_WRITE_LED, LOW);
  hostPrintLn("[OK]");
}
  • 步骤
    1. 向连接的主机发送初始化 VDIP 的消息。
    2. 设置 VDIP 状态 LED、写入 LED、RTS 引脚和复位引脚的模式。
    3. 复位 VDIP 模块,延迟 100 毫秒。
    4. 打开与 VDIP 模块的串口连接,设置波特率。
    5. 发送 IPA 命令,将 VDIP 设置为 ASCII 通信模式。
    6. 发送初始化成功的消息。
3.2 处理 VDIP 缓冲区函数 processVdipBuffer()

该函数用于将 VDIP 模块的串口缓冲区中的数据传递给主机,代码如下:

void processVdipBuffer()
{
  byte incomingByte;
  while( VDIP.available() > 0 )
  {
    incomingByte = VDIP.read();
    if( incomingByte == 13 ) {
      HOST.println();
    }
    HOST.print( incomingByte, BYTE );
  }
}
  • 步骤
    1. 检查 VDIP 串口缓冲区是否有数据。
    2. 若有数据,则读取并发送给主机。
    3. 若读取到换行符( 13 ),则主机换行。
3.3 模式按钮中断服务函数 modeButton()

该函数用于处理“日志开/关”按钮的中断,代码如下:

void modeButton()
{
  if((millis() - logButtonTimestamp) > 300)  // debounce
  {
    logButtonTimestamp = millis();
    digitalWrite(LOG_LED, !digitalRead(LOG_LED));
  }
}
  • 步骤
    1. 检查自上次中断以来的时间是否超过 300 毫秒,以进行消抖处理。
    2. 若超过 300 毫秒,则更新时间戳。
    3. 切换日志 LED 的状态。
4. 主机命令处理

Host.pde 文件中的 processHostCommands() 函数用于处理来自主机的命令,代码如下:

void processHostCommands()
{
  if(logActive && !digitalRead(LOG_LED))
  {
    logActive = 0;
    digitalWrite(VDIP_STATUS_LED, LOW);
    VDIP.print("CLF OBDUINO.CSV\r");
    HOST.println("Stop logging");
  }
  else if( !logActive && digitalRead(LOG_LED))
  {
    logActive = 1;
    digitalWrite(VDIP_STATUS_LED, HIGH);
    VDIP.print("OPW OBDUINO.CSV\r");
    HOST.println("Start logging");
  }
  if( HOST.available() > 0)
  {
    char readChar = HOST.read();
    if(readChar == '1')
    {
      HOST.println("Start logging");
      logActive = 1;
      digitalWrite(VDIP_STATUS_LED, HIGH);
      digitalWrite(LOG_LED, HIGH);
      VDIP.print("OPW OBDUINO.CSV\r");
      HOST.print("> ");
    }
    else if( readChar == '2') {
      HOST.println("Stop logging");
      while(digitalRead(VDIP_RTS_PIN) == HIGH)
      {
        HOST.println("VDIP BUFFER FULL");
      }
      logActive = 0;
      digitalWrite(VDIP_STATUS_LED, LOW);
      digitalWrite(LOG_LED, LOW);
      VDIP.print("CLF OBDUINO.CSV\r");
      HOST.print("> ");
    }
    else if (readChar == '3'){
      HOST.println("Reading file");
      VDIP.print("RD OBDUINO.CSV\r");
      processVdipBuffer();
      HOST.print("> ");
    }
    else if (readChar == '4'){
      logActive = 0;
      digitalWrite(VDIP_STATUS_LED, LOW);
      digitalWrite(LOG_LED, LOW);
      VDIP.print("CLF OBDUINO.CSV");
      HOST.println("Deleting file");
      VDIP.print("DLF OBDUINO.CSV\r");
      HOST.print("> ");
    }
    else if (readChar == '5'){
      HOST.println("Directory listing");
      VDIP.print("DIR\r");
      HOST.print("> ");
    }
    else if (readChar == '6'){
      HOST.print(" * Initializing flash storage   ");
      pinMode(VDIP_RESET, OUTPUT);
      digitalWrite(VDIP_RESET, LOW);
      delay( 100 );
      digitalWrite(VDIP_RESET, HIGH);
      delay( 100 );
      VDIP.print("IPA");
      VDIP.print(13, BYTE);
      HOST.println("[OK]");
      HOST.print("> ");
    }
    else {
      HOST.print("Unrecognized command '");
      HOST.print(readChar);
      HOST.println("'");
      HOST.println("1 - Start logging");
      HOST.println("2 - Stop logging");
      HOST.println("3 - Display logfile");
      HOST.println("4 - Delete logfile");
      HOST.println("5 - Directory listing");
      HOST.println("6 - Reset VDIP module");
      HOST.print("> ");
    }
  }
}
  • 步骤
    1. 检查日志状态 LED 的状态变化,若有变化则相应地启动或停止日志记录。
    2. 检查主机串口缓冲区是否有命令。
    3. 根据读取到的命令字符执行相应的操作:
      • 1 :启动日志记录,打开 CSV 文件。
      • 2 :停止日志记录,关闭 CSV 文件。
      • 3 :读取日志文件内容并显示。
      • 4 :删除日志文件。
      • 5 :显示目录列表。
      • 6 :复位 VDIP 模块。
      • 其他:显示帮助信息。

以下是相关功能的流程图:

graph TD;
    A[开始] --> B[LCD 初始化];
    B --> C[GPS 初始化];
    C --> D[VDIP 初始化];
    D --> E[主循环];
    E --> F{有主机命令?};
    F -- 是 --> G{处理命令};
    F -- 否 --> H{处理 GPS 缓冲区};
    H -- 是 --> I{更新 GPS 数据};
    H -- 否 --> J{处理 VDIP 缓冲区};
    G --> E;
    I --> E;
    J --> E;
5. 辅助函数

Host.pde 文件中还定义了两个辅助函数 hostPrint() hostPrintLn() ,用于向主机发送消息,代码如下:

void hostPrint( char* message )
{
  #ifdef MEGA
  HOST.print(message);
  #endif
}

void hostPrintLn( char* message )
{
  #ifdef MEGA
  HOST.println(message);
  #endif
}

这些函数通过 #ifdef MEGA 检查,确保只有在为 Mega 目标构建时才会执行实际的打印操作。

综上所述,通过上述各个模块的实现,可以构建一个完整的车辆遥测平台,实现 LCD 显示、GPS 数据处理、USB 存储等功能,并通过主机命令进行控制。在实际开发中,需要注意各个函数的调用频率和缓冲区的处理,以确保系统的稳定性和可靠性。

车辆遥测平台开发详解(续)

6. 各模块功能总结与对比

为了更清晰地了解各个模块的功能和特点,下面通过表格进行总结对比:
| 模块 | 主要功能 | 关键函数 | 注意事项 |
| — | — | — | — |
| LCD | 驱动并行 LCD 模块,实现光标定位、初始化、自定义字符显示等功能 | lcd_gotoXY() lcd_init() | 光标定位函数在超过 20 列显示器可能不适用;初始化步骤较多,需严格按顺序执行 |
| GPS | 与 GPS 模块通信,处理 GPS 数据 | initGps() processGpsBuffer() gpsdump() | processGpsBuffer() 需频繁调用,防止串口缓冲区溢出 |
| VDIP | 实现 USB 大容量存储功能,处理模块初始化、数据传输和模式切换 | initVdip() processVdipBuffer() modeButton() | 初始化时需正确设置通信模式;处理大文件读取时可能出现缓冲区溢出问题 |
| 主机命令处理 | 处理来自主机的命令,控制日志记录、文件操作等 | processHostCommands() | 注意命令字符的识别和执行顺序,以及 VDIP 缓冲区状态检查 |

7. 实际应用中的优化建议

在实际应用车辆遥测平台时,可以考虑以下优化建议:
- 内存优化
- 对于 LCD 功能,若内存紧张,可继续采用自定义函数代替完整库的方式,减少内存占用。
- 合理使用全局变量和局部变量,避免不必要的内存浪费。
- 性能优化
- 增加 processGpsBuffer() processVdipBuffer() 函数的调用频率,确保数据及时处理,防止缓冲区溢出。
- 对于 VDIP 模块处理大文件读取的问题,可以增加缓冲区检查和通信超时机制,避免程序阻塞。
- 用户体验优化
- 完善主机命令的反馈信息,让用户更清楚操作结果。
- 优化自定义字符的显示效果,提高显示的清晰度和可读性。

8. 操作步骤总结

下面以启动日志记录为例,总结具体的操作步骤:
1. 确保系统已完成 LCD、GPS 和 VDIP 模块的初始化。
2. 通过主机向系统发送命令字符 1
3. 系统执行 processHostCommands() 函数,检查到命令字符为 1 后:
- 将 logActive 标志设置为 1
- 点亮 VDIP 状态 LED。
- 点亮日志 LED。
- 向 VDIP 模块发送 OPW OBDUINO.CSV\r 命令,打开 CSV 文件。
- 向主机发送 Start logging 消息,并输出 > 提示符。

9. 未来扩展方向

可以根据实际需求对车辆遥测平台进行扩展,以下是一些可能的扩展方向:
- 传感器扩展 :增加更多类型的传感器,如加速度传感器、温度传感器等,获取更全面的车辆数据。
- 通信方式扩展 :支持更多的通信协议,如 Wi-Fi、蓝牙等,方便数据的远程传输和共享。
- 数据分析功能 :在本地或远程服务器上对采集到的数据进行分析,提供更有价值的信息,如车辆行驶习惯分析、故障预警等。

10. 总结

通过对车辆遥测平台各个模块的详细介绍,包括 LCD 功能实现、GPS 数据处理、VDIP 模块操作和主机命令处理等,我们了解了如何构建一个完整的车辆遥测系统。在开发过程中,需要注意各个模块的初始化步骤、函数调用频率和缓冲区处理,以确保系统的稳定性和可靠性。同时,通过优化建议和扩展方向的探讨,可以进一步提升平台的性能和功能,满足不同用户的需求。

以下是一个扩展功能的流程图,展示了添加新传感器后的系统流程:

graph TD;
    A[开始] --> B[LCD 初始化];
    B --> C[GPS 初始化];
    C --> D[VDIP 初始化];
    D --> E[新传感器初始化];
    E --> F[主循环];
    F --> G{有主机命令?};
    G -- 是 --> H{处理命令};
    G -- 否 --> I{处理 GPS 缓冲区};
    I -- 是 --> J{更新 GPS 数据};
    I -- 否 --> K{处理新传感器数据};
    K -- 是 --> L{更新传感器数据};
    K -- 否 --> M{处理 VDIP 缓冲区};
    H --> F;
    J --> F;
    L --> F;
    M --> F;

通过以上内容,我们对车辆遥测平台的开发有了更深入的理解,希望能为相关开发工作提供有价值的参考。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值