[原创] 虽然现在不是从事电子方面的工作,但还会经常回忆学习电子知识那时的点点滴滴..

曾有才   2014-10-4 15:01 楼主
如题,虽然现在不是从事电子方面的工作,但回忆起当年学习电子知识的点点滴滴,依然让人嘘唏不已..... 话说当年真的是虚度光阴,虽然不是迷恋于DOTA,但也实属于虚度光阴,而当年,也是曾经疯狂一般的迷恋起单片机,迷恋起C,迷恋起汇编 而对于当年的做的小东西,现在再回头看看仍旧耿耿于怀,当年由于条件所限未能得到很好的指导也没能有过多的探讨,以至于自己做的东西是否存在比较大的缺陷也未能了解到,所以今天重新翻出以前的旧资料,希望能得到大家的指导.... 当年做的这个小东西主要是 通过MCS-51单片机与YHY502B射频读写模块设计读写器对射频卡(Mifare非接触式IC卡)进行识别.. 下面这些是当年保存留下来的一些资料 射频读卡器的设计:
1、利用单片机AT89s52对YHY502B射频模块进行控制,实现射频卡的识别。
2、单片机AT89S52与YHY502B射频模块之间采用SPI接口进行通讯。
3、一个LED指示灯和一个蜂鸣器
4、支持ISO14443-A协议的RFID,频率:13.56MHz,感应距离为0-60mm。
5、电源电压:12V
6、功耗<0.2W、低功耗造就低故障
这个读卡器设计主要由以下几部分功能模块组成:MCU控制模块,射频模块,应答器,电源电路,时钟电路,看门狗电路,和天线部分电路,声音提示及显示模块组成;主要是对射频卡发出的信号进行读取与校验,因为本系统射频卡只是识别卡,只需将其唯一的32位ID读出,采用spi通讯方式,然后经校验无误后与MCU已存的ID进行比较,即可进行身份的识别确定。
射频卡阅读器的设计分为硬件电路设计和软件程序设计,硬件电路设计是射频卡阅读器的核心,也是软件程序设计的基础。硬件电路主要包括MCU控制模块(AT89S52)、YHY502B射频模块、声音提示模块等。软件程序设计主要包括射频模块的基本操作、MCU通过控制射频基站模块完成对射频卡的操作、声音提示程序等,采用SPI接口通讯方式
采用AT89S52作为主控芯片,管脚分布如下
图片1.png
YHY502B射频模块介绍
YHY502B射频读写模块采用基于ISO14443标准的非接触卡读卡机专用芯片,采用0.6微米CMOS EEPROM工艺,支持ISO14443-typeA协议,支持Mifare算法,支持Mifare one s50,s70,FM11RF08等兼容卡片,是低功耗模块,具有串行SPI接口器件,能自动感应到靠近天线区的卡片,并产生中断信号,具有TTL和CMOS两种电压工作方式,宽电压工作3.3-5V,内置硬看门狗,具备高可靠性,抗干扰处理,EMC性能优良。
YHY502B射频模块功能框图,引脚功能,接法
图片2.png
引脚配置:
图片3.png
图片4.png
图片5.png
天线引脚
TXl,TX2:发送器引脚。通过TXl和TX2发送13.56MHz的能量载波。
RX:接收器引脚。接收从天线耦合得到的来自卡片的13.56MHz输入载。
YHY502B射频模块采用13.56M的晶振。
单片机AT89S52与YHY502B射频模块的连接
图片6.png
图片7.png
电源部分:由于采用12V直流稳压电源,而AT89S52的工作电压为5(1±0.2)V,所以采用用7805cv稳压芯片,构成一个输出为5V的稳压电路. 图片8.png
7805在降压电路中的使用应注意以下事项:
1、输入输出压差不能太大,太大则转换效率急速降低,而且容易击穿损坏;
2、输出电流不能太大,1.5A 是其极限值。大电流的输出,散热片的尺寸要足够大,否则会导致高温保护或热击穿;
3、输入输出压差也不能太小,大小效率很差。
声音提示部分:对射频卡进行各种操作时,如果操作成功,射频卡阅读器发出声音提示操作成功。发声的器件选用蜂呜器,该器件使用方便、价格便宜。单片机的I/O口驱动能力有限,不能直接驱动蜂鸣器发声,通过三极管来驱动蜂鸣器,用AT89S52单片机的P21引脚来控制蜂鸣器的发声.
图片9.png
软件设计部分: 非接触式IC卡与阅读器的交易过程,实际上就是Mifare卡和阅读器之间的数据交换和对Mi fare卡内EEPROM存储器中的数据进行处理的过程。在数据交换过程中。为了确保卡和阅读器之间数据的同步及数据能被正确接收、识别,需要建立系统的通信协(ISO14443TypeA协议)。在交易的过程中MFl S50卡遵守通信协议,根据接收的指令,在有限状态机的控制下执行一个工作过程,从而完成需要的功能,由于YHY502B射频模块支ISO14443TypeA协议,支持MIFARE标准加密算法,所以我们不必关心射频基站复杂的控制算法,只需采用SPI接口发送命令就可以对卡片进行完整的操作. 本系统的软件设计核心是对射频卡发出的信号进行读取和校验,本系统所用到的射频卡是只读卡,所用只需将其唯一的32位ID读出,然后检验无误后与MCU中已存的ID进行比较,再进行相对的操作。
主程序流程图
图片10.png
YHY502B射频模块的命令列表
序号
命令
描述
1
0x01
读取模块型号
2
0x02
读取模块序列号
3
0x03
设置模块硬件掉电
4
0x10
读取模块固体版本
5
0x11
设置模块软件掉电
6
0x12
设置卡休眠
7
0x13
设置自动寻卡
8
0x19
读卡类型
9
0x20
寻卡
10
0x21
读块
11
0x22
写块
12
0x23
初始化钱包
13
0x24
读钱包
14
0x25
钱包充值
15
0x26
钱包扣款
16
0x30
读模块EEPROM
17
0x31
写模块EEPROM
SPI通讯协议: SPI 总线是Motorola公司推出的三线同步接口,同步串行3线方式进行通信:一条时钟线SCK,一条数据输入线MOSI,一条数据输出线MISO;用于CPU与各种外围器件进行全双工、同步串行通讯。SPI主要特点有:可以同时发出和接收串行数据;可以当作主机或从机工作;提供频率可编程时钟;发送结束中断标志;写冲突保护;总线竞争保护等
总时序图:
图片11.png
SPI是一个环形总线结构,由NSS、SCL、MOSI、MISO构成,其时序主要是在SCL的控制下,两个双向移位寄存器进行数据交换。
假设下面的8位寄存器装的是待发送的数据10101010,上升沿发送、下降沿接收、高位先发送。那么第一个上升沿来的时候 数据将会是MISO=1;寄存器=0101010x。下降沿到来的时候,MOSI上的电平将所存到寄存器中去,那么这时寄存器=0101010MOSI,这样在8个时钟脉冲以后,两个寄存器的内容互相交换一次。这样就完成里一个spi时序。
例子:
假设主机和从机初始化就绪:并且主机的sbuff=0xaa,从机的sbuff=0x55,下面将分步对spi的8个时钟周期的数据情况演示一遍:假设上升沿发送数据
脉冲 主机sbuff 从机sbuff MOSI MISO
0 10101010 01010101 0 0
1上 0101010x 1010101x 0 1
1下 01010100 10101011 0 1
2上 1010100x 0101011x 1 0
2下 10101001 01010110 1 0
3上 0101001x 1010110x 0 1
3下 01010010 10101101 0 1
4上 1010010x 0101101x 1 0
4下 10100101 01011010 1 0
5上 0100101x 1011010x 0 1
5下 01001010 10110101 0 1
6上 1001010x 0110101x 1 0
6下 10010101 01101010 1 0
7上 0010101x 1101010x 0 1
7下 00101010 11010101 0 1
8上 0101010x 1010101x 1 0
8下 01010101 10101010 1 0
2.SPI接口通讯协议定义
数据格式:状态字+长度字+命令字+数据域+校验字
状态字:总线状态字,开始通讯时发送一个状态字
长度字:指明从长度字到数据域最后一个字节的字节数
命令字:本条命令的含义
数据域:此项可以为空
校验字:从长度字到数据域最后一字节的逐字节异或值
返回数据格式:长度字+接收到的命令字+数据域+校验字
系统的子程序设计
状态字说明:
状态
状态字
说明
SPI Ready
0xF0
准备
SPI Busy
0xaa
SPI Read
0xbb
SPI Write
0xcc
(1)SPI的程序设计
本系统的软件设计核心是对射频卡发出的信号进行读取和校验,本系统所用到的射频卡是只读卡,所用只需将其唯一的32位ID读出,然后检验无误后与MCU中已存的ID进行比较,再进行相对的操作。
寻卡方式如下:
发送
状态字
长度字
命令字
检验字
0xcc
0x02
0x20
0x22
返回
状态字
长度字
命令字
CARDSN
检验字
正确
0xbb
0x06
0x20
4Bytes
检验值
错误
0xbb
0x02
0xdf
------------
0xdd
这就需要从SPI总线读和写数据,并进行检验码判断,首先还得判断SPI总线状态。
unsigned char SPIRWByte(unsigned char cSendByte)
函数说明:
功能:向SPI总线发送一个字节的数据
输入参数:cSendByte (将要发送到MISO的字节)
返回参数: 从MOSI读取到的字节
unsigned char SPIRWByte(unsigned char cSendByte)
{
unsigned char data i = 8;
unsigned char cRecByte;
while (i--)
{
cRecByte += cRecByte;
SCL = 0;
MOSI = (bit)(cSendByte & 0x80);
cSendByte += cSendByte;
cRecByte |= (unsigned char) MISO ;
SCL = 1;
}
SCL = 1;
return cRecByte;
}
unsigned char spi_cStatus(void)
函数说明:
功能:查询SPI总线状态
输入参数:无
返回参数: 从MOSI返回一个状态字
unsigned char spi_cStatus(void)
{
unsigned char cStatus;
NSS=0;
cStatus=SPIRWByte(spibusy);
cStatus=SPIRWByte(0xFF);
NSS=1;
return cStatus;
}
unsigned char SPI_Write(unsigned char *cP)
函数说明:
功能:向SPI总线发送数组CP中的数据
输入参数:*CP
返回参数: 返回一个校验值
unsigned char SPI_Write(unsigned char *cP)
{
unsigned char i,cStatus;
unsigned char cCheckSum = 0;
NSS=0;
cStatus=SPIRWByte(spiwrite);
for(i=0; i
{
cCheckSum ^= cP;
cStatus=SPIRWByte(cP);
}
cStatus=SPIRWByte(cCheckSum);
NSS=1;
return cStatus;
}
unsigned char SPI_Read(unsigned char *cP)
函数说明:
功能::读取SPI总线的数据包,并保存在数组CP中
输入参数:*CP
返回参数: 返回一个读取正确与否的标识位
unsigned char SPI_Read(unsigned char *cP)
{
unsigned char cCnt,cStatus;
unsigned char cCheckSum = 0;
for (cCnt=0; cCnt<100; cCnt++)
{
cStatus=spi_cStatus(); //查询SPI总线状态
if(cStatus==0xF0)
{
cCnt=253;
}
delay(10);
}
if(cCnt==254) //卡操作结束,可以回传数据
{
NSS=0;
cCnt=SPIRWByte(spiread);
cP[0]=2;
for (cCnt=0; cCnt
{
cP[cCnt] = SPIRWByte(0xFF);
cCheckSum ^= cP[cCnt];
if(cP[0]>32)
{
NSS=1;
return FAILURE;
}
}
cP[cCnt] = SPIRWByte(0xFF);
NSS=1;
if (cCheckSum == cP[cCnt])
{
return SUCCESS;
}
}
return FAILURE;
}
(2) 初始化程序设计
系统初始化:
void InitializeSystem()
延时函数:
void delay(unsigned int x)
void delay_50us(uchar _50us)
void delay_1ms (uint _1ms)
void delay_10ms(unsigned int _10ms)
蜂鸣器驱动:
void beep(uchar n)
模块复位程序:
void Reset_HY502(void)
软件测试
软件调试是通过对程序的汇编、连接、执行来发现程序中存在的语法错误与逻辑错误并加以排除纠正的过程。本设计通过软件模拟SPI总线接口,实现非SPI接口单片机与SPI
接口的射频模块之间的数据传输,解决好时序问题是一个关键点,使用keil uVision3进行调试,单片机的程序设计调试分为两种,一种是使用软件模拟调试,意思就是用开发单片机程序的计算机去模拟单片机的指令执行,并虚拟单片机片内资源,从而实现调试的目的,但是软件调试存在一些问题,如计算机本身是多任务系统,划分执行时间片是由操作系统本身完成的,无法得到控制,这样就无法时时的模拟单片机的执行时序,也就是说 ,不可能像真正的单片机运行环境那样执行的指令在同样一个时间能完成(往往要完成的比单片机慢)。为了解决软件调试的问题,第二种是硬件调试,硬件调试其实也需要计算机软件的配合,大致过程是这样的:计算机软件把编译好的程序通过串行口、并行口或者USB口传输到硬件调试设备中(这个设备叫仿真器),仿真器仿真全部的单片机资源(所有的单片机接口,并且有真实的引脚输出),仿真器可以接入实际的电路中,然后与单片机一样执行。同时,仿真器也会返回单片机内部内存与时序等情况给计算机的辅助软件,这样 就可以在软件里看到真实的执行情况。不仅如此,还可以通过计算机断的软件实现单步、全速、运行到光标的常规调试手段。由于没有仿真器,所以只能采用软件模拟调试,主要是模拟SPI总线接口的时序问题,用来跟踪变量的赋值过程,以及查看内存堆栈的内容,查看这些内容的目的在于观察变量的赋值过程与赋值情况从而达到调试的目的。
阅读器的电路原理图
图片12.png
系统程序
MAIN.C
#include "main.h"
#include "hy502.h"
// SPI 总线状态定义
#define SPI_RDY 0xF0; // 准备
#define spibusy 0xaa // 忙
#define spiread 0xbb // 读
#define spiwrite 0xcc // 写
/////////////////////////////////////////////////////////////////////////////////////////////////////
// 延时函数
void delay(unsigned int x)
{
while(x)
{
x--;
}
}
///////////////////////////////////////////////////////////////////////
// Delay 1ms
///////////////////////////////////////////////////////////////////////
void delay_1ms (uint _1ms)
{
RCAP2LH = RCAP2_1ms;
T2LH = RCAP2_1ms;
ET2 = 0; // Disable timer2 interrupt
T2CON = 0x04; // 16-bit auto-reload, clear TF2, start timer
while (_1ms--)
{
while (!TF2);
TF2 = FALSE;
}
TR2 = FALSE;
}
///////////////////////////////////////////////////////////////////////
// Delay 10ms
///////////////////////////////////////////////////////////////////////
void delay_10ms(unsigned int _10ms)
{
RCAP2LH = RCAP2_10ms;
T2LH = RCAP2_10ms;
ET2 = 0; // Disable timer2 interrupt
T2CON = 0x04; // 16-bit auto-reload, clear TF2, start timer
while (_10ms--)
{
while (!TF2)
{
if(g_bReceCommandOk) // change by lin 2006-08-21
{
TR2 = FALSE;
TF2 = FALSE;
return;
}
}
TF2 = FALSE;
}
TR2 = FALSE;
}
/////////////////////////////////////////////////////////////////////////
//beep(n);n为鸣叫的声音次数
/////////////////////////////////////////////////////////////////////////
void beep(uchar n)
{
uchar i;
for(i=0;i
{
BUZ=0; //产生周期为1s的方波
delay_10ms(50); //delay 0.5s
BUZ=1;
delay_10ms(50);
BUZ=0;
}
}
/******************* SPI FUNCTION DEFINE ************************************/
/*****************************************************************************
*function: send a byte over SPI bus
发送一个字节
*****************************************************************************/
unsigned char SPIRWByte(unsigned char cSendByte)
{
unsigned char data i = 8;
unsigned char cRecByte;
while (i--)
{
cRecByte += cRecByte;
SCL = 0;
MOSI = (bit)(cSendByte & 0x80);
cSendByte += cSendByte;
cRecByte |= (unsigned char) MISO ;
SCL = 1;
}
SCL = 1;
return cRecByte;
}
/***** 查询SPI总线状态 *********/
unsigned char spi_cStatus(void)
{
unsigned char cStatus;
NSS=0;
cStatus=SPIRWByte(spibusy);
cStatus=SPIRWByte(0xFF);
NSS=1;
return cStatus;
}
/***** 读SPI总线数据 *********/
unsigned char SPI_Read(unsigned char *cP)
{
unsigned char cCnt,cStatus;
unsigned char cCheckSum = 0;
for (cCnt=0; cCnt<100; cCnt++)
{
cStatus=spi_cStatus(); //查询SPI总线状态
if(cStatus==0xF0)
{
cCnt=253;
}
delay(10);
}
if(cCnt==254) //卡操作结束,可以回传数据
{
NSS=0;
cCnt=SPIRWByte(spiread);
cP[0]=2;
for (cCnt=0; cCnt
{
cP[cCnt] = SPIRWByte(0xFF);
cCheckSum ^= cP[cCnt];
if(cP[0]>32)
{
NSS=1;
return FAILURE;
}
}
cP[cCnt] = SPIRWByte(0xFF);
NSS=1;
if (cCheckSum == cP[cCnt])
{
return SUCCESS;
}
}
return FAILURE;
}
/***** 写SPI总线数据 *********/
unsigned char SPI_Write(unsigned char *cP)
{
unsigned char i,cStatus;
unsigned char cCheckSum = 0;
NSS=0;
cStatus=SPIRWByte(spiwrite);
for(i=0; i
{
cCheckSum ^= cP;
cStatus=SPIRWByte(cP);
}
cStatus=SPIRWByte(cCheckSum);
NSS=1;
return cStatus;
}
//-----Reset rf module -------//
void Reset_HY502(void)
{
RST_HY=0; //Reset HY502 复位模块
delay_10ms(50);
RST_HY=1;
}
/************************** main function start here *******************************/
void main()
{
uchar idata cStatus;
InitializeSystem(); // 初始化系统
Reset_HY502();
LED_YELLOW =0; //test led
LED_GREEN=0;
delay_1ms(500);
LED_YELLOW =1;
LED_GREEN=1; //LED light 0.5s
beep(1); //test buzz
while (1)
{
LED_YELLOW=sig;
SPI_Write(ComSearchCard); //send search card and get card serial number command to HY502
delay_1ms(100); // delay for module execution
cStatus = SPI_Read(g_cReceBuf);
g_bCard = !sig;
while(g_bCard)
{
LED_YELLOW=sig;
SPI_Write(ComSearchCard);//发送寻卡命令
delay_1ms(100);
cStatus = SPI_Read(g_cReceBuf); //接收数据,数据存放在g_cReceBuf这数组里面
if(SUCCESS == cStatus)//接收数据正确,进入UID比较
{
LED_YELLOW=0;//有卡,状态灯亮
if((g_cReceBuf[2]==0xb3)&&(g_cReceBuf[3]==0x58)&&(g_cReceBuf[4]==0xa6)&&(g_cReceBuf[5]==0xce))
{
BUZ=1; //读到的卡号正确,蜂鸣器响一下,LED_GREEN一直亮,显示读到的卡号正确
delay_10ms(100);
BUZ = 0;
while(1)
{
LED_GREEN = 0;
}
}
LED_GREEN=0;//读到的卡号错误,LED_GREEN亮一下,蜂鸣器一直响,表示读到的卡号错误
delay_10ms(100);
LED_GREEN = 1;
while(1)
{
BUZ=1;
}
}
LED_YELLOW=1;//接收数据错误 ,LED_YELLOW(状态等)灭
delay_10ms(100);
}
}
}
/*****************************************************************************
*system initialize
*UART baudrate = 19200bps, baud generator is T1
*****************************************************************************/
void InitializeSystem()
{
ET2 = 0;
T2CON = 0x04;
PCON = 0x80;
SCON = 0x70;
TMOD = 0x21; //TMOD = 0x22;
TH1 = BAUD_19200; //默认波特率
TL1 = TH1;
TR1 = TRUE; // 波特率发生器
ET1=0;
EA=1;
EX0=0;//1;
IT0 = 1;
TR2=0;
ES = TRUE;
g_bReceCommandOk=0;
BUZ=0;
}
MAIN.H
#ifndef __MAIN_H__
#define __MAIN_H__
#include "reg52.h"
//#include "at89s52.h"
//#include "stc89c51.h"
#include "intrins.h"
#include "string.h"
#define uchar unsigned char
#define uint unsigned int
#define SUCCESS 0
#define FAILURE 1
#define MF1_S50 0
#define MF1_S70 1
#define MF0_ULIGHT 2
#define MF1_LIGHT 3
//设置波特率
#define OSC_FREQ 11059200L
#define BAUD_115200 256 - (OSC_FREQ/192L)/115200L // 255
#define BAUD_57600 256 - (OSC_FREQ/192L)/57600L // 254
#define BAUD_38400 256 - (OSC_FREQ/192L)/38400L // 253
#define BAUD_28800 256 - (OSC_FREQ/192L)/28800L // 252
#define BAUD_19200 256 - (OSC_FREQ/192L)/19200L // 250
#define BAUD_14400 256 - (OSC_FREQ/192L)/14400L // 248
#define BAUD_9600 256 - (OSC_FREQ/192L)/9600L // 244
// Timer2
#define RCAP2_50us 65536L - OSC_FREQ/240417L
#define RCAP2_1ms 65536L - OSC_FREQ/12021L
#define RCAP2_10ms 65536L - OSC_FREQ/1200L
#define RCAP2_1s 65536L - OSC_FREQ/12L
sfr16 RCAP2LH = 0xCA;
sfr16 T2LH = 0xCC;
#define TRUE 1
#define FALSE 0
/*******************************************************************************
* pin declare
*******************************************************************************/
// Port define
sbit LED_GREEN =P1^0; // LED定义
sbit BUZ =P2^1; // 蜂鸣器,1=beep,0=silence
sbit RST_HY =P1^2; // 复位管脚,低电平有效
sbit HY_BEEP =P1^3; // 模块BEEP输出,高电平驱动
sbit SCL = P1^7;
// SPI port
sbit NSS = P1^4; //接HY502 NSS
sbit MOSI = P1^5; //接HY502 MOSI
sbit MISO = P1^6; //接HY502 MISO
sbit LED_YELLOW =P3^7; // LED定义
sbit sig = P3^2; // INT0 low level active
//-----------------------------------------------------------------------------------------SendBuffer
unsigned char data cp[30]; // HY502 buffer
unsigned char idata g_cReceBuf[30]; //uart Receive buffer
bit g_bReceCommandOk; //flag of command receive OK
bit g_bCard; //flag of card in
unsigned char idata SelectedSnr[4]; //卡序列号
//command list in both UART & IIC, with out command header "0xAA, 0xBB" of UART
// CL: command length
// CC: command code
// CD: command data
// CL CC CD
// search card and get card serial number
unsigned char code ComSearchCard[] = {0x02, 0x20};
void InitializeSystem(); //initialize the MCU system
#endif
//------------------File end--------------------------
虽然当年因为钱的原因没有继续从事电子设计方面的相关工作,但仍然还是舍不得对电子设计的这一分热情,写程序,调试硬件的焦急与幸福,依然历历在目,这么多年过去了,依旧是那颗对电子设计那一份热爱的心,正如一个挚友说过,人生,就是因为有太多的不舍得,才有那么的多舍不得,也正是如此,才能有更精彩的人生....
祝福朋友们,更上一层楼吧...
本帖最后由 曾有才 于 2014-10-4 21:34 编辑

回复评论 (7)

有才!楼主现在在做什么工作。这些文字看着太熟悉。
    懒得很
点赞  2014-10-4 19:14
祝福朋友们,更上一层楼吧...

点赞  2014-10-4 21:58
LZ现在从事哪一行了?
点赞  2014-10-4 23:02
希望楼主可以回来啊
点赞  2014-10-6 09:24
大学时代,真是难忘















www.qmgfw.com集成墙面 www.315jcz.com集成灶 www.315dbw.com地板十大品牌 www.tmjcq.com集成墙面
点赞  2014-10-6 17:29
也祝愿楼主更上一层楼,楼主现在做什么呢,激情还在,也可以花些时间再搞一些东西啊~~~
点赞  2014-10-7 22:39
引用: 凤凰息梧桐 发表于 2014-10-6 09:24
希望楼主可以回来啊

已经回不去了....
点赞  2014-10-8 23:58
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复