基于fs_11c14开发板的项目实践

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介绍完了******************************************/

 

/***************************************************************************/

 

 

 

posted on 2012-07-03 21:29  孟浩依然  阅读(3704)  评论(0编辑  收藏  举报