STM32 + FM1702NL读卡器使用记录

一、前言

本编文章记录使用stm32驱动fm1702nl的一些硬件经验,首次使用FM1702这款芯片,这款芯片分为SL和NL两种型号,SL封装是24脚的,NL封装是32脚的,SL只支持SPI通讯,NL支持IO和SPI通讯,本文虽然我使用了NL,但是仍然使用的是SPI通讯,与SL无区别。
STM32 + FM1702NL读卡器使用记录_第1张图片STM32 + FM1702NL读卡器使用记录_第2张图片

目录

  • 一、前言
  • 二、环境
  • 三、正文
      • 硬件部分
      • 软件部分
        • 下位机软件
        • 上位机软件
      • 卡片介绍
      • 结构部分
  • 四、结语

二、环境

stm32 spi驱动
参考链接

三、正文

硬件部分

这里首先总结一下硬件接线图,手册中引脚
图1
STM32 + FM1702NL读卡器使用记录_第3张图片
图2
STM32 + FM1702NL读卡器使用记录_第4张图片

注意图1,A0不是MOSI,AO是单片机端的MOSI,这里AO是MISO,与单片机MOSI输出连接。DO是MOSI,与单片机MISO连接,图2的网络标号是单片机端的标号。
因为这里我就搞错了,这个官方手册也不是很严谨,所以有50%的概率出错,这里我为大家纠正一下。放上引脚图
STM32 + FM1702NL读卡器使用记录_第5张图片
STM32 + FM1702NL读卡器使用记录_第6张图片

这里还有一个难点就是天线的设计,天线设计不好就会导致信号不好或者无法使用,这里天线我也没详细研究,参照成品电路设计直接使用,不同IC天线设计参照网上更详细天线线圈设计资料。
STM32 + FM1702NL读卡器使用记录_第7张图片STM32 + FM1702NL读卡器使用记录_第8张图片

软件部分

下位机软件

软件核心程序:
使用硬件SPI,首先main函数如下

