使用嵌入式开发板实现对车位锁控制的流程及程序实现

随着城市化进程的加快,新能源汽车呼之欲出。在笔者所在的城市,力帆和长安等公司都先后推出了可供市民租用的电动汽车。在享受租车所带来的便利的同时,汽车的充电就成了一个难题,在城市道路和停车场安装充电桩成为解决这个问题的一个有效的手段。汽车充电时,需要有一个可用于停靠的车位,并且这个车位只能在汽车充电的时候使用,在其他时间都不能被占用。也就是说,每个充电的车位需要有一个车位锁,当有汽车充电的时候,车位锁打开,否则车位锁关闭。

本文用嵌入式开发板实现了对车位锁开关的控制,可供相关项目的开发人员参考。

一、硬件选型
在嵌入式开发板这块,我们选择了天嵌科技的TQ335X开发板,如下图所示:

在车位锁这块,我们选择了泊享网络的带RS485的车位锁,如下图所示:

二、电路连接
嵌入式开发板通过RS485方式和车位锁相连,RS485的电路原理图如下所示:

如上图所示,电路为TTL转RS485电路,通过跳线SW5接口相应配置,使之与串口2相连。
为了实现485通信功能,板载SW5的跳线选择方式如下图所示:

对于车位锁与开发板的连接,要将车位锁的红色连接线(+)与485的A端相连,将车位锁的黄色连接线(-)与485的B端相连。

三、软件开发环境
由于嵌入式开发板使用的是裁剪版的Linux(ubuntu),因此需要在办公电脑(windows系统)上安装对应的交叉编译环境。
首先,我们要安装Vmware虚拟机,笔者使用的虚拟机版本如下图所示:

接着,我们要将嵌入式系统配备的资料光盘中的裁剪版的Linux(ubuntu)系统安装到虚拟机上,以供编译程序时使用。笔者使用的ubuntu版本如下图所示:

在安装了ubuntu之后,还要将交叉编译器装载到系统中。交叉编译器的安装过程可以参看开发板使用手册,只要按照手册中的步骤(如下图所示)安装就可以了。

四、车位锁协议
我们使用程序通过向车位锁下发指令来控制其开关并获取其状态。车位锁的协议如下图所示:

具体而言,各命令的样例如下:
1.开锁命令:55 ADDR 01 01 CRC AA
接收成功返回 5A ADDR 02 01 01 CRC AA
接收失败返回 5B ADDR 03 01 01 00 CRC AA

2.闭锁命令:55 ADDR 01 02 CRC AA
接收成功返回 5A ADDR 02 02 01 CRC AA
接收失败返回 5B ADDR 03 02 01 00 CRC AA

3.读取锁状态:55 ADDR 01 06 CRC AA
执行成功返回 5A ADDR 02 06 STATUS CRC AA
接收失败返回 5B ADDR 03 06 01 00 CRC AA
注:STATUS 各状态为:00-闭锁状态,01-开锁状态,02-中间状态0~90°,03-中间状态90~180°,88-运动状态,10-当前开锁状态并且检测到上面无车。

五、程序编写
有了开发环境,有了协议,那么接下来我们就可以开始编写程序了。根据我们在第二部分的描述,程序要通过串口来连接RS485电路,因此,程序的大体流程为:初始化串口—>向串口发消息—>接收串口的响应消息—>关闭串口。
完整的C代码(LockCtrl.h和LockCtrl.c)如下所示:
LockCtrl.h代码内容:

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>      //文件控制定义
#include <termios.h>    //终端控制定义
#include <errno.h> 

#define DEVICE "/dev/ttySAC2" 

int init_serial(void);  // 打开串口并初始化设置
int uart_send(int fd, char *data, int datalen);   // 串口发送数据
int uart_recv(int fd, char *data, int datalen);    // 串口接收数据
int OpenLock(void);      // 开车位锁
int CloseLock(void);     // 关车位锁
int GetLockStatus(void); // 获取车位锁 状态

LockCtrl.c代码内容:

#include "LockCtrl.h"

int serial_fd = 0; 

