- SPI
1、 SPI概念
SPI 是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。SPI 接口主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。
SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
2、SPI信号线
SCK:时钟线(由主机产生,用于同步数据,一个脉冲传输一位数据),另名:C SCL SCLK
CS:使能控制端(主机控制从机),选定哪个从机可以工作
MOSI:Master Output Slave Input,主机输出,从机输入
MISO:Master Input Slave Output,主机输入,从机输出
SPI工作原理总结
- 硬件上为4根线。
- 主机和从机都有一个串行移位寄存器,主机通过向它的SPI串行寄存器写入一个字节来发起一次传输。
- 串行移位寄存器通过MOSI信号线将字节传送给从机,从机也将自己的串行移位寄存器中的内容通过MISO信号线返回给主机。这样,两个移位寄存器中的内容就被交换。
- 外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字符(任意字符)节来引发从机的传输。
3、SPI总线四种工作方式
SPI 模块为了和外设模块进行数据交换,根据外设模块工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。
时序详解:
CPOL:时钟极性选择,为0时SPI总线空闲为低电平(时钟线由低电平开始),为1时SPI总线空闲为高电平(时钟线由高电平开始)
CPHA:时钟相位选择,为0时在SCK第一个跳变沿采样,为1时在SCK第二个跳变沿采样
工作方式0:
当CPHA=0、CPOL=0时SPI总线工作在方式1。MISO引脚上的数据在第一个SPSCK沿跳变之前已经上线了,而为了保证正确传输,MOSI引脚的MSB位必须与SPSCK的第一个边沿同步,在SPI传输过程中,首先将数据上线,然后在同步时钟信号的上升沿时,SPI的接收方捕捉位信号,在时钟信号的一个周期结束时(下降沿),下一位数据信号上线,再重复上述过程,直到一个字节的8位信号传输结束。
工作方式1:
当CPHA=0、CPOL=1时SPI总线工作在方式2。与前者唯一不同之处只是在同步时钟信号的下降沿时捕捉位信号,上升沿时下一位数据上线。
工作方式2:
当CPHA=1、CPOL=0时SPI总线工作在方式3。MISO引脚和MOSI引脚上的数据的MSB位必须与SPSCK的第一个边沿同步,在SPI传输过程中,在同步时钟信号周期开始时(上升沿)数据上线,然后在同步时钟信号的下降沿时,SPI的接收方捕捉位信号,在时钟信号的一个周期结束时(上升沿),下一位数据信号上线,再重复上述过程,直到一个字节的8位信号传输结束。
工作方式3:
当CPHA=1、CPOL=1时SPI总线工作在方式4。与前者唯一不同之处只是在同步时钟信号的上升沿时捕捉位信号,下降沿时下一位数据上线。
4、SPI配置步骤流程图
SPI配置需要的库函数文件:stm32f4xx_spi.c
- 理解电路原理图
使用SPI1
SCK连接PB3
MISO连接PB4
MOSI连接PB5
CS连接PB14
(2)使能SPIx和IO口时钟
RCC_AHBxPeriphClockCmd() / RCC_APBxPeriphClockCmd();
(3)初始化IO口为复用功能
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
(4)设置引脚复用映射:
GPIO_PinAFConfig();
(5)初始化SPIx,设置SPIx工作模式
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
(6)使能SPIx
void SPI_Cmd(SPI_TypeDef* SPIx, FunctionalState NewState);
(7)SPI传输数据
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;
(8)查看SPI传输状态
SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);
- W25Q128
2.1 W25Q128概念
Flash存储器属,内存器件的一种,是一种非易失性( Non-Volatile )内存。
flash闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。任何flash器件的写入操作只能在空或已擦除的单元内进行,所以大多数情况下,在进行写入操作之前必须先执行擦除。
W25Q128 将 16M 的容量分为 256 个块(Block),每个块大小为 64K 字节,每个块又分为 16个扇区(Sector),每个扇区 4K 个字节。 W25Q128 的最少擦除单位为一个扇区,也就是每次必须擦除 4K 个字节。操作需要给 W25Q128 开辟一个至少 4K 的缓存区,对 SRAM (运行内存)要求比较高,要求芯片必须有 4K 以上 SRAM 才能很好的操作。
块>扇区>页
2.2 W25Q128引脚与内部
引脚说明
内部
2.3 命令操作W25Q128
写使能
读状寄存器
擦除扇区
写数据
读数据
- 函数说明
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
函数说明:SPI总线初始化
返回值:无
SPI_TypeDef* SPIx:哪个SPI外设
typedef struct
{
uint16_t SPI_Direction; //选择工作模式
uint16_t SPI_Mode; //选择主从机模式
uint16_t SPI_DataSize; //数据大小
uint16_t SPI_CPOL; //时钟极性
uint16_t SPI_CPHA; //时钟相位
uint16_t SPI_NSS; //硬件控制CS
uint16_t SPI_BaudRatePrescaler; //分频
uint16_t SPI_FirstBit; //输出高位还是低位
uint16_t SPI_CRCPolynomial; //CRC校验
}SPI_InitTypeDef;
SPI
#include "spi.h"
/*************************************
引脚说明
SCK连接PB3
MISO连接PB4
MOSI连接PB5
CS连接PB14
*************************************/
#if 1
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
SPI_InitTypeDef SPI_InitStruct;
//(2)使能SPIx和IO口时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
//GPIO 初始化设置:要设置模式为复用功能。
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5; //引脚3 4 5
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //复用模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽式
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //50MHZ
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14; //引脚14
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出模式
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽式输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //50MHZ
GPIO_Init(GPIOB,&GPIO_InitStruct);
//(4)设置引脚复用映射:
GPIO_PinAFConfig(GPIOB, GPIO_PinSource3, GPIO_AF_SPI1); //SCK
GPIO_PinAFConfig(GPIOB, GPIO_PinSource4, GPIO_AF_SPI1); //MISO
GPIO_PinAFConfig(GPIOB, GPIO_PinSource5, GPIO_AF_SPI1); //MOSI
SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //全双工
SPI_InitStruct.SPI_Mode = SPI_Mode_Master; //主机
SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; //数据位8位
SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; //时钟相位 第一边沿
SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; //时钟极性 低电平
SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; //先发高位
SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; //CS端由软件控制
SPI_InitStruct.SPI_BaudRatePrescaler= SPI_BaudRatePrescaler_16; //分频
SPI_InitStruct.SPI_CRCPolynomial = 7; //CRC校验
//(5)初始化SPIx,设置SPIx工作模式
SPI_Init(SPI1, &SPI_InitStruct);
//(6)使能SPIx
SPI_Cmd(SPI1, ENABLE);
//不使能芯片
F_CS = 1;
}
//SPI收发数据
u8 Spi1_Recv_Send_Byte(u8 tx_tata)
{
u8 rx_data = 0x00;
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
SPI_I2S_SendData(SPI1, tx_tata);
while(SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
rx_data = SPI_I2S_ReceiveData(SPI1);
return rx_data;
}
#else
void SPI1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
//1、使能SPIx和IO口时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_14|GPIO_Pin_5; //GPIOB 3 14 5
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //配置IO口输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度 50MHz
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4; //引脚4
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOB,&GPIO_InitStruct);
//不使能芯片
F_CS = 1;
}
//SPI收发数据
u8 Spi1_Recv_Send_Byte(u8 tx_tata)
{
u8 i = 0;
u8 rx_data = 0x00;
SCK = 0;
for(i=0; i<8; i++)
{
//准备发送的数据
if(tx_tata & (0x01<<(7-i)))
{
MOSI = 1;
}
else
{
MOSI = 0;
}
delay_us(2);
SCK = 1;
delay_us(2);
//接受数据
if(MISO == 1)
{
rx_data |= (0x01<<(7-i));
}
SCK = 0;
}
return rx_data;
}
#endif
//获取芯片生产商/设备ID
u16 W25q128_Id(void)
{
u16 id = 0x00;
//使能芯片
F_CS = 0;
//发送命令
Spi1_Recv_Send_Byte(0x90);
//发送地址
Spi1_Recv_Send_Byte(0x00);
Spi1_Recv_Send_Byte(0x00);
Spi1_Recv_Send_Byte(0x00);
//发送任意字符
id |= Spi1_Recv_Send_Byte(0xFF)<<8;
id |= Spi1_Recv_Send_Byte(0xFF);
//不使能芯片
F_CS = 1;
return id;
}
void Write_Enable(void)
{
//使能芯片
F_CS = 0;
//发送命令
Spi1_Recv_Send_Byte(0x06);
//不使能芯片
F_CS = 1;
}
u8 W25q128_status1(void)
{
u8 status1 = 0;
//使能芯片
F_CS = 0;
//发送命令
Spi1_Recv_Send_Byte(0x05);
//发送任意字符,获取状态寄存器1的值
status1 = Spi1_Recv_Send_Byte(0xFF);
//不使能芯片
F_CS = 1;
return status1;
}
void W25q128_Sector_Erase(u32 addr)
{
//写使能
Write_Enable();
F_CS = 0;
//发送擦除命令
Spi1_Recv_Send_Byte(0x20);
//拆分地址,并发送
Spi1_Recv_Send_Byte((addr>>16)&0xFF); //发送16至23位地址
Spi1_Recv_Send_Byte((addr>>8)&0xFF); //发送8至15位地址
Spi1_Recv_Send_Byte((addr)&0xFF); //发送0至7位地址
F_CS = 1;
//查看是否擦除完成
while(1)
{
if((W25q128_status1() & 0x01) == 0x00)
{
break;
}
}
}
void W25Q128_Writer_data(u32 addr,u8 *buf, u32 len)
{
//写使能
Write_Enable();
F_CS = 0;
//发送写页命令
Spi1_Recv_Send_Byte(0x02);
//拆分地址,并发送
Spi1_Recv_Send_Byte((addr>>16)&0xFF); //发送16至23位地址
Spi1_Recv_Send_Byte((addr>>8)&0xFF); //发送8至15位地址
Spi1_Recv_Send_Byte((addr)&0xFF); //发送0至7位地址
//发送数据
while(len--)
{
Spi1_Recv_Send_Byte(*buf);
buf++;
}
F_CS = 1;
//查看是否擦除完成
while(1)
{
if((W25q128_status1() & 0x01) == 0x00)
{
break;
}
}
}
void W25Q128_Read_data(u32 addr,u8 *buf, u32 len)
{
F_CS = 0;
//发送读数据命令
Spi1_Recv_Send_Byte(0x03);
//拆分地址,并发送
Spi1_Recv_Send_Byte((addr>>16)&0xFF); //发送16至23位地址
Spi1_Recv_Send_Byte((addr>>8)&0xFF); //发送8至15位地址
Spi1_Recv_Send_Byte((addr)&0xFF); //发送0至7位地址
//读取数据
while(len--)
{
*buf = Spi1_Recv_Send_Byte(0xFF);
buf++;
}
F_CS = 1;
}
#ifndef __SPI_H
#define __SPI_H
#include "stm32f4xx.h"
#include "sys.h"
#include "delay.h"
#define F_CS PBout(14)
#define SCK PBout(3)
#define MISO PBin(4)
#define MOSI PBout(5)
void SPI1_Init(void);
u16 W25q128_Id(void);
void W25q128_Sector_Erase(u32 addr);
void W25Q128_Writer_data(u32 addr,u8 *buf, u32 len);
void W25Q128_Read_data(u32 addr,u8 *buf, u32 len);
#endif
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "delay.h"
#include "tim.h"
#include "pwm.h"
#include "usart.h"
#include "sys.h"
#include "dht11.h"
#include "infrared.h"
#include "iwdg.h"
#include "iic.h"
#include "adc.h"
#include "spi.h"
u8 buffer[64] = {0};
u8 rx_buffer[64] = {0};
u8 count = 0, rx_i = 0;
u8 rx_flag = 0; //接受标志位,rx_flag = 表示数据帧完毕
void USART1_IRQHandler(void)
{
//判断接收标志位是否为1
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
//清空接受标志位
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//接受数据
buffer[count++] = USART_ReceiveData(USART1);
//判断数据是否为':',如果是':'数据帧结束
if(buffer[count-1] == ':')
{
//数据赋值到rx_buffer,并过滤帧尾
for(rx_i=0; rx_i<(count-1); rx_i++)
{
rx_buffer[rx_i] = buffer[rx_i];
}
//清空数组
memset(buffer, 0, sizeof(buffer));
//标志位置1
rx_flag = 1;
//下一帧数据从buffer[0]开始接受
count = 0;
}
}
}
void USART2_IRQHandler(void)
{
u8 data;
//判断接收标志位是否为1
if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
{
//清空接受标志位
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
//接受数据
buffer[count++] = USART_ReceiveData(USART2);
//判断数据是否为':',如果是':'数据帧结束
if(buffer[count-1] == ':')
{
//数据赋值到rx_buffer,并过滤帧尾
for(rx_i=0; rx_i<(count-1); rx_i++)
{
rx_buffer[rx_i] = buffer[rx_i];
}
//清空数组
memset(buffer, 0, sizeof(buffer));
//标志位置1
rx_flag = 1;
//下一帧数据从buffer[0]开始接受
count = 0;
}
}
}
void USART3_IRQHandler(void)
{
u8 data;
//判断接收标志位是否为1
if(USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
{
//清空接受标志位
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
//接受数据
buffer[count++] = USART_ReceiveData(USART3);
//判断数据是否为':',如果是':'数据帧结束
if(buffer[count-1] == ':')
{
//数据赋值到rx_buffer,并过滤帧尾
for(rx_i=0; rx_i<(count-1); rx_i++)
{
rx_buffer[rx_i] = buffer[rx_i];
}
//清空数组
memset(buffer, 0, sizeof(buffer));
//标志位置1
rx_flag = 1;
//下一帧数据从buffer[0]开始接受
count = 0;
}
}
}
int main(void)
{
u8 write_buff[27] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
u8 read_buff[27];
u16 id;
//设置NVIC分组(一个工程只能设置一个分组)
//第二分组;抢占优先组取值范围:0~3 响应先组取值范围:0~3
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Led_Init();
Delay_Init();
Usart1_Init(115200);
SPI1_Init();
//id = W25q128_Id();
//printf("id:%#X\r\n", id);
//擦除第1块0扇区
W25q128_Sector_Erase(0x010000);
W25Q128_Writer_data(0x010000,write_buff, 26);
delay_ms(10);
W25Q128_Read_data(0x010000,read_buff, 26);
printf("read_buff:%s\r\n", read_buff);
while(1)
{
delay_s(1);
}
}
RFID
//
#include "MFRC522.h"
//test
u8 irq_regdata;
u16 wait_count;
u8 error_regdata;
u8 last_bitsdata;
//SPI3初始化
void STM32_SPI3_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
//1、使能SPIx和IO口时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0| GPIO_Pin_14| GPIO_Pin_9; //GPIOD 0 14 9
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //配置IO口输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //速度 50MHz
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOD,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7| GPIO_Pin_13| GPIO_Pin_15;
GPIO_Init(GPIOE,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9; //引脚9
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_Init(GPIOE,&GPIO_InitStruct);
GND = 0;
VCC = 1;
}
//
void SPI3_Send(u8 val)
{
u8 i = 0;
SCK = 0;
for(i=0; i<8; i++)
{
//准备发送的数据
if(val & (0x01<<(7-i)))
{
MOSI = 1;
}
else
{
MOSI = 0;
}
delay_us(2);
SCK = 1;
delay_us(2);
SCK = 0;
}
}
//
u8 SPI3_Receive(void)
{
u8 temp = 0x00;
u8 i = 0;
SCK = 0;
for(i=0; i<8; i++)
{
//输出引脚电平为0
MOSI = 0;
delay_us(2);
SCK = 1;
delay_us(2);
//接受数据
if(MISO == 1)
{
temp |= (0x01<<(7-i));
}
SCK = 0;
}
return temp;
}
//功能描述向MFRC522的某一寄存器写一个字节数据
//输入参数addr--寄存器地址val--要写入的值
void Write_MFRC522(u8 addr, u8 val)
{
//地址格式0XXXXXX0
MFRC522_CS(0);
SPI3_Send((addr<<1)&0x7E);
SPI3_Send(val);
MFRC522_CS(1);
}
//功能描述从MFRC522的某一寄存器读一个字节数据
//输入参数addr--寄存器地址
//返 回 值返回读取到的一个字节数据
u8 Read_MFRC522(u8 addr)
{
u8 val;
//地址格式1XXXXXX0
MFRC522_CS(0);
SPI3_Send(((addr<<1)&0x7E)|0x80);
val=SPI3_Receive();
MFRC522_CS(1);
//
return val;
}
//下面两个函数只对能读写位有效
//功能描述置RC522寄存器位
//输入参数reg--寄存器地址;mask--置位值
void SetBitMask(u8 reg, u8 mask)
{
u8 tmp=0;
//
tmp=Read_MFRC522(reg);
Write_MFRC522(reg,tmp|mask); // set bit mask
}
//功能描述清RC522寄存器位
//输入参数reg--寄存器地址;mask--清位值
void ClearBitMask(u8 reg, u8 mask)
{
u8 tmp=0;
//
tmp=Read_MFRC522(reg);
Write_MFRC522(reg,tmp&(~mask)); //clear bit mask
}
//功能描述开启天线,每次启动或关闭天线发射之间应至少有1ms的间隔
void AntennaOn(void)
{
u8 temp;
//
temp=Read_MFRC522(TxControlReg);
if ((temp&0x03)==0)
{
SetBitMask(TxControlReg,0x03);
}
}
//功能描述关闭天线,每次启动或关闭天线发射之间应至少有1ms的间隔
void AntennaOff(void)
{
ClearBitMask(TxControlReg,0x03);
}
//功能描述复位MFRC522
void MFRC522_Reset(void)
{
//外复位可以不用
MFRC522_Rst(1);
delay_us(1);
MFRC522_Rst(0);
delay_us(1);
MFRC522_Rst(1);
delay_us(1);
//内复位
Write_MFRC522(CommandReg, PCD_RESETPHASE);
}
//
void MFRC522_Initializtion(void)
{
STM32_SPI3_Init();
MFRC522_Reset();
//Timer: TPrescaler*TreloadVal/6.78MHz = 0xD3E*0x32/6.78=25ms
Write_MFRC522(TModeReg,0x8D); //TAuto=1为自动计数模式,受通信协议影向。低4位为预分频值的高4位
//Write_MFRC522(TModeReg,0x1D); //TAutoRestart=1为自动重载计时,0x0D3E是0.5ms的定时初值//test
Write_MFRC522(TPrescalerReg,0x3E); //预分频值的低8位
Write_MFRC522(TReloadRegL,0x32); //计数器的低8位
Write_MFRC522(TReloadRegH,0x00); //计数器的高8位
Write_MFRC522(TxAutoReg,0x40); //100%ASK
Write_MFRC522(ModeReg,0x3D); //CRC初始值0x6363
Write_MFRC522(CommandReg,0x00); //启动MFRC522
//Write_MFRC522(RFCfgReg, 0x7F); //RxGain = 48dB调节卡感应距离
AntennaOn(); //打开天线
}
//功能描述RC522和ISO14443卡通讯
//输入参数command--MF522命令字
// sendData--通过RC522发送到卡片的数据
// sendLen--发送的数据长度
// BackData--接收到的卡片返回数据
// BackLen--返回数据的位长度
//返 回 值成功返回MI_O
u8 MFRC522_ToCard(u8 command, u8 *sendData, u8 sendLen, u8 *backData, u16 *backLen)
{
u8 status=MI_ERR;
u8 irqEn=0x00;
u8 waitIRq=0x00;
u8 lastBits;
u8 n;
u16 i;
//根据命预设中断参数
switch (command)
{
case PCD_AUTHENT: //认证卡密
irqEn = 0x12; //
waitIRq = 0x10; //
break;
case PCD_TRANSCEIVE: //发送FIFO中数据
irqEn = 0x77; //
waitIRq = 0x30; //
break;
default:
break;
}
//
Write_MFRC522(ComIEnReg, irqEn|0x80); //允许中断请求
ClearBitMask(ComIrqReg, 0x80); //清除所有中断请求位
SetBitMask(FIFOLevelReg, 0x80); //FlushBuffer=1, FIFO初始化
Write_MFRC522(CommandReg, PCD_IDLE); //使MFRC522空闲
//向FIFO中写入数据
for (i=0; i<sendLen; i++)
Write_MFRC522(FIFODataReg, sendData[i]);
//执行命令
Write_MFRC522(CommandReg, command);
//天线发送数据
if (command == PCD_TRANSCEIVE) //如果是卡片通信命令,MFRC522开始向天线发送数据
SetBitMask(BitFramingReg, 0x80); //StartSend=1,transmission of data starts
//等待接收数据完成
i = 10000; //i根据时钟频率调整操作M1卡最大等待时间25ms
do
{
n = Read_MFRC522(ComIrqReg);
//irq_regdata=n; //test
i--;
//wait_count=i; //test
}while ((i!=0) && !(n&0x01) && !(n&waitIRq)); //接收完就退出n=0x64
//停止发送
ClearBitMask(BitFramingReg, 0x80); //StartSend=0
//如果在25ms内读到卡
if (i != 0)
{
if(!(Read_MFRC522(ErrorReg) & 0x1B)) //BufferOvfl Collerr CRCErr ProtecolErr
{
if (n & irqEn & 0x01) //
status = MI_NOTAGERR; //
//
if (command == PCD_TRANSCEIVE)
{
n = Read_MFRC522(FIFOLevelReg); //n=0x02
lastBits = Read_MFRC522(ControlReg) & 0x07; //lastBits=0
if (lastBits!=0)
*backLen = (n-1)*8 + lastBits;
else
*backLen = n*8; //backLen=0x10=16
//
if (n == 0)
n = 1;
if (n > MAX_LEN)
n = MAX_LEN;
//
for (i=0; i<n; i++)
backData[i] = Read_MFRC522(FIFODataReg);
}
//
status = MI_OK;
}
else
status = MI_ERR;
}
//
Write_MFRC522(ControlReg,0x80); //timer stops
Write_MFRC522(CommandReg, PCD_IDLE); //
//
return status;
}
//功能描述寻卡读取卡类型号
//输入参数reqMode--寻卡方式
// TagType--返回卡片类型
// 0x4400 = Mifare_UltraLight
// 0x0400 = Mifare_One(S50)
// 0x0200 = Mifare_One(S70)
// 0x0800 = Mifare_Pro(X)
// 0x4403 = Mifare_DESFire
//返 回 值成功返回MI_OK
u8 MFRC522_Request(u8 reqMode, u8 *TagType)
{
u8 status;
u16 backBits; //接收到的数据位数
//
Write_MFRC522(BitFramingReg, 0x07); //TxLastBists = BitFramingReg[2..0]
TagType[0] = reqMode;
status = MFRC522_ToCard(PCD_TRANSCEIVE, TagType, 1, TagType, &backBits);
//
if ((status != MI_OK) || (backBits != 0x10))
{
status = MI_ERR;
}
//
return status;
}
//功能描述防冲突检测读取选中卡片的卡序列号
//输入参数serNum--返回4字节卡序列号,第5字节为校验字节
//返 回 值成功返回MI_OK
u8 MFRC522_Anticoll(u8 *serNum)
{
u8 status;
u8 i;
u8 serNumCheck=0;
u16 unLen;
//
ClearBitMask(Status2Reg, 0x08); //TempSensclear
ClearBitMask(CollReg,0x80); //ValuesAfterColl
Write_MFRC522(BitFramingReg, 0x00); //TxLastBists = BitFramingReg[2..0]
serNum[0] = PICC_ANTICOLL1;
serNum[1] = 0x20;
status = MFRC522_ToCard(PCD_TRANSCEIVE, serNum, 2, serNum, &unLen);
//
if (status == MI_OK)
{
//校验卡序列号
for(i=0;i<4;i++)
serNumCheck^=serNum[i];
//
if(serNumCheck!=serNum[i])
status=MI_ERR;
}
SetBitMask(CollReg,0x80); //ValuesAfterColl=1
//
return status;
}
//功能描述用MF522计算CRC
//输入参数pIndata--要读数CRC的数据len--数据长度pOutData--计算的CRC结果
void CalulateCRC(u8 *pIndata, u8 len, u8 *pOutData)
{
u16 i;
u8 n;
//
ClearBitMask(DivIrqReg, 0x04); //CRCIrq = 0
SetBitMask(FIFOLevelReg, 0x80); //清FIFO指针
Write_MFRC522(CommandReg, PCD_IDLE);
//向FIFO中写入数据
for (i=0; i<len; i++)
Write_MFRC522(FIFODataReg, *(pIndata+i));
//开始RCR计算
Write_MFRC522(CommandReg, PCD_CALCCRC);
//等待CRC计算完成
i = 1000;
do
{
n = Read_MFRC522(DivIrqReg);
i--;
}while ((i!=0) && !(n&0x04)); //CRCIrq = 1
//读取CRC计算结果
pOutData[0] = Read_MFRC522(CRCResultRegL);
pOutData[1] = Read_MFRC522(CRCResultRegH);
Write_MFRC522(CommandReg, PCD_IDLE);
}
//功能描述选卡读取卡存储器容量
//输入参数serNum--传入卡序列号
//返 回 值成功返回卡容量
u8 MFRC522_SelectTag(u8 *serNum)
{
u8 i;
u8 status;
u8 size;
u16 recvBits;
u8 buffer[9];
//
buffer[0] = PICC_ANTICOLL1; //防撞码1
buffer[1] = 0x70;
buffer[6] = 0x00;
for (i=0; i<4; i++)
{
buffer[i+2] = *(serNum+i); //buffer[2]-buffer[5]为卡序列号
buffer[6] ^= *(serNum+i); //卡校验码
}
//
CalulateCRC(buffer, 7, &buffer[7]); //buffer[7]-buffer[8]为RCR校验码
ClearBitMask(Status2Reg,0x08);
status = MFRC522_ToCard(PCD_TRANSCEIVE, buffer, 9, buffer, &recvBits);
//
if ((status == MI_OK) && (recvBits == 0x18))
size = buffer[0];
else
size = 0;
//
return size;
}
//功能描述验证卡片密码
//输入参数authMode--密码验证模式
// 0x60 = 验证A密钥
// 0x61 = 验证B密钥
// BlockAddr--块地址
// Sectorkey--扇区密码
// serNum--卡片序列号4字节
//返 回 值成功返回MI_OK
u8 MFRC522_Auth(u8 authMode, u8 BlockAddr, u8 *Sectorkey, u8 *serNum)
{
u8 status;
u16 recvBits;
u8 i;
u8 buff[12];
//验证模式+块地址+扇区密码+卡序列号
buff[0] = authMode; //验证模式
buff[1] = BlockAddr; //块地址
for (i=0; i<6; i++)
buff[i+2] = *(Sectorkey+i); //扇区密码
//
for (i=0; i<4; i++)
buff[i+8] = *(serNum+i); //卡序列号
//
status = MFRC522_ToCard(PCD_AUTHENT, buff, 12, buff, &recvBits);
//
if ((status != MI_OK) || (!(Read_MFRC522(Status2Reg) & 0x08)))
status = MI_ERR;
//
return status;
}
//功能描述读块数据
//输入参数blockAddr--块地址;recvData--读出的块数据
//返 回 值成功返回MI_OK
u8 MFRC522_Read(u8 blockAddr, u8 *recvData)
{
u8 status;
u16 unLen;
//
recvData[0] = PICC_READ;
recvData[1] = blockAddr;
CalulateCRC(recvData,2, &recvData[2]);
status = MFRC522_ToCard(PCD_TRANSCEIVE, recvData, 4, recvData, &unLen);
//
if ((status != MI_OK) || (unLen != 0x90))
status = MI_ERR;
//
return status;
}
//功能描述写块数据
//输入参数blockAddr--块地址;writeData--向块写16字节数据
//返 回 值成功返回MI_OK
u8 MFRC522_Write(u8 blockAddr, u8 *writeData)
{
u8 status;
u16 recvBits;
u8 i;
u8 buff[18];
//
buff[0] = PICC_WRITE;
buff[1] = blockAddr;
CalulateCRC(buff, 2, &buff[2]);
status = MFRC522_ToCard(PCD_TRANSCEIVE, buff, 4, buff, &recvBits);
//
if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A))
status = MI_ERR;
//
if (status == MI_OK)
{
for (i=0; i<16; i++) //向FIFO写16Byte数据
buff[i] = *(writeData+i);
//
CalulateCRC(buff, 16, &buff[16]);
status = MFRC522_ToCard(PCD_TRANSCEIVE, buff, 18, buff, &recvBits);
if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A))
status = MI_ERR;
}
return status;
}
//功能描述命令卡片进入休眠状态
void MFRC522_Halt(void)
{
u16 unLen;
u8 buff[4];
//
buff[0] = PICC_HALT;
buff[1] = 0;
CalulateCRC(buff, 2, &buff[2]);
MFRC522_ToCard(PCD_TRANSCEIVE, buff, 4, buff,&unLen);
}
//
//
#ifndef _MFRC522_H_
#define _MFRC522_H_
#include "stm32f4xx.h"
#include "delay.h"
#include "sys.h"
#define SCK PDout(0)
#define MISO PEin(9)
#define MOSI PEout(7)
#define GND PEout(13)
#define VCC PDout(9)
//定义MFRC522的CS引脚操作,x=1时CS=1,x=0时CS=0
#define MFRC522_CS(x) x ? GPIO_SetBits(GPIOD,GPIO_Pin_14):GPIO_ResetBits(GPIOD,GPIO_Pin_14)
#define MFRC522_Rst(x) x ? GPIO_SetBits(GPIOE,GPIO_Pin_15):GPIO_ResetBits(GPIOE,GPIO_Pin_15)
/
//MF522命令字
/
#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 //休眠
/
//MF522 FIFO长度定义
/
#define DEF_FIFO_LENGTH 64 //FIFO size=64byte
/
//MF522寄存器定义
/
// PAGE 0
#define RFU00 0x00
#define CommandReg 0x01
#define ComIEnReg 0x02
#define DivlEnReg 0x03
#define ComIrqReg 0x04
#define DivIrqReg 0x05
#define ErrorReg 0x06
#define Status1Reg 0x07
#define Status2Reg 0x08
#define FIFODataReg 0x09
#define FIFOLevelReg 0x0A
#define WaterLevelReg 0x0B
#define ControlReg 0x0C
#define BitFramingReg 0x0D
#define CollReg 0x0E
#define RFU0F 0x0F
// PAGE 1
#define RFU10 0x10
#define ModeReg 0x11
#define TxModeReg 0x12
#define RxModeReg 0x13
#define TxControlReg 0x14
#define TxAutoReg 0x15
#define TxSelReg 0x16
#define RxSelReg 0x17
#define RxThresholdReg 0x18
#define DemodReg 0x19
#define RFU1A 0x1A
#define RFU1B 0x1B
#define RFU1C 0x1C
#define RFU1D 0x1D
#define RFU1E 0x1E
#define SerialSpeedReg 0x1F
// PAGE 2
#define RFU20 0x20
#define CRCResultRegH 0x21
#define CRCResultRegL 0x22
#define RFU23 0x23
#define ModWidthReg 0x24
#define RFU25 0x25
#define RFCfgReg 0x26
#define GsNReg 0x27
#define CWGsCfgReg 0x28
#define ModGsCfgReg 0x29
#define TModeReg 0x2A
#define TPrescalerReg 0x2B
#define TReloadRegH 0x2C
#define TReloadRegL 0x2D
#define TCounterValueRegH 0x2E
#define TCounterValueRegL 0x2F
// PAGE 3
#define RFU30 0x30
#define TestSel1Reg 0x31
#define TestSel2Reg 0x32
#define TestPinEnReg 0x33
#define TestPinValueReg 0x34
#define TestBusReg 0x35
#define AutoTestReg 0x36
#define VersionReg 0x37
#define AnalogTestReg 0x38
#define TestDAC1Reg 0x39
#define TestDAC2Reg 0x3A
#define TestADCReg 0x3B
#define RFU3C 0x3C
#define RFU3D 0x3D
#define RFU3E 0x3E
#define RFU3F 0x3F
/
//和MF522通讯时返回的错误代码
/
#define MI_OK 0
#define MI_NOTAGERR 1
#define MI_ERR 2
//
#define MAX_LEN 18
//MFRC522 test
extern u8 irq_regdata;
extern u16 wait_count;
extern u8 error_regdata;
extern u8 last_bitsdata;
//
void Delay1_us(vu16 count);
void STM32_SPI2_Init(void);
void STM32_SPI3_Init(void);
void SPI2_Send(u8 val);
u8 SPI2_Receive(void);
void SPI3_Send(u8 val);
u8 SPI3_Receive(void);
void MFRC522_Initializtion(void);
void Write_MFRC522(u8 addr, u8 val);
u8 Read_MFRC522(u8 addr);
void SetBitMask(u8 reg, u8 mask);
void ClearBitMask(u8 reg, u8 mask);
void AntennaOn(void);
void AntennaOff(void);
void MFRC522_Reset(void);
void MFRC522_Init(void);
u8 MFRC522_ToCard(u8 command, u8 *sendData, u8 sendLen, u8 *backData, u16 *backLen);
u8 MFRC522_Request(u8 reqMode, u8 *TagType);
u8 MFRC522_Anticoll(u8 *serNum);
void CalulateCRC(u8 *pIndata, u8 len, u8 *pOutData);
u8 MFRC522_SelectTag(u8 *serNum);
u8 MFRC522_Auth(u8 authMode, u8 BlockAddr, u8 *Sectorkey, u8 *serNum);
u8 MFRC522_Read(u8 blockAddr, u8 *recvData);
u8 MFRC522_Write(u8 blockAddr, u8 *writeData);
void MFRC522_Halt(void);
//
#endif
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
#include "delay.h"
#include "tim.h"
#include "pwm.h"
#include "usart.h"
#include "sys.h"
#include "dht11.h"
#include "infrared.h"
#include "iwdg.h"
#include "iic.h"
#include "adc.h"
#include "spi.h"
#include "MFRC522.h"
u8 buffer[64] = {0};
u8 rx_buffer[64] = {0};
u8 count = 0, rx_i = 0;
u8 rx_flag = 0; //接受标志位,rx_flag = 表示数据帧完毕
void USART1_IRQHandler(void)
{
//判断接收标志位是否为1
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
//清空接受标志位
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
//接受数据
buffer[count++] = USART_ReceiveData(USART1);
//判断数据是否为':',如果是':'数据帧结束
if(buffer[count-1] == ':')
{
//数据赋值到rx_buffer,并过滤帧尾
for(rx_i=0; rx_i<(count-1); rx_i++)
{
rx_buffer[rx_i] = buffer[rx_i];
}
//清空数组
memset(buffer, 0, sizeof(buffer));
//标志位置1
rx_flag = 1;
//下一帧数据从buffer[0]开始接受
count = 0;
}
}
}
void USART2_IRQHandler(void)
{
u8 data;
//判断接收标志位是否为1
if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
{
//清空接受标志位
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
//接受数据
buffer[count++] = USART_ReceiveData(USART2);
//判断数据是否为':',如果是':'数据帧结束
if(buffer[count-1] == ':')
{
//数据赋值到rx_buffer,并过滤帧尾
for(rx_i=0; rx_i<(count-1); rx_i++)
{
rx_buffer[rx_i] = buffer[rx_i];
}
//清空数组
memset(buffer, 0, sizeof(buffer));
//标志位置1
rx_flag = 1;
//下一帧数据从buffer[0]开始接受
count = 0;
}
}
}
void USART3_IRQHandler(void)
{
u8 data;
//判断接收标志位是否为1
if(USART_GetITStatus(USART3, USART_IT_RXNE) == SET)
{
//清空接受标志位
USART_ClearITPendingBit(USART3, USART_IT_RXNE);
//接受数据
buffer[count++] = USART_ReceiveData(USART3);
//判断数据是否为':',如果是':'数据帧结束
if(buffer[count-1] == ':')
{
//数据赋值到rx_buffer,并过滤帧尾
for(rx_i=0; rx_i<(count-1); rx_i++)
{
rx_buffer[rx_i] = buffer[rx_i];
}
//清空数组
memset(buffer, 0, sizeof(buffer));
//标志位置1
rx_flag = 1;
//下一帧数据从buffer[0]开始接受
count = 0;
}
}
}
//显示缓冲区
u8 dispnumber5buf[6];
u8 dispnumber3buf[4];
u8 dispnumber2buf[3];
//MFRC522数据区
u8 mfrc552pidbuf[18];
u8 card_pydebuf[2];
u8 card_numberbuf[5];
u8 card_key0Abuf[6]={0xff,0xff,0xff,0xff,0xff,0xff};
u8 card_writebuf[16]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
u8 card_readbuf[18];
//SM05-S数据区
u8 sm05cmdbuf[15]={14,128,0,22,5,0,0,0,4,1,157,16,0,0,21};
//MFRC522测试函数
void MFRC522Test(void)
{
u8 i,j,status,card_size;
//
status=MFRC522_Request(0x52, card_pydebuf); //寻卡
//
if(status==0) //如果读到卡
{
status=MFRC522_Anticoll(card_numberbuf); //防撞处理
card_size=MFRC522_SelectTag(card_numberbuf); //选卡
status=MFRC522_Auth(0x60, 4, card_key0Abuf, card_numberbuf); //验卡 验密钥
status=MFRC522_Write(4, card_writebuf); //写卡(写卡要小心,特别是各区的块3)
status=MFRC522_Read(4, card_readbuf); //读卡
//MFRC522_Halt(); //使卡进入休眠状态
//卡类型显示
printf("卡类型显示:%#x %#x\r\n", card_pydebuf[0], card_pydebuf[1]);
//卡序列号显,最后一字节为卡的校验码
printf("卡序列号:");
for(i=0;i<5;i++)
{
printf("%#x ", card_numberbuf[i]);
}
printf("\r\n");
//卡容量显示,单位为Kbits
printf("卡容量显示:%d Kbits\r\n", card_size);
printf("读取的块数据:%");
//读一个块的数据显示
for(i=0;i<2;i++) //分两行显示
{
for(j=0;j<9;j++) //每行显示8个
{
printf("%#x ", card_readbuf[j+i*9]);
}
}
printf("\r\n");
}
}
int main(void)
{
//设置NVIC分组(一个工程只能设置一个分组)
//第二分组;抢占优先组取值范围:0~3 响应先组取值范围:0~3
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
Usart1_Init(115200);
MFRC522_Initializtion();
printf("RDID test\r\n");
while(1)
{
MFRC522_Initializtion();
MFRC522Test();
delay_s(1);
}
}