欢迎界面
图3-10 系统程序流程图
M=0 充卡模式 注册 M=1 消费模式 余额小于金额 充值成功 余额大小于金额 扣款成功 3.2.2 读卡程序设计
S50卡要与MFRC522读卡器通讯,读卡器必须先执行寻卡操作,进而记性防碰撞机制,接着对进入范围的卡取一张进行选择,再接着验证IC卡中的密码,如果密码核对正确即可进行下一步操作,否则不可操作。验证成功之后,最后可以读取S50卡中的金额、卡号等数据信息,也可以往IC卡中写入金额信息,也可以对密码进行修改。
11
图3-11 读卡器读S50卡程序流程图
3.2.3 MCU与读卡模块通信程序设计
为了使编程方便,本系统使用简单的SPI通信协议来使单片机控制读卡器模块。
SPI是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口,是Motorola首先在其MC68HCXX系列处理器上定义的。SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。SPI是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便。
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要4根线,事实上3根也可以。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCLK(时钟),CS(片选)。
12
MOSI(SDO):主器件数据输出,从器件数据输入。MISO(SDI):主器件数据输入,从器件数据输出。SCLK :时钟信号,由主器件产生。CS:从器件使能信号,由主器件控制。
其中CS是控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(高电位或低电位),对此芯片的操作才有效,这就允许在同一总线上连接多个SPI设备成为可能。需要注意的是,在具体的应用中,当一条SPI总线上连接有多个设备时,SPI本身的CS有可能被其他的GPIO脚代替,即每个设备的CS脚被连接到处理器端不同的GPIO,通过操作不同的GPIO口来控制具体的需要操作的SPI设备,减少各个SPI设备间的干扰。
SPI是串行通讯协议,也就是说数据是一位一位从MSB或者LSB开始传输的,这就是SCK时钟线存在的原因,由SCK提供时钟脉冲,MISO、MOSI则基于此脉冲完成数据传输。 SPI支持4-32bits的串行数据传输,支持MSB和LSB,每次数据传输时当从设备的大小端发生变化时需要重新设置SPI Master的大小端。
图3-12 SPI协议时序图
13
4.部分代码(详细的注释)
4.1键盘程序
//扫描按键又没有按下 void KeyScan(void) {
char a = 0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)//读取按键是否按下 { Delay10ms(1);//延时10ms进行消抖 if(GPIO_KEY!=0x0f)//再次检测键盘是否按下 { //测试列 GPIO_KEY=0X0F; switch(GPIO_KEY) { case(0X07): KeyValue=0;break; case(0X0b): KeyValue=4;break; case(0X0d): KeyValue=8;break; case(0X0e): KeyValue=12;break; } //测试行 GPIO_KEY=0XF0; switch(GPIO_KEY) { case(0X70): KeyValue=KeyValue+3;break; case(0Xb0): KeyValue=KeyValue+2;break; case(0Xd0): KeyValue=KeyValue+1;break; case(0Xe0): KeyValue=KeyValue;break; } while((a<50) && (GPIO_KEY!=0xf0)) //检测按键松手检测 { Delay10ms(1); a++; } } } }
4.2 LCD1602程序
14
//延时程序
void Lcd1602_Delay1ms(uint c) //误差 0us {
uchar a,b; for (; c>0; c--) { for (b=199;b>0;b--) { for(a=1;a>0;a--); } } }
//写命令函数
void LcdWriteCom(uchar com) //写入命令 {
LCD1602_E = 0; //使能
LCD1602_RS = 0; //选择发送命令 LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = com; //放入命令 Lcd1602_Delay1ms(1); //等待数据稳定
LCD1602_E = 1; //写入时序 Lcd1602_Delay1ms(5); //保持时间 LCD1602_E = 0; }
//写数据函数
void LcdWriteData(uchar dat) //写入数据 {
LCD1602_E = 0; //使能清零
LCD1602_RS = 1; //选择输入数据 LCD1602_RW = 0; //选择写入
LCD1602_DATAPINS = dat; //写入数据 Lcd1602_Delay1ms(1);
LCD1602_E = 1; //写入时序
Lcd1602_Delay1ms(5); //保持时间 LCD1602_E = 0; }
//LCD初始化子程序 void LcdInit() {
LcdWriteCom(0x38); //开显示
15