// 打开串口并初始化设置
int init_serial(void)
{
    serial_fd = open(DEVICE, O_RDWR | O_NOCTTY | O_NDELAY);
    if (serial_fd < 0)
    {
        perror("open");
        return -1;
    }
    //串口主要设置结构体termios <termios.h>
    struct termios options; 

    /**1. tcgetattr函数用于获取与终端相关的参数。
      *    参数fd为终端的文件描述符,返回的结果保存在termios结构体中
      */
    tcgetattr(serial_fd, &options); 

    /*2. 修改所获得的参数*/
    options.c_cflag |= (CLOCAL | CREAD);    // 设置控制模式状态,本地连接,接收使能
    options.c_cflag &= ~CSIZE;              // 字符长度,设置数据位之前一定要屏掉这个位
    options.c_cflag &= ~CRTSCTS;            // 无硬件流控
    options.c_cflag |= CS8;                 // 8位数据长度
    options.c_cflag &= ~CSTOPB;             // 1位停止位
    options.c_iflag |= IGNPAR;              // 无奇偶检验位
    options.c_oflag = 0;                    // 输出模式
    options.c_lflag = 0;                    // 不激活终端模式
    cfsetospeed(&options, B9600);           // 设置波特率 

    /**3. 设置新属性,TCSANOW:所有改变立即生效*/
    tcflush(serial_fd, TCIFLUSH);           // 溢出数据可以接收,但不读
    tcsetattr(serial_fd, TCSANOW, &options); 

    return 0;
} 

/**
*串口发送数据
*@fd:串口描述符
*@data:待发送数据
*@datalen:数据长度
*/
int uart_send(int fd, char *data, int datalen)
{
    int len = 0;
    len = write(fd, data, datalen);    // 实际写入的长度 

    printf("uart_send:len=%d,datalen=%d\n", len, datalen); 

    if (len == datalen)      // 发送成功
    {
        return len;
    }
    else
    {
        tcflush(fd, TCOFLUSH);   // TCOFLUSH刷新写入的数据但不传送
        return -1;
    } 

    // return 0;
} 

/**
*串口接收数据
*要求启动后,在pc端发送ascii文件
*/
int uart_recv(int fd, char *data, int datalen)
{
    int len = 0, ret = 0;
    fd_set fs_read;
    struct timeval tv_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,代表在描述符状态改变前已超过timeout时间,错误返回-1 

    if (FD_ISSET(fd, &fs_read))
    {
        len = read(fd, data, datalen);
        printf("len = %d\n", len);
        return len;
    }
    else
    {
        perror("select");
        return -1;
    } 

    // return 0;
} 

/**
*开车位锁
*/
int OpenLock(void)
{
    int  iDataLen      = 0;
    int  iRetVal       = 0;
    char szRcvBuf[20]  = {0};

    unsigned char szOpen[6] = {0x55,0x01,0x01,0x01,0x9A,0xAA};     // 55 01 01 01 9A AA

    iRetVal = init_serial();
    if (iRetVal != 0)
    {
        printf("OpenLock: exec init_serial failed!\n");
        return -1;
    }

    iDataLen = uart_send(serial_fd, szOpen, sizeof(szOpen));
    if (iDataLen == -1)
    {
        printf("OpenLock: exec uart_send failed!\n");
        return -1;
    }

    iDataLen = uart_recv(serial_fd, szRcvBuf, sizeof(szRcvBuf));
    if (iDataLen == -1)
    {
        printf("OpenLock: exec uart_recv failed!\n");
        return -1;
    }

    printf("OpenLock: received len=%d\n", iDataLen); 

    if (szRcvBuf[0] == 0x5A)
    {
        printf("OpenLock: open lock success!\n");
    }
    else if (szRcvBuf[0] == 0x5B)
    {
        printf("OpenLock: open lock failed!\n");
    }
    else
    {
        printf("OpenLock: received data is wrong!\n");
    }

    close(serial_fd); 

    return 0;
}

/**
*关车位锁
*/
int CloseLock(void)
{
    int  iDataLen      = 0;
    int  iRetVal       = 0;
    char szRcvBuf[20]  = {0};

    unsigned char szClose[6] = {0x55,0x01,0x01,0x02,0x78,0xAA};    // 55 01 01 02 78 AA 

    iRetVal = init_serial();
    if (iRetVal != 0)
    {
        printf("CloseLock: exec init_serial failed!\n");
        return -1;
    }

    iDataLen = uart_send(serial_fd, szClose, sizeof(szClose));
    if (iDataLen == -1)
    {
        printf("CloseLock: exec uart_send failed!\n");
        return -1;
    }

    iDataLen = uart_recv(serial_fd, szRcvBuf, sizeof(szRcvBuf));
    if (iDataLen == -1)
    {
        printf("CloseLock: exec uart_recv failed!\n");
        return -1;
    }

    printf("CloseLock: received len=%d\n", iDataLen); 

    if (szRcvBuf[0] == 0x5A)
    {
        printf("CloseLock: close lock success!\n");
    }
    else if (szRcvBuf[0] == 0x5B)
    {
        printf("CloseLock: close lock failed!\n");
    }
    else
    {
        printf("CloseLock: received data is wrong!\n");
    }

    close(serial_fd); 

    return 0;
}

