建设中… What410802 Site

数字设计与计算机原理专题实验|基于树莓派的嵌入式系统综合项目 实验报告#

项目名称#

车辆传感器(智能小车)

设计功能描述#

  1. 自动大灯:通过光敏传感器获取环境亮度信息,当环境亮度低于某一范围后,点亮LED灯珠;
  2. 自动雨刷:通过雨滴传感器获取是否有雨水,当雨滴传感器面板有水时,在LCD面板上显示“Raining”字样;
  3. 障碍物报警:通过超声波传感器获取物体距离信息,通过蜂鸣器报警,并且根据距离不同报警声音间隔不同;
  4. 行驶里程计量:通过旋转编码器传感器获取转动角度,在LCD面板上实时显示转动情况以及总里程数;
  5. 姿态感知:通过陀螺仪加速度传感器获取x、y、z三个轴向的加速度数据或者姿态角度数据,在晃动或侧翻时利用发光二极管报警;
  6. 按键控制:通过旋转编码器自带按钮实现两种LCD屏显(环境传感器数据和里程计)之间的切换;
  7. 车载遥控音乐播放器:通过遥控器按键控制无源蜂鸣器进行音乐播放、暂停与切换。在播放时自动暂停有源蜂鸣器的距离报警以避免噪音;停止后恢复。

实现步骤#

  1. 硬件规划:概览需要完成的功能;进行管脚分配(如图1所示)。
  2. 软件准备:远程桌面。
  3. 程序编写及调试:各硬件驱动包含及初始化;中断处理;主循环(传感器数据更新;简单输出外设更新;复杂外设检测是否到达更新间隔并适时更新)。
接口规划
图1 接口规划

每个功能的实现方法与结果#

  1. 自动大灯(光敏传感器+AD->3色LED):通过光敏传感器获取环境亮度(越大越暗),控制LED亮度正相关于环境暗度。由于蓝色看起来更显眼,故适当调低蓝分量占比。
  2. 自动雨刷(雨滴传感器+AD->LCD):当雨滴传感器数值低于设定值时,在LCD面板上显示“Raining”;否则显示“No rain”。
  3. 障碍物报警(超声测距->有源蜂鸣器/LCD):通过超声波传感器获取物体距离信息(float类型),据此设置蜂鸣器变化周期;蜂鸣器端则借助定时器,在半周期后切换状态。注意低电平触发,以及后期的与音乐播放避让。
  4. 行驶里程计量:通过旋转编码器计数正、反转,判断正反转通过记录上一统计周期结束时的总里程并与当前周期末相比较来完成。旋转编码器原理是在每2个相邻机械位置之间,有2组交错的触点对,当转子转过一个周期(从一个机械位置转到相邻的一个)时,触点对A和触点对B导通状况依次经历了00-01-11-10-00状态。因此,设置中断为两个信号的上升、下降沿(一周期共4次触发)并与上次触发时的状况对比即可完成记录。
  5. 姿态感知(陀螺仪->红绿双色LED):通过陀螺仪加速度传感器获取z轴向的加速度数据。若小于设定值,则判断为侧翻,点亮红灯;若大于等于此值但小于更大的一个给定值,则点亮黄灯(红绿按比例混合);否则亮绿灯。
  6. 按键控制:检测按钮是否按下,实现两种LCD屏显(环境传感器数据和里程计)之间的切换;
  7. 车载遥控音乐播放器:通过遥控器按键控制无源蜂鸣器进行音乐播放、暂停与切换。在播放时自动暂停有源蜂鸣器的距离报警以避免噪音;停止后恢复。

实现本任务需要用到的软硬件功能模块;各模块端口配置参数#

  • 模数转换模块:

    #define	PCF 	        120
    ...
    pcf8591Setup(PCF, 0x48);
    c

    硬件按照指导教材连线即可,无冲突。

  • 光敏传感器(模拟):SIG连至PCF8591的AIN0。使用时读取analogRead(PCF)

  • 雨滴传感器(模拟):DO连至G12(这个在本实验中用处不大),AO连至AIN1。使用时读取analogRead(PCF+1)

  • 陀螺仪:硬件按照教材连线。使用时按照教材例程的方式对zz轴信号量进行经验性的放缩。代码中使用get_acclZ()读取即可。

  • 旋转编码器:CLK-G20 (pin-28), DT-G19 (pin-24), SW-G18 (pin-1)。初始化使用教材例程。

  • 超声波测距模块:Trig-G13, Echo-G16。软件上按照教材例程进行初始化,使用float disMeasure()函数获取当前距离。

  • 红外接收模块:DO-G23。需要根据指导教材包含lirc/lirc_client.h等头文件,并在树莓派系统配置中根据教材内容反注释一个设置项并重启。可使用irw命令来测试是否能够正常接收到红外信号。在读取接收到的信号时,需要轮询poll提供的队列,若不为空则说明已有信号到来,根据映射正确处理即可。

  • 红绿双色LED:R-G6, G-G5。软件上使用softPwm.h及教材提供的封装来配置各色占空比来调光。

  • 3色LED:B-G21, G-G22, R-G26。同上。

  • LCD:引脚根据教材配置。软件上,使用教材提供的例程来驱动,但需注意不能完全撤掉delay,否则可能导致时序错误。

  • 有源蜂鸣器:I/O-G24 (pin=5)。使用digitalWrite来控制高低电平即可。

  • 无源蜂鸣器:I/O-G27 (pin=2)。软件上使用softTone.h头文件来直接输出指定频率的音频(softToneCreate, softToneWrite, softToneStop)。

