标签:ucTemp USB revhead1 CH9350 RevState5 UART4 接收 刷卡机
- 项目需求添加个刷卡机设备,没有移植USB例程,直接使用了一个外置芯片(CH9350),无需驱动还是挺稳定的;
- 这个芯片我只使用了其中的一个功能:USB的HID数据转为串口数据,使用的下位机模式,还有许多其他功能具体看手册,开发过程中不太方便的地方是输出的串口数据加了协议,并不是完全的透传,出来的数据解析一下就行。
- 如果MCU内部不具备USB外设,或不想使用MCU内部的USB,相关USB的处理方案在 南京沁恒 应该都能找到,个别芯片还有测试板可以申请。
- 相关USB的文档去官网下载就行,非常方便,其他USB的配套资源大部分是51例程(年限原因吧),反正都是.c文件,结合自己的环境修改修改就OK(推荐VS Code打开工程)。
一、概述
二、原理电路
- 需要很少的外围电路,我这里使用的下位机模式,也只使用一个USB即可(DM\DP),
三、现象
-
1、使用的是网购的随机一款刷卡机,说实话刷卡灵敏度不是很高,也可能是我用的卡消磁了。
-
2、CH9350上电后会频繁的发出请求帧数据,而且速率很快,使用的时候注意关掉,上电后发送一次如下数据帧就能让其停止。
-
3、刷卡出来的数据如下,只截取了一部分(毕竟是张银行卡,该隐藏得隐藏好,避免不法之徒知道我的卡号想给我汇款_,“=”之前是卡号,“=”之后我个人猜是经过加密算法的密码)。
-
需要解析的数据帧就是中间的部分,其余的根据手册想解析就解析吧,刚开始还比较疑惑,这也不是标准的ASCII码,经过查验是“USB键盘常用码值表”,网上一搜就OK,后面我会展示出我用的一部分,红框中收到的一些0x00这个直接舍弃就行。
四、处理
- 主要代码就是串口处理的部分,这个很容易解析,我就记录一下这种数据我是怎么处理的吧以及注意事项:
- 1、刷一次卡会进来上百个字节,而且这些字节是有规律的,都是0x57开头,但其中包含0x00,因此就不能简单的使用C库函数strtok()进行分割;
- 这种刷卡后突然进来的数据,是没有数据帧头的(指解析后的数字62225210183*******,不知道哪个是开头),因此我这里使用的是超时判断为一帧,超时后清空解析的接收缓冲区,这样就能保证每次进来的数据是全部的卡号,没有与上次黏连或缺失。
- 串口中断中直接过滤掉其他数据,将需要的卡号数据存到数组中;代码如下,虽然这种过滤写法存在问题(开头出现两次0x57,这帧数据就会被丢掉),图省事也没加校验,但这个可靠度要求不高,在极端丢包情况下,重新刷一次卡就OK。
#define UART4_RxbufSize 128
uint8_t UART4_Rxbuf[UART4_RxbufSize]={0}; //串口4接收缓冲区
uint8_t UART4_RxbufCount=0; //串口4接收缓冲区数据存放位置
#define revhead1 1 //等待接收
#define revhead2 2 //等待接收
#define revhead3 3 //等待接收
#define revhead4 4 //等待接收
#define revhead5 5 //等待接收
#define revhead6 6 //等待接收
#define revhead7 7 //等待接收
#define revhead8 8 //等待接收
uint8_t RevState5=revhead1; // 接收状态
uint16_t Uart4TimeOut=0; //在软件定时器中计数,每次接收到中断数据就清0,超时判断为一帧
uint8_t Uart4GetIDFlag=0;
// 串口中断服务函数
void UART4_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetFlagStatus(UART4,USART_IT_RXNE)!=RESET)//非空中断
{
ucTemp = USART_ReceiveData(UART4); //接收完毕自动清除标志位
Uart4TimeOut=0; //软件定时器中计数
Uart4GetIDFlag=0;
switch(RevState5)
{
//等待接收第一个同步头0x57
case revhead1:
if (ucTemp==0x57)
{
RevState5=revhead2;
}
break;
//等待接收第二个同步头0xAB
case revhead2:
if (ucTemp==0xAB)
{
RevState5=revhead3;
}
else
{
RevState5=revhead1; //状态机复位
}
break;
//等待接收第三个同步头0x88
case revhead3:
if (ucTemp==0x88)
{
RevState5=revhead4;
}
else
{
RevState5=revhead1; //状态机复位
}
break;
//等待接收第4个同步头0x0B
case revhead4:
if (ucTemp==0x0B)
{
RevState5=revhead5;
}
else
{
RevState5=revhead1; //状态机复位
}
break;
//等待接收第5个同步头0x10
case revhead5: //注释掉的原因是插在CH9350另一个USB口就变为0x11了,因此干脆跳过
// if (ucTemp==0x10)
// {
RevState5=revhead6;
// }
// else
// {
// RevState5=revhead1; //状态机复位
// }
break;
//等待接收第6个同步头0x01
case revhead6:
if (ucTemp==0x01)
{
RevState5=revhead7;
}
else
{
RevState5=revhead1; //状态机复位
}
break;
//等待接收第7个同步头0x00
case revhead7:
if (ucTemp==0x00)
{
RevState5=revhead8;
}
else
{
RevState5=revhead1; //状态机复位
}
break;
//等待接收第8个同步头0x00
case revhead8:
if (ucTemp==0x00)
{
RevState5=revpakage;
}
else
{
RevState5=revhead1; //状态机复位
}
break;
//等待接收数据内容
case revpakage:
if(UART4_RxbufCount<UART4_RxbufSize&&ucTemp!=0)
{
UART4_Rxbuf[UART4_RxbufCount++]=USBDataToASCII(ucTemp);
}
RevState5=revhead1;
break;
default:
RevState5=revhead1; //状态机复位
break;
}
}
/* 串口4数据接收处理线程入口函数,用于处理刷卡机线程 */
void uart4recv_thread_entry(void *parameter)
{
while(1)
{
if(Uart4TimeOut>2&&Uart4GetIDFlag==0)
{
Uart4GetIDFlag=1; //已经超时,判断接收完一帧
memcpy(Vision_t.ID,UART4_Rxbuf,strlen((char*)UART4_Rxbuf));
memset(UART4_Rxbuf,0,UART4_RxbufSize);
UART4_RxbufCount=0;
}
rt_thread_mdelay(50);
}
}
标签:ucTemp,USB,revhead1,CH9350,RevState5,UART4,接收,刷卡机 来源: https://blog.csdn.net/Davidysw/article/details/115857276
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。