(2)数值帧指令分析
我们来看读卡器的功能配置指令(如图3),以上我说的相关功能都可以在这里配置。
图3 功能配置指令
由上图可知,一帧数据指令是由以上若干格式组成的。我们要更改相关功能只需要修改对应的值即可,以下贴出读卡器使用说明中各个位指令的作用(如图5)。
图4 各个位的作用
这里要注意的是校验和,上面可能说得不太清楚,根据我的理解是先把BCC校验(异或校验)算出来,然后把得出来的二进制位全部取反,取反后得到的就是校验和。写个例子,比如根据上面的功能配置指令,我写出来了一帧配置指令:
20 00 2C 04 00 00 96 00 校验和 03
配置指令的意思是开启主动输出卡号,主动输出卡片离开数据,9600的波特率,开启蜂鸣器提醒。我现在开始要算校验和,根据指示我得出来00 2C 04 00 00 96 00。
然后计算BBC校验码,如图5。
图5 BBC校验码计算
得出二进制位是1011 1110,按位取反获得0100 0001。
转换为十六进制为:41
所以我们的校验和是41,填入后完整的数据指令是:
20 00 2C 04 00 00 96 00 41 03
需要注意的是,数据指令的第二个位是读卡器的地址,你要改哪个读卡器的话就用地址定位你要改的读卡器。
2.准备步骤
(1)修改读卡器地址
如果你打算只使用一个读卡器完成测试的话,那就可以跳过这一步,否则的话请按照此步骤将读卡器的地址设置为不同的值。两个以上读卡器的话要修改读卡器地址,因为指令发送是发送给单个读卡器的,你需要读卡器的唯一地址才能确定这指令发给谁,否则后面发送指令时一样的地址会导致冲突。其中一个读卡器默认地址就为0x00就行了(如果你不想要0x00的话改成其他也行),其他的改成其他值(0xFF以内)。首先我们先把要改地址的读卡器接好线,电源线接3.3V,A、B线接到RS485转TTL芯片的A、B线,然后RS485转TTL芯片的串口线接到我们的USB转串口芯片上,j接好后插电脑。打开串口调试助手,按如图3所示配置:
图6 串口调试助手的相关配置
接下来我们查看修改模块地址指令(如图7)。
图7 修改模块地址指令
我们可以看到,我们要修改的有两个值,一个是原地址,一个是新地址,分别位于第2和第5个字节。由于读卡器出厂都是默认地址为0x00,原地址就填0x00。新地址的话填你想要的值,我填的是0x01,发送成功后会接受到反馈指令,如图8。
图8 修改读卡器地址
(2)测试读卡器功能
如果我们用默认功能配置的话,是开启主动输出卡号,主动输出卡片离开数据,9600的波特率,开启蜂鸣器提醒。所以我们先测试的时候是把卡片放到天线上去,可以听到蜂鸣器响一声,并且串口调试助手接受到数据(如图9)。卡片拿走后返回卡片离开数据(如图10)。(这里我使用的是地址为0x01的读卡器,所以数据帧第二个字节是01)
图9 检测到卡片时返回的数据
图10 卡片已离开
我们不仅要它读到卡号返回数据,还要随时能够确定读卡器天线上有没有卡片,接下来测试读卡指令,先看读卡指令的通讯格式(如图11)。
图11 寻卡指令格式
我们根据地址寻卡,比如地址为0x01的读卡器,我们就把第2字节的地址位改成0x01,然后计算校验值得到D9,指令是20 01 27 00 D9 03。如图12所示。
图12 有卡与无卡数据帧返回情况
测试完毕,开始写代码了。
3、准备接线
整个实验我使用了STM32的串口1和串口2,串口1连接电脑打印调试信息,串口2接RS485转TTl芯片,RS485转TTl芯片再接读卡器模块。
STM32 -> RS485转TTl芯片
PA2 -> TXD
PA3 -> RXD
RS485转TTl芯片 -> 读卡器模块
A -> A
B -> B
其他的比如接电源和串口1接电脑这些不再赘述了,如果读卡器没数据传回来可能是因为你的RS485转TTl芯片跟我的不太一样,有电平转换功能啥的,反接一下试试。
三、代码编写:
1、功能布局
首先我们使用串口2来发送接收数据,数据处理后通过串口1显示读卡信息。
2、编写代码
我们要配置串口2的中断服务函数。每接收完一帧数据帧,都会进行数据解析(RFID_LF_GetData())。(复制代码时建议复制下面完整版)
/*
函数名称: NVIC_Configuration
函数参数: void
函数返回值:static void
函数功能: 中断结构体配置
*/
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = RFID_LF_USART_IRQ;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
/*
函数名称: USART_Config
函数参数: void
函数返回值:void
函数功能: PA2--TX PA3--RX 串口2初始化
*/
void RFID_USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
RFID_LF_USART_GPIO_APBxClkCmd(RFID_LF_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
RFID_LF_USART_APBxClkCmd(RFID_LF_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = RFID_LF_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RFID_LF_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = RFID_LF_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(RFID_LF_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = RFID_LF_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(RFID_LF_USARTx, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(RFID_LF_USARTx, USART_IT_RXNE, ENABLE);
USART_ITConfig ( RFID_LF_USARTx, USART_IT_IDLE, ENABLE ); //使能串口总线空闲中断
// 使能串口
USART_Cmd(RFID_LF_USARTx, ENABLE);
}
/*
函数名称: RFID_LF_USART_IRQHandler
函数参数: void
函数返回值:void
函数功能: 接收一帧数据并且进行解析
*/
unsigned char RFID_LF_buff[512];
int RFID_LF_cnt = 0;
void RFID_LF_USART_IRQHandler(void)
{
static u8 i = 0;
//判断是接收中断触发
if(USART_GetITStatus(RFID_LF_USARTx,USART_IT_RXNE))
{
//清除中断标志位
USART_ClearITPendingBit(RFID_LF_USARTx,USART_IT_RXNE);
//紧急事件
RFID_LF_buff[i++] = USART_ReceiveData(RFID_LF_USARTx);
RFID_LF_cnt++;
}
//判断是空闲中断触发
if(USART_GetITStatus(RFID_LF_USARTx,USART_IT_IDLE))
{
//清除中断标志位
RFID_LF_USARTx->SR;
RFID_LF_USARTx->DR;
//紧急事件
RFID_LF_buff[i] = '\0';
i = 0;
RFID_LF_GetData();
}
}
返回的数据进行数据解析,并且根据数据打印相对应的信息和卡号。
struct RFID_status
{
int read_ok_flag; //表示是否读到卡
int R_read; //表示读到的是哪个编号
int R_no_read;
int R_hadRead; //表示哪个读卡器已经读到过卡了
}status;
/*
函数名称: RFID_LF_GetData
函数参数: void
函数返回值:void
函数功能: 分析数据
*/
void RFID_LF_GetData(void)
{
int i = 0,j = 0;
if(RFID_LF_cnt == ID_cnt) //读到的数据位数为卡号时
{
status.read_ok_flag = 1; //读卡成功标志位置1
if(RFID_LF_buff[1] == 0x00 && RFID_LF_buff[2] == 0x27) //当读到的数据确实是卡号时,并且读卡器是0
{
memcpy(R0_ID,RFID_LF_buff+4,5*sizeof(unsigned char));
status.R_read = 1;
status.R_hadRead = 1;
}
else if(RFID_LF_buff[1] == 0x01 && RFID_LF_buff[2] == 0x27) //当读到的数据确实是卡号时,并且读卡器是1
{
memcpy(R1_ID,RFID_LF_buff+4,5*sizeof(unsigned char));
status.R_read = 2;
status.R_hadRead = 2;
}
}
if(RFID_LF_cnt == fail_LF_cnt) //读到的数据为卡号不存在时
{
if(RFID_LF_buff[1] == 0x00) //读写器是0
{
status.R_no_read = 1;
}
else if(RFID_LF_buff[1] == 0x01) //读写器是1
{
status.R_no_read = 1;
}
}
RFID_display(); //显示卡号
RFID_LF_cnt = 0;
memset(RFID_LF_buff, 0, sizeof(RFID_LF_buff));//将内存的指针指到首地址----清空缓存
}
/*
函数名称: RFID_display
函数参数: void
函数返回值:void
函数功能: 显示卡号
*/
void RFID_display(void)
{
int i = 0;
if(status.read_ok_flag == 1)
{
if(status.R_read == 1)
{
printf("读写器0检测到卡片,卡号:");
for(i = 0;i < card_ID_cnt;i++)
{
printf("%X ",R0_ID[i]);
}
printf("\n");
}
if(status.R_read == 2)
{
printf("读写器1检测到卡片,卡号:");
for(i = 0;i < card_ID_cnt;i++)
{
printf("%X ",R1_ID[i]);
}
printf("\n");
}
}
else
{
if(status.R_no_read == 1)
{
printf("读写器0未检测到卡片\n");
printf("\n");
}
if(status.R_no_read == 2)
{
printf("读写器1未检测到卡片\n");
printf("\n");
}
}
status.read_ok_flag = 0;
status.R_read = 0;
status.R_no_read = 0;
memset(R0_ID, 0, sizeof(R0_ID));//将内存的指针指到首地址----清空缓存
memset(R1_ID, 0, sizeof(R1_ID));//将内存的指针指到首地址----清空缓存
}
最后再写个寻卡命令
/*
函数名称: RDID_SendCmd
函数参数: USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num
函数返回值:void
函数功能: 给RFID模块发送指令
*/
void RFID_SendCmd( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
uint8_t i;
for(i=0; i<num; i++)
{
/* 发送一个字节数据到USART */
Usart_SendByte(pUSARTx,array[i]);
}
/* 等待发送完成 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}
/*
函数名称: Find_LF_Card
函数参数: u8 reader
函数返回值:u8
函数功能: 低频指定读卡器寻卡函数,参数指定哪个读卡器(只有编号0或1)读卡,返回值为1表示读到卡了
*/
u8 Find_LF_Card(u8 reader)
{
if(reader == 0)
{
//读写器0的单次寻卡
RFID_SendCmd(RFID_LF_USARTx,find_R0_cmd,find_LF_cmd_cnt); //单次寻卡
while(USART_GetFlagStatus(RFID_LF_USARTx,USART_FLAG_TC)!=SET);
if(status.R_hadRead == 1) //说明读到卡了
{
printf("寻卡成功\n");
status.R_hadRead = 0;
return 1;
}
else
{
printf("寻卡失败\n");
return 0;
}
}
else if(reader == 1)
{
//读写器1的单次寻卡
RFID_SendCmd(RFID_LF_USARTx,find_R1_cmd,find_LF_cmd_cnt); //单次寻卡
while(USART_GetFlagStatus(RFID_LF_USARTx,USART_FLAG_TC)!=SET);
if(status.R_hadRead == 2) //说明读到卡了
{
status.R_hadRead = 0;
return 1;
}
else
return 0;
}
return 0;
}
最后是那些头文件的宏定义。
#define ID_cnt 11 //读到ID卡返回的数据数
#define fail_LF_cnt 7 //无卡时返回的数据数
#define find_LF_cmd_cnt 6 //读卡指令的字节数
#define card_ID_cnt 5 //卡号的数据数
//RFID_LF相关命令
//单次询问读写器0指令
unsigned char find_R0_cmd[find_LF_cmd_cnt] = {0x20,0x00,0x27,0x00,0xD8,0x03};
//单次询问读写器1指令
unsigned char find_R1_cmd[find_LF_cmd_cnt] = {0x20,0x01,0x27,0x00,0xD9,0x03};
//用来存放读写器0读出的卡号
unsigned char R0_ID[20];
//用来存放读写器1读出的卡号
unsigned char R1_ID[20];
我最后做成完整的RFID_LF.c和RFID_LF.h文件,整个工程和资料可以在下面下载。
RFID_LF.c
#include "RFID_LF.h"
//RFID_LF相关命令
//单次询问读写器0指令
unsigned char find_R0_cmd[find_LF_cmd_cnt] = {0x20,0x00,0x27,0x00,0xD8,0x03};
//单次询问读写器1指令
unsigned char find_R1_cmd[find_LF_cmd_cnt] = {0x20,0x01,0x27,0x00,0xD9,0x03};
//用来存放读写器0读出的卡号
unsigned char R0_ID[20];
//用来存放读写器1读出的卡号
unsigned char R1_ID[20];
//内部函数声明
void RFID_SendCmd( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num);
void RFID_LF_GetData(void);
void RFID_display(void);
struct RFID_status
{
int read_ok_flag; //表示是否读到卡
int R_read; //表示读到的是哪个编号
int R_no_read;
int R_hadRead; //表示哪个读卡器已经读到过卡了
}status;
/*
函数名称: NVIC_Configuration
函数参数: void
函数返回值:static void
函数功能: 中断结构体配置
*/
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = RFID_LF_USART_IRQ;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
/*
函数名称: USART_Config
函数参数: void
函数返回值:void
函数功能: PA2--TX PA3--RX 串口2初始化
*/
void RFID_USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
RFID_LF_USART_GPIO_APBxClkCmd(RFID_LF_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
RFID_LF_USART_APBxClkCmd(RFID_LF_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = RFID_LF_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(RFID_LF_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = RFID_LF_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(RFID_LF_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = RFID_LF_USART_BAUDRATE;
// 配置 针数据字长
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
// 配置停止位
USART_InitStructure.USART_StopBits = USART_StopBits_1;
// 配置校验位
USART_InitStructure.USART_Parity = USART_Parity_No ;
// 配置硬件流控制
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;
// 配置工作模式,收发一起
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
// 完成串口的初始化配置
USART_Init(RFID_LF_USARTx, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(RFID_LF_USARTx, USART_IT_RXNE, ENABLE);
USART_ITConfig ( RFID_LF_USARTx, USART_IT_IDLE, ENABLE ); //使能串口总线空闲中断
// 使能串口
USART_Cmd(RFID_LF_USARTx, ENABLE);
}
/*
函数名称: RFID_LF_USART_IRQHandler
函数参数: void
函数返回值:void
函数功能: 接收一帧数据并且进行解析
*/
unsigned char RFID_LF_buff[512];
int RFID_LF_cnt = 0;
void RFID_LF_USART_IRQHandler(void)
{
static u8 i = 0;
//判断是接收中断触发
if(USART_GetITStatus(RFID_LF_USARTx,USART_IT_RXNE))
{
//清除中断标志位
USART_ClearITPendingBit(RFID_LF_USARTx,USART_IT_RXNE);
//紧急事件
RFID_LF_buff[i++] = USART_ReceiveData(RFID_LF_USARTx);
RFID_LF_cnt++;
}
//判断是空闲中断触发
if(USART_GetITStatus(RFID_LF_USARTx,USART_IT_IDLE))
{
//清除中断标志位
RFID_LF_USARTx->SR;
RFID_LF_USARTx->DR;
//紧急事件
RFID_LF_buff[i] = '\0';
i = 0;
RFID_LF_GetData();
}
}
/*
函数名称: RFID_Init
函数参数: void
函数返回值:void
函数功能: 启用RFID模块
*/
void RFID_LF_Init(void)
{
RFID_USART_Config();
status.read_ok_flag = 0;
status.R_read = 0;
status.R_no_read = 0;
printf("\t\n天线初始化成功\t\n");
}
/*
函数名称: RFID_display
函数参数: void
函数返回值:void
函数功能: 显示卡号
*/
void RFID_display(void)
{
int i = 0;
if(status.read_ok_flag == 1)
{
if(status.R_read == 1)
{
printf("读写器0检测到卡片,卡号:");
for(i = 0;i < card_ID_cnt;i++)
{
printf("%X ",R0_ID[i]);
}
printf("\n");
}
if(status.R_read == 2)
{
printf("读写器1检测到卡片,卡号:");
for(i = 0;i < card_ID_cnt;i++)
{
printf("%X ",R1_ID[i]);
}
printf("\n");
}
}
## 最后
**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**
**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**
**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
![img](https://img-blog.csdnimg.cn/img_convert/7faced60dcfa2d7113c0d871a5614f39.png)
![img](https://img-blog.csdnimg.cn/img_convert/86fcc31367851a1f15bd9a2081763777.jpeg)
![img](https://img-blog.csdnimg.cn/img_convert/8669ff2c4dd3f400c5e436c2e638bf75.png)
![img](https://img-blog.csdnimg.cn/img_convert/ad9d8a995643c9269a1de92c226c00f1.png)
![img](https://img-blog.csdnimg.cn/img_convert/a341a25ff85a9e0dfc08379d045db87b.png)
![img](https://img-blog.csdnimg.cn/img_convert/583f01bd71802356e2de9a3c4f00c1b6.png)
![](https://img-blog.csdnimg.cn/img_convert/eb5047db434449b787784f30752780c2.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!
里一直到现在。**
**深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。**
**因此收集整理了一份《2024年嵌入式&物联网开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中...(img-2GcFhSsK-1715683694927)]
[外链图片转存中...(img-KCrRlHur-1715683694928)]
[外链图片转存中...(img-XSQajctr-1715683694929)]
[外链图片转存中...(img-g2oOi0xg-1715683694930)]
[外链图片转存中...(img-0IM1o7GQ-1715683694931)]
[外链图片转存中...(img-bY8sD3Xh-1715683694932)]
[外链图片转存中...(img-wv7vWXGZ-1715683694933)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上嵌入式&物联网开发知识点,真正体系化!**
[**如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!**](https://bbs.csdn.net/topics/618654289)
**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**!!