fs_11c14是华清远见的一款开发板,主芯片是LPC111C4,这个芯片一共有48个管脚,其中2个晶振管脚,4个电源引脚,42个通用输入输出管脚。芯片手册可以在网上下载。
LPC1114内部包含有cortex-m0内核,cortex-m0内核自己也集成了一些外设,其中比较重要的是一个时钟SysTick,即系统定时器,它采用的是晶振时钟,所以比较精确。
fs_11c14开发板集成了很多芯片资源,具体可以在百度上搜fs_11c14,里面有文档。
下面对fs_11c14进行详细分析:
时钟的设置:
LPC_SYSCON->PRESETCTRL |= (0x1<<2); //外设复位控制寄存器,就这么用
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<18); //SSP1时钟开启
LPC_SYSCON->SSP1CLKDIV = 0x02; //二分频
LPC_IOCON->PIO2_1 &= ~0x07;
LPC_IOCON->PIO2_1 |= 0x02; //
/*****************************************************************************/
__IO uint32_t PIO2_1; /*!< Offset: 0x028 I/O configuration for pin PIO2_1/nDSR/SCK1 (R/W) */
00:PIO2_1
01: nDSR
10: SCK1
PIO2_1引脚功能的选择,上面选择了10,则是选择了SCK1功能
/*****************************************************************************/
最重要,最基本的 .c文件:gpio.c,该文件的作用是初始化LIP1114芯片的各个引脚的功能。所以要想真正理解M0开发板就必须了解本文件。具体分析如下:
void GPIOInit( void )
{
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<16);
LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);
/* Set up NVIC when I/O pins are configured as external interrupts. */
NVIC_EnableIRQ(EINT2_IRQn); //这个是使能中断位,后面在中断处理中会提到
}
该函数是初始化GPIO的时钟,根据LPC1114芯片手册,LPC1114芯片各个的外围设备需要时钟控制开启或关闭,这个叫做系统外围时钟配置寄存器,这个寄存器管理了几乎所有的外围芯片时钟的开启和关闭,所以在初始化的时候,需要那个芯片时钟开启,则必须配置该寄存器。
在本文件中,需要开启第16位和第6位,根据芯片手册说明,GPIO时钟(第6位)系统默认是开启的,所以可以省略 LPC_SYSCON->SYSAHBCLKCTRL |= (1<<6);为了说明该寄存器的使用,还是加上了该语句。所以在GPIO操作之前要调用该函数,至此,GPIO位和IOCON位可以用了。
void GPIOSetValue( uint32_t portNum, uint32_t bitPosi, uint32_t bitVal )
{
LPC_GPIO[portNum]->MASKED_ACCESS[(1<<bitPosi)] = (bitVal<<bitPosi);
}
这个函数的作用是设置相应的GPIO管脚的功能。本句子的意思是GPIO0~4的_几,置成bitVal。LPC_GPIO[]是个LPC_GPIO_TypeDef类型的结构体数组,这个结构体很重要,里面有很多元素,以后的几个函数都会用到该结构体里的成员,如下图:
第一个成员 MASKED_ACCESS,选择PIOn的管脚进行赋值。某一位置1,说明管脚置1.例如 GPIOSetValue(3, 0, 1); 执行的过程
LPC_GPIO[3]->MASKED_ACCESS[(1<<0)] = (1<<0);意思是将GPIO3_0置为1。
uint32_t GPIOGetValue(uint32_t portNum, uint32_t bitPosi)
{
return LPC_GPIO[portNum]->MASKED_ACCESS[(1<<bitPosi)];
}
以上这个函数的作用是获取管脚的信息。分析如上。
/******************************************************************/
GPIOSetDir函数是设置管脚向内输入,还是向外输出。
void GPIOSetDir( uint32_t portNum, uint32_t bitPosi, uint32_t dir )
{
if(dir)
LPC_GPIO[portNum]->DIR |= 1<<bitPosi;
else
LPC_GPIO[portNum]->DIR &= ~(1<<bitPosi);
}
DIR是上面的结构体中定义的位,如果是1就是向外输出,如果是0,就是向内输入。
/*****************************************************************************/
以上是GPIO的引脚初始化的一些函数
以下则是中断一些处理函数,但是请记住LPC_GPIO[]这个LPC_GPIO_TypeDef类型的结构体数组,以下的函数中还会用到该结构体中的一些元素
/*****************************************************************************/
由于刷卡信息不是一直发送,所以利用中断来处理,效果比较好,所以在gpio.c中,要定义一些中断相关的一些函数。
如果需要中断,那么第一步必须使能中断,芯片厂商提供的函数NVIC_EnableIRQ,有四个中断位。
NVIC_EnableIRQ(EINT0_IRQn);
NVIC_EnableIRQ(EINT1_IRQn);
NVIC_EnableIRQ(EINT2_IRQn);
NVIC_EnableIRQ(EINT3_IRQn);
这几个调用应该放在gpio的初始化中,即上文已经说过的。
既然如此,那么,必须先设置如何触发中断,即采取上升沿,还是下降沿,或者是电平触发等等。下面将具体阐述:
该函数的功能:又用到了之前提过的LPC_GPIO_TypeDef类型结构体, IS如果是1,则选择的是电平触发,如果是0,则选择的是沿触发。
当sense为0时候,选择的是沿触发方式,当sense为1的时候选择的是电平触发。
IBE如果是0则选择下降沿,如果是1则选择上升沿。
当single为0的时候,或者选择下降沿触发方式或者选择低电平触发,single为1,或者选择高电平触发或者选择上升沿触发方式,前提是sense的选择。
调用该函数则中断的触发方式就会初始化了。
/****************************************************************************/
本函数用到了LPC_GPIO_TypeDef类型结构体中的MIS元素,它是只读的信息,如果MIS是1,说明有中断,MIS为0则无中断。
/****************************************************************************/
本函数用到了PC_GPIO_TypeDef类型结构体中的IE元素,它用来使能中断。
/****************************************************************************/
同理,disable将IE置0,关中断
/****************************************************************************/
还有个重要的函数GPIOIntClear,因为中断用完了要及时清理中断位,中断就那么几个资源,所以用完的时候及时清理,以便其他的模块占用
此函数用到了IC
/****************************************************************************/
中断处理函数,有四个,这里只写了1个,用来说明,这个是刷卡的中断,如果有刷卡信息,则frid_flag置为1,在主程序中用来判断是否为1,如果是则上传仓库信息,否则不处理
到此处,gpio.c的基本操作已经完成,再次重申LPC_GPIO_TypeDef类型的结构体数组很重要,要想理解GPIO的具体操作必须看懂该结构体。
射频模块采用的是CY1444芯片。
该芯片挺好,特性如下:
1.用户不必关心射频基站复杂的控制方法,只需要简单地通过选定的UART 或IIC 或SPI接口发送命令就可以对卡片进行完全的操作。CY-14443A系列全部有板载内置天线
2.SPI高速串行接口
3.能自动感应到靠近天线区的卡片,并产生中断信号
4. 把复杂的底层读写卡操作简化为简单的几个命令,这个很重要,因为这样就很方便的进行开发,不必在意内部具体的复杂实现方法,提高了开发的效率。
本项目采用SPI总线接口,来实现信息的传递。
既然用到了SPI总线传送,那么等会就介绍一下SPI总线接口的定义,这里为了适应芯片操作流畅性,暂时先不介绍,待会介绍完这个模块的操作然后再介绍SPI接口。
管脚对应关系:
LPC11C14 中间变量 CY1444
PIO0_6 SCK0 SPI_MSCK
PIO0_8 MISO0 SPI_MISO 输出端口CY1444向LPC输入
PIO0_9 MOSI0 SPI_MOSI
PIO2_7 Rfid_nCS SPI_SS
PIO2_8 Rfid_nINT SIG 输出端口CY1444向LPC输入
逻辑关系:
模块半双工模式,接受指令后才应答,所以要先由LPC11C14通过SPI总线向CY1444传送指令,CY1444才会进行相应的工作
工作原理:
当有卡刷的时候,SIG呈现出低电平,向PIO2_8发出中断信号,LPC11C14对该中断进行相应:LPC11C14通过SPI总线向CY1444传送指令(调用SPI总线协议函数SPI_PutGet()这个函数是封装好的,传送1个字节,并返回CY1444处理后的信息)。发送的指令根据CY1444指令系统和通信协议,应该首先发送0xaa,0xbb,再发送通信指令(所需指令通过手册内通信指令表查找)。
本项目需要的指令:
(变量数组定义规则:英文名字_命令字)
1.读头类型
const uint8_t RFID_READ_MOD_TYPE_01[2] ={0x02,0x01};
/*----------------------------------------------------------------------------*/
2.读卡
const uint8_t RFID_READ_CARD_20[2] ={0x02,0x20};
/*----------------------------------------------------------------------------*/
3.读数据块
第二个字节以后的内容表示:密钥标志+字节块号+6字节的密
const uint8_t RFID_READ_DATA_BLOCK_21[10] ={0x0a,0x21,0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff};
/*----------------------------------------------------------------------------*/
4.写数据块
const uint8_t RFID_WRITE_DATA_BLOCK_22[26] ={0x1a,0x22,0x00,0x01,0xff,0xff,0xff,0xff,0xff,0xff,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10};
/*-----------------------------------------------------------------------------*/
通讯速率不大于3Mbps,MSB在前,上升沿采样。
模块工作在半双工方式,即模块接受指令后才会做出应答,由于SPI接口发送数据的同时接受上一时钟周期的从机响应数据,因此在命令发送结束后,需要稍作延时,等待模块处理命令并作出响应,命令发送阶段,都会来上一次发送的命令和数据内容,可以用来作为校验,读响应时可以发送0数据给模块。
8个时钟传送完一次数据,8位的数据,片选SS低电平,上升沿采样。
SPI_Init初始化SPI的传输方式 MOSI模式。设置PIO0_6为时钟输出,本项目没有改变,即没有设置时钟的分频,那么这个管脚输出的时钟就是系统默认的时钟。
PIO2_8是2号中断,FRID接的是2号中断,所以先使能这个管脚,如果SIG产生低电平,即有中断,那么就执行GPIO里面的中断处理函数,中断处理函数将frid的标志位置为1.
初始化FRID,对应的管脚。
如果标志位是1,那么有中断产生,即有刷卡,然后调用operate函数,将读到的信息存放在goods结构体变量中。
该函数如果返回1,则读取成功,返回0,读取失败。
在M0主函数中不停的调用该函数,
如果读取成功,则将type置为‘g’,msg1.goods中就有数据,在A8程序的检测M0信息的线程中判断,如果读取的type为‘g’,则读取goods的数据,测试程序如下:
#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include<stdio.h> #include <errno.h> #include <termios.h> #include <unistd.h> #include <string.h> typedef signed char int8_t; typedef signed short int int16_t; typedef signed int int32_t; typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; struct M0_send_info { uint8_t head; uint8_t snum; uint8_t temp[2]; uint8_t hum[2]; int8_t x; int8_t y; int8_t z; uint32_t lin; uint32_t bet; uint32_t adc; }; struct goods_info{ uint8_t READ_DATA_BLOCK_21[16]; //16字节的数据块内容 uint8_t READ_MOD_TYPE_01[8]; //8字节的模块型号 uint8_t READ_CARD_20[4]; //4字节的卡序列号 //读卡中的信息,先写简单的测试,等完成后再封装成结构体,内容包括 //卡的信息和货物信息 }; struct msg{ char sign; char type; struct M0_send_info mes; struct goods_info goods; }; void serial_init(int fd) { struct termios options; tcgetattr(fd, &options); options.c_cflag |= ( CLOCAL | CREAD ); options.c_cflag &= ~CSIZE; options.c_cflag &= ~CRTSCTS; options.c_cflag |= CS8; options.c_cflag &= ~CSTOPB; options.c_iflag |= IGNPAR; options.c_iflag &= ~(ICRNL | IXON); options.c_oflag = 0; options.c_lflag = 0; cfsetispeed(&options, B115200); cfsetospeed(&options, B115200); tcsetattr(fd,TCSANOW,&options); } int main() { int dev_uart_fd; ssize_t len = 0; struct msg msg; printf("struct msg = %d*******************\n",sizeof(struct msg)); dev_uart_fd = open("/dev/ttyUSB0",O_RDWR) ; if(dev_uart_fd < 0) { perror("open ttyUSB0"); return 0 ; } serial_init(dev_uart_fd); printf("okfd =%d\n",dev_uart_fd); while(1) { memset(&msg,0,sizeof(struct msg)); len = read(dev_uart_fd,(char *)&msg ,sizeof(struct msg)); // printf("len = %d\n",len); // printf("sizeof(struct msg)=%d\n",sizeof(struct msg)); if(len != sizeof(struct msg)) { int i = 0; int j = 0; int k = 0; for(i = len; i<sizeof(struct msg);i++) { read(dev_uart_fd,((char *)&msg +i),1); } printf("OK!!!\n"); printf("light: %d\n",msg.mes.lin ); printf("ADC: %2.1f\n",msg.mes.adc * 3.3 / 1024 ); if(msg.type == 'g') { j = sizeof(msg.goods.READ_DATA_BLOCK_21); printf("CARD_DATA:\n"); for(i = 0;i<j;i++) { printf("%d",(int )msg.goods.READ_DATA_BLOCK_21[i]); } printf("\n"); j = sizeof(msg.goods.READ_MOD_TYPE_01); printf("CARD_TYPE:\n"); for(i = 0;i<j;i++) { printf("%d",(int )msg.goods.READ_MOD_TYPE_01[i]); } printf("\n"); j = sizeof(msg.goods.READ_CARD_20); printf("CARD_ID:\n"); for(k = 0;k<j;k++) { printf("%d",(int )msg.goods.READ_CARD_20[k]); } printf("\n"); } } } }
/*********************frid介绍完了******************************************/
/***************************************************************************/