/**
*获取车位锁 状态
*/
int GetLockStatus(void)
{
    int  iDataLen      = 0;
    int  iRetVal       = 0;
    char szRcvBuf[20]  = {0};

    unsigned char szStatus[6] = {0x55,0x01,0x01,0x06,0x19,0xAA};   // 55 01 01 06 19 AA 

    iRetVal = init_serial();
    if (iRetVal != 0)
    {
        printf("GetLockStatus: exec init_serial failed!\n");
        return -1;
    }

    iDataLen = uart_send(serial_fd, szStatus, sizeof(szStatus));
    if (iDataLen == -1)
    {
        printf("GetLockStatus: exec uart_send failed!\n");
        return -1;
    }

    iDataLen = uart_recv(serial_fd, szRcvBuf, sizeof(szRcvBuf));
    if (iDataLen == -1)
    {
        printf("GetLockStatus: exec uart_recv failed!\n");
        return -1;
    }

    printf("GetLockStatus: received len=%d\n", iDataLen); 

    if (szRcvBuf[0] == 0x5A)
    {
        printf("GetLockStatus: get lock status success!\n");

        if (szRcvBuf[4] == 0x00)
        {
            printf("Locked!\n");
        }
        else if (szRcvBuf[4] == 0x01)
        {
            printf("Opened!\n");
        }
        else if (szRcvBuf[4] == 0x02)
        {
            printf("Middle status(0~90)!\n");
        }
        else if (szRcvBuf[4] == 0x03)
        {
            printf("Middle status(90~180)!\n");
        }
        else if (szRcvBuf[4] == 0x88)
        {
            printf("Running status!\n");
        }
        else if (szRcvBuf[4] == 0x10)
        {
            printf("Opened and no car on it!\n");
        }
    }
    else if (szRcvBuf[0] == 0x5B)
    {
        printf("GetLockStatus: Get lock status failed!\n");
    }
    else
    {
        printf("GetLockStatus: GetLockStatus: received data is wrong!\n");
    }

    close(serial_fd); 

    return 0;
}

// 主函数
int main(int argc, char **argv)
{
    int iRetVal = 0;

    // 开锁
    iRetVal = OpenLock();
    if (iRetVal != 0)
    {
        printf("Open lock failed!\n");
        return -1;
    }

    // 闭锁
    sleep(10);
    iRetVal = CloseLock();
    if (iRetVal != 0)
    {
        printf("Close lock failed!\n");
        return -1;
    }

    // 开锁
    sleep(10);
    iRetVal = OpenLock();
    if (iRetVal != 0)
    {
        printf("Open lock failed!\n");
        return -1;
    }

    // 获取车位锁 状态
    sleep(10);
    iRetVal = GetLockStatus();
    if (iRetVal != 0)
    {
        printf("Get lock status failed!\n");
        return -1;
    }

    return 0;
}

六、程序编译及运行
将LockCtrl.c和LockCtrl.h文件拷贝到交叉编译环境上,并使用“arm-linux-gcc -g -o LockCtrl LockCtrl.c”命令编译程序,生成LockCtrl文件。

接着,通过FTP工具或“ftpget -u XXX -p XXX 10.10.10.10 LockCtrl”命令将LockCtrl文件上传到嵌入式开发板的特定目录下,修改权限之后执行“./LockCtrl”命令,可以看到程序正常运行,车位锁成功执行了开关操作。程序的输出如下:

[root@EmbedSky local]# ./LockCtrl
uart_send:len=6,datalen=6
ret = 1
len = 7
OpenLock: received len=7
OpenLock: open lock success!
uart_send:len=6,datalen=6
ret = 1
len = 7
CloseLock: received len=7
CloseLock: close lock success!
uart_send:len=6,datalen=6
ret = 1
len = 7
OpenLock: received len=7
OpenLock: open lock success!
uart_send:len=6,datalen=6
ret = 1
len = 7
GetLockStatus: received len=7
GetLockStatus: get lock status success!
Opened!

七、总结
本文介绍了通过嵌入式开发板控制车位锁开关的流程,并给出了完整的C代码实现。对于嵌入式开发,大家不仅要懂得如何编写程序,还要了解开发板的电路原理及接线方式,这对开发者提出了更高的要求。

时间: 2024-08-29 00:35:28

使用嵌入式开发板实现对车位锁控制的流程及程序实现的相关文章

Web开发:关于16进制颜色代码你又知道多少

开发:关于16进制颜色代码你又知道多少-"> 这有必要了解一颜色系统的概念: RGB:RGB色彩模式是工业界的一种颜色标准,是通过对红(R).绿(G).蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的,RGB即是代表红.绿.蓝三个通道的颜色,这个标准几乎包括了人类视力所能感知的所有颜色,是目前运用最广的颜色系统之一.RGB色彩模式使用 RGB模型为图像中每一个像素的RGB分量分配一个0~255范围内的强度值.RGB图像只使用三种颜色,就可以使它们按照不同的比例混合,在