int main(void)
{	 
	u32 len;	
	u8 usbstatus=0;	
	u8 led_count;
	u8 status = 0;
	u8 i=0;
	
	u8 bload=0;
	unsigned char readbuf[16];
	unsigned char writbuf[16];
	
//	u32 saveaddrgroup,address;//设备的编码地址
//	u8 datatemp[4]={0};//spiflash存储的地址,从内部flash读取

	//初始化系统
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 
	JTAG_Set(JTAG_SWD_DISABLE);     //=====关闭JTAG接口
  JTAG_Set(SWD_ENABLE);           //=====打开SWD接口 可以利用主板的SWD接口调试
	
	//初始化外设
	uart1_init(115200);	 	//串口初始化为115200 
	LED_Init();		  		//初始化与LED连接的硬件接口
	TIM3_Int_Init(49,7199);//10Khz的计数频率,计数到50为5ms  


//	//读取stm内部flash存储的spiflash存储到的地址
//	STMFLASH_Read(0X08070000,(u16*)datatemp,4);//设置FLASH 保存地址(必须为偶数,且其值要大于本代码所占用FLASH的大小+0X08000000),总共512k即0x80000,存储位置0x70000=458752,大于代码占用Code+RO-data+RW-data即可
//	saveaddrgroup=((datatemp[0]<<24)&0xFF000000)+((datatemp[1]<<16)&0x00FF0000)+((datatemp[2]<<8)&0x0000FF00)+(datatemp[3]&0x000000FF);
//	//读取stm内部flash存储的设备地址
//	STMFLASH_Read(0X08070400,(u16*)datatemp,4);
//	address=((datatemp[0]<<24)&0xFF000000)+((datatemp[1]<<16)&0x00FF0000)+((datatemp[2]<<8)&0x0000FF00)+(datatemp[3]&0x000000FF);

	Init_FM1702(0);
	

	delay_ms(500);
	USB_Port_Set(0); 	//USB先断开
	delay_ms(200);
	USB_Port_Set(1);	//USB再次连接
	Set_USBClock();   
	USB_Interrupts_Config();    
	USB_Init();	
	while(1)
	{
			/**************************USB虚拟串口部分*************************/
			//检测usb热插拔状态
			if(usbstatus!=bDeviceState){//USB连接状态发生了改变.
				usbstatus=bDeviceState;//记录新的状态
				if(usbstatus==CONFIGURED)printf("USB连接成功\r\n");//提示USB连接成功
				else printf("USB连接断开\r\n");//提示USB断开
			}
			//终端接收usb虚拟串口数据
			if(USB_USART_RX_STA&0x8000){	
				len=USB_USART_RX_STA&0x3FFF;//得到此次接收到的数据长度
				//printf("接收消息长度为:%d,内容为%X\r\n",len,USB_USART_RX_BUF[0]);//提示USB接收数据长度
				USB_USART_RX_STA=0;
				if(USB_USART_RX_BUF[0]==0x01&&len==1){//选取卡片
					status = Request1702(RF_CMD_REQUEST_ALL);//寻天线范围内所有的卡
					if(status == FM1702_OK){
						status = AntiColl();//冲突检测
						if(status != FM1702_OK){
							continue;
							usb_printf("卡片冲突!\r\n");//打印选卡状态
						}
						LEDG=1;
						LEDB=0;
						delay_ms(200);
						LEDB=1;
						status=Select_Card(); //选卡,这里不选卡,也可以直接进行读写
						if(status == FM1702_OK)
							usb_printf("选卡成功!\r\n");//打印选卡状态
						else
							usb_printf("选卡失败!\r\n");//打印选卡状态
						usb_printf("识别卡号:%02X%02X%02X%02X\r\n",UID[0],UID[1],UID[2],UID[3]);//打印卡号						
					}
					else{
						usb_printf("未识别到卡片\r\n");//打印选卡状态
						LEDG=1;
						LEDR=0;
						delay_ms(200);
						LEDR=1;
					}
				}
				else if(USB_USART_RX_BUF[0]==0x02&&len==3){//读取某一扇区和块数据
						bload=USB_USART_RX_BUF[1];//块0-63
					  
//					for(i=0;i<16;i++)
//							RevBuffer[i]=0xBB;
						for(i=0;i<6;i++)
							KeyAB_Buffer[i]=0xBB;
					
					  if(HL_Read(readbuf,bload,RF_CMD_AUTH_LB)==0)//RF_CMD_AUTH_LA、RF_CMD_AUTH_LB
							usb_printf("扇区:%d,块:%d  %d  读取成功!块数据内容:%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\r\n",bload/4,bload%4,bload,
					        readbuf[0],readbuf[1],readbuf[2],readbuf[3],readbuf[4],readbuf[5],readbuf[6],readbuf[7],readbuf[8],readbuf[9],readbuf[10],readbuf[11],readbuf[12],readbuf[13],readbuf[14],readbuf[15]);//提示USB接收数据长度
						else
							usb_printf("扇区:%d,块:%d  %d  读取失败!\r\n",bload/4,bload%4,bload);
					
				}
				else if(USB_USART_RX_BUF[0]==0x03&&len==2){
						bload=USB_USART_RX_BUF[1];//块0-63
						for(i=0;i<16;i++)
							writbuf[i]=0xaa;
					  if(HL_Write(writbuf,bload,RF_CMD_AUTH_LA)==0)//RF_CMD_AUTH_LA、RF_CMD_AUTH_LB
							usb_printf("写入成功!扇区:%d,块:%d  %d\r\n",bload/4,bload%4,bload);
						else
							usb_printf("写入失败!扇区:%d,块:%d  %d\r\n",bload/4,bload%4,bload);
				}
				else if(USB_USART_RX_BUF[0]==0x04&&len==2){
					
				}
				
				
				memset(USB_USART_RX_BUF,0,len);//缓存清零			
			}
			if(usbstatus==CONFIGURED){//判断USB已经连接
				
				
				
				
				//定时5*200ms进入一次
				if(led_flag>199){//5ms×200=1000ms
					led_flag=0;
					LEDB=LEDR=1;
					LEDG=!LEDG;//绿灯闪烁

				
				}
			}
			else{//USB未连接
				//定时500ms进入一次
				if(led_flag>99){//5ms×100=500ms
					led_flag=0;
					led_count++;
					if(led_count>7)led_count=0;
					led_run(led_count);//彩灯闪烁
				}
			}
			/**************************USB虚拟串口部分end*************************/
	}
}

使用到了USB虚拟串口通讯,程序文件目录如下
STM32 + FM1702NL读卡器使用记录_第9张图片
程序中基本只有1702驱动程序, 没有乱七八槽的程序
spi部分