程序流程图;实现具体功能的关键程序代码(核心语句有注释)#

程序流程图(主干)
图2(a) 程序流程图(主干)
程序流程图(定时器回调)
图2(b) 程序流程图(定时器回调)

以下为关键代码片段,包括实验系统的数据采集(超声波、光敏、雨滴、加速度计)、实时控制(蜂鸣器报警、LED 颜色/亮度)、人机交互(LCD 显示、编码器、红外遥控)等核心功能。

1. 超声波测距与有源蜂鸣器动态报警#

// 根据当前距离(cm)计算蜂鸣器的报警半周期(ms)
// 距离越近,半周期越短 → 蜂鸣器声音越急促
uint get_beep_T_ms(float distance_cm) {
    static const float min_dis = 10;      // 最小报警距离 10cm
    if (distance_cm < min_dis) distance_cm = min_dis;
    return 100 + (distance_cm - min_dis) * 2; // 半周期从 100ms 到 2100ms
}

// 蜂鸣器定时器回调函数:交替开/关蜂鸣器,实现间歇报警
void beep_alarm_handle(void) {
    if (!beep_ctrl.timer.expired) return;
    beep_ctrl.timer.expired = 0;

    uint half_cycle = get_beep_T_ms(beep_ctrl.current_distance) / 2;
    if (beep_ctrl.beep_phase == 0) {      // 响阶段
        digitalWrite(BuzPin, ABUZZER_HIGH);
        beep_ctrl.beep_phase = 1;
    } else {                              // 不响阶段
        digitalWrite(BuzPin, ABUZZER_LOW);
        beep_ctrl.beep_phase = 0;
    }
    timer_start_ms(&beep_ctrl.timer, half_cycle); // 重新启动下一阶段
}
c

2. 主循环中的传感器数据读取与报警触发#

while (main_running) {
    // 读取光敏电阻(暗度)和雨滴传感器(干度)
    DDark = analogRead(PCF);       // 通道0:光强(值越小越暗)
    DDry  = analogRead(PCF + 1);   // 通道1:湿度(<180 表示有雨)

    // 每 300ms 更新一次超声波距离
    if (millis() - last_distance_update_ms >= 300) {
        dis = disMeasure();                 // 获取距离(cm)
        beep_ctrl.current_distance = dis;   // 更新报警控制器的当前距离
        last_distance_update_ms = millis();
    }

    // 根据 MPU6050 的 Z 轴加速度值改变 LED2 颜色(倾斜报警)
    accl_z = get_acclZ();
    if (accl_z < 0.5) {
        led2ColorSet(100, 0);      // 偏红
    } else if (accl_z < 0.8) {
        led2ColorSet(75, 100);     // 橙黄
    } else {
        led2ColorSet(0, 100);      // 偏绿
    }

    // 根据光敏电阻值控制 LED3 亮度(模拟自动夜灯)
    white_lum = DDark * DDark / 128;        // 非线性映射,暗环境亮度高
    led3ColorSet(white_lum, white_lum, white_lum * 2 / 3);

    // 若无源蜂鸣器未播放音乐,则启动有源蜂鸣器距离报警
    if (!song_player.is_playing || music_paused) {
        beep_alarm_handle();                // 动态调整报警节奏
    }

    delay(50);
}
c

3. LCD 显示模式切换(通过旋转编码器按钮)#

