做完刷卡版充电插座这个项目遇到的问题做一些笔记
首先是硬件电路如下:
从 TX 发射出去的是调制过的 13.56MHz 载波信号,软件发送命令,6脚产生13.56MHZ的正弦波的载波信号,然后天线刷卡得到信号跟载波信号叠加传输到RX脚。
在使用SPI的时候配要根据FM1701的时序图,由下图可以判断出用的SPI模式(参考博客)
CPOL =0表示CLK空闲的时候低电平,则反之
CPHA =0表示第一个边沿采集数据 CPHA =1 表示第二个边沿采集数据
批量生产的时候测试的时候可以输出卡,后来到现场都不行,偶尔能刷出来,就是因为SPI模式配错。
软件部分
技术支持给的驱动是裸机的,我做项目的时候是用RT_Thread系统需要更换SPI的发送、接收接口函数。
初始化函数、SPI读写函数如下:
unsigned char Fm1701_init(void)
{
u8 temp = 0x55;
RFID_RST_H;
rt_thread_delay(10);
RFID_RST_L;
rt_thread_delay(50);
spi_fm1701 = (struct rt_spi_device *)rt_device_find("FM1701");
if(spi_fm1701 == RT_NULL)
{
return RT_ERROR;
}
/* config spi */
{
struct rt_spi_configuration cfg;
cfg.data_width = 8;
cfg.mode = RT_SPI_MODE_0 | RT_SPI_MSB;
cfg.max_hz = 5000;//50000000
if(rt_spi_configure(spi_fm1701, &cfg) != RT_EOK)
{
return RT_ERROR;
}
}
if(FM1702_Bus_Sel()) //总线选择
{
// rt_kprintf("FM1701 总线选择成功! \r\n");
temp = Fm1701_read(0x05);
if(temp == 0x60)
{
Fm1701_write(TxControl,0x5b);
return RT_EOK;
}
else
{
return RT_ERROR;
}
}
else
{
rt_kprintf("FM1701 总线选择失败!\r\n");
return RT_ERROR;
}
}
/******************************************************************************
*
* 描述:
* 输入:
* 输出:
* 返回:
*
******************************************************************************/
static void Fm1701_write(uchar address,uchar data )
{
u8 status;
address = address << 1;
address = address & 0x7f;
status = rt_spi_send_then_send(spi_fm1701, &address, 1, &data, 1);
// address = address << 1;
// address = address & 0x7f;
// send_buf[0]=address;
// send_buf[1]=data&0xff;
// rt_spi_send(spi_fm1701, send_buf, 2);
}
/******************************************************************************
*
* 描述:
* 输入:
* 输出:
* 返回:
*
******************************************************************************/
static u8 Fm1701_read(uchar address)
{
u8 status;
u8 result;
address=address<<1;
address=address | 0x80;
status = rt_spi_send_then_recv(spi_fm1701,&address,1,&result, 1);
return result;
// u8 send_buf[2];
// address=address<<1;
// address=address | 0x80;
// send_buf[0]=address;
// rt_spi_recv(spi_fm1701, send_buf, 2);
// return (send_buf[1]);
}
在项目中出现死机情况是在检测到卡冲突之后进入循环,刷卡这个线程应该有比较高的优先级,才不会在在读卡到一半被打断程序应该改成:
/****************************************************************/
/*名称: AntiColl */
/*功能: 该函数实现对放入FM1702操作范围之内的卡片的防冲突检测 */
/*输入: N/A */
/*输出: FM1702_NOTAGERR: 无卡 */
/* FM1702_BYTECOUNTERR: 接收字节错误 */
/* FM1702_SERNRERR: 卡片序列号应答错误 */
/* FM1702_OK: 卡片应答正确 */
/****************************************************************/
uchar AntiColl(void)
{
uchar temp;
uchar i;
uchar row, col;
uchar pre_row;
uchar fail_cnt=0;
row = 0;
col = 0;
pre_row = 0;
while(1)
{
Fm1701_write(CRCPresetLSB,0x63);
Fm1701_write(CWConductance,0x3f);
Fm1701_write(ModConductance,0x3f);
buf[0] = RF_CMD_ANTICOL;
buf[1] = 0x20;
Fm1701_write(ChannelRedundancy,0x03); // 关闭CRC,打开奇偶校验
temp = Command_Send(2, buf, Transceive);
if(temp == FALSE)
{
return(FM1702_NOTAGERR);
}
temp = Fm1701_read(FIFO_Length);
if(temp == 0)
{
return FM1702_BYTECOUNTERR;
}
Read_FIFO(buf);
Save_UID(row, col, temp); // 将收到的UID放入UID数组中
temp = Fm1701_read(ErrorFlag); // 判断接収数据是否出错
temp = temp & 0x01;
if(temp == 0x00)
{
temp = Check_UID(); // 校验收到的UID 卡ID前面4个字节是ID号最后一个字节是校验码
if(temp == FALSE)
{
return(FM1702_SERNRERR);
}
return(FM1702_OK);
}
else
{ /*查询位置,对应错误的位置清除*/
temp = Fm1701_read(CollPos); // 读取冲突检测寄存器
row = temp / 8;
col = temp % 8;
buf[0] = RF_CMD_ANTICOL;
Set_BitFraming(row + pre_row, col); // 设置待发送数据的字节数
pre_row = pre_row + row;
for(i = 0; i < pre_row + 1; i++)
{
buf[i + 2] = UID[i];
}
if(col != 0x00)
{
row = pre_row + 1;
}
else
{
row = pre_row;
}
temp = Command_Send(row + 2, buf, Transceive);
}
fail_cnt++;
if(fail_cnt>=5)
{
return(FM1702_NOTAGERR);// add by suguolin
}
APP_IWDGClr();
}
}