基于STM32设计的出租车计费系统

news2025/4/6 7:14:08

一、项目介绍

在城市交通中,出租车是一种常见的交通工具。为了方便乘客和司机之间的交易,出租车计费系统被广泛应用于出租车行业。系统能够自动计算乘客的费用,提供准确、方便的计费服务,并且能够记录乘客的行驶数据,方便后续查询和管理。

传统的出租车计费方式是基于人工计算,司机根据里程和时间进行估算并告知乘客费用。然而,这种计费方式容易存在误差和争议,并且对司机和乘客都不够方便和透明。因此,出租车行业迫切需要一种更加准确、高效和可靠的计费系统。

基于此背景,本项目设计和开发一种基于STM32微控制器的出租车计费系统,以替代传统的人工计费方式。该系统将利用STM32微控制器的强大处理能力和丰富的外设接口,集成各种功能模块,实现自动计算乘客费用、显示计费信息等功能。

通过该出租车计费系统,乘客只需在上车时按下对应按钮,系统将自动开始计费,并在显示屏上实时显示行驶时间、里程和费用等信息。乘客还可以通过按键输入特殊情况,如堵车或夜间行驶,以便系统进行相应的额外计费。当乘客下车时,系统将自动停止计费,并显示最终费用。同时,系统还将记录乘客的行驶数据以备查询和管理。

image-20230625095240766

image-20230625095227080

image-20230625094541137

项目源码下载,可以了解这里:

https://blog.csdn.net/xiaolong1126626497/category_10192120.html

二、系统设计思路

2.1 系统架构

出租车计费系统的主要组成部分包括:STM32微控制器、LCD显示屏、按键、计时电路、收费器和外部存储器。整个系统的架构如下:

  • STM32微控制器:采用STM32F103RCT6作为系统的控制核心,负责接收并处理来自各个模块的输入信号,并控制液晶显示屏上的信息显示和收费器的操作。
  • LCD显示屏:采用1.44寸LCD显示屏,用于显示当前的计费信息,包括行驶时间、里程和费用等。
  • 按键:用于输入乘客上车和下车的时间以及其他特殊情况,如堵车、夜间行驶等。
  • 计时电路:用于准确地测量行驶时间。
  • 收费器:负责根据计费规则和实时数据计算乘客的费用。
  • 外部存储器:用于存储行驶数据和计费规则。

2.2 系统功能

出租车计费系统具有以下主要功能:

  • 实时计算行驶时间和里程。
  • 根据计费规则自动计算乘客费用。
  • 在LCD显示屏上显示当前的计费信息。
  • 支持特殊情况的额外计费,如堵车、夜间行驶等。
  • 存储行驶数据和计费规则以备查询和更新。

三、代码设计

3.1 LCD显示屏代码

#include "stm32f10x.h"

// 定义LCD引脚连接
#define LCD_RS_PIN  GPIO_Pin_0
#define LCD_RS_PORT GPIOA
#define LCD_RW_PIN  GPIO_Pin_1
#define LCD_RW_PORT GPIOA
#define LCD_E_PIN   GPIO_Pin_2
#define LCD_E_PORT  GPIOA
#define LCD_D4_PIN  GPIO_Pin_3
#define LCD_D4_PORT GPIOA
#define LCD_D5_PIN  GPIO_Pin_4
#define LCD_D5_PORT GPIOA
#define LCD_D6_PIN  GPIO_Pin_5
#define LCD_D6_PORT GPIOA
#define LCD_D7_PIN  GPIO_Pin_6
#define LCD_D7_PORT GPIOA

// 定义命令和数据的宏
#define LCD_COMMAND 0
#define LCD_DATA    1

// 延时函数,用于产生适当的延时
void Delay(uint32_t nCount) {
    for (; nCount != 0; --nCount) {
    }
}