#include "spi.h"
void SPI2_Init(void)
{
 	GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef  SPI_InitStructure;

	RCC_APB2PeriphClockCmd(	RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能 
	RCC_APB1PeriphClockCmd(	RCC_APB1Periph_SPI2,  ENABLE );//SPI2时钟使能 	
 
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //PB13/14/15复用推挽输出 
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB

 	GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);  //PB13/14/15上拉

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;		//设置SPI工作模式:设置为主SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;		//设置SPI的数据大小:SPI发送接收8位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;		//串行同步时钟的空闲状态为低电平
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;	//串行同步时钟的第二个跳变沿(上升或下降)数据被采样
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;		//NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;		//定义波特率预分频的值:波特率预分频值为256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;	//指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7;	//CRC值计算的多项式
	SPI_Init(SPI2, &SPI_InitStructure);  //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
	SPI_Cmd(SPI2, ENABLE); //使能SPI外设
	SPI2_ReadWriteByte(0xff);//启动传输		 

}   
//SPI 速度设置函数
//SpeedSet:
//SPI_BaudRatePrescaler_2   2分频   
//SPI_BaudRatePrescaler_8   8分频   
//SPI_BaudRatePrescaler_16  16分频  
//SPI_BaudRatePrescaler_256 256分频 
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
  assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	SPI2->CR1&=0XFFC7;
	SPI2->CR1|=SPI_BaudRatePrescaler;	//设置SPI2速度 
	SPI_Cmd(SPI2,ENABLE); 
} 
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{		
	u8 retry=0;				 	
	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
	{
		retry++;
		if(retry>200)return 0;
	}			  
	SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
	retry=0;

	while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
	{
		retry++;
		if(retry>200)return 0;
	}	  						    
	return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据					    
}

FM1702部分核心代码

unsigned char 	F_FIFO_Buff[16];
unsigned char 	J_FIFO_Buff[16];
unsigned char 	RevBuffer[16];
unsigned char 	FIFO_Buff[16];
/* FM1702命令发送接收缓冲区 */
unsigned char 	UID[5];		            /* 序列号 */
unsigned char  Secnr;
unsigned char  tagtype[2];

unsigned char   KeyAB_Buffer[6];//A或B密钥

void FM1702NL_Init(void)
{ 
	GPIO_InitTypeDef GPIO_InitStructure;
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能PORTA,PORTC时钟

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_12;				 //reset  nss
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
	GPIO_Init(GPIOB, &GPIO_InitStructure);					 //根据设定参数初始化GPIOA.5
	GPIO_SetBits(GPIOB,GPIO_Pin_12);						 //FM17XX NSS拉高 开始复位

	SPI2_Init();//spi2初始化

}
/****************************************************************/
/*名称: Init_FM1702 */
/*功能: 该函数实现对FM1702初始化操作*/
/*输入: mode:工作模式,  0:TYPEA模式*/
                     /* 1:TYPEA模式*/
                     /* 2:上海模式*/
