基于STM32的门禁系统

阅读: 评论:0

基于STM32的门禁系统

基于STM32的门禁系统

基于STM32的门禁系统

参考大佬文章:STM32—驱动RFID-RC522模块_rc522 f4-CSDN博客

一、材料准备

硬件准备:

  • STM32f103c8t6开发板

  • RFID-RC522 射频模块(感应M1卡)

  • s90舵机(用于开门)

  • 3.3v稳压模块(将6v电压转为3.3v给stm32 供电)

  • 四节南孚电池加有盖电池盒(用于供电)

  • OLED显示屏(用于获取M1卡的卡号)

软件准备:

二、开门实现过程

  • RFID-RC522模块读取门卡信息

  • RC522将信息存储到stm32中

  • stm32 对门卡信息与先前定义好的门卡信息匹配

  • 匹配成功后驱动SG90舵机开门

三、舵机模块

1.工作原理

  • 输出一个PWM波形

  • 通过设置PWM波形的占空比来使舵机转到不同的角度上(改变CRR的值来改变占空比)

2.舵机接线

  • 棕色线:接地

  • 红色线:接5v或5v以上的正极

  • 橙色线:接PA0,接收输出的PWM波形

3.输出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;        //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;      //PSCTIM_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);//启动定时器TIM2TIM_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

4.舵机旋转角度模块

  • 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

四、RC522模块

1.工作原理

  • 电气部分: 卡片的电气部分由一个天线和一个ASIC组成。 天线:就是几组绕线的线圈,体积小,已经封装在卡片内 ASIC:ASIC即专用集成电路,是指应特定用户要求和特定电子系统的需要而设计、制造的集成电路。 目前用CPLD(复杂可编程逻辑器件)和 FPGA(现场可编程逻辑阵列)来进行ASIC设计是最为流行的方式之一,它们的共性是都具有用户现场可编程特性,都支持边界扫描技术,但两者在集成度、速度以及编程方式上具有各自的特点,这样理解,ASIC就是卡片特点的一个集成电路。 卡片的ASIC包含了一个高速(106KB)的RF接口、一个控制单元、一个8K的EEPROM

  • 工作过程: 读卡器会向M1卡发送一组固定频率的电磁波,卡片内有一个LC串联谐振电路,其工作频率与读卡器发送的电磁波频率相同,遂在电磁波的激励下,LC串联谐振电路会发生共振,从而使电容内产生电荷,在电容的另一端接有一个单向导电的电子泵,电子泵将产生的电荷转移到另一个电容中存储。当存储电容中的电荷达到2V的时候,此时电容就作为电源为其他电路提供工作电压,所以卡片就可以向读卡器发送数据,或者从读卡器接收数据,实现了读卡器与卡片的通信。

  • RC522各个引脚的作用

    • SDA:也叫CS,串行数据线,用于与主控设备进行数据通信

    • SCK:串行时钟线,用于同步数据传输。

    • MOSI:主输出从输入线,由主控设备发送数据到RC522模块上

    • MISO:主输入从输出线,由RC522 模块发送数据到主控设备上

2.RC522与M1通信

  • 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();elseRC522_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之间的通信基础机制就建立起来了,下一步就是建立在通信基础上的操作了。

3.STM32对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();elseRC522_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;
}

4.STM32对RC522的基础通信

  • 上面说了寄存器、指令、对寄存器的操作,这里介绍一些对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'); 
    }

5.STM32控制RC522与M1的通信

  • 这部分是最重要的步骤,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  允许空闲中断IdleIEnucWaitFor = 0x10;     //认证寻卡等待时候 查询空闲中断标志位break;case PCD_TRANSCEIVE:     //接收发送 发送接收ucIrqEn   = 0x77;     //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEnucWaitFor = 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 ] );         //写数据进FIFOdataRC522_Write_Register ( CommandReg, ucCommand );                 //写命令if ( ucCommand == PCD_TRANSCEIVE )RC522_SetBit_Register(BitFramingReg,0x80);                  //StartSend置位启动数据发送 该位与收发命令使用时才有效ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25msdo                                                      //认证 与寻卡等待时间    {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 );                    }                   }         elsecStatus = MI_ERR;               }RC522_SetBit_Register ( ControlReg, 0x80 );           // stop timer nowRC522_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 ];}elsecStatus = 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 ];           //读出UIDucSnr_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;  elseucN = 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 ];   }elsecStatus = 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;}
    ​

6.RC522通信模块

  • 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();elseRC522_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  允许空闲中断IdleIEnucWaitFor = 0x10;     //认证寻卡等待时候 查询空闲中断标志位break;case PCD_TRANSCEIVE:     //接收发送 发送接收ucIrqEn   = 0x77;     //允许TxIEn RxIEn IdleIEn LoAlertIEn ErrIEn TimerIEnucWaitFor = 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 ] );         //写数据进FIFOdataRC522_Write_Register ( CommandReg, ucCommand );                 //写命令if ( ucCommand == PCD_TRANSCEIVE )RC522_SetBit_Register(BitFramingReg,0x80);                  //StartSend置位启动数据发送 该位与收发命令使用时才有效ul = 1000;//根据时钟频率调整,操作M1卡最大等待时间25msdo                                                      //认证 与寻卡等待时间    {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 );                    }                   }         elsecStatus = MI_ERR;               }RC522_SetBit_Register ( ControlReg, 0x80 );           // stop timer nowRC522_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 ];}elsecStatus = 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 ];           //读出UIDucSnr_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;  elseucN = 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 ];   }elsecStatus = 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

