stm32_13:RFID-RC522项目代码

项目代码

1.SPI配置函数

cs 复制代码
#include "stm32f10x.h"
#include "spi.h"

static void SPI_RCC_Configuration(SPI_TypeDef* SPIx)
{
	if (SPIx == SPI1)
	{
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA,  ENABLE);
	}
	else
	{
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);
	}
}


static void SPI_GPIO_Configuration(SPI_TypeDef* SPIx)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	if(SPIx == SPI1)
	{
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOA,&GPIO_InitStructure);
		
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
		GPIO_Init(GPIOA,&GPIO_InitStructure);
	}
	else
	{
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
		GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
		GPIO_Init(GPIOB,&GPIO_InitStructure);
		
		GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
		GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
		GPIO_Init(GPIOB,&GPIO_InitStructure);
	}
}


void SPI_Configuration(SPI_TypeDef* SPIx)
{
	SPI_InitTypeDef SPI_InitStructure;
	
	SPI_RCC_Configuration(SPIx);
	SPI_GPIO_Configuration(SPIx);
	
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;
	SPI_Init(SPIx,&SPI_InitStructure);
	
	SPI_SSOutputCmd(SPIx,ENABLE);
	SPI_Cmd(SPIx,ENABLE);
	
}


int32_t SPI_WriteByte(SPI_TypeDef* SPIx,uint16_t TxData )
{
	uint8_t retry = 0;
	
	while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET)
	{
		retry ++;
		if(retry >= 200)
			return -1;
	}
	SPI_I2S_SendData(SPIx,TxData);
	
	retry = 0;
	while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET)
	{
		retry ++;
		if(retry >= 200)
			return -1;
	}
	SPI_I2S_ReceiveData(SPIx);
	
	return 0;
}


int32_t SPI_ReadByte(SPI_TypeDef* SPIx,uint16_t* p_RxData)
{
	uint8_t retry = 0;
	
	while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET)
	{
		retry ++;
		if(retry >= 200)
			return -1;
	}
	SPI_I2S_SendData(SPIx,0xFF);
	
	retry = 0;
	while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET)
	{
		retry ++;
		if(retry >= 200)
			return -1;
	}
	*p_RxData = SPI_I2S_ReceiveData(SPIx);
	return 0;
}

int32_t SPI_WriteNBytes(SPI_TypeDef* SPIx,uint8_t* p_TxData,uint32_t SendDataNum)
{
	uint8_t retry = 0;
	while(SendDataNum--)
	{
			while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET)
			{
				retry ++;
				if(retry >= 200000)
					return -1;
			}
			SPI_I2S_SendData(SPIx,*p_TxData++);
			
			retry = 0;
			while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET)
			{
				retry ++;
				if(retry >= 200000)
					return -1;
			}
			SPI_I2S_ReceiveData(SPIx);
	}
	return 0;
}

int32_t SPI_ReadNBytes(SPI_TypeDef* SPIx,uint8_t* p_RxData,uint32_t ReadDataNum)
{
	uint8_t retry = 0;
	
	while(ReadDataNum--)
	{
			while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_TXE) == RESET)
			{
				retry ++;
				if(retry >= 200)
					return -1;
			}
			SPI_I2S_SendData(SPIx,0xFF);
			
			retry = 0;
			while(SPI_I2S_GetFlagStatus(SPIx,SPI_I2S_FLAG_RXNE) == RESET)
			{
				retry ++;
				if(retry >= 200)
					return -1;
			}
			*p_RxData++ = SPI_I2S_ReceiveData(SPIx);
	}
	return 0;
}



/*
void SPI2_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef SPI_InitStructure;
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
	
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial = 7;
	SPI_Init(SPI2,&SPI_InitStructure);
}


u8 SPI2_ReadWriteByte(u8 data)
{
	u8 i;
	
	i = 0;
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE) == RESET)
	{
		i++;
		if(i >= 200)
		{
			return 0;
		}
	}
	SPI_I2S_SendData(SPI2,  data);
	
	i = 0;
	while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE) == RESET)
	{
		i++;
		if(i >= 200)
		{
			return 0;
		}
	}
	return SPI_I2S_ReceiveData(SPI2);
}


void SPI2_SetSpeed(uint16_t SPI_BaudRatePrescaler)
{
	assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
	SPI2->CR1 &= 0Xffc7;
	SPI2->CR1 |= SPI_BaudRatePrescaler;
	SPI_Cmd(SPI2,ENABLE);
	
}

*/

SPI_SSOutputCmd(SPIx,ENABLE);

SPI_Cmd(SPIx,ENABLE);

顺序不能颠倒!

2.RC522配置

选PA11为复位引脚

cs 复制代码
#include "stm32f10x.h"
#include "rc522.h"
#include "systick.h"
#include "spi.h"
#include "stm32f10x_spi.h"

void RC522_IO_Init(void) 
{
	GPIO_InitTypeDef GPIO_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);   //开启AFIO时钟
	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //关闭JTAG因为要使用PB3和4
	
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_4;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOA, &GPIO_InitStruct);	
	
	SPI_Configuration(SPI1); 
}

/*
/////////////////////////////////////////////////////////////////////
//功    能:写RC632寄存器
//参数说明:Address[IN]:寄存器地址
//          value[IN]:写入的值
/////////////////////////////////////////////////////////////////////
void WriteRawRC(unsigned char Address, unsigned char value)
{  
    unsigned char i, ucAddr;

    RC522_SCK_RESET();   //MF522_SCK = 0;
    RC522_NSEL_RESET();  //MF522_NSS = 0;
    ucAddr = ((Address<<1)&0x7E);
    RC522_Delay(10);
    for(i=8;i>0;i--)
    {
        //MF522_SI = ((ucAddr&0x80)==0x80);
			  if((ucAddr&0x80)==0x80)
				{
					RC522_MOSI_SET();
				}
				else
				{
					RC522_MOSI_RESET();
				}
        RC522_SCK_SET();  //MF522_SCK = 1;
        ucAddr <<= 1;
				RC522_Delay(10);
        RC522_SCK_RESET();  //MF522_SCK = 0;
				RC522_Delay(10);
    }

    for(i=8;i>0;i--)
    {
        //MF522_SI = ((value&0x80)==0x80);
			  if((value&0x80)==0x80)
				{
					RC522_MOSI_SET();
				}
				else
				{
					RC522_MOSI_RESET();
				}
				RC522_SCK_SET();  //MF522_SCK = 1;
        value <<= 1;
				RC522_Delay(10);
        RC522_SCK_RESET();  //MF522_SCK = 0;
				RC522_Delay(10);
//         MF522_SCK = 1;
//         value <<= 1;
//         MF522_SCK = 0;
    }
    RC522_NSEL_SET();  //MF522_NSS = 1;
    RC522_SCK_SET();    //MF522_SCK = 1;
}
/////////////////////////////////////////////////////////////////////
//功    能:读RC632寄存器
//参数说明:Address[IN]:寄存器地址
//返    回:读出的值
/////////////////////////////////////////////////////////////////////
unsigned char ReadRawRC(unsigned char Address)
{
     unsigned char i, ucAddr;
     unsigned char ucResult=0;

     RC522_SCK_RESET();   //MF522_SCK = 0;
     RC522_NSEL_RESET();  //MF522_NSS = 0;
     ucAddr = ((Address<<1)&0x7E)|0x80;
     RC522_Delay(10);
     for(i=8;i>0;i--)
     {
//          MF522_SI = ((ucAddr&0x80)==0x80);
//          MF522_SCK = 1;
//          ucAddr <<= 1;
//          MF522_SCK = 0;
			 
			  if((ucAddr&0x80)==0x80)
				{
					RC522_MOSI_SET();
				}
				else
				{
					RC522_MOSI_RESET();
				}
        RC522_SCK_SET();  //MF522_SCK = 1;
        ucAddr <<= 1;
				RC522_Delay(10);
        RC522_SCK_RESET();  //MF522_SCK = 0;
				RC522_Delay(10);
     }
     for(i=8;i>0;i--)
     {
         RC522_SCK_SET();  //MF522_SCK = 1;
         ucResult <<= 1;
			   RC522_Delay(10);
         //ucResult|=(bit)MF522_SO;
			 
// 			 	 if(RC522_MISO_STATUS==1)
// 				 {
// 					 ucResult|=0x01;
// 				 }
// 				 else
// 				 {
// 					 ucResult&=~0x01;
// 				 }
			   ucResult |=RC522_MISO_STATUS;
			 
         RC522_SCK_RESET();  //MF522_SCK = 0;
			   RC522_Delay(10);
     }

    RC522_NSEL_SET();   //MF522_NSS = 1;
    RC522_SCK_SET();    //MF522_SCK = 1;
     return ucResult;
}
*/
//#define MAXRLEN 18
                              
/////////////////////////////////////////////////////////////////////
//功    能:寻卡
//参数说明: req_code[IN]:寻卡方式
//                0x52 = 寻感应区内所有符合14443A标准的卡
//                0x26 = 寻未进入休眠状态的卡
//          pTagType[OUT]:卡片类型代码
//                0x4400 = Mifare_UltraLight
//                0x0400 = Mifare_One(S50)
//                0x0200 = Mifare_One(S70)
//                0x0800 = Mifare_Pro(X)
//                0x4403 = Mifare_DESFire
//返    回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdRequest(unsigned char req_code,unsigned char *pTagType)
{
   char status;  
   unsigned int  unLen;
   unsigned char ucComMF522Buf[MAXRLEN]; 

   ClearBitMask(Status2Reg,0x08); //清RC522寄存位
   WriteRawRC(BitFramingReg,0x07); //写RC623寄存器
   SetBitMask(TxControlReg,0x03); //置RC522寄存位
//   
   ucComMF522Buf[0] = req_code;

   status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,1,ucComMF522Buf,&unLen);
//   UART_send_byte(status);
   if ((status == MI_OK) && (unLen == 0x10))
   {    
       *pTagType     = ucComMF522Buf[0];
       *(pTagType+1) = ucComMF522Buf[1];
   }
   else
   {   status = MI_ERR;   }
   
   return status;
}

/////////////////////////////////////////////////////////////////////
//功    能:防冲撞
//参数说明: pSnr[OUT]:卡片序列号,4字节
//返    回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////  
char PcdAnticoll(unsigned char *pSnr)
{
    char status;
    unsigned char i,snr_check=0;
    unsigned int  unLen;
    unsigned char ucComMF522Buf[MAXRLEN]; 
    

    ClearBitMask(Status2Reg,0x08);
    WriteRawRC(BitFramingReg,0x00);
    ClearBitMask(CollReg,0x80);
 
    ucComMF522Buf[0] = PICC_ANTICOLL1;
    ucComMF522Buf[1] = 0x20;

    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,2,ucComMF522Buf,&unLen);

    if (status == MI_OK)
    {
    	 for (i=0; i<4; i++)
         {   
             *(pSnr+i)  = ucComMF522Buf[i];
             snr_check ^= ucComMF522Buf[i];
         }
         if (snr_check != ucComMF522Buf[i])
         {   status = MI_ERR;    }
    }
    
    SetBitMask(CollReg,0x80);
    return status;
}

/////////////////////////////////////////////////////////////////////
//功    能:选定卡片
//参数说明: pSnr[IN]:卡片序列号,4字节
//返    回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdSelect(unsigned char *pSnr)
{
    char status;
    unsigned char i;
    unsigned int  unLen;
    unsigned char ucComMF522Buf[MAXRLEN]; 
    
    ucComMF522Buf[0] = PICC_ANTICOLL1;
    ucComMF522Buf[1] = 0x70;
    ucComMF522Buf[6] = 0;
    for (i=0; i<4; i++)
    {
    	ucComMF522Buf[i+2] = *(pSnr+i);
    	ucComMF522Buf[6]  ^= *(pSnr+i);
    }
    CalulateCRC(ucComMF522Buf,7,&ucComMF522Buf[7]);
  
    ClearBitMask(Status2Reg,0x08);

    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,9,ucComMF522Buf,&unLen);
    
    if ((status == MI_OK) && (unLen == 0x18))
    {   status = MI_OK;  }
    else
    {   status = MI_ERR;    }

    return status;
}

/////////////////////////////////////////////////////////////////////
//功    能:验证卡片密码
//参数说明: auth_mode[IN]: 密码验证模式
//                 0x60 = 验证A密钥
//                 0x61 = 验证B密钥 
//          addr[IN]:块地址
//          pKey[IN]:密码
//          pSnr[IN]:卡片序列号,4字节
//返    回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////                   
char PcdAuthState(unsigned char auth_mode,unsigned char addr,unsigned char *pKey,unsigned char *pSnr)
{
    char status;
    unsigned int  unLen;
    unsigned char i,ucComMF522Buf[MAXRLEN]; 

    ucComMF522Buf[0] = auth_mode;
    ucComMF522Buf[1] = addr;
    for (i=0; i<6; i++)
    {    ucComMF522Buf[i+2] = *(pKey+i);   }
    for (i=0; i<6; i++)
    {    ucComMF522Buf[i+8] = *(pSnr+i);   }
    
    status = PcdComMF522(PCD_AUTHENT,ucComMF522Buf,12,ucComMF522Buf,&unLen);
    if ((status != MI_OK) || (!(ReadRawRC(Status2Reg) & 0x08)))
    {   status = MI_ERR;   }
    
    return status;
}

/////////////////////////////////////////////////////////////////////
//功    能:读取M1卡一块数据
//参数说明: addr[IN]:块地址
//          p [OUT]:读出的数据,16字节
//返    回: 成功返回MI_OK
///////////////////////////////////////////////////////////////////// 
char PcdRead(unsigned char addr,unsigned char *pData)
{
    char status;
    unsigned int  unLen;
    unsigned char i,ucComMF522Buf[MAXRLEN]; 

    ucComMF522Buf[0] = PICC_READ;
    ucComMF522Buf[1] = addr;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
   
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);
    if ((status == MI_OK) && (unLen == 0x90))
    {
        for (i=0; i<16; i++)
        {    *(pData+i) = ucComMF522Buf[i];   }
    }
    else
    {   status = MI_ERR;   }
    
    return status;
}

/////////////////////////////////////////////////////////////////////
//功    能:写数据到M1卡一块
//参数说明: addr[IN]:块地址
//          p [IN]:写入的数据,16字节
//返    回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////                 
char PcdWrite(unsigned char addr,unsigned char *pData)
{
    char status;
    unsigned int  unLen;
    unsigned char i,ucComMF522Buf[MAXRLEN]; 
    
    ucComMF522Buf[0] = PICC_WRITE;
    ucComMF522Buf[1] = addr;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }
        
    if (status == MI_OK)
    {
        for (i=0; i<16; i++)
        {    ucComMF522Buf[i] = *(pData+i);   }
        CalulateCRC(ucComMF522Buf,16,&ucComMF522Buf[16]);

        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,18,ucComMF522Buf,&unLen);
        if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
        {   status = MI_ERR;   }
    }
    
    return status;
}

/////////////////////////////////////////////////////////////////////
//功    能:扣款和充值
//参数说明: dd_mode[IN]:命令字
//               0xC0 = 扣款
//               0xC1 = 充值
//          addr[IN]:钱包地址
//          pValue[IN]:4字节增(减)值,低位在前
//返    回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////                
char PcdValue(unsigned char dd_mode,unsigned char addr,unsigned char *pValue)
{
    char status;
    unsigned int  unLen;
    unsigned char i,ucComMF522Buf[MAXRLEN]; 
    
    ucComMF522Buf[0] = dd_mode;
    ucComMF522Buf[1] = addr;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }
        
    if (status == MI_OK)
    {
        for (i=0; i<16; i++)
        {    ucComMF522Buf[i] = *(pValue+i);   }
        CalulateCRC(ucComMF522Buf,4,&ucComMF522Buf[4]);
        unLen = 0;
        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,6,ucComMF522Buf,&unLen);
        if (status != MI_ERR)
        {    status = MI_OK;    }
    }
    
    if (status == MI_OK)
    {
        ucComMF522Buf[0] = PICC_TRANSFER;
        ucComMF522Buf[1] = addr;
        CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]); 
   
        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

        if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
        {   status = MI_ERR;   }
    }
    return status;
}

/////////////////////////////////////////////////////////////////////
//功    能:备份钱包
//参数说明: sourceaddr[IN]:源地址
//          goaladdr[IN]:目标地址
//返    回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdBakValue(unsigned char sourceaddr, unsigned char goaladdr)
{
    char status;
    unsigned int  unLen;
    unsigned char ucComMF522Buf[MAXRLEN]; 

    ucComMF522Buf[0] = PICC_RESTORE;
    ucComMF522Buf[1] = sourceaddr;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }
    
    if (status == MI_OK)
    {
        ucComMF522Buf[0] = 0;
        ucComMF522Buf[1] = 0;
        ucComMF522Buf[2] = 0;
        ucComMF522Buf[3] = 0;
        CalulateCRC(ucComMF522Buf,4,&ucComMF522Buf[4]);
 
        status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,6,ucComMF522Buf,&unLen);
        if (status != MI_ERR)
        {    status = MI_OK;    }
    }
    
    if (status != MI_OK)
    {    return MI_ERR;   }
    
    ucComMF522Buf[0] = PICC_TRANSFER;
    ucComMF522Buf[1] = goaladdr;

    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    status = PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    if ((status != MI_OK) || (unLen != 4) || ((ucComMF522Buf[0] & 0x0F) != 0x0A))
    {   status = MI_ERR;   }

    return status;
}

/////////////////////////////////////////////////////////////////////
//功    能:命令卡片进入休眠状态
//返    回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdHalt(void)
{
    //char status;
    unsigned int  unLen;
    unsigned char ucComMF522Buf[MAXRLEN]; 

    ucComMF522Buf[0] = PICC_HALT;
    ucComMF522Buf[1] = 0;
    CalulateCRC(ucComMF522Buf,2,&ucComMF522Buf[2]);
 
    //status = 
	  PcdComMF522(PCD_TRANSCEIVE,ucComMF522Buf,4,ucComMF522Buf,&unLen);

    return MI_OK;
}

/////////////////////////////////////////////////////////////////////
//用MF522计算CRC16函数
/////////////////////////////////////////////////////////////////////
void CalulateCRC(unsigned char *pIndata,unsigned char len,unsigned char *pOutData)
{
    unsigned char i,n;
    ClearBitMask(DivIrqReg,0x04);
    WriteRawRC(CommandReg,PCD_IDLE);
    SetBitMask(FIFOLevelReg,0x80);
    for (i=0; i<len; i++)
    {   WriteRawRC(FIFODataReg, *(pIndata+i));   }
    WriteRawRC(CommandReg, PCD_CALCCRC);
    i = 0xFF;
    do 
    {
        n = ReadRawRC(DivIrqReg);
        i--;
    }
    while ((i!=0) && !(n&0x04));
    pOutData[0] = ReadRawRC(CRCResultRegL);
    pOutData[1] = ReadRawRC(CRCResultRegM);
}

/////////////////////////////////////////////////////////////////////
//功    能:复位RC522
//返    回: 成功返回MI_OK
/////////////////////////////////////////////////////////////////////
char PcdReset(void)
{
    RC522_RESET_SET();     //RST522_1;
    delay_us(10);  //_NOP();
    RC522_RESET_RESET();   //RST522_0;
    delay_ms(60);  //_NOP();_NOP();
    RC522_RESET_SET();     //RST522_1;RST522_1;
    delay_us(500);  //_NOP();_NOP();
    WriteRawRC(CommandReg,PCD_RESETPHASE);
    delay_ms(2);  //_NOP();_NOP();
  
  WriteRawRC(ModeReg,0x3D);            //?Mifare???,CRC???0x6363
  WriteRawRC(TReloadRegL,30);         //?30?????           
  WriteRawRC(TReloadRegH,0);          
  WriteRawRC(TModeReg,0x8D);
  WriteRawRC(TPrescalerReg,0x3E);
  WriteRawRC(TxAutoReg,0x40);
  
  ClearBitMask(TestPinEnReg, 0x80);//off MX and DTRQ out
  WriteRawRC(TxAutoReg,0x40);
   
  return MI_OK;
}

/////////////////////////////////////////////////////////////////////
//功    能:读RC632寄存器
//参数说明:Address[IN]:寄存器地址
//返    回:读出的值
/////////////////////////////////////////////////////////////////////
unsigned char ReadRawRC(unsigned char Address)
{
	unsigned char ucAddr;
	unsigned char ucResult=0;
	ucAddr = ((Address<<1)&0x7E)|0x80;
	delay_ms(1);
	RC522_ENABLE;
	SPI_WriteNBytes(SPI1,&ucAddr,1);  //向总线写多个数据
	SPI_ReadNBytes(SPI1,&ucResult,1);  //向总线读多个数据
	RC522_DISABLE;
	return ucResult;
}

/////////////////////////////////////////////////////////////////////
//功    能:写RC632寄存器
//参数说明:Address[IN]:寄存器地址
//          value[IN]:写入的值
/////////////////////////////////////////////////////////////////////
void WriteRawRC(unsigned char Address, unsigned char value)
{  
	unsigned char ucAddr;
	uint8_t write_buffer[2]={0};
	ucAddr = ((Address<<1)&0x7E);
	write_buffer[0] = ucAddr;
	write_buffer[1] = value;
	delay_ms(1);
	RC522_ENABLE;
	SPI_WriteNBytes(SPI1,write_buffer,2);
	RC522_DISABLE;
}

/////////////////////////////////////////////////////////////////////
//功    能:置RC522寄存器位
//参数说明:reg[IN]:寄存器地址
//          mask[IN]:置位值
/////////////////////////////////////////////////////////////////////
void SetBitMask(unsigned char reg,unsigned char mask)  
{
    char tmp = 0x0;
    tmp = ReadRawRC(reg);  //读RC632寄存器
    WriteRawRC(reg,tmp | mask);  // set bit mask
}

/////////////////////////////////////////////////////////////////////
//功    能:清RC522寄存器位
//参数说明:reg[IN]:寄存器地址
//          mask[IN]:清位值
/////////////////////////////////////////////////////////////////////
void ClearBitMask(unsigned char reg,unsigned char mask)  
{
    char tmp = 0x0;
    tmp = ReadRawRC(reg);
    WriteRawRC(reg, tmp & ~mask);  // clear bit mask
} 

/////////////////////////////////////////////////////////////////////
//功    能:通过RC522和ISO14443卡通讯
//参数说明:Command[IN]:RC522命令字
//          pIn [IN]:通过RC522发送到卡片的数据
//          InLenByte[IN]:发送数据的字节长度
//          pOut [OUT]:接收到的卡片返回数据
//          *pOutLenBit[OUT]:返回数据的位长度
/////////////////////////////////////////////////////////////////////
char PcdComMF522(unsigned char Command, 
                 unsigned char *pInData, 
                 unsigned char InLenByte,
                 unsigned char *pOutData, 
                 unsigned int  *pOutLenBit)
{
    char status = MI_ERR;
    unsigned char irqEn   = 0x00;
    unsigned char waitFor = 0x00;
    unsigned char lastBits;
    unsigned char n;
    unsigned int i;
    switch (Command)
    {
       case PCD_AUTHENT:
          irqEn   = 0x12;
          waitFor = 0x10;
          break;
       case PCD_TRANSCEIVE:
          irqEn   = 0x77;
          waitFor = 0x30;
          break;
       default:
         break;
    }
   
    WriteRawRC(ComIEnReg,irqEn|0x80);
    ClearBitMask(ComIrqReg,0x80);
    WriteRawRC(CommandReg,PCD_IDLE);
    SetBitMask(FIFOLevelReg,0x80);
    
    for (i=0; i<InLenByte; i++)
    {   WriteRawRC(FIFODataReg, pInData[i]);    }
    WriteRawRC(CommandReg, Command);
   
    
    if (Command == PCD_TRANSCEIVE)
    {    SetBitMask(BitFramingReg,0x80);  }
    
    i = 800 ; //600;//????????,??M1???????25ms
    do 
    {
         n = ReadRawRC(ComIrqReg);
         i--;
    }
    while ((i!=0) && !(n&0x01) && !(n&waitFor));
    ClearBitMask(BitFramingReg,0x80);
	      
    if (i!=0)
    {    
         if(!(ReadRawRC(ErrorReg)&0x1B))
         {
             status = MI_OK;
             if (n & irqEn & 0x01)
             {   status = MI_NOTAGERR;   }
             if (Command == PCD_TRANSCEIVE)
             {
               	n = ReadRawRC(FIFOLevelReg);
              	lastBits = ReadRawRC(ControlReg) & 0x07;
                if (lastBits)
                {   *pOutLenBit = (n-1)*8 + lastBits;   }
                else
                {   *pOutLenBit = n*8;   }
                if (n == 0)
                {   n = 1;    }
                if (n > MAXRLEN)
                {   n = MAXRLEN;   }
                for (i=0; i<n; i++)
                {   pOutData[i] = ReadRawRC(FIFODataReg);    }
            }
         }
         else
         {   status = MI_ERR;   }
        
   }
   

   SetBitMask(ControlReg,0x80);           // stop timer now
   WriteRawRC(CommandReg,PCD_IDLE); 
   return status;
}


/////////////////////////////////////////////////////////////////////
//开启天线  
//每次启动或关闭天险发射之间应至少有1ms的间隔
/////////////////////////////////////////////////////////////////////
void PcdAntennaOn(void)
{
    unsigned char i;
    i = ReadRawRC(TxControlReg);
    if (!(i & 0x03))
    {
        SetBitMask(TxControlReg, 0x03);
    }
}


/////////////////////////////////////////////////////////////////////
//关闭天线
/////////////////////////////////////////////////////////////////////
void PcdAntennaOff(void)
{
    ClearBitMask(TxControlReg, 0x03);
}

void RC522_Config(unsigned char Card_Type)
{
	   ClearBitMask(Status2Reg,0x08);
     WriteRawRC(ModeReg,0x3D);//3F
     WriteRawRC(RxSelReg,0x86);//84
     WriteRawRC(RFCfgReg,0x7F);   //4F
   	 WriteRawRC(TReloadRegL,30);//tmoLength);// TReloadVal = 'h6a =tmoLength(dec) 
	   WriteRawRC(TReloadRegH,0);
     WriteRawRC(TModeReg,0x8D);
	   WriteRawRC(TPrescalerReg,0x3E);
//	   WriteRawRC(TxAutoReg,0x40);//???
	   delay_ms(5);//delay_10ms(1);
	
	
     PcdAntennaOn();
}

3.检查卡号函数

cs 复制代码
#include "stm32f10x.h"
#include "Check_Card_Id.h"
#include <stdio.h>

extern uint8_t Run_flag;
extern uint8_t Lock_flag;

uint8_t Err_Count = 0;

uint8_t Check_Card_Id(uint8_t *value)
{
	uint8_t i = 0,flag = 0;
	uint8_t ave = 0;
	int num = 0;
	
	for(i = 0;i < 4;i++)
	{
		printf("card_id=%d\t",value[i]);
		num += value[i]*(i+1);
	}
	printf("\r\n");
	printf("num=%d\r\n",num);
	
	ave = num/4;
	printf("ave = %d\r\n",ave);
	
	switch(ave)
	{
		case 218:flag = 1;break;
		
		
		default:
			flag = 0;	Err_Count++;
	}
	
	if(Err_Count == 5)
	{
		Run_flag = 0;
		Lock_flag = 1;
		
	}
	
	return flag;
}

num+=value[i]*(i+1); 加权求和算法

num = value[0] × 1 + value[1] × 2 + value[2] × 3 + value[3] × 4

假设一张卡的UID是:value[] = {100, 200, 50, 150}

第1字节:100 × 1 = 100

第2字节:200 × 2 = 400

第3字节:50 × 3 = 150

第4字节:150 × 4 = 600

总和:num = 100 + 400 + 150 + 600 = 1250

平均值:ave = 1250 / 4 = 312

目的1:区分不同字节的重要性

  • 不同位置的字节有不同的权重
  • 第4字节的权重最高(×4),第1字节最低(×1)
  • 这样即使字节值相同,位置不同也会产生不同结果

目的2:简单的特征提取

目的3:降低碰撞概率

算法的优缺点

优点:

  • 简单快速:计算量小,适合嵌入式系统
  • 一定程度区分:能区分字节顺序不同的卡片
  • 代码简洁:几行代码实现

缺点:

碰撞仍然可能:不同UID可能算出相同值

4.mian函数

cs 复制代码
#include "stm32f10x.h"
#include "main.h"
#include "led.h"
#include "buzzer.h"
#include "key.h"
#include "Relay.h"
#include "Shake.h"
#include "Exti.h"
#include "usart.h"
#include "stdio.h"
#include "Time.h"
#include "sg90.h"
#include "systick.h"
#include "I2C_soft.h"
#include "dht11.h"
#include "spi.h"
#include "dma.h"
#include "car_driver.h"
#include "rc522.h"
#include "Check_Card_Id.h"
#include "IWDG.h"

uint8_t Card_Type1[2];  
uint8_t Card_ID[4]; 

uint8_t status;
uint8_t flag;
uint8_t Run_flag=1;
uint8_t Lock_flag=0;

int  main()
{
	Card_Type1[0]=0x04;
	Card_Type1[1]=0x00;
	
	initSysTick();
	my_usart1_init();
	BEEP_Init();
	OLED_Init();
	SG90_Init();
	Time2_IWDG_Init(9999,7199);
	
	printf("\r\n***************************** 串口测试 *****************************\r\n");
	
	RC522_IO_Init();
	PcdReset();
	PcdAntennaOff();
	delay_ms(100);
	PcdAntennaOn();
	
	OLED_Clear();
	OLED_ShowString(0,0,"ClockSystem");
	SG90_run(0);

	delay_ms(500);
	printf("\r\n***************************** 智能门锁 *****************************\r\n");
	
	while(Run_flag)
	{
		if(PcdRequest(0x52,Card_Type1) == MI_OK)
		{
			uint16_t Card_Type = (Card_Type1[0]<<8 |Card_Type1[1]);
			printf("Card TYpe:0x%04x\r\n",Card_Type);
			
			TIM_Cmd(TIM2,DISABLE);
			
			switch(Card_Type)
			{
				case 0x4400:
							printf("Mifare UltraLight\r\n");
							break;
					case 0x0400:
							printf("Mifare One(S50)\r\n");
							break;
					case 0x0200:
							printf("Mifare One(S70)\r\n");
							break;
					case 0x0800:
							printf("Mifare Pro(X)\r\n");
							break;
					case 0x4403:
							printf("Mifare DESFire\r\n");
							break;
					default:
							printf("Unknown Card\r\n");
							break;
			}
			
			status = PcdAnticoll(Card_ID);
			if(status != MI_OK)
			{
				printf("Anticoll Fault!\r\n");
			}
			else
			{
				printf("Card Id = %d %d %d %d\r\n",Card_ID[0],Card_ID[1],Card_ID[2],Card_ID[3]);
				
				flag = Check_Card_Id(Card_ID);
				printf("flag = %d\r\n",flag);
				if(flag != 0)
				{
					OLED_ShowString(0,2,"OpenDoor");
					SG90_run(90);
					BEEP_SUC();
					delay_ms(5000);
					
					OLED_ShowString(0,2,"          ");
					SG90_run(0);
				}
				else
				{
					printf("flag fault\r\n");
					BEEP_FAIL();
					delay_ms(5000);
				}
			}
			
			status = PcdSelect(Card_ID);
			if(status != MI_OK)
			{
				printf("Select Fault!\r\n");
			}
			else
			{
				printf("Select Success!\r\n");
			}
		
			status = PcdHalt();
			if(status != MI_OK)
			{
				printf("PcdHalt Fault!\r\n");
			}
			else
			{
				printf("PcdHalt Success!\r\n");
			}
			printf("****************************************************************\r\n");
			TIM_Cmd(TIM2,ENABLE);
		}
	}
	
	delay_ms(500);
	PcdAntennaOff();  //关闭天线
	
	//错误次数太多
	while(Lock_flag)
	{
		printf("Most Error,Please wait 1 minutes\r\n");
		delay_ms(5000);
	}
   
}

1.代码逻辑

  • 首先查找卡的类型0x4000,成功返回MI_OK,并打印卡的类型
  • 然后防冲撞,并返回卡号放到数组里,并返回MI_OK,并打印卡号,4个字节
  • 然后检查卡是否被收入,收入执行开锁,未收入报警,报警六次,Run_flag=0;Lock_flag=1;
  • 进入锁定状态,等待看门狗复位

2.Card_Type1[0]=0x04; Card_Type1[1]=0x00;

设置 寻卡请求(PICC Request)的命令参数,用于告诉RC522模块寻找哪种类型的RFID卡。

5.看门狗

cs 复制代码
#include "Time.h"
#include "stm32f10x.h"
#include "systick.h"
#include "IWDG.h"

int tim_count;
u8 sec = 0;
//u8 min = 0;
	
void Time2_IWDG_Init(uint16_t arr,uint16_t psc)
{
	TIM_TimeBaseInitTypeDef Time_Initstructure;
	NVIC_InitTypeDef NVIC_Initstructure; 
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	
	Time_Initstructure.TIM_ClockDivision = TIM_CKD_DIV1;
	Time_Initstructure.TIM_CounterMode = TIM_CounterMode_Up;
	Time_Initstructure.TIM_Period = arr;
	Time_Initstructure.TIM_Prescaler = psc;
	Time_Initstructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2,&Time_Initstructure);
	
	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
	TIM_Cmd(TIM2,ENABLE);
	
	NVIC_Initstructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_Initstructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_Initstructure);
	

	
}

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
	{
		sec++;	
		if(sec == 30)
		{
			sec = 0;
			IWDG_Init(4,2000);
		}
		
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
	}
	
}
cs 复制代码
#include "stm32f10x.h"
#include "IWDG.h"


void IWDG_Init(u8 prer,u16 rlr)
{
	IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
	IWDG_SetPrescaler(prer);
	IWDG_SetReload(rlr);
	IWDG_ReloadCounter();
	IWDG_Enable();
}

void IWDG_Feed(void)
{
	IWDG_ReloadCounter();
}

实现的功能是

TIM2定时器计一万个数为1s,每记一个数(每一秒),执行一次中断,sec++,一分钟后初始化看门狗,重新复位程序。

一、简介

(原文链接:https://blog.csdn.net/QF_7777777/article/details/126537162)

STM32F10xxx内置两个看门狗,提供了更高的安全性、时间的精确性和使用的灵活性。两个看门狗设备(独立看门狗和窗口看门狗)可用来检测和解决由软件错误引起的故障;当计数器达到给

定的超时值时,触发一个中断(仅适用于窗口型看门狗)或产生系统复位。

独立看门狗(IWDG)由专用的40kHz的低速时钟驱动,即使主时钟发生故障它也仍然有效。窗口看门狗由从APB1时钟分频后得到的时钟驱动,通过可配置的时间窗口来检测应用程序非正常的

过迟或过早的操作。

IWDG最适合应用于那些需要看门狗作为一个在主程序之外,能够完全独立工作,并且对时间精度要求较低的场合。 WWDG最适合那些要求看门狗在精确计时窗口起作用的应用程序。

二、为什么需要看门狗

在由单片机构成的微型计算机系统中,由于单片机的工作常常会受到来自外界电磁场的干扰,造成程序的跑飞,而陷入死循环,程序的正常运行被打断,单片机控制的系统无法继续工作,会造成整个系统陷入停滞状态,发生不可预料的后果,所以出于对单片机运行状态进行实时监测的考虑,便产生了一种专门用于检测单片机程序运行状态的模块或者芯片,俗称"看门狗"。

三、独立看门狗

1、独立看门狗功能描述

  • 在键值寄存器(IWDG_KR)中写入0xCCCC,开始启用独立看门狗。此时计数器开始从其复位值0xFFF递减,当计数器值计数到尾值0x000时会产生一个复位信号(IWDG_RESET)。
  • 无论何时,只要在键值寄存器IWDG_KR中写入0xAAAA(通常说的喂狗),自动重装载寄存器IWDG_RLR的值就会重新加载到计数器,从而避免看门狗复位。
  • 如果程序异常,就无法正常喂狗,从而系统复位。

2、独立看门狗框图

3、独立看门狗超时时间

溢出时间(Overflow Time)指的是定时器从开始计数到计数值达到最大值后归零(溢出)所需要的时间。

溢出时间计算:

Tout = ((4*2^prer)*rlr )/ 40

时钟频率LSI = 40K,一个看门狗时钟周期就是最短超时时间。

最长超时时间 = (IWDG_RLR寄存器最大值)* 看门狗时钟周期。

// 你的配置

TIME2_IWDG_Init(9999, 7199)

计算:

  1. ARR = 9999
  2. PSC = 7199
  3. 时钟频率 = 72 MHz

IWDG_Init(4, 2000); // prer=4, rlr=2000

// 计算:

预分频系数 = 64 4*2^4

重载值 = 2000

频率 = 40kHz = 40000Hz

溢出时间 T = (64 × 2000) / 40000 = 128000 / 40000 = 3.2秒

4、独立看门狗操作步骤

  1. IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable)

作用:启用对IWDG预分频器(PR)和重装载寄存器(RLR)的写访问

  • IWDG的配置寄存器默认是写保护的
  • 必须先调用此函数解锁,才能配置预分频器和重装载值
  • 参数可以是IWDG_WriteAccess_Enable(使能写访问)或IWDG_WriteAccess_Disable(禁止写访问)
  1. IWDG_SetPrescaler(prer)

作用:设置IWDG的预分频值

  • 决定看门狗计数器的时钟频率
  • 参数prer可选值:
    • IWDG_Prescaler_4 (4分频)
    • IWDG_Prescaler_8 (8分频)
    • IWDG_Prescaler_16 (16分频)
    • IWDG_Prescaler_32 (32分频)
    • IWDG_Prescaler_64 (64分频)
    • IWDG_Prescaler_128 (128分频)
    • IWDG_Prescaler_256 (256分频)
  1. IWDG_SetReload(rlr)

作用:设置IWDG的重装载值

  • 决定看门狗超时时间
  • 参数rlr范围:0x000-0xFFF(0-4095)
  • 超时时间计算公式:Tout = (4 × 2^prer × rlr) / LSI
  • 其中LSI是内部低速RC振荡器频率(约40kHz,具体以芯片手册为准)
  1. IWDG_ReloadCounter()

作用:重装载看门狗计数器(俗称"喂狗")

  • 将重装载寄存器(RLR)的值重新加载到计数器
  • 必须在超时前定期调用此函数,防止系统复位
  • 这是应用程序中需要周期性调用的函数
  1. IWDG_Enable()

作用:启动独立看门狗

  • 一旦使能,看门狗就开始递减计数
  • 使能后无法被软件禁用,只有复位才能停止看门狗
  • 必须在所有配置完成后最后调用

注意事项

1.static

主要含义:将函数的作用域限制在当前源文件内,使其成为文件内部的"私有"函数。

具体作用和优点:

  1. 限制作用域
  • 该函数只能在定义它的.c文件中被访问
  • 其他源文件无法调用此函数
  • 避免了命名冲突的可能性
  1. 信息隐藏(封装性)
  • 只暴露必要的接口函数给外部
  • 隐藏内部实现细节
  • 提高代码的模块化和可维护性

2.SPI1

在STM32标准库中:SPI1本质上是一个已经定义好的指针常量

SPI_TypeDef* SPIx // SPIx是一个指针

// 函数声明

static void SPI_RCC_Configuration(SPI_TypeDef* SPIx)

// 函数调用

SPI_RCC_Configuration(SPI1);

// 实际相当于:

SPI_TypeDef* SPIx = SPI1; // SPIx现在指向SPI1的地址

if(SPIx == SPI1)

这里比较的是指针的值(地址),不是指针指向的内容。

3.片选NSS引脚

SPI的片选引脚有两种使用方式:

方式一:硬件自动控制

GPIO_Mode_AF_PP // 复用推挽输出

  • SPI硬件自动控制片选信号
  • 传输开始时自动拉低,传输结束时自动拉高
  • 缺点:通常只支持单主模式,灵活性差

方式二:软件手动控制(常用)

GPIO_Mode_Out_PP // 推挽输出

  • 软件手动控制片选电平
  • 传输前手动拉低,传输后手动拉高
  • 优点:灵活,支持多从设备、可控制时序

3.SPI主模式的SS输出管理

void SPI_SSOutputCmd(SPI_TypeDef* SPIx, FunctionalState NewState);

  1. 功能说明

这个函数控制SPI主设备是否输出SS(NSS)信号。

  • ENABLE: SPI主设备会在它的NSS引脚上输出低电平信号
  • DISABLE: SPI主设备的NSS引脚不输出信号(高阻态)

SPI_SSOutputCmd是STM32 SPI硬件NSS功能的关键部分。要使硬件NSS正常工作,需要:

  1. GPIO配置为复用功能(AF_PP
  2. SPI配置为SPI_NSS_Hard
  3. 调用SPI_SSOutputCmd(SPIx, ENABLE)

注意:必须在SPI_SSOutputCmd之后进行SPI_Cmd!!

相关推荐
安庆平.Я5 小时前
STM32——定时器:高级定时器
stm32·单片机·嵌入式硬件
项目題供诗5 小时前
51单片机入门(一)
单片机·嵌入式硬件·51单片机
1621B驱动芯片原厂6 小时前
YL1621 芯片成功案例推荐
stm32·单片机·嵌入式硬件·mcu·51单片机
Hello_Embed6 小时前
RS485 双串口通信 + LCD 实时显示(DMA+IDLE 空闲中断版)
笔记·单片机·学习·操作系统·嵌入式·freertos
__万波__7 小时前
STM32L475基于完全空白的项目,完成时钟树初始化配置并验证
单片机·嵌入式硬件
XINVRY-FPGA7 小时前
XC7VX690T-2FFG1761I Xilinx AMD FPGA Virtex-7
arm开发·嵌入式硬件·fpga开发·硬件工程·fpga
良许Linux8 小时前
STM32F103每个符号的意思是什么?
stm32·单片机·嵌入式硬件
小痞同学8 小时前
【铁头山羊STM32】HAL库 4.时钟系统部分
stm32·单片机·嵌入式硬件
周周记笔记9 小时前
ESP32-S3 :开发方式笔记(五)
笔记·单片机·嵌入式硬件