/*输出: N/A */
/****************************************************************/
void Init_FM1702(uchar mode)
{
    uchar temp;
    uint	i;
	
	  FM1702NL_Init();//初始化SPI接口	
	
	  //复位芯片
	  delay_ms(100);
		FM1702NL_RST_H;
		delay_ms(200);
		FM1702NL_RST_L;
		delay_ms(100); //RST引脚下降沿 FM17xx复位 
    
   
    while(FM1702NL_Read(Command) != 0)		 // 等待Command = 0,FM1702复位成功 
    { 	
      temp=FM1702NL_Read(Command); 			
			usb_printf("复位中...%d\r\n",temp);		
			if(temp==FM1702_OK)break;
			delay_ms(500);
			LEDR=!LEDR;//红灯闪烁
    }

					
    FM1702NL_Write(Page_Sel,0x80);    //往Page寄存器写80hex初始化SPI接口
    for(i = 0; i < 0x1fff; i++){ /* 延时 */
        if(FM1702NL_Read(Command) == 0x00){
            FM1702NL_Write(Page_Sel,0x00);
        }
    }   		
    if(mode ==2){
        FM1702NL_Write(TypeSH,0x01);//上海标准
    }
    else{
        FM1702NL_Write(TypeSH,0x00);//MIFARE标准
    }  
}
/****************************************************************/
/*名称: FM1702NL_Write                                          */
/*功能: 写FM1702NL寄存器                                        */
/*输入: Address - 寄存器地址; value - 写入的值                   */
/*输出: N/A                                                     */
/****************************************************************/
void FM1702NL_Write(unsigned char Address, unsigned char Data)
{
    Address = ((Address<<1)&0x7E);
    FM1702NL_NSS_L;
    SPI2_ReadWriteByte(Address);
    SPI2_ReadWriteByte(Data);
    FM1702NL_NSS_H;
}
/****************************************************************/
/*名称:  FM1702NL_Read                                          */
/*功能: 读FM1702NL寄存器                                        */
/*输入: Address-寄存器地址                                      */
/*输出: 读出的值                                                */
/****************************************************************/
unsigned char FM1702NL_Read(unsigned char Address)
{
    unsigned char Temp;
    Address=(Address<<1)|0x80;   
    FM1702NL_NSS_L;
	  SPI2_ReadWriteByte(Address);
    Temp=SPI2_ReadWriteByte(0x0); 
    FM1702NL_NSS_H;
    return Temp;

头文件

#ifndef __FM1702NL_H
#define __FM1702NL_H	 
#include "sys.h"
#define FM1702NL_RST_H     {GPIOB->BSRR = GPIO_Pin_11;} 
#define FM1702NL_RST_L     {GPIOB->BRR = GPIO_Pin_11;}
#define FM1702NL_NSS_H     {GPIOB->BSRR = GPIO_Pin_12;} 
#define FM1702NL_NSS_L     {GPIOB->BRR = GPIO_Pin_12;}
/* FM1702命令发送接收缓冲区 */
extern unsigned char 	UID[5];		            /* 序列号 */
extern unsigned char   KeyAB_Buffer[6];//A或B密钥
#define uchar	unsigned char
#define uint	unsigned int
#define FM1702_TRUE   1
#define FM1702_FALSE  0
//#define ERROR		1
#define	OK		0
/* FM1702命令码 */
#define Transceive	0x1E			/* 发送接收命令 */
#define Transmit	0x1a			/* 发送命令 */
#define ReadE2		0x03			/* 读FM1702 EEPROM命令 */
#define WriteE2		0x01			/* 写FM1702 EEPROM命令 */
#define Authent1	0x0c			/* 验证命令认证过程第1步 */
#define Authent2	0x14			/* 验证命令认证过程第2步 */
#define LoadKeyE2	0x0b			/* 将密钥从EEPROM复制到KEY缓存 */
#define LoadKey		0x19			/* 将密钥从FIFO缓存复制到KEY缓存 */
#define RF_TimeOut	0xff			/* 发送命令延时时间 */
#define Req		0x01
#define Sel		0x02
/* 卡片类型定义定义 */
#define TYPEA_MODE	0			/* TypeA模式 */
//#define SHANGHAI_MODE	2			/* 上海模式 */
#define TM0_HIGH	0xf0			/* 定时器0高位,4MS定时 */
#define TM0_LOW		0x60			/* 定时器0低位 */
#define TIMEOUT		100			/* 超时计数器4MS×100=0.4秒 */
/* 射频卡通信命令码定义 */
#define RF_CMD_REQUEST_STD	0x26
#define RF_CMD_REQUEST_ALL	0x52
#define RF_CMD_ANTICOL		0x93
#define RF_CMD_SELECT		0x93
#define RF_CMD_AUTH_LA		0x60
#define RF_CMD_AUTH_LB		0x61
#define RF_CMD_READ		0x30
#define RF_CMD_WRITE		0xa0
#define RF_CMD_INC		    0xc1
#define RF_CMD_DEC		    0xc0
#define RF_CMD_RESTORE		0xc2
#define RF_CMD_TRANSFER		0xb0
#define RF_CMD_HALT		    0x50
/* 函数错误代码定义 ERR CODE  */
#define FM1702_OK		0		/* 正确 */
#define FM1702_NOTAGERR		1		/* 无卡 */
#define FM1702_CRCERR		2		/* 卡片CRC校验错误 */
#define FM1702_EMPTY		3		/* 数值溢出错误 */
#define FM1702_AUTHERR		4		/* 验证不成功 */
#define FM1702_PARITYERR	5		/* 卡片奇偶校验错误 */
#define FM1702_CODEERR		6		/* 通讯错误(BCC校验错) */
#define FM1702_SERNRERR		8		/* 卡片序列号错误(anti-collision 错误) */
#define FM1702_SELECTERR	9		/* 卡片数据长度字节错误(SELECT错误) */
#define FM1702_NOTAUTHERR	10		/* 卡片没有通过验证 */
#define FM1702_BITCOUNTERR	11		/* 从卡片接收到的位数错误 */
#define FM1702_BYTECOUNTERR	12		/* 从卡片接收到的字节数错误仅读函数有效 */
#define FM1702_RESTERR		13		/* 调用restore函数出错 */
#define FM1702_TRANSERR		14		/* 调用transfer函数出错 */
#define FM1702_WRITEERR		15		/* 调用write函数出错 */
#define FM1702_INCRERR		16		/* 调用increment函数出错 */
#define FM1702_DECRERR		17		/* 调用decrement函数出错 */
#define FM1702_READERR		18		/* 调用read函数出错 */
#define FM1702_LOADKEYERR	19		/* 调用LOADKEY函数出错 */
#define FM1702_FRAMINGERR	20		/* FM1702帧错误 */
#define FM1702_REQERR		21		/* 调用req函数出错 */
#define FM1702_SELERR		22		/* 调用sel函数出错 */
#define FM1702_ANTICOLLERR	23		/* 调用anticoll函数出错 */
#define FM1702_INTIVALERR	24		/* 调用初始化函数出错 */
#define FM1702_READVALERR	25		/* 调用高级读块值函数出错 */
#define FM1702_DESELECTERR	26
#define FM1702_CMD_ERR		42		/* 命令错误 */
/* FM1702寄存器定义 */
#define Page_Sel		0x00	/* 页写寄存器 */
#define Command			0x01	/* 命令寄存器 */
#define FM_FIFO			0x02	/* 64字节FIFO缓冲的输入输出寄存器 */
#define PrimaryStatus		0x03	/* 发射器接收器及FIFO的状态寄存器1 */
#define FIFO_Length		0x04	/* 当前FIFO内字节数寄存器 */
#define SecondaryStatus		0x05	/* 各种状态寄存器2 */
#define InterruptEn		0x06	/* 中断使能/禁止寄存器 */
#define Int_Req			0x07	/* 中断请求标识寄存器 */
#define Control			0x09	/* 控制寄存器 */
#define ErrorFlag		0x0A	/* 错误状态寄存器 */
#define CollPos			0x0B	/* 冲突检测寄存器 */
#define TimerValue		0x0c	/* 定时器当前值 */
#define Bit_Frame		0x0F	/* 位帧调整寄存器 */
#define TxControl		0x11	/* 发送控制寄存器 */
#define CWConductance		0x12	/* 选择发射脚TX1和TX2发射天线的阻抗 */
#define ModConductance		0x13	/* 定义输出驱动阻抗 */
#define CoderControl		0x14	/* 定义编码模式和时钟频率 */
#define DecoderControl		0x1a	/* 解码控制寄存器 */
#define RxControl1		0x19	/*接收增益控制*/
#define RxControl2		0x1e	/* 解码控制及选择接收源 */
#define RxWait			0x21	/* 选择发射和接收之间的时间间隔 */
#define ChannelRedundancy	0x22	/* RF通道检验模式设置寄存器 */
#define CRCPresetLSB		0x23
#define CRCPresetMSB		0x24
#define MFOUTSelect		0x26	/* mf OUT 选择配置寄存器 */
#define TimerClock		0x2a	/* 定时器周期设置寄存器 */
#define TimerControl		0x2b	/* 定时器控制寄存器 */
#define TimerReload		0x2c	/* 定时器初值寄存器 */
#define TypeSH			0x31	/* 上海标准选择寄存器 */
#define TestDigiSelect		0x3d	/* 测试管脚配置寄存器 */
/* Status Values */
#define ALL	0x01
#define KEYB	0x04
#define KEYA	0x00
#define _AB	0x40
#define CRC_A	1
#define CRC_B	2
#define CRC_OK	0
#define CRC_ERR 1
#define BCC_OK	0
#define BCC_ERR 1
/* 卡类型定义 */
//#define MIFARE_8K	0			/* MIFARE系列8KB卡片 */
#define MIFARE_TOKEN	1			/* MIFARE系列1KB TOKEN卡片 */
//#define SHANGHAI_8K	2			/* 上海标准系列8KB卡片 */
#define SHANGHAI_TOKEN	3			/* 上海标准系列1KB TOKEN卡片 */
#define mifare1			1
#define mifarepro		2
#define mifarelight		3
#define unknowncard     4
void FM1702NL_Init(void);//IO初始化
void Init_FM1702(uchar mode);/*功能: 该函数实现对FM1702初始化操作*/
void FM1702NL_Write(unsigned char Address, unsigned char Data);/*功能: 写FM1702NL寄存器*/
unsigned char FM1702NL_Read(unsigned char Address);/*功能: 读FM1702NL寄存器*/
uchar Clear_FIFO(void);/*功能: 该函数实现清空FM1702中FIFO的数据*/
void Write_FIFO(uchar count, uchar  *buff);/*功能: 该函数实现向FM1702的FIFO中写入x bytes数据*/
uchar Read_FIFO(uchar  *buff);/*功能: 该函数实现从FM1702的FIFO中读出x bytes数据*/
uchar Judge_Req(uchar  *buff);/*功能: 该函数实现对卡片复位应答信号的判断*/
uchar Check_UID(void);/*功能: 该函数实现对收到的卡片的序列号的判断*/
void Save_UID(uchar row, uchar col, uchar length);/*功能: 该函数实现保存卡片收到的序列号*/
void Set_BitFraming(uchar row, uchar col);/*功能: 该函数设置待发送数据的字节数*/
uchar Command_Send(uchar count, uchar  *buff, uchar Comm_Set);/*功能: 该函数实现向FM1702发送命令集的功能*/
uchar Read_E2(uchar lsb, uchar msb, uchar count, uchar  *buff);/*功能: 该函数实现从FM1702的EE中读出数据*/
uchar Write_E2(uchar lsb, uchar msb, uchar count, uchar  *buff);/*功能: 该函数实现向FM1702的EE中写入数据*/
uchar MIF_Halt(void);/*功能: 该函数实现暂停MIFARE卡*/
char M500HostCodeKey( uchar *uncoded, uchar *coded);/*转换密钥格式*/
uchar Load_keyE2_CPY(uchar Secnr, uchar Mode);/*功能: 该函数实现把E2中密码存入FM1702的keyRevBuffer中*/
uchar Load_keyE2(uchar Secnr, uchar Mode);/*功能: 该函数实现把E2中密码存入FM1702的keyRevBuffer中*/
uchar Request1702(uchar mode);/*功能: 该函数实现对放入FM1702操作范围之内的卡片的Request操作*/
uchar AntiColl(void);/*功能: 该函数实现对放入FM1702操作范围之内的卡片的防冲突检测*/
uchar Select_Card(void);/*功能: 该函数实现对放入FM1702操作范围之内的某张卡片进行选择*/
uchar Authentication(uchar  *UID, uchar SecNR, uchar mode);/*功能: 该函数实现密码认证的过程*/
uchar MIF_READ(uchar  *buff, uchar Block_Adr);/*功能: 该函数实现读MIFARE卡块的数值*/
uchar MIF_Write(uchar  *buff, uchar Block_Adr);/*功能: 该函数实现写MIFARE卡块的数值*/
uchar HL_Active(uchar Block_Adr, uchar Mode);/*功能: 该函数实现高级MIFARE卡激活命令*/
uchar HL_Read(uchar /*idata*/ *buff, uchar Block_Adr, uchar Mode);/*功能: 该函数实现高级读命令*/
uchar HL_Write(uchar /*idata*/ *buff, uchar Block_Adr, uchar Mode);/*功能: 该函数实现高级写命令*/
uchar MIF_Initival(uchar /*idata*/ *buff, uchar Block_Adr);/*功能: 该函数实现MIFARE卡初始化值操作*/
uchar MIF_Increment(uchar  *buff, uchar Block_Adr);/*功能: 该函数实现MIFARE卡自动增值操作*/
uchar MIF_Decrement(uchar  *buff, uchar Block_Adr);/*功能: 该函数实现MIFARE卡自动减值操作*/
uchar MIF_Restore(uchar Block_Adr);/*功能: 该函数实现MIFARE卡自动恢复,备份操作*/
uchar MIF_Transfer(uchar Block_Adr);/*功能: 该函数实现MIFARE卡电子钱包保存操作*/
#endif

程序源码,有意想不到的二重惊喜哦,第二重绝对值

上位机软件

。。。由于项目原因,按照下位机软件自定义协议后对应通讯匹配,暂不开源

卡片介绍

这里在简单记录一下S50卡扇区和后续程序二次开发重要注意事项:
简介
M1卡就是非接触式IC卡中应用最广泛的卡。M1卡就是Mifare非接触式感应卡,M1卡数据保存期为10年,可改写10万次,读无限次。无电源,自带天线,工作频率为13.56MHZ.M1卡内含加密控制逻辑和通讯逻辑电路。M1卡主要有两种,一种是S50和一种是S70。主要应用:门禁、考勤、会议签到、身份识别、物流、工业自动化、各种会员卡、如售饭、地铁、公交代币卡、俱乐部等电子消费、电子门票、动物识别、目标跟踪、洗衣管理、各种一卡通等等
卡片内容
M1 卡分为 16 个扇区,每个扇区由 4 块(块 0、块 1、块 2、块 3)组成,(我们也将 16 个扇区的 64 个块按绝对地址编号为 0~63,存贮结构如下图所示。而每个扇区的块3为控制块,包括密码keyA,存取控制,密码keyB。存取控制的作用是控制对应扇区记录的读写权限与keyA和keyB的关系。由于每个扇区都有独立的key和存取控制,因此M1卡可以做到一卡多用互不干扰。
每一张M1卡的0扇区0块都称作绝对地址块,这一串是在卡片出厂时厂商赋予的,代表着这张卡独立的身份识别信息。绝对地址块的内容已被固化,无法更改。
STM32 + FM1702NL读卡器使用记录_第10张图片
M1卡控制位

以下为控制位的结构,前6位为密钥A,中间4位存取控制,后6位密钥B。
例如 A1A2A3A4A5 FF078069 B1B2B3B4B5

          密码A           控制位         密码B

         字节0-5          字节6-9      字节10-15

4位控制位中前3位是真实的控制位,第4位是备用控制位,一般用不上,因此我们可以只分析前3位。
控制字这里不详细介绍,因为一般我们不涉及修改控制字,因为改不好会把卡片扇区改废,我手里的卡片就是将扇区块3全部数据改为0xAA,然后肯本没有这种控制字,密钥也无法解读数据,也无法覆写数据,扇区就废了,所以这里要说明的是在上面我的程序上二次开发,在涉及到修改扇区块3数据时,要避开控制字部分,上述链接程序支持密钥识别功能,输入A或B密钥,然后选择识别方式,验证成功即可读取和修改任意扇区数据(扇区0的块0除外)
卡片使用总结:
1)0扇区的1-2块,1-15扇区的0-3块随便用
2)密钥扇区非必要情况不要更改,密码默认全为FF,虽然A密钥读取为00,是隐藏字符,实际默认为FF,修改后A密钥读取也是00,实际为修改的密钥
3)控制字非必要不要更改,容易照成扇区损坏,当然调试程序成熟后可以使用
4)下载上述程序,免去基础专研,直接二次开发,要把软件核心掌握在自己手中

结构部分

简单设计一款结构,当天晚上就安排打印,妥妥的
顶视图
STM32 + FM1702NL读卡器使用记录_第11张图片
前视图
STM32 + FM1702NL读卡器使用记录_第12张图片
后视图
STM32 + FM1702NL读卡器使用记录_第13张图片
汉堡包结构
STM32 + FM1702NL读卡器使用记录_第14张图片
睡一觉取件
STM32 + FM1702NL读卡器使用记录_第15张图片
定制亚克力顶盖在路上
STM32 + FM1702NL读卡器使用记录_第16张图片
STM32 + FM1702NL读卡器使用记录_第17张图片

四、结语

技术发展和创新的路上有很多坎坷,在每一个困难面前抱有认真面对、努力专研、沟通交流的态度,一切的路皆是平坦无阻的,只要有一颗坚持的本心,剩下的交给时间!

你可能感兴趣的:(#,STM32/GD32,FM1702NL读卡器,FM1702+STM32,FM1702程序,FM1702密钥识别,读卡器设计开源)