STM32
直播中

李秀珍

7年用户 1010经验值
私信 关注
[问答]

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

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

回帖(1)

张琪

2021-12-14 13:54:43

        淘宝客服给的历程是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("%drn", ret);
        return         ret;
}
SPI读写同时进行。写入DR的数据被并行传输到移位寄存器,然后与从机串行传输数据,主句接收在移位寄存器中的数据将被并行传输到DR中,接收到的数据即使不使用到也到读取掉。
至于对从器件的读取则得依据从器件的读写结构





/
//功    能:读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接收到寄存器地址后由从机发送,所以主机仍需发送一个数据才能接收到。




/
/
//功    能:写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;
}
举报

更多回帖

发帖
×
20
完善资料,
赚取积分