关于这篇主要是运行在MT7688上的应用程序的实现。
这部分程序很早之前写的,后来没想到要放出来,所以一开始也没什么设计可言,就是想到哪里写哪里。最后为了能拿的出手,代码改了一部分,还有一部分因为懒,就都没改了,所以算一个四不像体。如果要改很多地方要做重构,电脑配置和时间有限就跳过这一步。
多亏叽叽叽老司机提醒,终于不输出只输出到终端了,有的以日志形式输出。这样即使后台运行也可以看到状态了。但是转化太麻烦就省略了= =
主目录:
http://blog.csdn.net/DFSAE/article/details/78715815
本篇目录
一.设计
二.实现
1.线程实现
2.socket通信
3.通信协议的打包和解析
4.电机控制
5.摄像头控制
6.串口转发控制
三.makefile解析
四.编译,运行
五.设置为上电启动
一.设计
这里没有具体的设计,只有大概的设计思路。
整个软件分为5个部分,包括电机控制,摄像头控制,串口转发控制这三部分底层和socket网络通信,协议的打包及解析这里通信部分。
大概存在3个线程:socket通信线程,电机控制线程和连接超时线程(这个本来想用定时器的,但是没找到定时器的依赖库= =)。
1>.socket通信线程
程序主要在socket线程的监听上位机发送下来的数据中度过。当收到数据后进行解析。然后执行响应的动作。应用软件最核心的部分就在这里了。另外这里的电机操作正常情况是需要分开来的,后面解释为什么,如果做一起就说明比较懒了。
2>.电机控制线程
这个线程里面处理上位机发下来的电机控制信号,实现对电机的输出。这就是操作分离开来的部分,后面有详细解释。
3>.连接超时线程
其实就是一个定时器的功能,保证在一定时间没有接收上位机数据(连接断开)后,重新进行socket监听,以便进行新的连接。正常情况下,上位机应该隔一段时间就发下连接数据,以免断开连接。
*注:另外,这里IP地址暂时先定死,由前面的配置的ip为准:192.168.55.1,默认端口号为1234。两个摄像头的端口号分别为8090和8070。
二.实现
自顶向下开始分析实现。
注:这里要现在用命令行建一个car_server的文件夹,里面放个car_server.log的日志文件。
1.线程实现
在main函数里开启了3个线程:
err = pthread_create(&socket_th_id, NULL, socketListen_thread, NULL);
err = pthread_create(&moter_th_id, NULL, moterControl_thread, NULL);
err = pthread_create(&timer_th_id, NULL, timer_thread, NULL);
socket监听线程中做的事情:判断是否掉线?如果掉线,开启socket监听,等待再次连接。其中car_recvAnalysisCmd函数在没有收到数据时其实是阻塞的。
/*************************************************
* 函数名: socketListen_thread
* 说明:scoket监听线程。
* 输入参数:
* 输出参数:
**************************************************/
void * socketListen_thread(void * arg)
{
system("echo start socket listen thread! >> /car_server/car_server.log");
printf("start socket listen thread!\n");
while (1)
{
//如果处于未连接状态,进行socket监听连接
if (get_carLinkStatus(car) == Car_Link_Disconnet)
{
system("echo remote link! >> /car_server/car_server.log");
car_waitRemoteLink(car);
}
car_recvAnalysisCmd(car);//接受数据
usleep(2000);
}
return (void *)0;
}
电机控制线程,20ms执行一次控制。(目前采用直接控制的方式了)
/*************************************************
* 函数名: moterControl_thread
* 说明:电机控制线程。
* 输入参数:
* 输出参数:
**************************************************/
void * moterControl_thread(void * arg)
{
system("echo start moter control thread! >> /car_server/car_server.log");
printf("start moter control thread!\n");
while (1)
{
//电机控制20ms一次
usleep(20000);
}
}
该线程是用来判断连接超时的。
/*************************************************
* 函数名: timer_thread
* 说明:电机控制线程。
* 输入参数:
* 输出参数:
**************************************************/
void * timer_thread(void * arg)
{
system("echo start timer thread! >> /car_server/car_server.log");
printf("start timer thread!\n");
while (1)
{
car_linkTimeoutDece(car);
//判断失去连接,清空电机参数列表,并停车
sleep(1);
}
}
2.socket通信
socket基础操作参考:http://c.biancheng.net/cpp/html/3030.html
socket.h里定义了一堆宏,来定义连接的IP和端口。默认数据端口为1234。
/*! 本机服务器IP及通信端口号 */
#define SERVER_IP "192.168.55.1"
#define SERVER_DATA_PORT 1234
#define SERVER_CAMERA_PORT1 8070
#define SERVER_CAMERA_PORT2 8090
socket部分封装了基础的socket通信,比如连接,断开,接收数据,发送数据等,具体代码如下:
/*************************************************
* 函数名:socket_send
* 说明:发送socket数据
* 输入参数:sInfo
* dataBuff 发送缓冲
* size 发送长度
* 输出参数:错误号
**************************************************/
SocketErrNo socket_send(const SocketInfo * const sInfo,
const unsigned char *dataBuff, int size)
{
if (write(sInfo->clnt_sock, dataBuff, size) == -1)
{
socket_err_deal(Send_Err);
return Send_Err;
}
return No_Err;
}
/*************************************************
* 函数名:socket_recv
* 说明:接收socket数据
* 输入参数:sInfo
* dataBuff 接收数据缓冲
* size 接受长度
* 输出参数:错误号
**************************************************/
SocketErrNo socket_recv(const SocketInfo * const sInfo,
unsigned char *dataBuff, int size, int *ret_size)
{
if ((*ret_size = read(sInfo->clnt_sock, dataBuff, size)) == -1)
{
socket_err_deal(Recv_Err);
return Recv_Err;
}
return No_Err;
}
/*************************************************
* 函数名:socket_close
* 说明:关闭socket
* 输入参数:sInfo
* 输出参数:错误号
**************************************************/
SocketErrNo socket_close(const SocketInfo * const sInfo)
{
//关闭套接字
close(sInfo->clnt_sock);
close(sInfo->serv_sock);
return No_Err;
}
/*************************************************
* 函数名:socket_listen
* 说明:关闭socket
* 输入参数:sInfo
* 输出参数:错误号
**************************************************/
void socket_listen(SocketInfo * const sInfo)
{
socklen_t clnt_addr_size;
struct sockaddr_in clnt_addr;
#ifdef SOCKET_ERROR_PRINT
static unsigned int link_t = 0;
#endif
//进入监听状态,等待用户发起请求
listen(sInfo->serv_sock, 20);
//接收客户端请求
clnt_addr_size = sizeof(clnt_addr);
sInfo->clnt_sock = accept(sInfo->serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
#ifdef SOCKET_ERROR_PRINT
link_t++;
pri