// 在 LCD 定时器回调函数中处理编码器按钮事件
void handle_lcd_update(...) {
    // 检测编码器按钮是否被按下(消抖 250ms)
    if (encoder_state.button_pressed) {
        if (millis() - *last_mode_change > 250) {
            *display_mode = !(*display_mode);   // 在 0(传感器模式)和 1(编码器模式)间切换
            *last_mode_change = millis();
        }
        encoder_clear_button();
    }

    LCD_clear();
    if (*display_mode == 0) {
        // 模式0:显示暗度、雨滴状态和超声波距离
        sprintf(for_LCD[0], "Dark=%3d %s", *DDark, *DDry < 180 ? "Raining" : "No rain");
        sprintf(for_LCD[1], "Dis=%7.2fcm", *dis);
    } else {
        // 模式1:显示旋转编码器累计值和瞬时速度
        long delta = encoder_state.count - prev_encoder_state.count;
        sprintf(for_LCD[0], "Total=%+08ld", encoder_state.count);
        sprintf(for_LCD[1], "Dir:%s v=%8.3f",
            delta == 0 ? "P" : (delta < 0 ? "-" : "+"),
            delta * 1e3f / (millis() - prev_encoder_state.timestamp));
    }
    LCD_write(0, 0, for_LCD[0]);
    LCD_write(0, 1, for_LCD[1]);
}
c

4. 红外遥控控制无源蜂鸣器播放音乐(切换歌曲、暂停/继续)#

// 红外按键处理函数
void handle_ir_command(int key) {
    // 数字键 1~9 对应歌曲库中的前9首歌曲
    if (key >= 13 && key <= 21) {
        int idx = key - 13;               // 按键13对应数字1,映射到歌曲索引0
        if (idx < song_count) {
            current_song = song_library[idx];
            music_paused = 0;
            song_player_play(&song_player, current_song); // 播放新歌曲
        }
    }
    // 播放/暂停键(红外码为 KEY_PLAYPAUSE)
    else if (key == IR_KEY_PLAYPAUSE) {
        if (song_player.is_playing && !music_paused) {
            song_player_pause(&song_player);
            music_paused = 1;
        } else if (music_paused) {
            song_player_resume(&song_player);
            music_paused = 0;
        } else {
            // 完全停止 → 从头播放当前歌曲
            song_player_play(&song_player, current_song);
        }
    }

    // 音乐播放时关闭距离报警,否则启动报警
    if (song_player.is_playing && !music_paused) {
        beep_alarm_stop();
    } else {
        beep_alarm_start();
    }
}
c

实验过程中遇到什么疑难问题?是怎么解决的?#

  • 有源蜂鸣器低电平触发,需要为其更换为反转后的 HIGHLOW
  • 实验中需要有多个传感器接入AD模块,但器材只有1个AD模块。查阅指导书后,得知可以将多个模拟元件接入同一AD模块,软件上用PCD+n的地址即可。
  • 超声波测距有时会使程序卡死,经排查,可能是超声波测距模块的接线接触不良导致驱动出现状态异常,重新用牢固的杜邦线连接后解决了问题。
  • LCD显示屏有时会出现花屏的现象,可能是和上一个问题类似的问题导致驱动状态异常,更换杜邦线及避免移动LCD屏解决了问题。
  • 在有定时器的程序中,按下Ctrl-C后程序可能不能停止,解决方法是加入一个表示程序是否正在运行的变量,在按下Ctrl-C后设置为0,主循环中及定时器中检测这一变量,若变为0则停止并正常退出。

最终成果#

true
图3 下雨测试
true
图4 黑暗环境
true
图5(a) 无倾倒风险
true
图5(b) 存在倾倒风险
true
图5(c) 倾倒
true
图6 连线概况
视频1 总演示

Extra: 待改进方向#

  • 有源蜂鸣器切换频率不稳,推测有可能是LCD屏更新导致阻滞。改进方向可能包括减少LCD屏单次更新时间,以及在主循环中降低更新LCD的软件优先级。
  • 歌曲数量较少,可以将music_gen项目的歌曲迁移到这里。[菜汪] 1
  • 旋转编码器仍存在较小(约1/160)概率会丢失一次的计数。基本确定是软件原因,但可能较难修正。
  • 可以将项目整体重构为基于poll与轮询的,这样可以进一步减小丢失信号的可能,但较难改进LCD屏更新拖延时间的问题(可能能通过拆解为小操作来解决?)。

脚注#

  1. 表情来自koishijs/QFace。所有表情资源均来自腾讯官方,版权归腾讯公司所有。仅供学习交流使用。

RaspCar|数字设计与计算机原理专题实验 实验报告
https://guileless-beijinho-42fd22.netlify.app/blog/rasp-car-readme
Author What410802
Published at June 16, 2026
Copyright CC BY-NC-SA 4.0