一、项目概述 智能一卡通是基于STM32开发的一种集合了多种功能的智能卡片,可以用于学校、企事业单位以及公共交通等场景。本项目使用STM32微控制器来实现一个简单的智能一卡通系统,具体功能包括:卡片信息存储、充值、消费、余额查询以及简单的数据加密和解密等。
二、硬件准备
- STM32微控制器(如STM32F103C8T6)
- RC522射频卡读写模块
- OLED显示屏
- 数字键盘
- 若干LED灯
三、软件准备
- Keil MDK集成开发环境
- STM32CubeMX配置工具
- MFRC522 RFID库
- u8glib OLED库
四、实现步骤
-
硬件连接 将RC522模块的SDA、SCK、MISO、MOSI、IRQ、RST和GND接到STM32开发板的对应引脚上,将RC522模块的3.3V接到STM32的3.3V引脚上。将OLED的SDA、SCK和GND接到STM32的对应引脚上,将OLED的VCC引脚接到3.3V引脚上。将数字键盘的行引脚接到STM32的GPIO引脚上,将列引脚接到GPIO的输入引脚上。将LED的引脚接到STM32的GPIO引脚上。
-
STM32CubeMX配置 使用STM32CubeMX配置工具生成项目代码。首先选择正确的设备型号(如STM32F103C8T6),然后配置相应的引脚。将RC522模块的SDA引脚、SCK引脚、MISO引脚和MOSI引脚设置为SPI模式,并配置相应的时钟。将OLED的SDA引脚和SCK引脚设置为I2C模式,并配置相应的时钟。将数字键盘的行引脚设置为输出模式,列引脚设置为输入模式。将LED的引脚设置为输出模式。
-
代码编写 在Keil MDK中创建一个新工程,将生成的STM32CubeMX代码复制到工程中。
(1)编写MFRC522 RFID库的代码,包括初始化RC522模块、读取卡片信息、写入卡片信息等函数。
(2)编写u8glib OLED库的代码,包括显示文本、图形等函数。
(3)编写数字键盘的驱动代码,包括按键扫描、获取键值等函数。
(4)编写主函数main(),实现智能一卡通的功能。
- 主要功能实现 (1)卡片信息存储 当用户第一次使用卡片时,将用户的个人信息(如姓名、学号、余额等)存储到卡片中。
(2)充值 用户可以通过输入金额实现卡片余额的充值。
(3)消费 用户可以通过输入金额实现卡片余额的消费。
(4)余额查询 用户可以通过按键操作实现卡片余额的查询。
(5)数据加密和解密 为了保证卡片信息的安全,可以对存储的卡片信息进行简单的数据加密和解密。
五、代码案例
#include "stm32f1xx_hal.h"
#include "mfrc522.h"
#include "u8g2.h"
#include "keypad.h"
#define OLED_I2C_ADDRESS 0x78
extern SPI_HandleTypeDef hspi1;
extern UART_HandleTypeDef huart1;
uint8_t cardUID[4];
char cardData[64];
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
static void MX_USART1_UART_Init(void);
void OLED_Init();
void OLED_DrawString(char* str, int x, int y);
void OLED_Clear();
void OLED_DrawDecimal(int dec, int x, int y);
void OLED_DrawCardData(char* cardData);
void OLED_DrawBalance(int balance);
void OLED_DrawMenu(int currentMenuItem);
void RFID_Init();
void RFID_ReadCardUID();
void RFID_ReadCardData();
void RFID_WriteCardData();
void Keypad_Init();
char Keypad_GetKey();
enum MenuItem {
BALANCE, RECHARGE, CONSUME
};
int balance = 0;
enum MenuItem currentMenuItem = BALANCE;
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI1_Init();
MX_USART1_UART_Init();
OLED_Init();
Keypad_Init();
RFID_Init();
OLED_Clear();
OLED_DrawMenu(currentMenuItem);
while (1) {
char key = Keypad_GetKey();
if (key == '1') {
currentMenuItem = BALANCE;
OLED_Clear();
OLED_DrawMenu(currentMenuItem);
} else if (key == '2') {
currentMenuItem = RECHARGE;
OLED_Clear();
OLED_DrawMenu(currentMenuItem);
} else if (key == '3') {
currentMenuItem = CONSUME;
OLED_Clear();
OLED_DrawMenu(currentMenuItem);
}
if (currentMenuItem == BALANCE) {
RFID_ReadCardData();
OLED_DrawCardData(cardData);
OLED_DrawBalance(balance);
} else if (currentMenuItem == RECHARGE) {
RFID_ReadCardData();
OLED_DrawCardData(cardData);
OLED_DrawString("Amount to recharge:", 0, 32);
int rechargeAmount = 0;
char key = '0';
do {
key = Keypad_GetKey();
if (key >= '0' && key <= '9') {
rechargeAmount = rechargeAmount * 10 + key - '0';
OLED_DrawDecimal(rechargeAmount, 128, 32);
}
} while (key != '#');
balance += rechargeAmount;
RFID_WriteCardData();
OLED_Clear();
OLED_DrawMenu(currentMenuItem);
OLED_DrawCardData(cardData);
OLED_DrawBalance(balance);
} else if (currentMenuItem == CONSUME) {
RFID_ReadCardData();
OLED_DrawCardData(cardData);
OLED_DrawString("Amount to consume:", 0, 32);
int consumeAmount = 0;
char key = '0';
do {
key = Keypad_GetKey();
if (key >= '0' && key <= '9') {
consumeAmount = consumeAmount * 10 + key - '0';
OLED_DrawDecimal(consumeAmount, 128, 32);
}
} while (key != '#');
if (balance >= consumeAmount) {
balance -= consumeAmount;
RFID_WriteCardData();
} else {
OLED_Clear();
OLED_DrawString("Insufficient balance", 0, 16);
HAL_Delay(2000);
}
OLED_Clear();
OLED_DrawMenu(currentMenuItem);
OLED_DrawCardData(cardData);
OLED_DrawBalance(balance);
}
}
}
void OLED_Init() {
u8g2_t u8g2;
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_sw_i2c_delay);
u8x8_SetI2CAddress(u8g2_GetU8x8(&u8g2), OLED_I2C_ADDRESS);
u8g2_InitDisplay(&u8g2);
u8g2_SetPowerSave(&u8g2, 0);
}
void OLED_DrawString(char* str, int x, int y) {
u8g2_t u8g2;
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, u8x8_sw_i2c_delay);
u8x8_SetI2CAddress(u8g2_GetU8x8(&u8g2), OLED_I2C_ADDRESS);
u8g2_FirstPage(&u8g2);
do {
u8g2_DrawStr(&u8g2, x, y, str);
} while (u8g2_NextPage(&u8g2));
}
void OLED_Clear() {
u8g2_t u8g2;
u8g2_Setup_ssd1306_i2c_128x