一、项目介绍
在城市交通中,出租车是一种常见的交通工具。为了方便乘客和司机之间的交易,出租车计费系统被广泛应用于出租车行业。系统能够自动计算乘客的费用,提供准确、方便的计费服务,并且能够记录乘客的行驶数据,方便后续查询和管理。
传统的出租车计费方式是基于人工计算,司机根据里程和时间进行估算并告知乘客费用。然而,这种计费方式容易存在误差和争议,并且对司机和乘客都不够方便和透明。因此,出租车行业迫切需要一种更加准确、高效和可靠的计费系统。
基于此背景,本项目设计和开发一种基于STM32微控制器的出租车计费系统,以替代传统的人工计费方式。该系统将利用STM32微控制器的强大处理能力和丰富的外设接口,集成各种功能模块,实现自动计算乘客费用、显示计费信息等功能。
通过该出租车计费系统,乘客只需在上车时按下对应按钮,系统将自动开始计费,并在显示屏上实时显示行驶时间、里程和费用等信息。乘客还可以通过按键输入特殊情况,如堵车或夜间行驶,以便系统进行相应的额外计费。当乘客下车时,系统将自动停止计费,并显示最终费用。同时,系统还将记录乘客的行驶数据以备查询和管理。
项目源码下载,可以了解这里:
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和LCDRCC_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) {// 在第一行第一列显示数字1LCD_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);}
}