// 发送命令或数据到LCD函数
void LCD_Send(uint8_t byte, uint8_t mode) {
    GPIO_WriteBit(LCD_RS_PORT, LCD_RS_PIN, (mode == LCD_DATA) ? Bit_SET : Bit_RESET);
    GPIO_WriteBit(LCD_RW_PORT, LCD_RW_PIN, Bit_RESET);
    
    GPIO_WriteBit(LCD_D4_PORT, LCD_D4_PIN, (byte >> 4) & 0x01 ? Bit_SET : Bit_RESET);
    GPIO_WriteBit(LCD_D5_PORT, LCD_D5_PIN, (byte >> 5) & 0x01 ? Bit_SET : Bit_RESET);
    GPIO_WriteBit(LCD_D6_PORT, LCD_D6_PIN, (byte >> 6) & 0x01 ? Bit_SET : Bit_RESET);
    GPIO_WriteBit(LCD_D7_PORT, LCD_D7_PIN, (byte >> 7) & 0x01 ? Bit_SET : Bit_RESET);
    
    GPIO_WriteBit(LCD_E_PORT, LCD_E_PIN, Bit_SET);
    Delay(1000);
    GPIO_WriteBit(LCD_E_PORT, LCD_E_PIN, Bit_RESET);
    
    GPIO_WriteBit(LCD_D4_PORT, LCD_D4_PIN, (byte >> 0) & 0x01 ? Bit_SET : Bit_RESET);
    GPIO_WriteBit(LCD_D5_PORT, LCD_D5_PIN, (byte >> 1) & 0x01 ? Bit_SET : Bit_RESET);
    GPIO_WriteBit(LCD_D6_PORT, LCD_D6_PIN, (byte >> 2) & 0x01 ? Bit_SET : Bit_RESET);
    GPIO_WriteBit(LCD_D7_PORT, LCD_D7_PIN, (byte >> 3) & 0x01 ? Bit_SET : Bit_RESET);
    
    GPIO_WriteBit(LCD_E_PORT, LCD_E_PIN, Bit_SET);
    Delay(1000);
    GPIO_WriteBit(LCD_E_PORT, LCD_E_PIN, Bit_RESET);
    
    Delay(1000);
}

// 初始化LCD函数
void LCD_Init(void) {
    Delay(45000);
    LCD_Send(0x30, LCD_COMMAND);
    Delay(4500);
    LCD_Send(0x30, LCD_COMMAND);
    Delay(150);
    LCD_Send(0x30, LCD_COMMAND);
    Delay(150);
    
    LCD_Send(0x20, LCD_COMMAND);
    Delay(150);
    
    LCD_Send(0x28, LCD_COMMAND);
    Delay(150);

    LCD_Send(0x08, LCD_COMMAND);
    Delay(150);
    
    LCD_Send(0x01, LCD_COMMAND);
    Delay(150);
    
    LCD_Send(0x06, LCD_COMMAND);
    Delay(150);
    
    LCD_Send(0x0C, LCD_COMMAND);
    Delay(150);
}

// 在指定位置显示数字函数
void LCD_DisplayNumber(uint8_t number, uint8_t x, uint8_t y) {
    uint8_t data = 0x30 + number;  // 转换数字为对应的ASCII码
    
    if (x >= 0 && x < 16 && y >= 0 && y < 2) {
        uint8_t addr = 0x80 + (y * 0x40) + x;
        
        LCD_Send(addr, LCD_COMMAND);
        LCD_Send(data, LCD_DATA);
    }
}

int main(void) {
    // 初始化GPIO和LCD
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = LCD_RS_PIN | LCD_RW_PIN | LCD_E_PIN | LCD_D4_PIN | LCD_D5_PIN | LCD_D6_PIN | LCD_D7_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    LCD_Init();
    
    while (1) {
        // 在第一行第一列显示数字1
        LCD_DisplayNumber(1, 0, 0);
    }
}

3.2 计时代码

通过定时器2实现了收费计时功能,并在串口上打印出计时的实时时间。通过按下’S’键启动计时器,按下’Q’键停止计时器。每隔500毫秒,在串口上打印出实时时间。

#include "stm32f10x.h"
#include <stdio.h>

// 定义计时状态
typedef enum {
    TIMER_STOPPED,
    TIMER_RUNNING
} TimerState;

TimerState timerState = TIMER_STOPPED; // 计时器初始状态为停止
uint32_t startTime = 0; // 开始计时的时间

// 初始化定时器2
void Timer2_Init(void) {
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 设置预分频值,产生1ms的时间基准
    TIM_TimeBaseStructure.TIM_Period = 1000 - 1;  // 设置计数器的重载值,每1秒中断一次
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM2, ENABLE);

    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

// 开始计时
void StartTimer(void) {
    if (timerState == TIMER_STOPPED) {
        startTime = TIM_GetCounter(TIM2); // 记录开始计时的时间
        timerState = TIMER_RUNNING;
    }
}

// 停止计时
void StopTimer(void) {
    if (timerState == TIMER_RUNNING) {
        timerState = TIMER_STOPPED;
    }
}

// 获取实时时间,返回单位为毫秒
uint32_t GetElapsedTime(void) {
    if (timerState == TIMER_RUNNING) {
        return TIM_GetCounter(TIM2) - startTime;
    } else {
        return 0;
    }
}