XP下超级终端与嵌入式开发板交互技巧

一.简介 超级终端是Windows操作系统自带的一个通用的串行交互软件,可以通过这个工具对路由器交换机等进行配置.使用调制解调器.一条零调制解调电缆或以太网连接,再调用此程序能够连接到其他计算机.Telnet 站点.公告板系统 (BBS).联机服务和主机.我们可以用它来调试电路是否可行. 嵌入式开发板基本都有串口,可以通过超级终端与嵌入式系统的串口交互,使超级终端成为嵌入式系统的"显示器". 使用:开始→程序→附件→通讯→超级终端(可新建或者使用现有的连接对设备进行配置); 启动命令:

用vs2013+velt-0.1.4进行嵌入式开发 进行海思平台 UBOOT 开发

1.1    什么是VELT VELT的全称是Visual EmbedLinuxTools,它是一个与visual gdb类似的visual studio插件,用以辅助完成Linux开发.利用这个插件,将可以在visual studio的IDE中进行Linux应用程序的开发(包括编译和调试),也可以进行uboot和linux内核的编译,并根据编译时的错误信息正确定位到源码.目前的版本是0.1.4,仅支持vs2013.此插件可以在CSDN下载频道下载(http://download.csdn.ne

嵌入式开发基础知识:Linux支持的多种文件系统类型

Linux支持多种文件系统类型,在嵌入式开发中上常用有:ROMFS.JFFS2.NFS.CRAMFS.YAFFS.UBIFS等. JFFS文件系统 JFFS文件系统最早是由瑞典Axis Communications公司基于Linux2.0的内核为嵌入式系统开发的文件系统.JFFS2是RedHat公司基于JFFS开发的闪存文件系统,最初是针对RedHat公司的嵌入式产品eCos开发的嵌入式文件系统,所以JFFS2也可以用在Linux, uCLinux中. Jffs2: 日志闪存文件系统版本2 (J

嵌入式开发-嵌入式的底层驱动方向和上层应用方向的分析

问题描述 嵌入式的底层驱动方向和上层应用方向的分析 刚刚得到帮助了解了方向,觉得搞嵌入式软件这两个方向其中之一,还是想进一步了解两者,以及两者的区别和学习内容,如果可以,给小弟提一点建议,谢谢 解决方案 底层驱动的技术要求比应用高,可以从应用入手,再掌握驱动之类的底层开发 解决方案二: 嵌入式行业新人系列之一 - 如何选择自己的嵌入式开发方向? 嵌入式开发联盟-www.mcuos.com Osboy原创:qq:82475491mcuos.com@gmail.com 废话不多说.首先声明osboy

线程同步-linux可以用互斥锁控制多个线程的执行顺序吗?如何实现?

问题描述 linux可以用互斥锁控制多个线程的执行顺序吗?如何实现? 假设有三个线程:1.2.3,各自打印A,B,C.是否可以只用互斥锁就实现"ACBCACBC--"的打印输出?(不使用其他的线程同步方法) 解决方案 这种用自旋锁更好呀,而且实现更简单,顺序执行即可.

嵌入式开发 shell编程的作用

问题描述 嵌入式开发 shell编程的作用 本人初学嵌入式,想知道shell编程在做嵌入式开发时作用是什么 解决方案 主要是一些系统操作等 shell能够方便的实现 解决方案二: shell, linux系统下的脚本工具,类似于windows下的bat 解决方案三: 自动化和批处理很有用

【详解】嵌入式开发中固件的烧录方式

版本:v1.2   Crifan Li 摘要 本文主要介绍了嵌入式开发过程中,将固件从PC端下载到开发板中的各种方式,主要包括NFS挂载,Nand Flash和Nor Flash,USB,RS232,网卡NIC等方式. 本文提供多种格式供: 在线阅读 HTML HTMLs PDF CHM TXT RTF WEBHELP 下载(7zip压缩包) HTML HTMLs PDF CHM TXT RTF WEBHELP HTML版本的在线地址为: http://www.crifan.com/files/

嵌入式开发 ARM Cortex-M3处理器技术优势分析

在嵌入式开发设计中,对客户来说用什么技术.芯片不是主要的.主要的是能否满足要求.高性价比.开发门槛底.易于使用才是硬道理.Cortex-M3是一个32位处理器内核.从理论上来说性价比高. 三级流水线+分支预测 ARM Cortex-M3与ARM7内核一样,采用适合于微控制器应用的三级流水线,但增加了分支预测功能.现代处理器大多采用指令预取和流水线技术,以提高处理器的指令执行速度.流水线处理器在正常执行指令时,如果碰到分支(跳转)指令,由于指令执行的顺序可能会发生变化,指令预取队列和流水线中的部分