1. Introducción del proyecto
En el tráfico urbano, los taxis son un medio de transporte habitual. Para facilitar las transacciones entre pasajeros y conductores, los sistemas de facturación de taxis se utilizan ampliamente en la industria de taxis. El sistema puede calcular automáticamente la tarifa del pasajero, proporcionar servicios de facturación precisos y convenientes, y puede registrar los datos de conducción del pasajero para facilitar la gestión y la consulta posterior.
El método tradicional de facturación de taxis se basa en cálculos manuales, y el conductor estima e informa a los pasajeros del costo en función del kilometraje y el tiempo. Sin embargo, este método de facturación es propenso a errores y disputas, y no es lo suficientemente conveniente y transparente para los conductores y pasajeros. Por lo tanto, la industria del taxi necesita urgentemente un sistema de facturación más preciso, eficiente y confiable.
En base a estos antecedentes, este proyecto diseña y desarrolla un sistema de facturación de taxis basado en el microcontrolador STM32 para reemplazar el método tradicional de facturación manual. El sistema utilizará la poderosa capacidad de procesamiento y las ricas interfaces periféricas del microcontrolador STM32 para integrar varios módulos funcionales para realizar funciones como el cálculo automático de tarifas de pasajeros y la visualización de información de facturación.
A través del sistema de facturación de taxis, los pasajeros solo necesitan presionar el botón correspondiente al subir al autobús, y el sistema comenzará a facturar automáticamente y mostrará el tiempo de conducción, el kilometraje y la información del costo en la pantalla en tiempo real. Los pasajeros también pueden ingresar condiciones especiales, como atascos o conducción nocturna, a través de los botones, para que el sistema realice la facturación adicional correspondiente. Cuando el pasajero se baja del autobús, el sistema dejará de facturar automáticamente y mostrará la tarifa final. Al mismo tiempo, el sistema también registrará los datos de conducción de los pasajeros para su consulta y gestión.
Para descargar el código fuente del proyecto, puede obtener más información aquí:
https://blog.csdn.net/xiaolong1126626497/category_10192120.html
2. Ideas de diseño del sistema
2.1 Arquitectura del sistema
Los componentes principales del sistema de facturación de taxis incluyen: microcontrolador STM32, pantalla LCD, botones, circuito de tiempo, cobrador de peaje y memoria externa. La arquitectura de todo el sistema es la siguiente:
- Microcontrolador STM32: STM32F103RCT6 se utiliza como núcleo de control del sistema, que es responsable de recibir y procesar las señales de entrada de varios módulos y controlar la visualización de información en la pantalla LCD y el funcionamiento del recaudador de peaje.
- Pantalla LCD: se utiliza una pantalla LCD de 1,44 pulgadas para mostrar la información de facturación actual, incluido el tiempo de conducción, el kilometraje y las tarifas.
- Botones: Se utilizan para ingresar la hora en que los pasajeros suben y bajan del autobús y otras situaciones especiales, como atascos, conducción nocturna, etc.
- Circuito de tiempo: se utiliza para medir con precisión el tiempo de conducción.
- Recaudador de peaje: responsable de calcular las tarifas de los pasajeros en función de las reglas de facturación y los datos en tiempo real.
- Memoria externa: se utiliza para almacenar datos de conducción y reglas de facturación.
2.2 Funciones del sistema
El sistema de facturación de taxis tiene las siguientes funciones principales:
- Calcule el tiempo de conducción y el kilometraje en tiempo real.
- Las tarifas de los pasajeros se calculan automáticamente de acuerdo con las reglas de facturación.
- Muestra la información de facturación actual en la pantalla LCD.
- Admite facturación adicional para circunstancias especiales, como atascos de tráfico, conducción nocturna, etc.
- Almacene datos de conducción y reglas de facturación para consulta y actualización.
3. Diseño de código
3.1 Código de pantalla 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 Códigos de tiempo
La función de temporización de carga se realiza a través del temporizador 2, y el tiempo de temporización en tiempo real se imprime en el puerto serie. Inicie el temporizador presionando la tecla 'S' y deténgalo presionando la tecla 'Q'. Cada 500 milisegundos, imprime la hora real en el puerto serie.
#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);
}
}