随着城市化进程的加快,新能源汽车也即将到来。在笔者居住的城市,力帆、长安等企业相继推出了可供市民租赁的电动汽车。在享受租车带来的便利的同时,汽车的充电却成了一个难题。在城市道路和停车场安装充电桩成为解决这一问题的有效手段。汽车在充电时,需要有一个车位可以用来停车,而这个车位只能在汽车充电时使用,其他时间不能被占用。也就是说,每个充电车位都需要有车位锁。当有汽车充电时,车位锁打开,否则车位锁关闭。本文使用嵌入式开发板实现车位锁开关的控制,可供相关项目开发者参考。1、硬件选择嵌入式开发板,我们选择天嵌科技的TQ335X开发板,如下图:车位锁,我们选择ParkShareNetwork的带RS485的车位锁,如下图如图:2.电路连接嵌入式开发板通过RS485与车位锁相连。RS485电路原理图如下:如上图电路为TTL转RS485电路,相应配置跳线SW5接口,使其连接串口2。为了实现485通信功能,板载SW5的跳线选择方法如下图:车位锁与开发板的连接,车位锁的红色连接线(+)要接在车位锁的A端485,黄色连接线(-)接到485的B端。3.软件开发环境由于嵌入式开发板使用的是量身定做的Linux(ubuntu)版本,所以需要安装相应的cross-办公电脑上的编译环境(windows系统)。首先,我们需要安装Vmware虚拟机。笔者使用的虚拟机版本如下图所示:接下来,我们需要在虚拟机上安装嵌入式系统自带数据光盘中量身定制的Linux(ubuntu)系统版本,以供使用。编译程序时使用。笔者使用的ubuntu版本如下图所示:安装完ubuntu后,系统必须加载交叉编译器。交叉编译器的安装过程请参考开发板手册,按照手册中的步骤(如下图)安装即可。4.车位锁协议我们用一个程序来控制它的开关,通过向车位锁发送指令来获取它的状态。车位锁的协议如下图所示:具体各指令示例如下:1.开锁指令:55ADDR0101CRCAA如果接收成功,则返回5AADDR020101CRCAA如果接收失败,返回5BADDR03010100CRCAA2。锁定命令:55ADDR0102CRCAA接收成功返回5AADDR020201CRCAA接收失败返回5BADDR03020100CRCAA3。读取锁定状态:55ADDR0106CRCAA执行成功返回5AADDR0206STATUSCRCAA返回5BADDR03060100CRCAA注:STATUS状态有:00-锁定状态,01-解锁状态,02-中间状态0~90°,03-中间状态90~180°,88-运动状态,10-当前解锁状态,上面没有检测到汽车。5.程序编写现在我们有了开发环境和协议,我们就可以开始编写程序了。根据我们在第二部分的描述,程序需要通过串口连接RS485电路。因此,程序的大致流程是:初始化串口->向串口发送报文->接收串口的响应报文->关闭串口。完整的C代码(LockCtrl.h和LockCtrl.c)如下:#include#include#include#include#include#include#include#include#include#include//文件控制定义#include//终端控制定义#include#defineDEVICE"/dev/ttySAC2"intinit_serial(void);//打开串口并初始化设置intuart_send(intfd,char*data,intdatalen);//SerialportsendDataintuart_recv(intfd,char*data,intdatalen);//串口接收数据intOpenLock(void);//驱动锁intCloseLock(void);//关闭锁intGetLockStatus(void);//获取锁状态锁定控制。c代码内容:#include"LockCtrl.h"intserial_fd=0;//打开串口并初始化设置intinit_serial(void){serial_fd=open(DEVICE,O_RDWR|O_NOCTTY|O_NDELAY);if(serial_fd<0){perror(“打开”);return-1;}//串口主要设置结构体termiosstructtermiosoptions;/**1.tcgetattr函数用于获取终端相关的参数。*参数fd为终端的文件描述符,返回结果保存在termios结构体中*/tcgetattr(serial_fd,&options);/*2.修改获取的参数*/options.c_cflag|=(CLOCAL|CREAD);//设置控制模式状态,本地连接,接收使能options.c_cflag&=~CSIZE;//字符长度,设置数据位前必须屏蔽该位options.c_cflag&=~CRTSCTS;//无硬件流控选项。c_cflag|=CS8;//8位数据长度选项.c_cflag&=~CSTOPB;//1位停止位选项.c_iflag|=IGNPAR;//无校验位选项.c_oflag=0;//输出模式选项。c_lflag=0;//关闭终端模式cfsetospeed(&options,B9600);//设置波特率/**3.设置新属性,TCSANOW:所有更改立即生效*/tcflush(serial_fd,TCIFLUSH);//溢出数据可以接收,但不能读取tcsetattr(serial_fd,TCSANOW,&options);return0;}/***串口发送数据*@fd:串口描述符*@data:待发送数据*@datalen:数据长度*/intuart_send(intfd,char*data,intdatalen){intlen=0;len=write(fd,data,datalen);//实际写入长度printf("uart_send:len=%d,datalen=%d\n",len,datalen);if(len==datalen)//发送成功{returnlen;}else{tcflush(fd,TCOFLUSH);//TCOFLUSH刷写数据但不传输return-1;}//return0;}/***串口接收数据*请求启动后,发送PC端ascii文件*/intuart_recv(intfd,char*data,intdatalen){intlen=0,ret=0;fd_setfs_read;structtimevaltv_timeout;FD_ZERO(&fs_read);FD_SET(fd,&fs_read);tv_timeout.tv_sec=(10*20/115200+2);tv_timeout.tv_usec=0;ret=select(fd+1,&fs_read,NULL,NULL,&tv_timeout);printf("ret=%d\n",ret);//如果返回0,说明描述符状态发生变化前超时时间已过,错误返回-1if(FD_ISSET(fd,&fs_read)){len=read(fd,data,datalen);printf("len=%d\n",len);returnlen;}else{perror("select");return-1;}//return0;}/***停车锁*/intOpenLock(void){intiDataLen=0;intiRetVal=0;charszRcvBuf[20]={0};unsignedcharszOpen[6]={0x55,0x01,0x01,0x01,0x9A,0xAA};//550101019AAAiRetVal=init_serial();if(iRetVal!=0){printf("OpenLock:execinit_serialfailed!\n");return-1;}iDataLen=uart_send(serial_fd,szOpen,sizeof(szOpen));if(iDataLen==-1){printf("OpenLock:execuart_sendfailed!\n");return-1;}iDataLen=uart_recv(serial_fd,szRcvBuf,sizeof(szRcvBuf));if(iDataLen==-1){printf("OpenLock:execuart_recvfailed!\n");return-1;}printf("OpenLock:receivedlen=%d\n",iDataLen);if(szRcvBuf[0]==0x5A){printf("OpenLock:openlocksuccess!\n");}elseif(szRcvBuf[0]==0x5B){printf("OpenLock:openlockfailed!\n");}else{printf("OpenLock:receiveddataiswrong!\n");}close(serial_fd);return0;}/***关车位锁*/intCloseLock(void){intiDataLen=0;intiRetVal=0;charszRcvBuf[20]={0};unsignedcharszClose[6]={0x55,0x01,0x01,0x02,0x78,0xAA};//5501010278AAiRetVal=init_serial();if(iRetVal!=0){printf("CloseLock:execinit_serialfailed!\n");return-1;}iDataLen=uart_send(serial_fd,szClose,sizeof(szClose));if(iDataLen==-1){printf("CloseLock:execuart_sendfailed!\n");return-1;}iDataLen=uart_recv(serial_fd,szRcvBuf,sizeof(szRcvBuf));if(iDataLen==-1){printf("CloseLock:execuart_recvfailed!\n");return-1;}printf("CloseLock:receivedlen=%d\n",iDataLen);if(szRcvBuf[0]==0x5A){printf("CloseLock:closelocksuccess!\n");}elseif(szRcvBuf[0]==0x5B){printf("CloseLock:closelockfailed!\n");}else{printf("CloseLock:receiveddataiswrong!\n");}close(serial_fd);return0;}/***获得取车位置锁状态*/intGetLockStatus(void){intiDataLen=0;intiRetVal=0;charszRcvBuf[20]={0};unsignedcharszStatus[6]={0x55,0x01,0x01,0x06,0x19,0xAA};//5501010619AAiRetVal=init_serial();if(iRetVal!=0){printf("GetLockStatus:execinit_serialfailed!\n");return-1;}iDataLen=uart_send(serial_fd,szStatus,sizeof(szStatus));if(iDataLen==-1){printf("GetLockStatus:execuart_sendfailed!\n");return-1;}iDataLen=uart_recv(serial_fd,szRcvBuf,sizeof(szRcvBuf));if(iDataLen==-1){printf("GetLockStatus:execuart_recvfailed!\n");return-1;}printf("GetLockStatus:receivedlen=%d\n",iDataLen);if(szRcvBuf[0]==0x5A){printf("GetLockStatus:getlockstatussuccess!\n");if(szRcvBuf[4]==0x00){printf("Locked!\n");}elseif(szRcvBuf[4]==0x01){printf("打开!\n");}elseif(szRcvBuf[4]==0x02){printf("Middlestatus(0~90)!\n");}elseif(szRcvBuf[4]==0x03){printf("Mmiddlestatus(90~180)!\n");}elseif(szRcvBuf[4]==0x88){printf("Runningstatus!\n");}elseif(szRcvBuf[4]==0x10){printf("Openedandnocaronit!\n");}}elseif(szRcvBuf[0]==0x5B){printf("GetLockStatus:Getlockstatusfailed!\n");}else{printf("GetLockStatus:GetLockStatus:receiveddataiswrong!\n");}close(serial_fd);return0;}//主函数intmain(intargc,char**argv){intiRetVal=0;//解锁iRetVal=OpenLock();if(iRetVal!=0){printf("Openlockfailed!\n");return-1;}//锁定睡眠(10);iRetVal=CloseLock();if(iRetVal!=0){printf("Closelockfailed!\n");return-1;}//解锁睡眠(10);iRetVal=OpenLock();if(iRetVal!=0){printf("Openlockfailed!\n");return-1;}//获取车位锁状态sleep(10);iRetVal=GetLockStatus();if(iRetVal!=0){printf("Getlockstatusfailed!\n");return-1;}return0;}六、程序编译运行将LockCtrl.c和LockCtrl.h文件复制到交叉编译环境中,使用“arm-linux-gcc-g-oLockCtrlLockCtrl.c”命令编译程序,生成LockCtrl文件然后,通过FTP工具或者“ftpget-uXXX-pXXX10.10.10.10LockCtrl”命令将LockCtrl文件上传到嵌入式开发板的具体目录,修改权限后执行“./LockCtrl”命令,可以看到程序正常运行,车位锁成功执行了开关操作。程序输出如下:[root@EmbedSkylocal]#./LockCtrlart_send:len=6,datalen=6ret=1len=7OpenLock:receivedlen=7OpenLock:openlocksuccess!uart_send:len=6,datalen=6ret=1len=7CloseLock:receivedlen=7CloseLock:closelocksuccess!uart_send:len=6,datalen=6ret=1len=7OpenLock:receivedlen=7OpenLock:openlocksuccess!uart_send:len=6,datalen=6ret=1len=7GetLockStatus:receivedlen=7GetLockStatus:getlockstatussuccess!打开!7.小结介绍描述了通过嵌入式开发板控制车位锁开关的过程,并给出了完整的C代码实现。对于嵌入式开发,大家不仅要会写程序,还要了解开发板的电路原理和接线方式,这对开发人员提出了更高的要求。【本文为专栏作家周兆雄原创文章,作者微信公众号:周氏逻辑(logiczhou)】点此阅读更多本作者好文