基于MSP430与RC522的RFID简单应用(2)
程序及相关函数解释,原作者为孙登波,感谢孙老师指导。
1.IAR工程文件基本配置
源代码编译好后,需要先设置工程下的器件类型、开发工具类型以及项目的 输出文件相关设置,因为 BSL 下载一般使用 TXT 类型的目标文件,IAREW430 软件默认并不会输出 TXT 目标代码文件。 点击界面左侧窗口的项目名称,选中后点击右键,选择“option”。
到这里,关于项目的设置工作已经完成,保存一下,下面就可以开始进
行编译生成目标文件了。
2.公交卡实现原理
1.
分为16个扇区,每个扇区为4块,每块16个字节,以块为存取单位
每个扇区有独立的一组密码及访问控制
每张卡有唯一***,为32位
具有防冲突机制,支持多卡操作
读写距离:10 cm以内(与读写器有关)
Mifare_Std 卡片的**属性取决于控制字。控制字的默认值是“FF078069”,此时
A**:不可被读出,有全部权限
B**:可被读出,没有任何权限
2.
基本流程
初始化
寻卡 (预置通信协议与通信波特率,判断是否为M1卡)
防冲撞 ( 多张进入射频范围,该机制会选择一张卡片)
选定卡片 (选择被选中的卡的***,并同时返回卡的容量代码)
验证卡片密码 (对某一扇区进行密码验证)
读卡 (读一个块)
充值
显示
充值完毕
锁定机器无法连续充值 (break)
main.c 源程序
//MSP430F149单片机 + RC522 +M1
//实现公交卡充值功能
//作者:孙登波
*/
#include "msp430x14x.h"
#include "PIN_DEF.H"
#include "RC522.H"
#include "UART0_Func.c"
#include "ctype.h"
#include "BoardConfig.h"
#include "lcd.h"//顺序的重要性
#include "led8run.h"
#include "stdio.h"
#include "string.h"
#include "strtonum.h"
unsigned char writeData[16] = {1, 2, 3, 4, 0};
unsigned char str[16];
unsigned char UD[4],Temp[4];
unsigned char RF_Buffer[18];
unsigned char Password_Buffer[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // Mifare One 缺省密码
unsigned char money_ok[4] = {0x00, 0x00, 0x00, 0x00}; //充值、消费数额数组
unsigned char money_ob[16] = {0x00,0x00,0x00,0x14}; //充值、消费数额数组
unsigned char input_ncount = 0; //输入数字个数统计
unsigned char Result[9]; //存显示字符
unsigned char money[4] = {0x00, 0x00, 0x00, 0x00};
unsigned char money_dot[4] = {0x00, 0x00, 0x00, 0x00};
unsigned char money_inch[6], money_inch_hex[5];
unsigned char money_inch_dot[6], money_inch_hex_dot[5];
unsigned char money_i[4] = {0x00, 0x00, 0x00, 0x00}; //这个初值一定要有,否则出错
unsigned char money_i_dot[4] = {0x00, 0x00, 0x00, 0x00};
long int inputnum = 20; //充值数值
unsigned char inputnum_dot = 0; //充值数值的小数部分的值
long int sum_dot = 0; //充值后小数部分的额度
long int inteDec = 0; //寄存器内部的整数部分值
long int dotDec = 0; //寄存器内部的小数部分值
char MBRX[30];
char MBKeyTP[30];
char Event;
unsigned char DISP_MODE, i; // 编辑控件显示模式
unsigned char des_on = 0; // DES加密标志
void Key_TP_Task(void);
//***************************************************************************//
// 初始化主时钟: MCLK = XT1×(FLL_FACTOR+1) //
//***************************************************************************//
void Init_CLK(void)
{
unsigned int qq;
WDTCTL = WDTPW + WDTHOLD; // 关看门狗
BCSCTL1 &= ~XT2OFF; //打开XT2高速晶体振荡器
do
{
IFG1 &= ~OFIFG; //Clear oscFault flag清除振荡器失效标志
for (qq = 0xff; qq > 0; qq--)
;
} while ((IFG1 & OFIFG)); //oscFault flag still set
BCSCTL2 |= SELM_2; //MCLK=XT2
//BCSCTL2 |= DIVM_0; //控制MCLK不分频,默认
BCSCTL2 |= SELS; //SMCLK=XT2
//BCSCTL2 |= DIVS_0; //控制SMCLK不分频,默认
}
void Delay(unsigned int time)
{
unsigned int i, k;
for (i = 0; i < 255; i++)
for (k = 0; k < time; k++)
_NOP();
}
//***************************************************************************//
// //十进制数转为字符串 //
//***************************************************************************//
void Int_char(long int datas)
{
if (datas / 1000000)
{
Result[0] = datas / 1000000 + '0';
Result[1] = datas % 1000000 / 100000 + '0';
Result[2] = datas % 100000 / 10000 + '0';
Result[3] = datas % 10000 / 1000 + '0';
Result[4] = datas % 1000 / 100 + '0';
Result[5] = datas % 100 / 10 + '0';
Result[6] = datas % 10 + '0';
Result[7] = '\0';
}
else if (datas / 100000)
{
Result[0] = datas / 100000 + '0';
Result[1] = datas % 100000 / 10000 + '0';
Result[2] = datas % 10000 / 1000 + '0';
Result[3] = datas % 1000 / 100 + '0';
Result[4] = datas % 100 / 10 + '0';
Result[5] = datas % 10 + '0';
;
Result[6] = '\0';
Result[7] = 0;
}
else if (datas / 10000)
{
Result[0] = datas / 10000 + '0';
Result[1] = datas % 10000 / 1000 + '0';
Result[2] = datas % 1000 / 100 + '0';
Result[3] = datas % 100 / 10 + '0';
Result[4] = datas % 10 + '0';
Result[5] = '\0';
Result[6] = 0;
Result[7] = 0;
}
else if (datas / 1000)
{
Result[0] = datas / 1000 + '0';
Result[1] = datas % 1000 / 100 + '0';
Result[2] = datas % 100 / 10 + '0';
Result[3] = datas % 10 + '0';
Result[4] = '\0';
Result[5] = 0;
Result[6] = 0;
Result[7] = 0;
}
else if (datas / 100)
{
Result[0] = datas / 100 + '0';
Result[1] = datas % 100 / 10 + '0';
Result[2] = datas % 10 + '0';
Result[3] = '\0';
Result[4] = 0;
Result[5] = 0;
Result[6] = 0;
Result[7] = 0;
}
else if (datas / 10)
{
Result[0] = datas / 10 + '0';
Result[1] = datas % 10 + '0';
Result[2] = '\0';
Result[3] = 0;
Result[4] = 0;
Result[5] = 0;
Result[6] = 0;
Result[7] = 0;
}
else
{
Result[0] = datas % 10 + '0';
Result[1] = '\0';
Result[2] = 0;
Result[3] = 0;
Result[4] = 0;
Result[5] = 0;
Result[6] = 0;
Result[7] = 0;
}
}
/************************************************
新增函数名称:incharg
功 能:实现充值功能
参 数:
返回值:
/***********************************************/
char incharg(void)
{
unsigned char g_ucTempbuf[20];
unsigned char num;
unsigned char temp1[10];
unsigned char temp2[5];
unsigned char status2;
long int sum = 0; //充值后整数部分的额度
long int re;
temp1[0] = '\0';
temp2[0] = '\0';
memset(money, 0, sizeof(money)); //清空钱包临时数组
status2 = PcdRead(1, g_ucTempbuf);//读块2部分
if (status2 == MI_OK)
{
for (num = 0; num < 2; num++)
{
money[num] = g_ucTempbuf[1 - num];//存储芯片寄存器读出来的内容,并且调换高低位(因为存储的时候,按照低位在前的顺序)
}
itoa(money[0], temp1, 16); //十六进制数值转十六进制字符串(字符串无法进行计算)
itoa(money[1], temp2, 16); //十六进制数值转十六进制字符串(字符串无法进行计算)
strcat(temp1, temp2); //将两个char类型连接。输出temp1=temp1temp2
inteDec = str_dec(temp1); //余额整数 十六进制字符串转十进制数
temp1[0] = '\0'; // 字符常量占一个字节的内存空间
temp2[0] = '\0'; // 字符常量占一个字节的内存空间
memset(money, 0, sizeof(money)); //将money中当前位置后面清零 。
sum = inputnum + inteDec; //充值后余额整数部分之和
re = sum;
Int_char(re); //十进制转字符串
led(); //led灯亮
LCD_clear(); //清屏
LCD_Desk2(); //lcd显示正在充值
while (Result[i] != '\0')
{
LCD_write_str(Result); //lcd显示10进制字符串
i++;
}
delay_ms(1800);
}
if(sum<65536) //将充值完毕的余额总数再次存储到寄存器内。
{
itoa(sum,money_inch_hex,16); //10进制数值转为16进制字符串,整数部分
i=strlen(money_inch_hex);
switch(i)
{
case 0:
for(num=0;num<4;num++)
{
money_inch_hex[num]='0';
}
money_inch_hex[4]='\0';
break;
case 1:
money_inch_hex[4]='\0';
money_inch_hex[3]=money_inch_hex[0];
money_inch_hex[2]='0';
money_inch_hex[1]='0';
money_inch_hex[0]='0';
break;
case 2:
money_inch_hex[4]='\0';
money_inch_hex[3]=money_inch_hex[1];
money_inch_hex[2]=money_inch_hex[0];
money_inch_hex[1]='0';
money_inch_hex[0]='0';
break;
case 3:
money_inch_hex[4]='\0';
money_inch_hex[3]=money_inch_hex[2];
money_inch_hex[2]=money_inch_hex[1];
money_inch_hex[1]=money_inch_hex[0];
money_inch_hex[0]='0';
break;
}
StringToHex(money_i, money_inch_hex); //16进制字符串转为16进制字节数组
for(num=0;num<2;num++) //置高位在前
{
money_ok[num]=money_i[1-num];
}
}
return status2; //返回判断值
}
/*******************************************
函数名称:Init_Port
功 能:初始化端口
参 数:无
返回值 :无
********************************************/
void Init_Port(void)
{
P4DIR = RF_LPCTL + RF_SS + RF_SCLK + RF_DATA_OUT;
P2DIR |= BIT0 + RF_LPCTL + RF_SS + RF_SCLK + RF_DATA_OUT;
P1DIR = RF_LPCTL + RF_SS + RF_SCLK + RF_DATA_OUT;
}
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 关看门狗
int i = 0;
unsigned char status;
BoardConfig(0xf0);
Init_Port();
InitUART();
Port_init(); //系统初始化,设置IO口属性
delay_ms(100); //延时100ms
LCD_init(); //液晶参数初始化设置
LCD_clear(); //清屏
LCD_Desk1();
_EINT();
PcdReset(); //复位RC522
PcdAntennaOn(); //开启天线发射
while (1)
{ status = PcdRequest(PICC_REQIDL, Temp); ////寻卡,输出为卡类型
if (status == MI_OK)
status = PcdAnticoll(UD); //防冲撞处理,输出卡片***,4字节
if (status == MI_OK)
status = PcdSelect(UD); //选择卡片,输入卡片***,4字节
if (status == MI_OK)
status = PcdAuthState(PICC_AUTHENT1A, 1, Password_Buffer, UD); //在进行读写操作之前需要先进行认证
if (status == MI_OK)
status == incharg();//读
if (status == MI_OK)
{
status = PcdWrite(1,money_ok);//充钱
memset(money_ok,0,sizeof(money_ok));
break;
}
}
LCD_clear(); //清屏
LCD_Desk3();
led();
}
3.
存储在寄存器内部的数据格式是:十六进制数值
需要实现十六进制数值和十六进制字符串的互相转换
十六进制的字符串需要转换成十进制的数值进行计算
显示在LCD屏幕之前十进制数值又需要转换成十进制字符串
连续刷卡导致多充钱 (break 解决)
strtonum.h 源文件(进制转换操作)
/********************************************************************
//实现寄存器内数值类型转换函数
//itoa 十六进制数值转(16进制)字符串
//str_dec 十六进制字符串转为十进制数
//StringToHex 十六进制字符串转十六进制字节数组,要求字符串偶数个
//调试环境:IAR + MSP430F149 +RC522
//作者:孙登波
********************************************************************/
//
//新增函数名称:itoa
//功 能:十六进制数值转(16进制)字符串
//参 数:
//返回值: str( 16进制)字符串
//
unsigned char *itoa(uint num, uchar *str, int radix) //十六进制数值转(16进制)字符串
{
uchar string[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
uchar *ptr = str;
int i;
int j;
if (num == 0)
{
str[0] = '0';
str[1] = '0';
str[2] = '\0';
}
while (num)
{
*ptr++ = string[num % radix];
num /= radix;
if (num < radix)
{
*ptr++ = string[num];
*ptr = '\0';
break;
}
}
j = ptr - str - 1;
for (i = 0; i < (ptr - str) / 2; i++)
{
int temp = str[i];
str[i] = str[j];
str[j--] = temp;
}
return str;
}
/************************************************
新增函数名称:str_dec
功 能:十六进制字符串转为十进制数
参 数:uchar s[]
返回值: temp十进制数
/***********************************************/
long int str_dec(uchar s[]) //十六进制字符串转为十进制数
{
uchar i, m, n;
long int temp = 0;
m = 4; //十六进制是按字符串传进来的,所以要获得他的长度
for (i = 0; i < m; i++)
{
if (s[i] >= 'A' && s[i] <= 'F') //十六进制还要判断他是不是在A-F或者a-f之间a=10。。
n = s[i] - 'A' + 10;
else if (s[i] >= 'a' && s[i] <= 'f')
n = s[i] - 'a' + 10;
else
n = s[i] - '0';
temp = temp * 16 + n;
}
return temp;
}
/************************************************
新增函数名称:StringToHex
功 能:十六进制字符串转十六进制字节数组,要求字符串偶数个
参 数:uchar* hex, uchar* str
返回值: hex
/***********************************************/
void StringToHex(uchar *hex, uchar *str) //十六进制字符串转十六进制字节数组,要求字符串偶数个
{
int len = strlen(str); //计数器,计算str长度?
int tmp, i;
for (i = 0; i < len / 2; i++)
{
if (str[2 * i] >= '0' && str[2 * i] <= '9')
{
tmp = ((str[2 * i] - '0') << 4);
}
else if (str[2 * i] >= 'A' && str[2 * i] <= 'F')
{
tmp = (((str[2 * i] - 'A') + 10) << 4);
}
else if (str[2 * i] >= 'a' && str[2 * i] <= 'f')
{
tmp = (((str[2 * i] - 'a') + 10) << 4);
}
if (str[2 * i + 1] >= '0' && str[2 * i + 1] <= '9')
{
hex[i] = tmp + (str[2 * i + 1] - '0');
}
else if (str[2 * i + 1] >= 'A' && str[2 * i + 1] <= 'F')
{
hex[i] = tmp + (str[2 * i + 1] - 'A') + 10;
}
else if (str[2 * i + 1] >= 'a' && str[2 * i + 1] <= 'f')
{
hex[i] = tmp + (str[2 * i + 1] - 'a') + 10;
}
}
}
4.
十进制数转为字符串 的原因:
方便LCD屏幕12864的显示
通过字符串的形式,利用例程自带的字库,实现变量的动态输出。
lcd.h文件
/********************************************************************
//DM430-L型系统板控制带字库型12864液晶模块显示测试程序,显示汉字字符
//显示模式为汉字模式,直接将12864插入12864接口即可,朝外,液晶接口位于主板上方
//请注意安装位置,左边有字符说明,为靠近1602接口的1X20座子
//注意选择液晶的电源,位于电位器附近,可选5V或3.3V,根据液晶电压进行选择,默认5V
//调试环境:IAR + MSP430F149 +RC522
//小昭改写
//时间:2019.12.19
********************************************************************/
#include <msp430x14x.h>
#include "Config.h"
//*************************************************************************
// 初始化IO口子程序
//*************************************************************************
void Port_init()
{
P4SEL = 0x00;
P4DIR = 0xFF;
P5SEL = 0x00;
P5DIR |= BIT0 + BIT1 + BIT5 + BIT6 + BIT7;
PSB_SET; //液晶并口方式
RST_SET;
}
//***********************************************************************
// 显示屏命令写入函数
//***********************************************************************
void LCD_write_com(unsigned char com)
{
RS_CLR;
RW_CLR;
EN_SET;
DataPort = com;
delay_ms(5);
EN_CLR;
}
//***********************************************************************
// 显示屏数据写入函数
//***********************************************************************
void LCD_write_data(unsigned char data)
{
RS_SET;
RW_CLR;
EN_SET;
DataPort = data;
delay_ms(5);
EN_CLR;
}
//***********************************************************************
// 显示屏清空显示
//***********************************************************************
void LCD_clear(void)
{
LCD_write_com(0x01);
delay_ms(5);
}
//***********************************************************************
//函数名称:DisplayCgrom(uchar hz)显示CGROM里的汉字
//***********************************************************************
void DisplayCgrom(uchar addr, uchar *hz)
{
LCD_write_com(addr);
delay_ms(5);
while (*hz != '\0')
{
LCD_write_data(*hz);
hz++;
delay_ms(5);
}
}
//***********************************************************************
// 显示屏单字符写入函数
//***********************************************************************
void LCD_write_char(unsigned char data)
{
LCD_write_com(0x98); //第三行显示
delay_ms(1);
LCD_write_data(data);
delay_ms(1);
}
//***********************************************************************
// 显示屏单字符写入函数2
//***********************************************************************
//***********************************************************************
// 显示屏字符串写入函数
//***********************************************************************
void LCD_write_str(unsigned char *s)
{
LCD_write_com(0x98); //第三行显示 显示余额
delay_ms(2);
while (*s)
{
LCD_write_data(*s);
delay_ms(2);
s++;
}
}
//****************************************************************
//函数名称:Display()显示测试结果
//****************************************************************
//void Display(void)
//{
//DisplayCgrom(0x80, "欣世纪电子欢迎你");
//DisplayCgrom(0x88, "旺:jingyehanxing");
//DisplayCgrom(0x90, "www.avrgcc.com ");
//DisplayCgrom(0x98, "电话057487470625");
//}
//***********************************************************************
// 显示屏初始化函数
//***********************************************************************
void LCD_init(void)
{
LCD_write_com(FUN_MODE); //显示模式设置
delay_ms(5);
LCD_write_com(FUN_MODE); //显示模式设置
delay_ms(5);
LCD_write_com(CURSE_DIR); //显示模式设置
delay_ms(5);
LCD_write_com(DISPLAY_ON); //显示开
delay_ms(5);
LCD_write_com(CLEAR_SCREEN); //清屏
delay_ms(5);
}
//***********************************************************************
// 液晶显示界面欢迎
//***********************************************************************
void LCD_Desk1(void)
{
LCD_clear();
DisplayCgrom(0x80, "小昭");
DisplayCgrom(0x90, "欢迎您");
DisplayCgrom(0x88, "单次充值20元");
DisplayCgrom(0x98, "未完成请勿拿走卡");
delay_ms(20);
}
//***********************************************************************
// 液晶显示界面正在充值
//***********************************************************************
void LCD_Desk2(void)
{
LCD_clear();
DisplayCgrom(0x80, "请勿移动卡片");
DisplayCgrom(0x90, "正在充值20元");
DisplayCgrom(0x88, "充值完成余额:");
delay_ms(20);
}
//***********************************************************************
// 液晶显示界面充值完成
//***********************************************************************
void LCD_Desk3(void)
{
LCD_clear();
DisplayCgrom(0x80, "╭∞━━╮");
DisplayCgrom(0x90, "┃● ● ┃");
DisplayCgrom(0x88, "┃ ε ┃");
DisplayCgrom(0x98, "○━━━╯");
delay_ms(20);
}
//***********************************************************************
// 液晶显示界面充值完成
//***********************************************************************
void LCD_Desk(void)
{
LCD_clear();
DisplayCgrom(0x80, "公交公司欢迎您");
DisplayCgrom(0x90, "正在充值20元");
DisplayCgrom(0x88, "充值完成余额:");
delay_ms(20);
}
注意:
显示扣款后余额:
sum = inteDec – inputnum ;
显示充值后余额:
um = inputnum + inteDec; //充值后余额整数部分之和
re = sum;
Int_char(re); //十进制数转为字符串 //
led();
LCD_clear(); //清屏
LCD_Desk2();
while (Result[i] != ‘\0’) //Result[i]为转出来的字符串
{
LCD_write_str(Result); //显示10进制字符串
i++;
}
delay_ms(1800);
}
3.实验结果
LCD_Desk1();
LCD_Desk2();
LCD_Desk3();