一、項(xiàng)目介紹
在城市交通中,出租車是一種常見的交通工具。為了方便乘客和司機(jī)之間的交易,出租車計(jì)費(fèi)系統(tǒng)被廣泛應(yīng)用于出租車行業(yè)。系統(tǒng)能夠自動(dòng)計(jì)算乘客的費(fèi)用,提供準(zhǔn)確、方便的計(jì)費(fèi)服務(wù),并且能夠記錄乘客的行駛數(shù)據(jù),方便后續(xù)查詢和管理。
傳統(tǒng)的出租車計(jì)費(fèi)方式是基于人工計(jì)算,司機(jī)根據(jù)里程和時(shí)間進(jìn)行估算并告知乘客費(fèi)用。然而,這種計(jì)費(fèi)方式容易存在誤差和爭議,并且對司機(jī)和乘客都不夠方便和透明。因此,出租車行業(yè)迫切需要一種更加準(zhǔn)確、高效和可靠的計(jì)費(fèi)系統(tǒng)。
基于此背景,本項(xiàng)目設(shè)計(jì)和開發(fā)一種基于STM32微控制器的出租車計(jì)費(fèi)系統(tǒng),以替代傳統(tǒng)的人工計(jì)費(fèi)方式。該系統(tǒng)將利用STM32微控制器的強(qiáng)大處理能力和豐富的外設(shè)接口,集成各種功能模塊,實(shí)現(xiàn)自動(dòng)計(jì)算乘客費(fèi)用、顯示計(jì)費(fèi)信息等功能。
通過該出租車計(jì)費(fèi)系統(tǒng),乘客只需在上車時(shí)按下對應(yīng)按鈕,系統(tǒng)將自動(dòng)開始計(jì)費(fèi),并在顯示屏上實(shí)時(shí)顯示行駛時(shí)間、里程和費(fèi)用等信息。乘客還可以通過按鍵輸入特殊情況,如堵車或夜間行駛,以便系統(tǒng)進(jìn)行相應(yīng)的額外計(jì)費(fèi)。當(dāng)乘客下車時(shí),系統(tǒng)將自動(dòng)停止計(jì)費(fèi),并顯示最終費(fèi)用。同時(shí),系統(tǒng)還將記錄乘客的行駛數(shù)據(jù)以備查詢和管理。
項(xiàng)目源碼下載,可以了解這里:
https://blog.csdn.net/xiaolong1126626497/category_10192120.html
二、系統(tǒng)設(shè)計(jì)思路
2.1 系統(tǒng)架構(gòu)
出租車計(jì)費(fèi)系統(tǒng)的主要組成部分包括:STM32微控制器、LCD顯示屏、按鍵、計(jì)時(shí)電路、收費(fèi)器和外部存儲器。整個(gè)系統(tǒng)的架構(gòu)如下:
- STM32微控制器:采用STM32F103RCT6作為系統(tǒng)的控制核心,負(fù)責(zé)接收并處理來自各個(gè)模塊的輸入信號,并控制液晶顯示屏上的信息顯示和收費(fèi)器的操作。
- LCD顯示屏:采用1.44寸LCD顯示屏,用于顯示當(dāng)前的計(jì)費(fèi)信息,包括行駛時(shí)間、里程和費(fèi)用等。
- 按鍵:用于輸入乘客上車和下車的時(shí)間以及其他特殊情況,如堵車、夜間行駛等。
- 計(jì)時(shí)電路:用于準(zhǔn)確地測量行駛時(shí)間。
- 收費(fèi)器:負(fù)責(zé)根據(jù)計(jì)費(fèi)規(guī)則和實(shí)時(shí)數(shù)據(jù)計(jì)算乘客的費(fèi)用。
- 外部存儲器:用于存儲行駛數(shù)據(jù)和計(jì)費(fèi)規(guī)則。
2.2 系統(tǒng)功能
出租車計(jì)費(fèi)系統(tǒng)具有以下主要功能:
- 實(shí)時(shí)計(jì)算行駛時(shí)間和里程。
- 根據(jù)計(jì)費(fèi)規(guī)則自動(dòng)計(jì)算乘客費(fèi)用。
- 在LCD顯示屏上顯示當(dāng)前的計(jì)費(fèi)信息。
- 支持特殊情況的額外計(jì)費(fèi),如堵車、夜間行駛等。
- 存儲行駛數(shù)據(jù)和計(jì)費(fèi)規(guī)則以備查詢和更新。
三、代碼設(shè)計(jì)
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
// 定義命令和數(shù)據(jù)的宏
#define LCD_COMMAND 0
#define LCD_DATA 1
// 延時(shí)函數(shù),用于產(chǎn)生適當(dāng)?shù)难訒r(shí)
void Delay(uint32_t nCount) {
for (; nCount != 0; --nCount) {
}
}
// 發(fā)送命令或數(shù)據(jù)到LCD函數(shù)
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函數(shù)
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);
}
// 在指定位置顯示數(shù)字函數(shù)
void LCD_DisplayNumber(uint8_t number, uint8_t x, uint8_t y) {
uint8_t data = 0x30 + number; // 轉(zhuǎn)換數(shù)字為對應(yīng)的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) {
// 在第一行第一列顯示數(shù)字1
LCD_DisplayNumber(1, 0, 0);
}
}
3.2 計(jì)時(shí)代碼
通過定時(shí)器2實(shí)現(xiàn)了收費(fèi)計(jì)時(shí)功能,并在串口上打印出計(jì)時(shí)的實(shí)時(shí)時(shí)間。通過按下’S’鍵啟動(dòng)計(jì)時(shí)器,按下’Q’鍵停止計(jì)時(shí)器。每隔500毫秒,在串口上打印出實(shí)時(shí)時(shí)間。
#include "stm32f10x.h"
#include <stdio.h>
// 定義計(jì)時(shí)狀態(tài)
typedef enum {
TIMER_STOPPED,
TIMER_RUNNING
} TimerState;
TimerState timerState = TIMER_STOPPED; // 計(jì)時(shí)器初始狀態(tài)為停止
uint32_t startTime = 0; // 開始計(jì)時(shí)的時(shí)間
// 初始化定時(shí)器2
void Timer2_Init(void) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Prescaler = 7200 - 1; // 設(shè)置預(yù)分頻值,產(chǎn)生1ms的時(shí)間基準(zhǔn)
TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 設(shè)置計(jì)數(shù)器的重載值,每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);
}
// 開始計(jì)時(shí)
void StartTimer(void) {
if (timerState == TIMER_STOPPED) {
startTime = TIM_GetCounter(TIM2); // 記錄開始計(jì)時(shí)的時(shí)間
timerState = TIMER_RUNNING;
}
}
// 停止計(jì)時(shí)
void StopTimer(void) {
if (timerState == TIMER_RUNNING) {
timerState = TIMER_STOPPED;
}
}
// 獲取實(shí)時(shí)時(shí)間,返回單位為毫秒
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函數(shù)到串口輸出
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) {
// 初始化定時(shí)器和串口
Timer2_Init();
USART1_Init();
printf("Press 'S' to start the timer.rn");
printf("Press 'Q' to stop the timer.rn");
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.rn");
} else if (rxData == 'Q' || rxData == 'q') {
StopTimer();
printf("Timer stopped.rn");
}
}
// 每隔500毫秒打印實(shí)時(shí)時(shí)間
if (GetElapsedTime() >= 500) {
printf("Elapsed time: %lu msrn", GetElapsedTime());
startTime = TIM_GetCounter(TIM2); // 更新開始計(jì)時(shí)的時(shí)間
}
}
}
// 定時(shí)器2中斷處理函數(shù)
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}