// 初始化串口1
void USART1_Init(void) {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; // TX引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);

    USART_Cmd(USART1, ENABLE);
}

// 重定向printf函数到串口输出
int fputc(int ch, FILE *f) {
    if (ch == '\n') {
        USART_SendData(USART1, '\r');
        while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    }

    USART_SendData(USART1, ch);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);

    return ch;
}

int main(void) {
    // 初始化定时器和串口
    Timer2_Init();
    USART1_Init();

    printf("Press 'S' to start the timer.\r\n");
    printf("Press 'Q' to stop the timer.\r\n");

    while (1) {
        if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) {
            uint8_t rxData = (uint8_t)USART_ReceiveData(USART1);
            
            if (rxData == 'S' || rxData == 's') {
                StartTimer();
                printf("Timer started.\r\n");
            } else if (rxData == 'Q' || rxData == 'q') {
                StopTimer();
                printf("Timer stopped.\r\n");
            }
        }

        // 每隔500毫秒打印实时时间
        if (GetElapsedTime() >= 500) {
            printf("Elapsed time: %lu ms\r\n", GetElapsedTime());
            startTime = TIM_GetCounter(TIM2); // 更新开始计时的时间
        }
    }
}

// 定时器2中断处理函数
void TIM2_IRQHandler(void) {
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/847650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA 指定spring.profiles.active本地启动

spring.profiles.activedev spring.profiles.activepro

SpringBoot项目配置文件

文章目录 一、配置文件作用二、配置文件格式三、properties配置文件说明基本语法读取配置文件优缺点 四、yml配置文件说明基本语法使用yml连接数据库使用yml配置不同数据类型及nullyml配置的读取配置字符串的单双引号问题配置对象 两种格式配置文件对比properties格式配置文件示…

Keburnetes YAML配置文件管理

Kubernetes 支持 YAML 和 JSON 格式管理资源对象JSON 格式&#xff1a;主要用于 api 接口之间消息的传递YAML 格式&#xff1a;用于配置和管理&#xff0c;YAML 是一种简洁的非标记性语言&#xff0c;内容格式人性化&#xff0c;较易读 YAML 语法格式 大小写敏感使用缩进表示层…

Linux之AWK

目录 Linux之AWK 定义 语法格式 常用选项 awk 变量 内置变量 格式 案例 &#xff08;1&#xff09;编写一个文本名字叫做awkdemo&#xff0c;里面内容如下 &#xff08;2&#xff09;FS指定输入分隔符 &#xff08;3&#xff09;OFS指定输出分隔符 &#xff08;4&#xf…

EditText设置inputType=“textPassword“后hint 英文字体不对,而且不能换行

问题如题,我先上图看下遇到的问题 ( ps: 我只想唱一句 : 你这该死的安卓~~ 让我心在痛 累在流!!!) 很明显字体偏大,字间隔很宽,关键还不会换行, 看一下我们需要的效果: 看到只要把 android:inputType"textPassword" 这一行干掉 一切就是我们想要的,但是产…

sonar 错误 提示 Synchronize on a new “Object“ instead.使用Id 作为锁

文章目录 错误分析先看sonar 给解决方案最终解决方案 public void synchronizedMethod(Integer id) {synchronized (id) {// Code to be synchronized}}错误分析 Synchronize on a new "Object" instead. 不应该在原始包装器&#xff08;即整数、长整型、布尔值等&am…

HDFS中的Trash垃圾桶回收机制

Trash垃圾桶回收机制 文件系统垃圾桶背景功能概述Trash Checkpoint Trash功能开启关闭HDFS集群修改core-site.xml删除文件到trash删除文件跳过从trash中恢复文件清空trash 文件系统垃圾桶背景 回收站&#xff08;垃圾桶&#xff09;是windows操作系统里的一个系统文件夹&#…

c#设计模式-创建型模式 之 工厂模式

前言&#xff1a; 工厂模式&#xff08;Factory Pattern&#xff09;是一种常用的对象创建型设计模式。该模式的主要思想是提供一个创建对象的接口&#xff08;也可以是抽象类、静态方法等&#xff09;&#xff0c;将实际创建对象的工作推迟到子类中进行。这样一来&#xff0c…

华为认证 | 云计算HCIE3.0改版后有什么变化?

随着技术的不断进步和行业的发展&#xff0c;云计算的HCIE作为华为公司的顶级认证&#xff0c;也进行了版本的更新。 那改版后有哪些变化呢&#xff0c;今天给大家讲讲。 01 HCIE认证简介 HCIE认证是华为公司旗下的顶级专业认证&#xff0c;面向IT领域的高级专业人士。 它涵…

华为数通题库,新一波分享来袭(有答案哟)

1、以下关于OSPF的Router ID描述不正确的是&#xff08;&#xff09;。&#xff08;多选&#xff09; A. OSPF协议正常运行的前提条件是该路由器有Router ID B. Router ID必须是路由器某接口的IP地址 C. 必须通过手工配置方式来指定Router IDD. 在同一区域内Router I D必须相同…

MobiSys 2023 | 基于毫米波振动测量的无接触材料识别

注1:本文系“无线感知论文速递”系列之一,致力于简洁清晰完整地介绍、解读无线感知领域最新的顶会/顶刊论文(包括但不限于 Nature/Science及其子刊; MobiCom, Sigcom, MobiSys, NSDI, SenSys, Ubicomp; JSAC, 雷达学报 等)。本次介绍的论文是:《MobiSys 2023 | 基于毫米波振…

高忆管理:股票低开说明什么?股价跟什么有关?

股票的开盘价和收盘价是投资者重视的重要指标&#xff0c;它反映了股票价值的变化。那么股票低开阐明什么&#xff1f;股价跟什么有关&#xff1f;高忆管理也为大家预备了相关内容&#xff0c;以供参阅。 股票低开阐明什么&#xff1f; 股票低开是指股票的开盘价低于上一个交易…

SpringCloud(31):Nacos配置管理基础应用

1 Nacos配置管理模型 对于Nacos配置管理&#xff0c;通过Namespace、group、Data ID能够定位到一个配置集。 配置集(Data ID) 在系统中&#xff0c;一个配置文件通常就是一个配置集&#xff0c;一个配置集可以包含了系统的各种配置信息&#xff0c;例如&#xff0c;一个配置集…

C 语言的转义字符

转义字符也叫转移序列&#xff0c;包含如下&#xff1a; 转移序列 \0oo 和 \xhh 是 ASCII 码的特殊表示。 八进制数示例&#xff1a; 代码&#xff1a; #include<stdio.h> int main(void) {char beep\007;printf("%c\n",beep);return 0; }结果&#xff1a; …

IntelliJ IDEA Bookmark使用

1 增加 右键行号栏 2 查看 从favorite这里查看 参考IntelliJ IDEA 小技巧&#xff1a;Bookmark(书签)的使用_bookmark idea 使用_大唐冠军侯的博客-CSDN博客

刘汉清:从生活到画布,宠物成为灵感源泉

出生于中国镇江的艺术家刘汉清&#xff0c;其作品展现出他对日常生活的深入洞察力&#xff0c;以及对美的独特理解。他的作品通常没有视觉参考&#xff0c;而是通过对他周围环境的理解&#xff0c;尤其是他的宠物&#xff0c;来进行创作。 在刘汉清的创作过程中&#xff0c;他…

docker容器监控:Cadvisor +Prometheus+Grafana的安装部署

目录 Cadvisor PrometheusGrafana的安装部署 一、安装docker&#xff1a; 1、安装docker-ce 2、阿里云镜像加速器 3、下载组件镜像 4、创建自定义网络 二、部署Cadvisor 1、被监控主机上部署Cadvisor容器 2、访问cAdvisor页面 三、安装prometheus 1、部署Prometheus…

【Linux 网络】 数据链路层协议

数据链路层协议 数据链路层解决的问题以太网协议认识以太网以太网帧格式 认识MAC地址对比理解MAC地址和IP地址认识MTUMTU对IP协议的影响MTU对UDP协议的影响MTU对于TCP协议的影响ARP协议ARP协议的作用ARP协议的工作流程ARP数据报的格式 总结 数据链路层解决的问题 IP拥有将数据跨…

hcip——期中小试

要求&#xff1a; 1、该拓扑为公司网络&#xff0c;其中包括公司总部、公司分部以及公司骨干网&#xff0c;不包含运营商公网部分。 2 、设备名称均使用拓扑上名称改名&#xff0c;并且区分大小写。 3 、整张拓扑均使用私网地址进行配置。 4 、整张网络中&#xff0c;运行 O…

扩增子分析流程——Lotus2: 一行命令完成所有分析

为什么介绍lotus2 因为快&#xff0c;作者比较了lotus2流程和qiime2、dada2、vsearch等&#xff0c;lotus2的速度最快、占用内存最小。 因为方便&#xff0c;只需要一行代码&#xff0c;即可完成全部分析。 lotus2 -i Example/ -m Example/miSeqMap.sm.txt -o myTestRun而且分…