参考大佬文章:STM32—驱动RFID-RC522模块_rc522 f4-程序员宅基地
硬件准备:
STM32f103c8t6开发板
RFID-RC522 射频模块(感应M1卡)
s90舵机(用于开门)
3.3v稳压模块(将6v电压转为3.3v给stm32 供电)
四节南孚电池加有盖电池盒(用于供电)
OLED显示屏(用于获取M1卡的卡号)
软件准备:
RFID-RC522模块读取门卡信息
RC522将信息存储到stm32中
stm32 对门卡信息与先前定义好的门卡信息匹配
匹配成功后驱动SG90舵机开门
输出一个PWM波形
通过设置PWM波形的占空比来使舵机转到不同的角度上(改变CRR的值来改变占空比)
棕色线:接地
红色线:接5v或5v以上的正极
橙色线:接PA0,接收输出的PWM波形
PWM.c
代码:
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
//开启时钟TIM2的外设时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//初始化PA0端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//选择内部时钟
TIM_InternalClockConfig(TIM2);
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 20000 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//初始化输出比较单元(PA1口对应的就是OC2通道)
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //设置CRR寄存器值
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
//启动定时器TIM2
TIM_Cmd(TIM2, ENABLE);
}
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2, Compare);
}
PWM.h代码:
#ifndef __PWM_H
#define __PWM_H
void PWM_Init(void);
void PWM_SetCompare2(uint16_t Compare);
#endif
servo.c 代码:
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Servo_Init(void)
{
PWM_Init();
}
void Servo_SetAngle(float Angle)
{
PWM_SetCompare2(Angle/180*2000+500);
}
Servo.h 代码:
#ifndef __SERVO_H__
#define __SERVO_H__
void Servo_Init(void);
void Servo_SetAngle(float Angle);
#endif
电气部分: 卡片的电气部分由一个天线和一个ASIC组成。 天线:就是几组绕线的线圈,体积小,已经封装在卡片内 ASIC:ASIC即专用集成电路,是指应特定用户要求和特定电子系统的需要而设计、制造的集成电路。 目前用CPLD(复杂可编程逻辑器件)和 FPGA(现场可编程逻辑阵列)来进行ASIC设计是最为流行的方式之一,它们的共性是都具有用户现场可编程特性,都支持边界扫描技术,但两者在集成度、速度以及编程方式上具有各自的特点,这样理解,ASIC就是卡片特点的一个集成电路。 卡片的ASIC包含了一个高速(106KB)的RF接口、一个控制单元、一个8K的EEPROM
工作过程: 读卡器会向M1卡发送一组固定频率的电磁波,卡片内有一个LC串联谐振电路,其工作频率与读卡器发送的电磁波频率相同,遂在电磁波的激励下,LC串联谐振电路会发生共振,从而使电容内产生电荷,在电容的另一端接有一个单向导电的电子泵,电子泵将产生的电荷转移到另一个电容中存储。当存储电容中的电荷达到2V的时候,此时电容就作为电源为其他电路提供工作电压,所以卡片就可以向读卡器发送数据,或者从读卡器接收数据,实现了读卡器与卡片的通信。
RC522各个引脚的作用:
SDA:也叫CS,串行数据线,用于与主控设备进行数据通信
SCK:串行时钟线,用于同步数据传输。
MOSI:主输出从输入线,由主控设备发送数据到RC522模块上
MISO:主输入从输出线,由RC522 模块发送数据到主控设备上
RC522
通过SPI
接口和单片机(stm32)进行通信,通过单片机向 RC522
内的寄存器写入特定的指令,使 RC522
根据寄存器中的值来执行相关的操作,从而达到和 M1
的通信。
SPI(Serial Peripheral Interface 串行外设接口)
是一种用于在微控制器和外部设备之间进行串行通信的协议。
SPI协议通过四条线(或更多)进行通信:
SCK(Serial Clock):时钟线,由主设备提供,用于同步数据的传输
MOSI(Master Out Slave In):主输出从输入线,由主设备发送数据到外设(此处为stm32 向RC522 模块发送数据)
MISO(Master In Slave In):主输入从输出线,由外设数据到主设备。
NSS(Slave Select):片选线,由主设备选择与之通信的特定外设
串行通信
串行通信是指一种逐位进行数据传输的通信方式,其中每个数据为按顺序依次发送或接收,在串行通信中,每个数据位与时钟信号同步,以确保正确的数据传输。
一下是RC522的寄存器和一些相关指令:
/
//RC522命令字
/
#define PCD_IDLE 0x00 //取消当前命令
#define PCD_AUTHENT 0x0E //验证密钥
#define PCD_RECEIVE 0x08 //接收数据
#define PCD_TRANSMIT 0x04 //发送数据
#define PCD_TRANSCEIVE 0x0C //发送并接收数据
#define PCD_RESETPHASE 0x0F //复位
#define PCD_CALCCRC 0x03 //CRC计算
/
//Mifare_One卡片命令字
/
#define PICC_REQIDL 0x26 //寻天线区内未进入休眠状态
#define PICC_REQALL 0x52 //寻天线区内全部卡
#define PICC_ANTICOLL1 0x93 //防冲撞
#define PICC_ANTICOLL2 0x95 //防冲撞
#define PICC_AUTHENT1A 0x60 //验证A密钥
#define PICC_AUTHENT1B 0x61 //验证B密钥
#define PICC_READ 0x30 //读块
#define PICC_WRITE 0xA0 //写块
#define PICC_DECREMENT 0xC0 //扣款
#define PICC_INCREMENT 0xC1 //充值
#define PICC_RESTORE 0xC2 //调块数据到缓冲区
#define PICC_TRANSFER 0xB0 //保存缓冲区中数据
#define PICC_HALT 0x50 //休眠
/* RC522 FIFO长度定义 */
#define DEF_FIFO_LENGTH 64 //FIFO size=64byte
#define MAXRLEN 18
/* RC522寄存器定义 */
// PAGE 0
#define RFU00 0x00 //保留
#define CommandReg 0x01 //启动和停止命令的执行
#define ComIEnReg 0x02 //中断请求传递的使能(Enable/Disable)
#define DivlEnReg 0x03 //中断请求传递的使能
#define ComIrqReg 0x04 //包含中断请求标志
#define DivIrqReg 0x05 //包含中断请求标志
#define ErrorReg 0x06 //错误标志,指示执行的上个命令的错误状态
#define Status1Reg 0x07 //包含通信的状态标识
#define Status2Reg 0x08 //包含接收器和发送器的状态标志
#define FIFODataReg 0x09 //64字节FIFO缓冲区的输入和输出
#define FIFOLevelReg 0x0A //指示FIFO中存储的字节数
#define WaterLevelReg 0x0B //定义FIFO下溢和上溢报警的FIFO深度
#define ControlReg 0x0C //不同的控制寄存器
#define BitFramingReg 0x0D //面向位的帧的调节
#define CollReg 0x0E //RF接口上检测到的第一个位冲突的位的位置
#define RFU0F 0x0F //保留
// PAGE 1
#define RFU10 0x10 //保留
#define ModeReg 0x11 //定义发送和接收的常用模式
#define TxModeReg 0x12 //定义发送过程的数据传输速率
#define RxModeReg 0x13 //定义接收过程中的数据传输速率
#define TxControlReg 0x14 //控制天线驱动器管教TX1和TX2的逻辑特性
#define TxAutoReg 0x15 //控制天线驱动器的设置
#define TxSelReg 0x16 //选择天线驱动器的内部源
#define RxSelReg 0x17 //选择内部的接收器设置
#define RxThresholdReg 0x18 //选择位译码器的阈值
#define DemodReg 0x19 //定义解调器的设置
#define RFU1A 0x1A //保留
#define RFU1B 0x1B //保留
#define MifareReg 0x1C //控制ISO 14443/MIFARE模式中106kbit/s的通信
#define RFU1D 0x1D //保留
#define RFU1E 0x1E //保留
#define SerialSpeedReg 0x1F //选择串行UART接口的速率
// PAGE 2
#define RFU20 0x20 //保留
#define CRCResultRegM 0x21 //显示CRC计算的实际MSB值
#define CRCResultRegL 0x22 //显示CRC计算的实际LSB值
#define RFU23 0x23 //保留
#define ModWidthReg 0x24 //控制ModWidth的设置
#define RFU25 0x25 //保留
#define RFCfgReg 0x26 //配置接收器增益
#define GsNReg 0x27 //选择天线驱动器管脚(TX1和TX2)的调制电导
#define CWGsCfgReg 0x28 //选择天线驱动器管脚的调制电导
#define ModGsCfgReg 0x29 //选择天线驱动器管脚的调制电导
#define TModeReg 0x2A //定义内部定时器的设置
#define TPrescalerReg 0x2B //定义内部定时器的设置
#define TReloadRegH 0x2C //描述16位长的定时器重装值
#define TReloadRegL 0x2D //描述16位长的定时器重装值
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F //显示16位长的实际定时器值
// PAGE 3
#define RFU30 0x30 //保留
#define TestSel1Reg 0x31 //常用测试信号配置
#define TestSel2Reg 0x32 //常用测试信号配置和PRBS控制
#define TestPinEnReg 0x33 //D1-D7输出驱动器的使能管脚(仅用于串行接口)
#define TestPinValueReg 0x34 //定义D1-D7用作I/O总线时的值
#define TestBusReg 0x35 //显示内部测试总线的状态
#define AutoTestReg 0x36 //控制数字自测试
#define VersionReg 0x37 //显示版本
#define AnalogTestReg 0x38 //控制管脚AUX1和AUX2
#define TestDAC1Reg 0x39 //定义TestDAC1的测试值
#define TestDAC2Reg 0x3A //定义TestDAC2的测试值
#define TestADCReg 0x3B //显示ADCI和Q通道的实际值
#define RFU3C 0x3C //保留
#define RFU3D 0x3D //保留
#define RFU3E 0x3E //保留
#define RFU3F 0x3F //保留
/* 和RC522通信时返回的错误代码 */
#define MI_OK 0x26
#define MI_NOTAGERR 0xcc
#define MI_ERR 0xbb
由于RC522 是通过 SPI 与STM32 进行通信的,所以我们要配置STM32 中的引脚,以及宏定义一些引脚的电平操作。
/* RC522引脚连接说明(SPI1的引脚) :
CS:PA4( 接的SDA引脚 )
SCK:PA5
MISO:PA6
MOSI:PA7
RST:PB0
*/
void RC522_GPIO_Init( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_Init( GPIOA, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init( GPIOA, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_Init( GPIOA, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init( GPIOB, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init( GPIOA, &GPIO_InitStructure );
}
/* IO口操作函数 */
#define RC522_CS_Enable() GPIO_ResetBits ( GPIOA, GPIO_Pin_4 ) //打开串行数据传输
#define RC522_CS_Disable() GPIO_SetBits ( GPIOA, GPIO_Pin_4 ) //关闭串行数据传输
#define RC522_Reset_Enable() GPIO_ResetBits( GPIOB, GPIO_Pin_0 )
#define RC522_Reset_Disable() GPIO_SetBits( GPIOB, GPIO_Pin_0 )
#define RC522_SCK_0() GPIO_ResetBits( GPIOA, GPIO_Pin_5 )
#define RC522_SCK_1() GPIO_SetBits( GPIOA, GPIO_Pin_5 )
#define RC522_MOSI_0() GPIO_ResetBits( GPIOA, GPIO_Pin_7 )
#define RC522_MOSI_1() GPIO_SetBits( GPIOA, GPIO_Pin_7 )
#define RC522_MISO_GET() GPIO_ReadInputDataBit( GPIOA, GPIO_Pin_6 )
初始化好单片机的引脚后,接着是通过SPI 发送和接收STM32 的数据,具体函数代码如下(高位先行):
/* 软件模拟SPI发送一个字节数据,高位先行 */
void RC522_SPI_SendByte( uint8_t byte )
{
uint8_t n;
for( n=0;n<8;n++ )
{
if( byte&0x80 )
RC522_MOSI_1();
else
RC522_MOSI_0();
Delay_us(200);
RC522_SCK_0();
Delay_us(200);
RC522_SCK_1();
Delay_us(200);
byte<<=1;
}
}
/* 软件模拟SPI读取一个字节数据,先读高位 */
uint8_t RC522_SPI_ReadByte( void )
{
uint8_t n,data;
for( n=0;n<8;n++ )
{
data<<=1;
RC522_SCK_0();
Delay_us(200);
if( RC522_MISO_GET()==1 )
data|=0x01;
Delay_us(200);
RC522_SCK_1();
Delay_us(200);
}
return data;
}
如此,单片机和RC522之间的通信基础机制就建立起来了,下一步就是建立在通信基础上的操作了。
单片机是向RC522的寄存器操作来驱动RC522的,所以会有这几种基本操作:
读取RC522指定寄存器的值
向RC522指定寄存器中写入指定的数据
置位RC522指定寄存器的指定位
清位RC522指定寄存器的指定位
具体操作函数如下:
/* 软件模拟SPI发送一个字节数据,高位先行 */
void RC522_SPI_SendByte( uint8_t byte )
{
uint8_t n;
for( n=0;n<8;n++ )
{
if( byte&0x80 )
RC522_MOSI_1();
else
RC522_MOSI_0();
Delay_us(200);
RC522_SCK_0();
Delay_us(200);
RC522_SCK_1();
Delay_us(200);
byte<<=1;
}
}
/* 软件模拟SPI读取一个字节数据,先读高位 */
uint8_t RC522_SPI_ReadByte( void )
{
uint8_t n,data;
for( n=0;n<8;n++ )
{
data<<=1;
RC522_SCK_0();
Delay_us(200);
if( RC522_MISO_GET()==1 )
data|=0x01;
Delay_us(200);
RC522_SCK_1();
Delay_us(200);
}
return data;
}
上面说了寄存器、指令、对寄存器的操作,这里介绍一些对RC522的基本操作,包括:
开启天线
关闭天线
复位RC522
设置RC522工作方式
RC522与M1通信前必须开启天线,进行复位,然后设置RC522的工作方式!下面介绍一下相关代码:
/**
* @brief :开启天线
* @param :无
* @retval :无
*/
void RC522_Antenna_On( void )
{
uint8_t k;
k = RC522_Read_Register( TxControlReg );
/* 判断天线是否开启 */
if( !( k&0x03 ) )
RC522_SetBit_Register( TxControlReg, 0x03 );
}
/**
* @brief :关闭天线
* @param :无
* @retval :无
*/
void RC522_Antenna_Off( void )
{
/* 直接对相应位清零 */
RC522_ClearBit_Register( TxControlReg, 0x03 );
}
/**
* @brief :复位RC522
* @param :无
* @retval :无
*/
void RC522_Rese( void )
{
RC522_Reset_Disable();
Delay_us ( 1 );
RC522_Reset_Enable();
Delay_us ( 1 );
RC522_Reset_Disable();
Delay_us ( 1 );
RC522_Write_Register( CommandReg, 0x0F );
while( RC522_Read_Register( CommandReg )&0x10 );
/* 缓冲一下 */
Delay_us ( 1 );
RC522_Write_Register( ModeReg, 0x3D ); //定义发送和接收常用模式
RC522_Write_Register( TReloadRegL, 30 ); //16位定时器低位
RC522_Write_Register( TReloadRegH, 0 ); //16位定时器高位
RC522_Write_Register( TModeReg, 0x8D ); //内部定时器的设置
RC522_Write_Register( TPrescalerReg, 0x3E ); //设置定时器分频系数
RC522_Write_Register( TxAutoReg, 0x40 ); //调制发送信号为100%ASK
}
/**
* @brief :设置RC522的工作方式
* @param :Type:工作方式
* @retval :无
M500PcdConfigISOType
*/
void RC522_Config_Type( char Type )
{
if( Type=='A' )
{
RC522_ClearBit_Register( Status2Reg, 0x08 );
RC522_Write_Register( ModeReg, 0x3D );
RC522_Write_Register( RxSelReg, 0x86 );
RC522_Write_Register( RFCfgReg, 0x7F );
RC522_Write_Register( TReloadRegL, 30 );
RC522_Write_Register( TReloadRegH, 0 );
RC522_Write_Register( TModeReg, 0x8D );
RC522_Write_Register( TPrescalerReg, 0x3E );
Delay_us(2);
/* 开天线 */
RC522_Antenna_On();
}
}
/**
* @brief :初始化RC522引脚,打开天线,复位RC522,设置RC522的工作方式
* @param :无
* @retval :无
*/
void RC522_Init(void)
{
RC522_GPIO_Init();
RC522_Antenna_On();
RC522_Rese();
RC522_Config_Type('A');
}
这部分是最重要的步骤,RC522与M1的通信是工程要实现的目的,而且要遵守前面提到的M1卡与RC522通信的步骤以及M1卡的内部构造,包括以下操作:
通过RC522和M1卡通讯(数据的双向传输)
寻卡
防冲突
用RC522计算CRC16(循环冗余校验)
选定卡片
校验卡片密码
在M1卡的指定块地址写入指定数据
读取M1卡的指定块地址的数据
让卡片进入休眠模式
话不多说,上代码,代码中都有按照我理解的一些注释:
/**
* @brief :通过RC522和ISO14443卡通讯
* @param :ucCommand:RC522命令字
* pInData:通过RC522发送到卡片的数据
* ucInLenByte:发送数据的字节长度
* pOutData:接收到的卡片返回数据
* pOutLenBit:返回数据的位长度
* @retval :状态值MI_OK,成功
*/
char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit )
{
char cStatus = MI_ERR;
uint8_t ucIrqEn = 0x00;
uint8_t ucWaitFor = 0x00;
uint8_t ucLastBits;
uint8_t ucN;
uint32_t ul;
switch ( ucCommand )
{
case PCD_AUTHENT: //Mifare认证
ucIrqEn = 0x12; //允许错误中断请求ErrIEn 允许空闲中断IdleIEn
ucWaitFor = 0x10; //认证寻卡等待时候 查询空闲中断标志位
break;
case PCD_TRANSCEIVE: //接收发送 发送接收
ucIrqEn = 0x77; //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
ucWaitFor = 0x30; //寻卡等待时候 查询接收中断标志位与 空闲中断标志位
break;
default:
break;
}
RC522_Write_Register ( ComIEnReg, ucIrqEn | 0x80 ); //IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
RC522_ClearBit_Register ( ComIrqReg, 0x80 ); //Set1该位清零时,CommIRqReg的屏蔽位清零
RC522_Write_Register ( CommandReg, PCD_IDLE ); //写空闲命令
RC522_SetBit_Register ( FIFOLevelReg, 0x80 ); //置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
for ( ul = 0; ul < ucInLenByte; ul ++ )
RC522_Write_Register ( FIFODataReg, pInData [ ul ] ); //写数据进FIFOdata
RC522_Write_Register ( CommandReg, ucCommand ); //写命令
if ( ucCommand == PCD_TRANSCEIVE )
RC522_SetBit_Register(BitFramingReg,0x80); //StartSend置位启动数据发送 该位与收发命令使用时才有效
ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25ms
do //认证 与寻卡等待时间
{
ucN = RC522_Read_Register ( ComIrqReg ); //查询事件中断
ul --;
} while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) ); //退出条件i=0,定时器中断,与写空闲命令
RC522_ClearBit_Register ( BitFramingReg, 0x80 ); //清理允许StartSend位
if ( ul != 0 )
{
if ( ! ( RC522_Read_Register ( ErrorReg ) & 0x1B ) ) //读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
{
cStatus = MI_OK;
if ( ucN & ucIrqEn & 0x01 ) //是否发生定时器中断
cStatus = MI_NOTAGERR;
if ( ucCommand == PCD_TRANSCEIVE )
{
ucN = RC522_Read_Register ( FIFOLevelReg ); //读FIFO中保存的字节数
ucLastBits = RC522_Read_Register ( ControlReg ) & 0x07; //最后接收到得字节的有效位数
if ( ucLastBits )
* pOutLenBit = ( ucN - 1 ) * 8 + ucLastBits; //N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
else
* pOutLenBit = ucN * 8; //最后接收到的字节整个字节有效
if ( ucN == 0 )
ucN = 1;
if ( ucN > MAXRLEN )
ucN = MAXRLEN;
for ( ul = 0; ul < ucN; ul ++ )
pOutData [ ul ] = RC522_Read_Register ( FIFODataReg );
}
}
else
cStatus = MI_ERR;
}
RC522_SetBit_Register ( ControlReg, 0x80 ); // stop timer now
RC522_Write_Register ( CommandReg, PCD_IDLE );
return cStatus;
}
/**
* @brief :寻卡
* @param ucReq_code,寻卡方式
* = 0x52:寻感应区内所有符合14443A标准的卡
* = 0x26:寻未进入休眠状态的卡
* pTagType,卡片类型代码
* = 0x4400:Mifare_UltraLight
* = 0x0400:Mifare_One(S50)
* = 0x0200:Mifare_One(S70)
* = 0x0800:Mifare_Pro(X))
* = 0x4403:Mifare_DESFire
* @retval :状态值MI_OK,成功
*/
char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType )
{
char cStatus;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
RC522_ClearBit_Register ( Status2Reg, 0x08 ); //清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
RC522_Write_Register ( BitFramingReg, 0x07 ); // 发送的最后一个字节的 七位
RC522_SetBit_Register ( TxControlReg, 0x03 ); //TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号
ucComMF522Buf [ 0 ] = ucReq_code; //存入寻卡方式
/* PCD_TRANSCEIVE:发送并接收数据的命令,RC522向卡片发送寻卡命令,卡片返回卡的型号代码到ucComMF522Buf中 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, & ulLen ); //寻卡
if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) ) //寻卡成功返回卡类型
{
/* 接收卡片的型号代码 */
* pTagType = ucComMF522Buf [ 0 ];
* ( pTagType + 1 ) = ucComMF522Buf [ 1 ];
}
else
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :防冲突
* @param :Snr:卡片序列,4字节,会返回选中卡片的序列
* @retval :状态值MI_OK,成功
*/
char PcdAnticoll ( uint8_t * pSnr )
{
char cStatus;
uint8_t uc, ucSnr_check = 0;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
RC522_ClearBit_Register ( Status2Reg, 0x08 ); //清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
RC522_Write_Register ( BitFramingReg, 0x00); //清理寄存器 停止收发
RC522_ClearBit_Register ( CollReg, 0x80 ); //清ValuesAfterColl所有接收的位在冲突后被清除
ucComMF522Buf [ 0 ] = 0x93; //卡片防冲突命令
ucComMF522Buf [ 1 ] = 0x20;
/* 将卡片防冲突命令通过RC522传到卡片中,返回的是被选中卡片的序列 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, & ulLen);//与卡片通信
if ( cStatus == MI_OK) //通信成功
{
for ( uc = 0; uc < 4; uc ++ )
{
* ( pSnr + uc ) = ucComMF522Buf [ uc ]; //读出UID
ucSnr_check ^= ucComMF522Buf [ uc ];
}
if ( ucSnr_check != ucComMF522Buf [ uc ] )
cStatus = MI_ERR;
}
RC522_SetBit_Register ( CollReg, 0x80 );
return cStatus;
}
/**
* @brief :用RC522计算CRC16(循环冗余校验)
* @param :pIndata:计算CRC16的数组
* ucLen:计算CRC16的数组字节长度
* pOutData:存放计算结果存放的首地址
* @retval :状态值MI_OK,成功
*/
void CalulateCRC ( uint8_t * pIndata, u8 ucLen, uint8_t * pOutData )
{
uint8_t uc, ucN;
RC522_ClearBit_Register(DivIrqReg,0x04);
RC522_Write_Register(CommandReg,PCD_IDLE);
RC522_SetBit_Register(FIFOLevelReg,0x80);
for ( uc = 0; uc < ucLen; uc ++)
RC522_Write_Register ( FIFODataReg, * ( pIndata + uc ) );
RC522_Write_Register ( CommandReg, PCD_CALCCRC );
uc = 0xFF;
do
{
ucN = RC522_Read_Register ( DivIrqReg );
uc --;
} while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );
pOutData [ 0 ] = RC522_Read_Register ( CRCResultRegL );
pOutData [ 1 ] = RC522_Read_Register ( CRCResultRegM );
}
/**
* @brief :选定卡片
* @param :pSnr:卡片序列号,4字节
* @retval :状态值MI_OK,成功
*/
char PcdSelect ( uint8_t * pSnr )
{
char ucN;
uint8_t uc;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
/* PICC_ANTICOLL1:防冲突命令 */
ucComMF522Buf [ 0 ] = PICC_ANTICOLL1;
ucComMF522Buf [ 1 ] = 0x70;
ucComMF522Buf [ 6 ] = 0;
for ( uc = 0; uc < 4; uc ++ )
{
ucComMF522Buf [ uc + 2 ] = * ( pSnr + uc );
ucComMF522Buf [ 6 ] ^= * ( pSnr + uc );
}
CalulateCRC ( ucComMF522Buf, 7, & ucComMF522Buf [ 7 ] );
RC522_ClearBit_Register ( Status2Reg, 0x08 );
ucN = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, & ulLen );
if ( ( ucN == MI_OK ) && ( ulLen == 0x18 ) )
ucN = MI_OK;
else
ucN = MI_ERR;
return ucN;
}
/**
* @brief :校验卡片密码
* @param :ucAuth_mode:密码验证模式
* = 0x60,验证A密钥
* = 0x61,验证B密钥
* ucAddr:块地址
* pKey:密码
* pSnr:卡片序列号,4字节
* @retval :状态值MI_OK,成功
*/
char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr )
{
char cStatus;
uint8_t uc, ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = ucAuth_mode;
ucComMF522Buf [ 1 ] = ucAddr;
/* 前俩字节存储验证模式和块地址,2~8字节存储密码(6个字节),8~14字节存储序列号 */
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );
/* 进行冗余校验,14~16俩个字节存储校验结果 */
cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen );
/* 判断验证是否成功 */
if ( ( cStatus != MI_OK ) || ( ! ( RC522_Read_Register ( Status2Reg ) & 0x08 ) ) )
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :在M1卡的指定块地址写入指定数据
* @param :ucAddr:块地址
* pData:写入的数据,16字节
* @retval :状态值MI_OK,成功
*/
char PcdWrite ( uint8_t ucAddr, uint8_t * pData )
{
char cStatus;
uint8_t uc, ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = PICC_WRITE;//写块命令
ucComMF522Buf [ 1 ] = ucAddr;//写块地址
/* 进行循环冗余校验,将结果存储在& ucComMF522Buf [ 2 ] */
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
/* PCD_TRANSCEIVE:发送并接收数据命令,通过RC522向卡片发送写块命令 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
/* 通过卡片返回的信息判断,RC522是否与卡片正常通信 */
if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
cStatus = MI_ERR;
if ( cStatus == MI_OK )
{
//memcpy(ucComMF522Buf, pData, 16);
/* 将要写入的16字节的数据,传入ucComMF522Buf数组中 */
for ( uc = 0; uc < 16; uc ++ )
ucComMF522Buf [ uc ] = * ( pData + uc );
/* 冗余校验 */
CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );
/* 通过RC522,将16字节数据包括2字节校验结果写入卡片中 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, & ulLen );
/* 判断写地址是否成功 */
if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
cStatus = MI_ERR;
}
return cStatus;
}
/**
* @brief :读取M1卡的指定块地址的数据
* @param :ucAddr:块地址
* pData:读出的数据,16字节
* @retval :状态值MI_OK,成功
*/
char PcdRead ( uint8_t ucAddr, uint8_t * pData )
{
char cStatus;
uint8_t uc, ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = PICC_READ;
ucComMF522Buf [ 1 ] = ucAddr;
/* 冗余校验 */
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
/* 通过RC522将命令传给卡片 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
/* 如果传输正常,将读取到的数据传入pData中 */
if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) )
{
for ( uc = 0; uc < 16; uc ++ )
* ( pData + uc ) = ucComMF522Buf [ uc ];
}
else
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :让卡片进入休眠模式
* @param :无
* @retval :状态值MI_OK,成功
*/
char PcdHalt( void )
{
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = PICC_HALT;
ucComMF522Buf [ 1 ] = 0;
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
return MI_OK;
}
RC522.c 代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
//RC522命令字
#define PCD_IDLE 0x00 //取消当前命令
#define PCD_AUTHENT 0x0E //验证密钥
#define PCD_RECEIVE 0x08 //接收数据
#define PCD_TRANSMIT 0x04 //发送数据
#define PCD_TRANSCEIVE 0x0C //发送并接收数据
#define PCD_RESETPHASE 0x0F //复位
#define PCD_CALCCRC 0x03 //CRC计算
//Mifare_One卡片命令字
#define PICC_REQIDL 0x26 //寻天线区内未进入休眠状态
#define PICC_REQALL 0x52 //寻天线区内全部卡
#define PICC_ANTICOLL1 0x93 //防冲撞
#define PICC_ANTICOLL2 0x95 //防冲撞
#define PICC_AUTHENT1A 0x60 //验证A密钥
#define PICC_AUTHENT1B 0x61 //验证B密钥
#define PICC_READ 0x30 //读块
#define PICC_WRITE 0xA0 //写块
#define PICC_DECREMENT 0xC0 //扣款
#define PICC_INCREMENT 0xC1 //充值
#define PICC_RESTORE 0xC2 //调块数据到缓冲区
#define PICC_TRANSFER 0xB0 //保存缓冲区中数据
#define PICC_HALT 0x50 //休眠
/* RC522 FIFO长度定义 */
#define DEF_FIFO_LENGTH 64 //FIFO size=64byte
#define MAXRLEN 18
/* RC522寄存器定义 */
// PAGE 0
#define RFU00 0x00 //保留
#define CommandReg 0x01 //启动和停止命令的执行
#define ComIEnReg 0x02 //中断请求传递的使能(Enable/Disable)
#define DivlEnReg 0x03 //中断请求传递的使能
#define ComIrqReg 0x04 //包含中断请求标志
#define DivIrqReg 0x05 //包含中断请求标志
#define ErrorReg 0x06 //错误标志,指示执行的上个命令的错误状态
#define Status1Reg 0x07 //包含通信的状态标识
#define Status2Reg 0x08 //包含接收器和发送器的状态标志
#define FIFODataReg 0x09 //64字节FIFO缓冲区的输入和输出
#define FIFOLevelReg 0x0A //指示FIFO中存储的字节数
#define WaterLevelReg 0x0B //定义FIFO下溢和上溢报警的FIFO深度
#define ControlReg 0x0C //不同的控制寄存器
#define BitFramingReg 0x0D //面向位的帧的调节
#define CollReg 0x0E //RF接口上检测到的第一个位冲突的位的位置
#define RFU0F 0x0F //保留
// PAGE 1
#define RFU10 0x10 //保留
#define ModeReg 0x11 //定义发送和接收的常用模式
#define TxModeReg 0x12 //定义发送过程的数据传输速率
#define RxModeReg 0x13 //定义接收过程中的数据传输速率
#define TxControlReg 0x14 //控制天线驱动器管教TX1和TX2的逻辑特性
#define TxAutoReg 0x15 //控制天线驱动器的设置
#define TxSelReg 0x16 //选择天线驱动器的内部源
#define RxSelReg 0x17 //选择内部的接收器设置
#define RxThresholdReg 0x18 //选择位译码器的阈值
#define DemodReg 0x19 //定义解调器的设置
#define RFU1A 0x1A //保留
#define RFU1B 0x1B //保留
#define MifareReg 0x1C //控制ISO 14443/MIFARE模式中106kbit/s的通信
#define RFU1D 0x1D //保留
#define RFU1E 0x1E //保留
#define SerialSpeedReg 0x1F //选择串行UART接口的速率
// PAGE 2
#define RFU20 0x20 //保留
#define CRCResultRegM 0x21 //显示CRC计算的实际MSB值
#define CRCResultRegL 0x22 //显示CRC计算的实际LSB值
#define RFU23 0x23 //保留
#define ModWidthReg 0x24 //控制ModWidth的设置
#define RFU25 0x25 //保留
#define RFCfgReg 0x26 //配置接收器增益
#define GsNReg 0x27 //选择天线驱动器管脚(TX1和TX2)的调制电导
#define CWGsCfgReg 0x28 //选择天线驱动器管脚的调制电导
#define ModGsCfgReg 0x29 //选择天线驱动器管脚的调制电导
#define TModeReg 0x2A //定义内部定时器的设置
#define TPrescalerReg 0x2B //定义内部定时器的设置
#define TReloadRegH 0x2C //描述16位长的定时器重装值
#define TReloadRegL 0x2D //描述16位长的定时器重装值
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F //显示16位长的实际定时器值
// PAGE 3
#define RFU30 0x30 //保留
#define TestSel1Reg 0x31 //常用测试信号配置
#define TestSel2Reg 0x32 //常用测试信号配置和PRBS控制
#define TestPinEnReg 0x33 //D1-D7输出驱动器的使能管脚(仅用于串行接口)
#define TestPinValueReg 0x34 //定义D1-D7用作I/O总线时的值
#define TestBusReg 0x35 //显示内部测试总线的状态
#define AutoTestReg 0x36 //控制数字自测试
#define VersionReg 0x37 //显示版本
#define AnalogTestReg 0x38 //控制管脚AUX1和AUX2
#define TestDAC1Reg 0x39 //定义TestDAC1的测试值
#define TestDAC2Reg 0x3A //定义TestDAC2的测试值
#define TestADCReg 0x3B //显示ADCI和Q通道的实际值
#define RFU3C 0x3C //保留
#define RFU3D 0x3D //保留
#define RFU3E 0x3E //保留
#define RFU3F 0x3F //保留
/* 和RC522通信时返回的错误代码 */
#define MI_OK 0x26
#define MI_NOTAGERR 0xcc
#define MI_ERR 0xbb
/* RC522引脚连接说明(SPI1的引脚) :
CS:PA4( 接的SDA引脚 )
SCK:PA5
MISO:PA6
MOSI:PA7
RST:PB0
*/
void RC522_GPIO_Init( void )
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE );
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_Init( GPIOA, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_Init( GPIOA, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_Init( GPIOA, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_Init( GPIOB, &GPIO_InitStructure );
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init( GPIOA, &GPIO_InitStructure );
}
/* IO口操作函数 */
#define RC522_CS_Enable() GPIO_ResetBits ( GPIOA, GPIO_Pin_4 )
#define RC522_CS_Disable() GPIO_SetBits ( GPIOA, GPIO_Pin_4 )
#define RC522_Reset_Enable() GPIO_ResetBits( GPIOB, GPIO_Pin_0 )
#define RC522_Reset_Disable() GPIO_SetBits( GPIOB, GPIO_Pin_0 )
#define RC522_SCK_0() GPIO_ResetBits( GPIOA, GPIO_Pin_5 )
#define RC522_SCK_1() GPIO_SetBits( GPIOA, GPIO_Pin_5 )
#define RC522_MOSI_0() GPIO_ResetBits( GPIOA, GPIO_Pin_7 )
#define RC522_MOSI_1() GPIO_SetBits( GPIOA, GPIO_Pin_7 )
#define RC522_MISO_GET() GPIO_ReadInputDataBit( GPIOA, GPIO_Pin_6 )
/*****************************************************************************************************************************/
/* 软件模拟SPI发送一个字节数据,高位先行 */
void RC522_SPI_SendByte( uint8_t byte )
{
uint8_t n;
for( n=0;n<8;n++ )
{
if( byte&0x80 )
RC522_MOSI_1();
else
RC522_MOSI_0();
Delay_us(200);
RC522_SCK_0();
Delay_us(200);
RC522_SCK_1();
Delay_us(200);
byte<<=1;
}
}
/* 软件模拟SPI读取一个字节数据,先读高位 */
uint8_t RC522_SPI_ReadByte( void )
{
uint8_t n,data;
for( n=0;n<8;n++ )
{
data<<=1;
RC522_SCK_0();
Delay_us(200);
if( RC522_MISO_GET()==1 )
data|=0x01;
Delay_us(200);
RC522_SCK_1();
Delay_us(200);
}
return data;
}
/*************************************** STM32对RC522寄存器的操作 **************************************************************************************/
/**
* @brief :读取RC522指定寄存器的值
* @param :Address:寄存器的地址
* @retval :寄存器的值
*/
uint8_t RC522_Read_Register( uint8_t Address )
{
uint8_t data,Addr;
Addr = ( (Address<<1)&0x7E )|0x80;
RC522_CS_Enable();
RC522_SPI_SendByte( Addr );
data = RC522_SPI_ReadByte();//读取寄存器中的值
RC522_CS_Disable();
return data;
}
/**
* @brief :向RC522指定寄存器中写入指定的数据
* @param :Address:寄存器地址
data:要写入寄存器的数据
* @retval :无
*/
void RC522_Write_Register( uint8_t Address, uint8_t data )
{
uint8_t Addr;
Addr = ( Address<<1 )&0x7E;
RC522_CS_Enable();
RC522_SPI_SendByte( Addr );
RC522_SPI_SendByte( data );
RC522_CS_Disable();
}
/**
* @brief :置位RC522指定寄存器的指定位
* @param :Address:寄存器地址
mask:置位值
* @retval :无
*/
void RC522_SetBit_Register( uint8_t Address, uint8_t mask )
{
uint8_t temp;
/* 获取寄存器当前值 */
temp = RC522_Read_Register( Address );
/* 对指定位进行置位操作后,再将值写入寄存器 */
RC522_Write_Register( Address, temp|mask );
}
/**
* @brief :清位RC522指定寄存器的指定位
* @param :Address:寄存器地址
mask:清位值
* @retval :无
*/
void RC522_ClearBit_Register( uint8_t Address, uint8_t mask )
{
uint8_t temp;
/* 获取寄存器当前值 */
temp = RC522_Read_Register( Address );
/* 对指定位进行清位操作后,再将值写入寄存器 */
RC522_Write_Register( Address, temp&(~mask) );
}
/************************************ STM32对RC522的基础通信 **************************************************************************************/
/**
* @brief :开启天线
* @param :无
* @retval :无
*/
void RC522_Antenna_On( void )
{
uint8_t k;
k = RC522_Read_Register( TxControlReg );
/* 判断天线是否开启 */
if( !( k&0x03 ) )
RC522_SetBit_Register( TxControlReg, 0x03 );
}
/**
* @brief :关闭天线
* @param :无
* @retval :无
*/
void RC522_Antenna_Off( void )
{
/* 直接对相应位清零 */
RC522_ClearBit_Register( TxControlReg, 0x03 );
}
/**
* @brief :复位RC522
* @param :无
* @retval :无
*/
void RC522_Rese( void )
{
RC522_Reset_Disable();
Delay_us ( 1 );
RC522_Reset_Enable();
Delay_us ( 1 );
RC522_Reset_Disable();
Delay_us ( 1 );
RC522_Write_Register( CommandReg, 0x0F );
while( RC522_Read_Register( CommandReg )&0x10 )
;
/* 缓冲一下 */
Delay_us ( 1 );
RC522_Write_Register( ModeReg, 0x3D ); //定义发送和接收常用模式
RC522_Write_Register( TReloadRegL, 30 ); //16位定时器低位
RC522_Write_Register( TReloadRegH, 0 ); //16位定时器高位
RC522_Write_Register( TModeReg, 0x8D ); //内部定时器的设置
RC522_Write_Register( TPrescalerReg, 0x3E ); //设置定时器分频系数
RC522_Write_Register( TxAutoReg, 0x40 ); //调制发送信号为100%ASK
}
/**
* @brief :设置RC522的工作方式
* @param :Type:工作方式
* @retval :无
M500PcdConfigISOType
*/
void RC522_Config_Type( char Type )
{
if( Type=='A' )
{
RC522_ClearBit_Register( Status2Reg, 0x08 );
RC522_Write_Register( ModeReg, 0x3D );
RC522_Write_Register( RxSelReg, 0x86 );
RC522_Write_Register( RFCfgReg, 0x7F );
RC522_Write_Register( TReloadRegL, 30 );
RC522_Write_Register( TReloadRegH, 0 );
RC522_Write_Register( TModeReg, 0x8D );
RC522_Write_Register( TPrescalerReg, 0x3E );
Delay_us(2);
/* 开天线 */
RC522_Antenna_On();
}
}
/**
* @brief :初始化RC522引脚,打开天线,复位RC522,设置RC522的工作方式
* @param :无
* @retval :无
*/
void RC522_Init(void)
{
RC522_GPIO_Init();
RC522_Antenna_On();
RC522_Rese();
RC522_Config_Type('A');
}
/************************************ STM32控制RC522与M1的通信函数 **************************************************************************************/
/**
* @brief :通过RC522和ISO14443卡通讯
* @param :ucCommand:RC522命令字
* pInData:通过RC522发送到卡片的数据
* ucInLenByte:发送数据的字节长度
* pOutData:接收到的卡片返回数据
* pOutLenBit:返回数据的位长度
* @retval :状态值MI_OK,成功
*/
char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit )
{
char cStatus = MI_ERR;
uint8_t ucIrqEn = 0x00;
uint8_t ucWaitFor = 0x00;
uint8_t ucLastBits;
uint8_t ucN;
uint32_t ul;
switch ( ucCommand )
{
case PCD_AUTHENT: //Mifare认证
ucIrqEn = 0x12; //允许错误中断请求ErrIEn 允许空闲中断IdleIEn
ucWaitFor = 0x10; //认证寻卡等待时候 查询空闲中断标志位
break;
case PCD_TRANSCEIVE: //接收发送 发送接收
ucIrqEn = 0x77; //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEn
ucWaitFor = 0x30; //寻卡等待时候 查询接收中断标志位与 空闲中断标志位
break;
default:
break;
}
RC522_Write_Register ( ComIEnReg, ucIrqEn | 0x80 ); //IRqInv置位管脚IRQ与Status1Reg的IRq位的值相反
RC522_ClearBit_Register ( ComIrqReg, 0x80 ); //Set1该位清零时,CommIRqReg的屏蔽位清零
RC522_Write_Register ( CommandReg, PCD_IDLE ); //写空闲命令
RC522_SetBit_Register ( FIFOLevelReg, 0x80 ); //置位FlushBuffer清除内部FIFO的读和写指针以及ErrReg的BufferOvfl标志位被清除
for ( ul = 0; ul < ucInLenByte; ul ++ )
RC522_Write_Register ( FIFODataReg, pInData [ ul ] ); //写数据进FIFOdata
RC522_Write_Register ( CommandReg, ucCommand ); //写命令
if ( ucCommand == PCD_TRANSCEIVE )
RC522_SetBit_Register(BitFramingReg,0x80); //StartSend置位启动数据发送 该位与收发命令使用时才有效
ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25ms
do //认证 与寻卡等待时间
{
ucN = RC522_Read_Register ( ComIrqReg ); //查询事件中断
ul --;
} while ( ( ul != 0 ) && ( ! ( ucN & 0x01 ) ) && ( ! ( ucN & ucWaitFor ) ) ); //退出条件i=0,定时器中断,与写空闲命令
RC522_ClearBit_Register ( BitFramingReg, 0x80 ); //清理允许StartSend位
if ( ul != 0 )
{
if ( ! ( RC522_Read_Register ( ErrorReg ) & 0x1B ) ) //读错误标志寄存器BufferOfI CollErr ParityErr ProtocolErr
{
cStatus = MI_OK;
if ( ucN & ucIrqEn & 0x01 ) //是否发生定时器中断
cStatus = MI_NOTAGERR;
if ( ucCommand == PCD_TRANSCEIVE )
{
ucN = RC522_Read_Register ( FIFOLevelReg ); //读FIFO中保存的字节数
ucLastBits = RC522_Read_Register ( ControlReg ) & 0x07; //最后接收到得字节的有效位数
if ( ucLastBits )
* pOutLenBit = ( ucN - 1 ) * 8 + ucLastBits; //N个字节数减去1(最后一个字节)+最后一位的位数 读取到的数据总位数
else
* pOutLenBit = ucN * 8; //最后接收到的字节整个字节有效
if ( ucN == 0 )
ucN = 1;
if ( ucN > MAXRLEN )
ucN = MAXRLEN;
for ( ul = 0; ul < ucN; ul ++ )
pOutData [ ul ] = RC522_Read_Register ( FIFODataReg );
}
}
else
cStatus = MI_ERR;
}
RC522_SetBit_Register ( ControlReg, 0x80 ); // stop timer now
RC522_Write_Register ( CommandReg, PCD_IDLE );
return cStatus;
}
/**
* @brief :寻卡
* @param ucReq_code,寻卡方式
* = 0x52:寻感应区内所有符合14443A标准的卡
* = 0x26:寻未进入休眠状态的卡
* pTagType,卡片类型代码
* = 0x4400:Mifare_UltraLight
* = 0x0400:Mifare_One(S50)
* = 0x0200:Mifare_One(S70)
* = 0x0800:Mifare_Pro(X))
* = 0x4403:Mifare_DESFire
* @retval :状态值MI_OK,成功
*/
char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType )
{
char cStatus;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
RC522_ClearBit_Register ( Status2Reg, 0x08 ); //清理指示MIFARECyptol单元接通以及所有卡的数据通信被加密的情况
RC522_Write_Register ( BitFramingReg, 0x07 ); // 发送的最后一个字节的 七位
RC522_SetBit_Register ( TxControlReg, 0x03 ); //TX1,TX2管脚的输出信号传递经发送调制的13.56的能量载波信号
ucComMF522Buf [ 0 ] = ucReq_code; //存入寻卡方式
/* PCD_TRANSCEIVE:发送并接收数据的命令,RC522向卡片发送寻卡命令,卡片返回卡的型号代码到ucComMF522Buf中 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 1, ucComMF522Buf, & ulLen ); //寻卡
if ( ( cStatus == MI_OK ) && ( ulLen == 0x10 ) ) //寻卡成功返回卡类型
{
/* 接收卡片的型号代码 */
* pTagType = ucComMF522Buf [ 0 ];
* ( pTagType + 1 ) = ucComMF522Buf [ 1 ];
}
else
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :防冲突
* @param :Snr:卡片序列,4字节,会返回选中卡片的序列
* @retval :状态值MI_OK,成功
*/
char PcdAnticoll ( uint8_t * pSnr )
{
char cStatus;
uint8_t uc, ucSnr_check = 0;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
RC522_ClearBit_Register ( Status2Reg, 0x08 ); //清MFCryptol On位 只有成功执行MFAuthent命令后,该位才能置位
RC522_Write_Register ( BitFramingReg, 0x00); //清理寄存器 停止收发
RC522_ClearBit_Register ( CollReg, 0x80 ); //清ValuesAfterColl所有接收的位在冲突后被清除
ucComMF522Buf [ 0 ] = 0x93; //卡片防冲突命令
ucComMF522Buf [ 1 ] = 0x20;
/* 将卡片防冲突命令通过RC522传到卡片中,返回的是被选中卡片的序列 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 2, ucComMF522Buf, & ulLen);//与卡片通信
if ( cStatus == MI_OK) //通信成功
{
for ( uc = 0; uc < 4; uc ++ )
{
* ( pSnr + uc ) = ucComMF522Buf [ uc ]; //读出UID
ucSnr_check ^= ucComMF522Buf [ uc ];
}
if ( ucSnr_check != ucComMF522Buf [ uc ] )
cStatus = MI_ERR;
}
RC522_SetBit_Register ( CollReg, 0x80 );
return cStatus;
}
/**
* @brief :用RC522计算CRC16(循环冗余校验)
* @param :pIndata:计算CRC16的数组
* ucLen:计算CRC16的数组字节长度
* pOutData:存放计算结果存放的首地址
* @retval :状态值MI_OK,成功
*/
void CalulateCRC ( uint8_t * pIndata, u8 ucLen, uint8_t * pOutData )
{
uint8_t uc, ucN;
RC522_ClearBit_Register(DivIrqReg,0x04);
RC522_Write_Register(CommandReg,PCD_IDLE);
RC522_SetBit_Register(FIFOLevelReg,0x80);
for ( uc = 0; uc < ucLen; uc ++)
RC522_Write_Register ( FIFODataReg, * ( pIndata + uc ) );
RC522_Write_Register ( CommandReg, PCD_CALCCRC );
uc = 0xFF;
do
{
ucN = RC522_Read_Register ( DivIrqReg );
uc --;
} while ( ( uc != 0 ) && ! ( ucN & 0x04 ) );
pOutData [ 0 ] = RC522_Read_Register ( CRCResultRegL );
pOutData [ 1 ] = RC522_Read_Register ( CRCResultRegM );
}
/**
* @brief :选定卡片
* @param :pSnr:卡片序列号,4字节
* @retval :状态值MI_OK,成功
*/
char PcdSelect ( uint8_t * pSnr )
{
char ucN;
uint8_t uc;
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
/* PICC_ANTICOLL1:防冲突命令 */
ucComMF522Buf [ 0 ] = PICC_ANTICOLL1;
ucComMF522Buf [ 1 ] = 0x70;
ucComMF522Buf [ 6 ] = 0;
for ( uc = 0; uc < 4; uc ++ )
{
ucComMF522Buf [ uc + 2 ] = * ( pSnr + uc );
ucComMF522Buf [ 6 ] ^= * ( pSnr + uc );
}
CalulateCRC ( ucComMF522Buf, 7, & ucComMF522Buf [ 7 ] );
RC522_ClearBit_Register ( Status2Reg, 0x08 );
ucN = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 9, ucComMF522Buf, & ulLen );
if ( ( ucN == MI_OK ) && ( ulLen == 0x18 ) )
ucN = MI_OK;
else
ucN = MI_ERR;
return ucN;
}
/**
* @brief :校验卡片密码
* @param :ucAuth_mode:密码验证模式
* = 0x60,验证A密钥
* = 0x61,验证B密钥
* ucAddr:块地址
* pKey:密码
* pSnr:卡片序列号,4字节
* @retval :状态值MI_OK,成功
*/
char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr )
{
char cStatus;
uint8_t uc, ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = ucAuth_mode;
ucComMF522Buf [ 1 ] = ucAddr;
/* 前俩字节存储验证模式和块地址,2~8字节存储密码(6个字节),8~14字节存储序列号 */
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 2 ] = * ( pKey + uc );
for ( uc = 0; uc < 6; uc ++ )
ucComMF522Buf [ uc + 8 ] = * ( pSnr + uc );
/* 进行冗余校验,14~16俩个字节存储校验结果 */
cStatus = PcdComMF522 ( PCD_AUTHENT, ucComMF522Buf, 12, ucComMF522Buf, & ulLen );
/* 判断验证是否成功 */
if ( ( cStatus != MI_OK ) || ( ! ( RC522_Read_Register ( Status2Reg ) & 0x08 ) ) )
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :在M1卡的指定块地址写入指定数据
* @param :ucAddr:块地址
* pData:写入的数据,16字节
* @retval :状态值MI_OK,成功
*/
char PcdWrite ( uint8_t ucAddr, uint8_t * pData )
{
char cStatus;
uint8_t uc, ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = PICC_WRITE;//写块命令
ucComMF522Buf [ 1 ] = ucAddr;//写块地址
/* 进行循环冗余校验,将结果存储在& ucComMF522Buf [ 2 ] */
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
/* PCD_TRANSCEIVE:发送并接收数据命令,通过RC522向卡片发送写块命令 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
/* 通过卡片返回的信息判断,RC522是否与卡片正常通信 */
if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
cStatus = MI_ERR;
if ( cStatus == MI_OK )
{
//memcpy(ucComMF522Buf, pData, 16);
/* 将要写入的16字节的数据,传入ucComMF522Buf数组中 */
for ( uc = 0; uc < 16; uc ++ )
ucComMF522Buf [ uc ] = * ( pData + uc );
/* 冗余校验 */
CalulateCRC ( ucComMF522Buf, 16, & ucComMF522Buf [ 16 ] );
/* 通过RC522,将16字节数据包括2字节校验结果写入卡片中 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 18, ucComMF522Buf, & ulLen );
/* 判断写地址是否成功 */
if ( ( cStatus != MI_OK ) || ( ulLen != 4 ) || ( ( ucComMF522Buf [ 0 ] & 0x0F ) != 0x0A ) )
cStatus = MI_ERR;
}
return cStatus;
}
/**
* @brief :读取M1卡的指定块地址的数据
* @param :ucAddr:块地址
* pData:读出的数据,16字节
* @retval :状态值MI_OK,成功
*/
char PcdRead ( uint8_t ucAddr, uint8_t * pData )
{
char cStatus;
uint8_t uc, ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = PICC_READ;
ucComMF522Buf [ 1 ] = ucAddr;
/* 冗余校验 */
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
/* 通过RC522将命令传给卡片 */
cStatus = PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
/* 如果传输正常,将读取到的数据传入pData中 */
if ( ( cStatus == MI_OK ) && ( ulLen == 0x90 ) )
{
for ( uc = 0; uc < 16; uc ++ )
* ( pData + uc ) = ucComMF522Buf [ uc ];
}
else
cStatus = MI_ERR;
return cStatus;
}
/**
* @brief :让卡片进入休眠模式
* @param :无
* @retval :状态值MI_OK,成功
*/
char PcdHalt( void )
{
uint8_t ucComMF522Buf [ MAXRLEN ];
uint32_t ulLen;
ucComMF522Buf [ 0 ] = PICC_HALT;
ucComMF522Buf [ 1 ] = 0;
CalulateCRC ( ucComMF522Buf, 2, & ucComMF522Buf [ 2 ] );
PcdComMF522 ( PCD_TRANSCEIVE, ucComMF522Buf, 4, ucComMF522Buf, & ulLen );
return MI_OK;
}
RC522.h 代码:
#ifndef __RC522_H__
#define __RC522_H__
#include "stm32f10x.h" // Device header
void RC522_Init(void); //xinzeng
void RC522_GPIO_Init( void );
void RC522_SPI_SendByte( uint8_t byte );
uint8_t RC522_Read_Register( uint8_t Address );
void RC522_Write_Register( uint8_t Address, uint8_t data );
void RC522_SetBit_Register( uint8_t Address, uint8_t mask );
void RC522_ClearBit_Register( uint8_t Address, uint8_t mask );
void RC522_Antenna_On( void );
void RC522_Rese( void );
void RC522_Config_Type( char Type );
char PcdComMF522 ( uint8_t ucCommand, uint8_t * pInData, uint8_t ucInLenByte, uint8_t * pOutData, uint32_t * pOutLenBit );
char PcdRequest ( uint8_t ucReq_code, uint8_t * pTagType );
char PcdAnticoll ( uint8_t * pSnr );
void CalulateCRC ( uint8_t * pIndata, u8 ucLen, uint8_t * pOutData );
char PcdSelect ( uint8_t * pSnr );
char PcdAuthState ( uint8_t ucAuth_mode, uint8_t ucAddr, uint8_t * pKey, uint8_t * pSnr );
char PcdWrite ( uint8_t ucAddr, uint8_t * pData );
char PcdRead ( uint8_t ucAddr, uint8_t * pData );
char PcdHalt( void );
void IC_test ( void );
#endif
将以下获取卡号的函数代码加进 RC522.c中,同时在RC522.h 中声明以下函数。
/* OLED显示屏
vcc:PA6
GND:PA7
SCL:PA8
SDA:PA9
(PA6接上电源正极,PA7接电源负极)
*/
#include "OLED.h"
/* 卡的ID存储,32位,4字节 */
u8 ucArray_ID [ 4 ];
void IC_test ( void )
{
uint8_t ucStatusReturn; //返回状态
while ( 1 )
{
/* 寻卡(方式:范围内全部),第一次寻卡失败后再进行一次,寻卡成功时卡片序列传入数组ucArray_ID中 */
if ( ( ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ) ) != MI_OK )
ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID );
if ( ucStatusReturn == MI_OK)
{
/* 防冲突操作,被选中的卡片序列传入数组ucArray_ID中 */
if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
{
}
}
OLED_Init();
OLED_ShowHexNum(1,1,ucArray_ID[0],2);
OLED_ShowHexNum(1,4,ucArray_ID[1],2);
OLED_ShowHexNum(1,7,ucArray_ID[2],2);
OLED_ShowHexNum(1,10,ucArray_ID[3],2);
}
}
OLED.c 文件代码
#include "stm32f10x.h"
#include "OLED_Font.h"
/*引脚配置*/
#define OLED_W_SCL(x) GPIO_WriteBit(GPIOB, GPIO_Pin_8, (BitAction)(x))
#define OLED_W_SDA(x) GPIO_WriteBit(GPIOB, GPIO_Pin_9, (BitAction)(x))
/*引脚初始化*/
void OLED_I2C_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOB, &GPIO_InitStructure);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C开始
* @param 无
* @retval 无
*/
void OLED_I2C_Start(void)
{
OLED_W_SDA(1);
OLED_W_SCL(1);
OLED_W_SDA(0);
OLED_W_SCL(0);
}
/**
* @brief I2C停止
* @param 无
* @retval 无
*/
void OLED_I2C_Stop(void)
{
OLED_W_SDA(0);
OLED_W_SCL(1);
OLED_W_SDA(1);
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的一个字节
* @retval 无
*/
void OLED_I2C_SendByte(uint8_t Byte)
{
uint8_t i;
for (i = 0; i < 8; i++)
{
OLED_W_SDA(Byte & (0x80 >> i));
OLED_W_SCL(1);
OLED_W_SCL(0);
}
OLED_W_SCL(1); //额外的一个时钟,不处理应答信号
OLED_W_SCL(0);
}
/**
* @brief OLED写命令
* @param Command 要写入的命令
* @retval 无
*/
void OLED_WriteCommand(uint8_t Command)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x00); //写命令
OLED_I2C_SendByte(Command);
OLED_I2C_Stop();
}
/**
* @brief OLED写数据
* @param Data 要写入的数据
* @retval 无
*/
void OLED_WriteData(uint8_t Data)
{
OLED_I2C_Start();
OLED_I2C_SendByte(0x78); //从机地址
OLED_I2C_SendByte(0x40); //写数据
OLED_I2C_SendByte(Data);
OLED_I2C_Stop();
}
/**
* @brief OLED设置光标位置
* @param Y 以左上角为原点,向下方向的坐标,范围:0~7
* @param X 以左上角为原点,向右方向的坐标,范围:0~127
* @retval 无
*/
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
OLED_WriteCommand(0xB0 | Y); //设置Y位置
OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); //设置X位置高4位
OLED_WriteCommand(0x00 | (X & 0x0F)); //设置X位置低4位
}
/**
* @brief OLED清屏
* @param 无
* @retval 无
*/
void OLED_Clear(void)
{
uint8_t i, j;
for (j = 0; j < 8; j++)
{
OLED_SetCursor(j, 0);
for(i = 0; i < 128; i++)
{
OLED_WriteData(0x00);
}
}
}
/**
* @brief OLED显示一个字符
* @param Line 行位置,范围:1~4
* @param Column 列位置,范围:1~16
* @param Char 要显示的一个字符,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
uint8_t i;
OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); //设置光标位置在上半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i]); //显示上半部分内容
}
OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); //设置光标位置在下半部分
for (i = 0; i < 8; i++)
{
OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); //显示下半部分内容
}
}
/**
* @brief OLED显示字符串
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param String 要显示的字符串,范围:ASCII可见字符
* @retval 无
*/
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
uint8_t i;
for (i = 0; String[i] != '\0'; i++)
{
OLED_ShowChar(Line, Column + i, String[i]);
}
}
/**
* @brief OLED次方函数
* @retval 返回值等于X的Y次方
*/
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
uint32_t Result = 1;
while (Y--)
{
Result *= X;
}
return Result;
}
/**
* @brief OLED显示数字(十进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~4294967295
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十进制,带符号数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:-2147483648~2147483647
* @param Length 要显示数字的长度,范围:1~10
* @retval 无
*/
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
uint8_t i;
uint32_t Number1;
if (Number >= 0)
{
OLED_ShowChar(Line, Column, '+');
Number1 = Number;
}
else
{
OLED_ShowChar(Line, Column, '-');
Number1 = -Number;
}
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
}
}
/**
* @brief OLED显示数字(十六进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~0xFFFFFFFF
* @param Length 要显示数字的长度,范围:1~8
* @retval 无
*/
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i, SingleNumber;
for (i = 0; i < Length; i++)
{
SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
if (SingleNumber < 10)
{
OLED_ShowChar(Line, Column + i, SingleNumber + '0');
}
else
{
OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
}
}
}
/**
* @brief OLED显示数字(二进制,正数)
* @param Line 起始行位置,范围:1~4
* @param Column 起始列位置,范围:1~16
* @param Number 要显示的数字,范围:0~1111 1111 1111 1111
* @param Length 要显示数字的长度,范围:1~16
* @retval 无
*/
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
uint8_t i;
for (i = 0; i < Length; i++)
{
OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
}
}
/**
* @brief OLED初始化
* @param 无
* @retval 无
*/
void OLED_Init(void)
{
uint32_t i, j;
for (i = 0; i < 1000; i++) //上电延时
{
for (j = 0; j < 1000; j++);
}
OLED_I2C_Init(); //端口初始化
OLED_WriteCommand(0xAE); //关闭显示
OLED_WriteCommand(0xD5); //设置显示时钟分频比/振荡器频率
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); //设置多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); //设置显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); //设置显示开始行
OLED_WriteCommand(0xA1); //设置左右方向,0xA1正常 0xA0左右反置
OLED_WriteCommand(0xC8); //设置上下方向,0xC8正常 0xC0上下反置
OLED_WriteCommand(0xDA); //设置COM引脚硬件配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); //设置对比度控制
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); //设置预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); //设置VCOMH取消选择级别
OLED_WriteCommand(0x30);
OLED_WriteCommand(0xA4); //设置整个显示打开/关闭
OLED_WriteCommand(0xA6); //设置正常/倒转显示
OLED_WriteCommand(0x8D); //设置充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0xAF); //开启显示
OLED_Clear(); //OLED清屏
}
OLED_front.h 文件代码
#ifndef __OLED_FONT_H
#define __OLED_FONT_H
/*OLED字模库,宽8像素,高16像素*/
const uint8_t OLED_F8x16[][16]=
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 0
0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1
0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2
0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,
0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3
0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,
0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4
0xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,
0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 5
0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,
0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 6
0x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 7
0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,
0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 8
0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,
0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 9
0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,
0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 10
0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,
0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 11
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 12
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 13
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 14
0x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,
0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 15
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 16
0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 17
0x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,
0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 18
0x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,
0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 19
0x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,
0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 20
0x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,
0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 21
0x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,
0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 22
0x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,
0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 23
0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,
0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 24
0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,
0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 25
0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,
0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 26
0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,
0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 27
0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,
0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 28
0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,
0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 29
0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,
0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 30
0x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,
0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 31
0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,
0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 32
0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,
0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 33
0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,
0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 34
0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,
0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 35
0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,
0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 36
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 37
0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,
0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 38
0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,
0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 39
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 40
0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 41
0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,
0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 42
0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,
0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 43
0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,
0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 44
0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,
0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 45
0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,
0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 46
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 47
0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,
0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 48
0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,
0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 49
0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,
0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 50
0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,
0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 51
0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,
0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 52
0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,
0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 53
0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,
0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 54
0xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,
0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 55
0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,
0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 56
0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,
0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 57
0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,
0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 58
0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,
0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 59
0x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,//\ 60
0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,
0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 61
0x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 62
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 63
0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 64
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 65
0x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,
0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 66
0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,
0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 67
0x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,
0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 68
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 69
0x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 70
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 71
0x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,
0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 72
0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 73
0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,
0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 74
0x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,
0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 75
0x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,
0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 76
0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 77
0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,
0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 78
0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,
0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 79
0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,
0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 80
0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,
0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 81
0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 82
0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 83
0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,
0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 84
0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,
0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 85
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 86
0x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,
0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 87
0x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,
0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 88
0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,
0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 89
0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,
0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 90
0x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,
0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 91
0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 92
0x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,
0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 93
0x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};
#endif
OLED.h 文件代码
#ifndef __OLED_H
#define __OLED_H
void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char);
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String);
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length);
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length);
#endif
获取M1卡的卡号,对卡号进行匹配,匹配成功驱动舵机进行转动,实现开门。
main.c 代码
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Servo.h"
#include "Delay.h"
#include "RC522.h"
u8 serretArray_ID[4] = {0xf3,0x1d,0x52,0x10};//密码卡号
int main(void)
{
OLED_Init();
Servo_Init();
RC522_Init();
while (1)
{
//用于存储读取到的卡号
u8 ucArray_ID [ 4 ];
/* 寻卡(方式:范围内全部),第一次寻卡失败后再进行一次,寻卡成功时卡片序列传入数组ucArray_ID中 */
if ( ( ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID ) ) != MI_OK )
ucStatusReturn = PcdRequest ( PICC_REQALL, ucArray_ID );
if ( ucStatusReturn == MI_OK)
{
/* 防冲突操作,被选中的卡片序列传入数组ucArray_ID中 */
if ( PcdAnticoll ( ucArray_ID ) == MI_OK )
{
char i=0,flag=0;
//对获取到的卡号进行匹配
for(i=0;i<4;i++)
{
if(serretArray_ID[i] != ucArray_ID[i])
{
flag=1;
break;
}
}
//匹配成功,驱动舵机转动
if(flag==0)
{
Servo_SetAngle(90);
Delay_ms(2000);
Servo_SetAngle(0);
}
}
}
}
}
好了,本项目是基于STM32f103C8T6制作的一款普通的门禁,如果有帮助到你的话记得点个赞!谢谢啦!
文章浏览阅读645次。这个肯定是末尾的IDAT了,因为IDAT必须要满了才会开始一下个IDAT,这个明显就是末尾的IDAT了。,对应下面的create_head()代码。,对应下面的create_tail()代码。不要考虑爆破,我已经试了一下,太多情况了。题目来源:UNCTF。_攻防世界困难模式攻略图文
文章浏览阅读2.9k次,点赞3次,收藏10次。偶尔会用到,记录、分享。1. 数据库导出1.1 切换到dmdba用户su - dmdba1.2 进入达梦数据库安装路径的bin目录,执行导库操作 导出语句:./dexp cwy_init/[email protected]:5236 file=cwy_init.dmp log=cwy_init_exp.log 注释: cwy_init/init_123..._达梦数据库导入导出
文章浏览阅读1.9k次。1. 在官网上下载KindEditor文件,可以删掉不需要要到的jsp,asp,asp.net和php文件夹。接着把文件夹放到项目文件目录下。2. 修改html文件,在页面引入js文件:<script type="text/javascript" src="./kindeditor/kindeditor-all.js"></script><script type="text/javascript" src="./kindeditor/lang/zh-CN.js"_kindeditor.js
文章浏览阅读2.3k次,点赞6次,收藏14次。SPI的详情简介不必赘述。假设我们通过SPI发送0xAA,我们的数据线就会变为10101010,通过修改不同的内容,即可修改SPI中0和1的持续时间。比如0xF0即为前半周期为高电平,后半周期为低电平的状态。在SPI的通信模式中,CPHA配置会影响该实验,下图展示了不同采样位置的SPI时序图[1]。CPOL = 0,CPHA = 1:CLK空闲状态 = 低电平,数据在下降沿采样,并在上升沿移出CPOL = 0,CPHA = 0:CLK空闲状态 = 低电平,数据在上升沿采样,并在下降沿移出。_stm32g431cbu6
文章浏览阅读1.2k次,点赞2次,收藏8次。数据链路层习题自测问题1.数据链路(即逻辑链路)与链路(即物理链路)有何区别?“电路接通了”与”数据链路接通了”的区别何在?2.数据链路层中的链路控制包括哪些功能?试讨论数据链路层做成可靠的链路层有哪些优点和缺点。3.网络适配器的作用是什么?网络适配器工作在哪一层?4.数据链路层的三个基本问题(帧定界、透明传输和差错检测)为什么都必须加以解决?5.如果在数据链路层不进行帧定界,会发生什么问题?6.PPP协议的主要特点是什么?为什么PPP不使用帧的编号?PPP适用于什么情况?为什么PPP协议不_接收方收到链路层数据后,使用crc检验后,余数为0,说明链路层的传输时可靠传输
文章浏览阅读587次。软件测试工程师移民加拿大 无证移民,未受过软件工程师的教育(第1部分) (Undocumented Immigrant With No Education to Software Engineer(Part 1))Before I start, I want you to please bear with me on the way I write, I have very little gen...
文章浏览阅读304次。Thinkpad X250笔记本电脑,装的是FreeBSD,进入BIOS修改虚拟化配置(其后可能是误设置了安全开机),保存退出后系统无法启动,显示:secure boot failed ,把自己惊出一身冷汗,因为这台笔记本刚好还没开始做备份.....根据错误提示,到bios里面去找相关配置,在Security里面找到了Secure Boot选项,发现果然被设置为Enabled,将其修改为Disabled ,再开机,终于正常启动了。_安装完系统提示secureboot failure
文章浏览阅读10w+次,点赞93次,收藏352次。1、用strtok函数进行字符串分割原型: char *strtok(char *str, const char *delim);功能:分解字符串为一组字符串。参数说明:str为要分解的字符串,delim为分隔符字符串。返回值:从str开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。其它:strtok函数线程不安全,可以使用strtok_r替代。示例://借助strtok实现split#include <string.h>#include <stdio.h&_c++ 字符串分割
文章浏览阅读2.3k次。1 .高斯日记 大数学家高斯有个好习惯:无论如何都要记日记。他的日记有个与众不同的地方,他从不注明年月日,而是用一个整数代替,比如:4210后来人们知道,那个整数就是日期,它表示那一天是高斯出生后的第几天。这或许也是个好习惯,它时时刻刻提醒着主人:日子又过去一天,还有多少时光可以用于浪费呢?高斯出生于:1777年4月30日。在高斯发现的一个重要定理的日记_2013年第四届c a组蓝桥杯省赛真题解答
文章浏览阅读851次,点赞17次,收藏22次。摘要:本文利用供需算法对核极限学习机(KELM)进行优化,并用于分类。
文章浏览阅读1.1k次。一、系统弱密码登录1、在kali上执行命令行telnet 192.168.26.1292、Login和password都输入msfadmin3、登录成功,进入系统4、测试如下:二、MySQL弱密码登录:1、在kali上执行mysql –h 192.168.26.129 –u root2、登录成功,进入MySQL系统3、测试效果:三、PostgreSQL弱密码登录1、在Kali上执行psql -h 192.168.26.129 –U post..._metasploitable2怎么进入
文章浏览阅读257次。本文将为初学者提供Python学习的详细指南,从Python的历史、基础语法和数据类型到面向对象编程、模块和库的使用。通过本文,您将能够掌握Python编程的核心概念,为今后的编程学习和实践打下坚实基础。_python人工智能开发从入门到精通pdf