7.获取M1卡的卡号

  • 将以下获取卡号的函数代码加进 RC522.c中,同时在RC522.h 中声明以下函数。

    /* OLED显示屏vcc:PA6GND:PA7SCL:PA8SDA: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] != ''; 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,//  00x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 10x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 20x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 30x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 40xF0,0x08,0xF0,0x00,0xE0,0x18,0x00,0x00,0x00,0x21,0x1C,0x03,0x1E,0x21,0x1E,0x00,//% 50x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x24,0x19,0x27,0x21,0x10,//& 60x10,0x16,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//' 70x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00,//( 80x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00,//) 90x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00,//* 100x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x1F,0x01,0x01,0x01,0x00,//+ 110x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xB0,0x70,0x00,0x00,0x00,0x00,0x00,//, 120x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,//- 130x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00,//. 140x00,0x00,0x00,0x00,0x80,0x60,0x18,0x04,0x00,0x60,0x18,0x06,0x01,0x00,0x00,0x00,/// 150x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00,//0 160x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//1 170x00,0x70,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00,//2 180x00,0x30,0x08,0x88,0x88,0x48,0x30,0x00,0x00,0x18,0x20,0x20,0x20,0x11,0x0E,0x00,//3 190x00,0x00,0xC0,0x20,0x10,0xF8,0x00,0x00,0x00,0x07,0x04,0x24,0x24,0x3F,0x24,0x00,//4 200x00,0xF8,0x08,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x21,0x20,0x20,0x11,0x0E,0x00,//5 210x00,0xE0,0x10,0x88,0x88,0x18,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x11,0x0E,0x00,//6 220x00,0x38,0x08,0x08,0xC8,0x38,0x08,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x00,//7 230x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00,//8 240x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x00,0x31,0x22,0x22,0x11,0x0F,0x00,//9 250x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,//: 260x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x60,0x00,0x00,0x00,0x00,//; 270x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00,//< 280x40,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x00,//= 290x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00,//> 300x00,0x70,0x48,0x08,0x08,0x08,0xF0,0x00,0x00,0x00,0x00,0x30,0x36,0x01,0x00,0x00,//? 310xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x24,0x23,0x14,0x0B,0x00,//@ 320x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20,//A 330x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00,//B 340xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00,//C 350x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00,//D 360x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00,//E 370x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00,//F 380xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00,//G 390x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20,//H 400x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//I 410x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00,//J 420x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00,//K 430x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00,//L 440x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x00,0x3F,0x00,0x3F,0x20,0x00,//M 450x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00,//N 460xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00,//O 470x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00,//P 480xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x18,0x24,0x24,0x38,0x50,0x4F,0x00,//Q 490x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20,//R 500x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00,//S 510x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//T 520x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//U 530x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00,//V 540xF8,0x08,0x00,0xF8,0x00,0x08,0xF8,0x00,0x03,0x3C,0x07,0x00,0x07,0x3C,0x03,0x00,//W 550x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20,//X 560x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00,//Y 570x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00,//Z 580x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00,//[ 590x00,0x0C,0x30,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00,// 600x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00,//] 610x00,0x00,0x04,0x02,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//^ 620x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,//_ 630x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//` 640x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x19,0x24,0x22,0x22,0x22,0x3F,0x20,//a 650x08,0xF8,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00,//b 660x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00,//c 670x00,0x00,0x00,0x80,0x80,0x88,0xF8,0x00,0x00,0x0E,0x11,0x20,0x20,0x10,0x3F,0x20,//d 680x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x22,0x22,0x22,0x22,0x13,0x00,//e 690x00,0x80,0x80,0xF0,0x88,0x88,0x88,0x18,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//f 700x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00,//g 710x08,0xF8,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//h 720x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//i 730x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,//j 740x08,0xF8,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x02,0x2D,0x30,0x20,0x00,//k 750x00,0x08,0x08,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00,//l 760x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F,//m 770x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20,//n 780x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00,//o 790x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0xA1,0x20,0x20,0x11,0x0E,0x00,//p 800x00,0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0xA0,0xFF,0x80,//q 810x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00,//r 820x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00,//s 830x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x00,0x00,//t 840x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20,//u 850x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x01,0x0E,0x30,0x08,0x06,0x01,0x00,//v 860x80,0x80,0x00,0x80,0x00,0x80,0x80,0x80,0x0F,0x30,0x0C,0x03,0x0C,0x30,0x0F,0x00,//w 870x00,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x31,0x2E,0x0E,0x31,0x20,0x00,//x 880x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x80,0x81,0x8E,0x70,0x18,0x06,0x01,0x00,//y 890x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00,//z 900x00,0x00,0x00,0x00,0x80,0x7C,0x02,0x02,0x00,0x00,0x00,0x00,0x00,0x3F,0x40,0x40,//{ 910x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,//| 920x00,0x02,0x02,0x7C,0x80,0x00,0x00,0x00,0x00,0x40,0x40,0x3F,0x00,0x00,0x00,0x00,//} 930x00,0x06,0x01,0x01,0x02,0x02,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//~ 94
};#endif

OLED.h 文件代码

#ifndef __OLED_H
#define __OLED_Hvoid 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制作的一款普通的门禁,如果有帮助到你的话记得点个赞!谢谢啦!

本文发布于:2024-01-30 21:00:11,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170661961422808.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:门禁系统
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23