本编文章记录使用stm32驱动fm1702nl的一些硬件经验,首次使用FM1702这款芯片,这款芯片分为SL和NL两种型号,SL封装是24脚的,NL封装是32脚的,SL只支持SPI通讯,NL支持IO和SPI通讯,本文虽然我使用了NL,但是仍然使用的是SPI通讯,与SL无区别。
stm32 spi驱动
参考链接
注意图1,A0不是MOSI,AO是单片机端的MOSI,这里AO是MISO,与单片机MOSI输出连接。DO是MOSI,与单片机MISO连接,图2的网络标号是单片机端的标号。
因为这里我就搞错了,这个官方手册也不是很严谨,所以有50%的概率出错,这里我为大家纠正一下。放上引脚图
这里还有一个难点就是天线的设计,天线设计不好就会导致信号不好或者无法使用,这里天线我也没详细研究,参照成品电路设计直接使用,不同IC天线设计参照网上更详细天线线圈设计资料。
软件核心程序:
使用硬件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虚拟串口通讯,程序文件目录如下
程序中基本只有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块都称作绝对地址块,这一串是在卡片出厂时厂商赋予的,代表着这张卡独立的身份识别信息。绝对地址块的内容已被固化,无法更改。
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)下载上述程序,免去基础专研,直接二次开发,要把软件核心掌握在自己手中
简单设计一款结构,当天晚上就安排打印,妥妥的
顶视图
前视图
后视图
汉堡包结构
睡一觉取件
定制亚克力顶盖在路上
技术发展和创新的路上有很多坎坷,在每一个困难面前抱有认真面对、努力专研、沟通交流的态度,一切的路皆是平坦无阻的,只要有一颗坚持的本心,剩下的交给时间!