MINI2440 ARM开发板可以跑HTTP服务器,因此可以在上面开发一些web应用。比如做一个简单的LED灯控制页面。在PC浏览器可以访问开发板上的网页,这样可以远程控制开发板上的LED或者其他所有的IO口,甚至可以视频监控。
友善之臂公司开发了一个这样的例子,可以在网页上控制LED跑马灯的运行速度。现在笔者尝试对其中所用到的技术和整个的流程逻辑进行分析。
IPC
开机后通过网页发送命令控制开发板上的LED闪烁模式,是IPC共享资源的一个典型例子。IPC(Inter-Process Communication)进程间通信,提供了各种进程间通信的方法。
进程间通信的目的一般有: 1) 数据传输 2) 共享数据 3) 通知事件 4) 资源共享 5) 进程控制
这里采用的通信机制是管道(Pipe)。
先看最简单的html
先看网页文件
<td colspan="2" align="center"><form method="get" action="leds.cgi" name="LED-TEST">
<div align="left">
<table border="0" width="280" align="center">
<tr>
<td width="131">
<p align="center">类型</td>
<td width="135">
<p align="center">速率</td>
</tr>
<tr>
<td width="131">
<p align="center"> <input type="radio" value="ping" checked name="type">跑马灯</td>
<td width="135">
<p align="center"><input type="radio" name="speed" value="slow" checked>慢速</td>
</tr>
<tr>
<td width="131">
<p align="center"> <input type="radio" name="type" value="counter">计数器</td>
<td width="135">
<p align="center"><input type="radio" name="speed" value="normal">中速</td>
</tr>
<tr>
<td width="131">
<p align="center"><input type="radio" name="type" value="stop">停止</td>
<td width="135">
<p align="center"><input type="radio" name="speed" value="fast">高速</td>
</tr>
<tr>
<td colspan="2" width="272">
<p align="center"><input type="submit" value="确定(OK)" name="submit"></td>
</tr>
</table>
</div>
<div align="center"></div><div align="center"></div><div align="left"></div><div align="left"></div></form> </td>
可以看到采用get方法传参数,submit调用leds.cgi(shell脚本)程序,把radio框的参数传递到脚本中。
CGI调用
CGI是公共网关接口(Common Gateway Interface),是外部应用程序(leds-player.c)和web服务器(index.html)之间的接口标准。
case $QUERY_STRING in
*slow*)
period=0.25
;;
*normal*)
period=0.125
;;
*normal*)
period=0.0625
;;
esac
/bin/echo $type $period > /tmp/led-control
由submit传来的slow normal normal参数在这里进行解析赋给变量period,最后把参数以字符串的形式写入到管道文件/tmp/led-control。这个管道文件是在leds-player.c中创建的。
管道创建
使用命令mkfifo("/tmp/led-control", 0666)
创建命令管道。
for (;;) {
fd_set rds;
struct timeval step;
int ret;
FD_ZERO(&rds);
FD_SET(led_control_pipe, &rds);
step.tv_sec = period;
step.tv_usec = (period - step.tv_sec) * 1000000L;
ret = select(led_control_pipe + 1, &rds, NULL, NULL, &step);
if (ret < 0) {
perror("select");
exit(1);
}
if (ret == 0) {
push_leds();
} else if (FD_ISSET(led_control_pipe, &rds)) {
static char buffer[200];
for (;;) {
char c;
int len = strlen(buffer);
if (len >= sizeof buffer - 1) {
memset(buffer, 0, sizeof buffer);
break;
}
if (read(led_control_pipe, &c, 1) != 1) {
break;
}
if (c == '\r') {
continue;
}
if (c == '\n') {
int tmp_type;
double tmp_period;
if (sscanf(buffer,"%d%lf", &tmp_type, &tmp_period) == 2) {
type = tmp_type;
period = tmp_period;
}
fprintf(stderr, "type is %d, period is %lf\n", type, period);
memset(buffer, 0, sizeof buffer);
break;
}
buffer[len] = c;
}
}
}
接着就在一个for循环中监听管道中的参数变换,一旦变化调用push_leds()对led进行操作。操作的方法是ioctl操作/dev下的leds驱动。核心代码只有一句话ioctl(led_fd, led_bitmap & 1, i)
。
整个控制逻辑的流程图如下: