使用STM3232F103C8T6的硬件SPI与RC522进行通信

	淘宝客服给的历程是32的F103的基于库函数写的IO口模拟SPI,首先自己用F407的开发板用寄存器写法顺利地实现了功能,然后就是直接用F103C8T6的最小系统的寄存器写法来实现功能,遇到了一些问题。经过不断地调试修改,最后还是成功实现了读卡的功能。
	错误原由之一是将SCK管脚,连接错误;其二是由于设置Master的波特率时将波特率设置为了Fpclk/2,而我在通信中使用的是SPI2,SPI2的时钟源为APB1:36MHZ,因此波特率设置成了18MHZ,而RC522使用SPI进行通信时,最高支持的波特率为10Mbit/s,因此造成了寻卡函数一直返回ERR(254),将这些问题解决了也就成功了。
	在调试的过程中一直怀疑是SPI配置的问题,因此,便再细看了32的硬件SPI的手册内容,查漏补缺,自己之前对SPI是比较模糊,仅仅是使用过便不管不顾,这次,增加了对SPI的了解和注意到了以前没注意的点。
	在此过程中还出现了一点意外,我在SPI的读写函数中加入了printf来观察接收到的数据,然后主函数中却将Uart的初始化放在Spi后面,导致了串口助手没有数据出来,程序也没有正常运行,调整了顺序便好了。
	***SPi端口初始化***
/*
***********************************************************
* 函数功能:SPI端口初始化
* 函数返回值:None
* 函数形参:None
* 备注:sck -- PB_13 
		miso -- PB_14 
		mosi -- PB_15
* 修改作者:None
* 修改时间:None
***********************************************************
*/
static void SPI_GPIO_Init(void)
{
	//IO功能配置
	RCC->APB2ENR |=0x01<<3;
	GPIOB->CRH &=~((unsigned int)0xFFF<<20);
	GPIOB->CRH |=((unsigned int)0xB8B<<20);
}
	此处将SCK和MOSI管脚均设置为复用模式推挽输出,MISO管脚设置为上下拉输入。之前在调试的时候怀疑过是MISO管脚的问题,网上看有的程序将MISO设置为复用推挽输出,查了下
	***
   恐怕大家对MISO端口的设置就会产生疑惑了,MISO不是应该设置成为输入端口(GPIO_Mode_IN_FLOATING)才行的吗?
  答题是肯定的,对于STM32的这一类管脚来说(如USART_RX)即可以设置成为输入模式,也可以设置成为复用的推挽输出。其工作都是正常的,不过建议大家还是设置成为输入端口的好,容易理解。
  具体产生这一问题的原因是:从功能上来说,MISO应该配置为输入模式才对,但为什么也可以配置为GPIO_Mode_AF_PP?请看下面的GPIO复用功能配置框图。当一个GPIO端口配置为GPIO_Mode_AF_PP是,这个端口的内部结构框图如下:图中可以看到,片上外设的复用功能输出信号会连接到输出控制电路,然后在端口上产生输出信号。但是在芯片内部,MISO是SPI模块的输入引脚,而不是输出引脚,也就是说图中的"复用功能输出信号"根本不存在,因此"输出控制电路"不能对外产生输出信号。而另一方面看,即使在GPIO_Mode_AF_PP模式下,复用功能输入信号却与外部引脚之间相互连接,既MISO得到了外部信号的电平,实现了输入的功能。***
  原子哥的回答是MISO的方向由硬件SPi控制,所以设置为复用推挽输出也是可以的。不过还是建议设置成输入模式,便于理解。
  至于代码中在oxFFF和0xB8B前面加入(unsigned int)是因为一开始没加的话会报编译警告,百度了下
  ***编译器默认signed int即32位有符号整数类型,而1<<31实际为0x80000000,这样就有可能改写了符号位(最高位) ***在前面加(unsigned int)就解决了警告。

SPI初始化

/*
***********************************************************
* 函数功能:SPI初始化
* 函数返回值:None
* 函数形参:None
* 备注:PB_13 PB_14 PB_15

* 修改作者:None
* 修改时间:None
***********************************************************
*/
void SPI_Init(void)
{
	SPI_GPIO_Init();
	
	RCC->APB1ENR |=1<<14;  //SPI2时钟使能
	SPI2->CR1 &=~(1<<11); //8位数据帧
	SPI2->CR1 |=1<<9;  //启用软件从设备管理
	SPI2->CR1 |=1<<8;  //NSS引脚电平为高
	SPI2->CR1 &=~(1<<7);  //先发高位
	SPI2->CR1 &=~(0x7<<3);  
	SPI2->CR1 |=0x01<<3;  //波特率:36/4
    
	SPI2->CR1 |=1<<1;  //时钟空闲状态为高电平
	SPI2->CR1 |=1<<0;  //在时钟第二个电平发送数据
	SPI2->CR1 |=1<<2; //主SPI
	
	SPI2->CR1 |=1<<6;     //使能SPI
	SPI_Read_Write(0xff);
}

波特率要与从器件匹配。数据采集时序也要与从器件一致,rc522在上升沿采集数据。

SPI读写数据

/*
***********************************************************
* 函数功能:SPI读写数据
* 函数返回值:None
* 函数形参:None
* 备注:PB_13 PB_14 PB_15
* 修改作者:None
* 修改时间:None
***********************************************************
*/
u8 SPI_Read_Write(u8 data)
{
    u8 ret;
	while(!(SPI2->SR&(0x01<<1)))
    {
        
    }
	SPI2->DR=data;
	while(!(SPI2->SR&(0x01<<0)))
    {
    
    }   
    ret = SPI2->DR;
//    printf("%d\r\n", ret);
	return 	ret;
}

SPI读写同时进行。写入DR的数据被并行传输到移位寄存器,然后与从机串行传输数据,主句接收在移位寄存器中的数据将被并行传输到DR中,接收到的数据即使不使用到也到读取掉。

至于对从器件的读取则得依据从器件的读写结构
使用STM3232F103C8T6的硬件SPI与RC522进行通信_第1张图片

/////////////////////////////////////////////////////////////////////
//功    能:读RC632寄存器
//参数说明:Address[IN]:寄存器地址
//返    回:读出的值
/////////////////////////////////////////////////////////////////////
unsigned char ReadRawRC(unsigned char Address)
{
    u8   ucAddr;
    u8   ucResult=0;
    RFID_CSL;
    ucAddr = ((Address<<1)&0x7E)|0x80;

    SPI_Read_Write(ucAddr);
    ucResult=SPI_Read_Write(0);
    RFID_CSH;
    return ucResult;
}
	需要得到的数据在RC522接收到寄存器地址后由从机发送,所以主机仍需发送一个数据才能接收到。

使用STM3232F103C8T6的硬件SPI与RC522进行通信_第2张图片

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

    RFID_CSL;
    ucAddr = ((Address<<1)&0x7E);

    SPI_Read_Write(ucAddr);
    SPI_Read_Write(value);
    RFID_CSH;
}

先写入寄存器地址,再写数据。

你可能感兴趣的:(使用STM3232F103C8T6的硬件SPI与RC522进行通信)