zoukankan      html  css  js  c++  java
  • STM32F407使用MFRC522射频卡调试及程序移植成功

    基于STM32的MFRC522射频卡模块使用

    本学期感测技术选修课需要做一个作品出来,用到了MFRC522射频卡模块,经历一个星期的调试,终于可以正常使用并寻卡成功了了。 成功的把C51的程序移植到了STM32上面。 现在分享一下调试过程

    1、操作环境

    我所使用的是STM32F407的开发板,使用STM32CubeMX配置初始代码。 MFRC522使用软件模拟SPI通信

    在这里插入图片描述

    在这里插入图片描述

    2、 关于引脚的配置

    淘宝买来的模块,店家都会送资料 ,也可以点下面连接保存至网盘

    链接:http://pan.baidu.com/s/1boMyMlx

    1、SPI通信引脚

    	NSS(SDA)       --------->> 片选信号
    	SCK            --------->> 时钟信号
    	MOSI           --------->> 信号输出端(即单片机引脚设置为输入,MFRC522该引脚输出)
    	MISO           --------->> 信号输入端
    

    在这里插入图片描述
    (上图截图于数据手册,移植别人的程序最好看一下所使用的芯片的数据手册,很有用,方便自己理解程序)

    这里说明一下,在 MFRC522数据手册里面说了, MFRC522需要工作在从机模式下。  
    所以MFRC522这个模块就是从机(Slave),而所使用的单片机就是主机(Master)
    

    这就是为什么上面的MOSI对应的单片机引脚要设置为输出,(Master Output Slave Input)
    MISO信号输入端是指的输入给单片机了

    2、 通信时序
    在这里插入图片描述

    这是数据手册里面的,一定要注意时序的正确性

    片选信号在数据写入期间一定要保持低电平,而无数据时(即空闲状态)必须保持高电平

    强调:时序很重要

    时序出错,一切都白扯

    3、 程序流程

    在这里插入图片描述

    下面我把我用STM32CubeMX的配置贴出来

    在这里插入图片描述

    一定要注意按照这样配置,因为数据手册里面的时序要求是NSS(SDA)引脚默认状态必须是高电平,即1,所以IO口设置必须为High, 且上拉,其他引脚同理,只是不需要上拉了

    3、 下面先贴一下寻卡结果在这里插入图片描述

    S50的卡是0x04000, 所以打印的就是40了

    主函数里面程序

    int main(void)
    {
      /* USER CODE BEGIN 1 */
    	unsigned char status,i;
    	unsigned int temp;
    	
      /* USER CODE END 1 */
    
      /* MCU Configuration--------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
    
      /* USER CODE END Init */
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* USER CODE BEGIN SysInit */
    
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_USART1_UART_Init();
      /* USER CODE BEGIN 2 */
      
      printf("The USART Is Ok!!!
    ");
      
      //  下面进行的是初始化
      PcdReset();
      PcdAntennaOff(); //关闭天线
      PcdAntennaOn();  //开启天线
      M500PcdConfigISOType('A');  // 选择工作方式
    	
      printf("开始寻卡... ...
    ");
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
    	  
    	  status = PcdRequest(PICC_REQALL, g_ucTempbuf);//寻卡
    	  if (status == MI_ERR)     // 如果寻卡失败,则重新初始化 然后continue 继续寻卡
    	  {
    		  PcdReset();
    		  PcdAntennaOff(); //关闭天线
    		  PcdAntennaOn();  //开启天线
    		  M500PcdConfigISOType('A');
    		  continue;
    	  }		 
    	  
    	  // 如果寻卡成功  则LED1闪烁   然后串口打印出来卡的类型
    	  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
    	  HAL_Delay(10);
    	  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
    	  HAL_Delay(10);
    	  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
    	  HAL_Delay(10);
    	  HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
    	  HAL_Delay(10);
    	  printf("
    卡的类型:");
    	  for (i = 0; i < 2; i++)
    	  {
    		  temp = g_ucTempbuf[i];
    		  printf("%X", temp);
    	  }
    	  //PcdHalt();
      }
      /* USER CODE END 3 */
    }
    

    下面是我移植的底层驱动程序,应该也是大部分人想要的吧,不过最好还是自己好好看看那手册改一下
    我只贴出有关SPI通讯的程序,其他部分跟我上面给出的网盘资料里面的C51例程是差不多的,通用

    /*******************************************************************
     @func		: ReadRawRC
     @brief 	: 读RC632寄存器
     @pram		: Address[IN]:寄存器地址
     @retval	: 读出的值
     @NOTE		: MFRC522数据手册.pdf 10.2是关于SPI的详细说明   10.2.2 Read data
    			: unsigned char === uint8_t
     @Call		: 内部调用
    *******************************************************************/
    unsigned char ReadRawRC(unsigned char Address) 
    {
         unsigned char i, ucAddr;
         unsigned char ucResult=0;
       
    	 HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_RESET);// MF522_NSS = 0;
    	 HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);// MF522_SCK = 0;
    	 
    	 
    	 // 地址左移一位是因为LSB是要保留 即RFU位(Reserved for Future Use)
    	 // &0x7E 是把bit1~bit6 的地址(address)写入
    	 // |0x80 是为了使最高位为1   1(Read) 0(Write) 即使能 '读'
         ucAddr = ((Address<<1)&0x7E)|0x80;
    	 
    	 for(i=8;i>0;i--)
    	 {
    		 if((ucAddr&0x80)==0x80)
    		 {
    			 HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_SET);
    		 }
    		 else
    		 {
    			 HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_RESET);
    		 }
    		 HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);
    		 ucAddr <<= 1;
    		 HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
    
    	 }
    	 
    	 for(i=8;i>0;i--)
    	 {
    		HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);
    		ucResult <<= 1;
    		ucResult |= HAL_GPIO_ReadPin(MISO_GPIO_Port, MISO_Pin);
    		HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
    		// 有人说对于STM32这里需要加一句延时,这个是没必要的  这个我经过测试是可以使用的,不用延时
    	 }
    
          
         HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_SET);// MF522_NSS = 1;
    	 HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);// MF522_SCK = 1; 
    	 
    	 
         return ucResult;
    }
    
    
    
    /*******************************************************************
     @func		: WriteRawRC
     @brief 	: 写RC632寄存器
     @pram		: Address[IN]:寄存器地址
    			: value[IN]:写入的值
     @retval	: None
     @Call		: 内部调用
    *******************************************************************/
    void WriteRawRC(unsigned char Address, unsigned char value)
    {  
        unsigned char i, ucAddr;
    	
    	HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);// MF522_SCK = 0;
    	HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_RESET);// MF522_NSS = 0;
    	
    	ucAddr = ((Address << 1) & 0x7E);
    	
    	for(i=8;i>0;i--)
    	{
    		if ((ucAddr&0x80)==0x80)
    		{
    			HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_SET);
    		}
    		else
    		{
    			HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_RESET);
    		}
    		HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);
    		ucAddr <<= 1;
    		HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
    	}
        
    	
    	for(i=8;i>0;i--)
    	{
    		// MF522_SI = ((value&0x80)==0x80);
    		if ((value&0x80)==0x80)
    		{
    			HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_SET);
    		}
    		else
    		{
    			HAL_GPIO_WritePin(MOSI_GPIO_Port, MOSI_Pin, GPIO_PIN_RESET);
    		}
    		HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);
    		value <<= 1;
    		HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_RESET);
    	}
    	
        
    	 
    	
    	HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_SET);// MF522_NSS = 1; 
    	HAL_GPIO_WritePin(SCK_GPIO_Port, SCK_Pin, GPIO_PIN_SET);// MF522_SCK = 1;
    	
    }
    

    复位函数

    /*******************************************************************
     @func		: PcdReset
     @brief		: 复位RC522
     @pram		: None
     @retval	: 成功返回MI_OK
     @NOTE		: 外部调用
    *******************************************************************/
    char PcdReset(void)
    {
        /* MF522_RST=1; */
    	HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_SET);
        HAL_Delay(10);
        /* MF522_RST=0; */
    	HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_RESET);
        HAL_Delay(10);
        /* MF522_RST=1; */
    	HAL_GPIO_WritePin(NSS_GPIO_Port, NSS_Pin, GPIO_PIN_SET);
        HAL_Delay(10);
        WriteRawRC(CommandReg,PCD_RESETPHASE);	// 复位
        HAL_Delay(10);
        
        WriteRawRC(ModeReg,0x3D);            	// 和Mifare卡通讯,CRC初始值0x6363
    	WriteRawRC(TReloadRegL,30);      		// 16位定时器低位
    	WriteRawRC(TReloadRegH,0);				// 16位定时器高位
    	WriteRawRC(TModeReg,0x8D);				// 定时器内部设置
    	WriteRawRC(TPrescalerReg,0x3E);			// 定时器分频系数设置
    	WriteRawRC(TxAutoReg, 0x40);			// 调制发送信号为100%ASK	 调试的时候加上这一句试试
        return MI_OK;
    }
    

    其他的底层驱动函数就不需要改了,由于总的代码量比较长,我就只贴出关键的,其他不需要改的直接参考资料里面的例程即可
    我自己移植过来完整的有很多程序的注注释,有兴趣的可以下载一下,不过自己花时间看看数据手册打个注释是最好的

    点击这里下载完整移植驱动程序

    任何你的不足,在你成功地那一刻,都会被别人说成特色!! 加油吧

  • 相关阅读:
    获取当前div中的所有div的个数和每一个div的ID and 根据屏幕分辨率计算高度
    在当前页获取父窗口中母版页中的服务器控件的ID
    Asp.net C# 获取本周上周本月上月本年上年第一天最后一天时间大全
    C#后台调用前台js方法
    IComparable与排序
    C# 与.NET2.0 中类型Type的GetMethod方法
    下拉菜单及时间段的获取
    黑马程序员——JAVA基础之简述集合collection
    黑马程序员——JAVA基础之基本数据类型包装类和1.5JDK新特性装箱
    黑马程序员——JAVA基础之String和StringBuffer
  • 原文地址:https://www.cnblogs.com/kevin-nancy/p/12569394.html
Copyright © 2011-2022 走看看