第一次尝试用PC机去控制FriendlyArm友善之臂开发板, 以下的成果是本学期嵌入式系统课程的一个课程设计, 希望能帮助大家了解FriendlyArm友善之臂开发板的使用.
注: 下图中我通过PC端的Linux虚拟机, 以ssh命令远程控制ARM开发板, 远程控制ARM开发板的方法在博文最后会有说明.
一开始对于以跨语言方式完成socket通信有些头疼, 但后来发现不同语言对于socket通信的实现仅仅是实现形式不同, 基本的原理和流程是基本相同的.
这个课程设计要求我们通过PC机向ARM开发板发送命令, ARM开发板在接收到不同的命令后, 用其两个发绿色光的LED灯分别以00/01/10/11(0-> 灭, 1 -> 亮)方式做出回应. 补充说明一下, 我将命令分为四大种类: 电压、电导率、电流mA、调零mV(只有电导率档才可调电导挡位). 这里实现的是PC发命令至ARM开发板, 故下图所制作的可视化操作界面仅是命令选择的载体, 在此并无其它含义, 大家不必深入思考.
两设备的通信采用TCP协议, PC端作为client, ARM开发板端作为server: 另外PC端可自由开启或关闭程序(窗体).
ARM开发板端的server.c实现了多客户连接进程并发处理这一功能, 这也是很重要的一点.
下面说一下该课程设计的启动方式.
1. 开启PC端的热点.
2. 在ARM开发板端连接PC端的热点, 这里ARM开发板的IP地址为 192.168.137.243.
3. 在开发板端终端输入 gcc -o server server.c, 之后输入命令 sudo ./server.
4. 在PC端启动窗体程序, 选择某一命令, 点击发送即可.
5. 此时ARM开发板的两个LED灯会随着PC端选择命令的不同而展现出不同的亮灭组合.
//Led_control.h
void led_control(int L1, int L2)
{
//system("echo 43 > /sys/class/gpio/export");
//system("echo 44 > /sys/class/gpio/export");
//以上两条语句在第一次运行server时取消注释, 之后变为注释
if(L1 == 0 && L2 == 0)//D1 D2 灭
{
system("echo low > /sys/class/gpio/gpio43/direction");
system("echo low > /sys/class/gpio/gpio44/direction");
}
else if(L1 == 0 && L2 == 1)//D1灭 D2亮
{
system("echo low > /sys/class/gpio/gpio43/direction");
system("echo high > /sys/class/gpio/gpio44/direction");
}
else if(L1 == 1 && L2 == 0)//D1亮 D2灭
{
system("echo high > /sys/class/gpio/gpio43/direction");
system("echo low > /sys/class/gpio/gpio44/direction");
}
else if(L1 == 1 && L2 == 1)//D1 D2 亮
{
system("echo high > /sys/class/gpio/gpio43/direction");
system("echo high > /sys/class/gpio/gpio44/direction");
}
}
//运行于ARM开发板上
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Led_control.h"
#define SERVPORT 3333
#define BACKLOG 10
int main()
{
int sockfd,client_fd;
int sin_size;
char s[100];
struct sockaddr_in my_addr;
struct sockaddr_in remote_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);//建立socket --
my_addr.sin_family=AF_INET;//AF_INET地址族
my_addr.sin_port=htons(SERVPORT);//设定端口号(host -> networks)
my_addr.sin_addr.s_addr = INADDR_ANY;//32位IPv4地址
bzero(&(my_addr.sin_zero),8); //置前8个字节为0
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
{
perror("bind 出错!");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) //监听socket连接,设置队列中最多拥有连接个数为10 --
{
perror("listen 出错!");
exit(1);
}
while(1)
{
sin_size = sizeof(struct sockaddr_in);//记录sockaddr_in结构体所占字节数
if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1) //accept()缺省是阻塞函数,阻塞到有连接请求为止 --
{
perror("accept error");
continue;
}
printf("收到一个连接来自: %s\n", inet_ntoa(remote_addr.sin_addr));
if (!fork())
{
if (send(client_fd, "Successfully connected.", 23, 0) == -1) //--
error("send 出错!");
//receive a piece of message from host
int nbytes = recv(client_fd, s, 100, 0);
s[nbytes] = '\0';
//show the message that it has just received.
int i =0;
while(i < strlen(s))
{
putchar(s[i++]);
}
printf("\n");
if(strcmp(s, "gear1") == 0)
{
//command 'gear1'
//D1 off, D2 off
puts("function gear1\n");
led_control(0, 0);
}
else if(strcmp(s, "gear2_1") == 0)
{
//command 'gear2_1'
//D1 off, D2 on
puts("function gear2_1\n");
led_control(0, 1);
}
else if(strcmp(s, "gear2_2") == 0)
{
//command 'gear2_2'
//D1 off, D2 on
puts("function gear2_2\n");
led_control(0, 1);
}
else if(strcmp(s, "gear2_3") == 0)
{
//command 'gear2_3'
//D1 off, D2 on
puts("function gear2_3\n");
led_control(0, 1);
}
else if(strcmp(s, "gear2_4") == 0)
{
//command 'gear2_4'
//D1 off, D2 on
puts("function gear2_4\n");
led_control(0, 1);
}
else if(strcmp(s, "gear2_5") == 0)
{
//command 'gear2_5'
//D1 off, D2 on
puts("function gear2_5\n");
led_control(0, 1);
}
else if(strcmp(s, "gear2_6") == 0)
{
//command 'gear2_6'
//D1 off, D2 on
puts("function gear2_6\n");
led_control(0, 1);
}
else if(strcmp(s, "gear2_7") == 0)
{
//command 'gear2_7'
//D1 off, D2 on
puts("function gear2_7\n");
led_control(0, 1);
}
else if(strcmp(s, "gear2_8") == 0)
{
//command 'gear2_8'
//D1 off, D2 on
puts("function gear2_8\n");
led_control(0, 1);
}
else if(strcmp(s, "gear2_9") == 0)
{
//command 'gear 2_9'
//D1 off, D2 on
puts("function gear2_9\n");
led_control(0, 1);
}
else if(strcmp(s, "gear3") == 0)
{
//command 'gear3'
//D1 on, D2 off
puts("function gear3\n");
led_control(1, 0);
}
else if(strcmp(s, "gear4") == 0)
{
//comand 'gear4'
//D1 on, D2 on
puts("function gear4\n");
led_control(1, 1);
}
close(client_fd);
exit(0);
}
else
{
close(client_fd);
}
}
}
//PC端 Win Form窗体程序
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
namespace 离子色谱仪
{
public partial class Form1 : Form
{
public static Socket ClientSocket;
String IP = "192.168.137.243";//友善之臂的IP地址
string command = "";//即将发送至友善之臂的命令
public Form1()
{
InitializeComponent();
}
public void create_command()//生成操作命令
{
if (rb_volt.Checked == true)//"电压档"选中
{
command = "gear1";
}
else if (rb_conductivity.Checked == true)//"电导率档"选中
{
if (rb_gear1.Checked == true)
{
command = "gear2_1";
}
else if (rb_gear2.Checked == true)
{
command = "gear2_2";
}
else if (rb_gear3.Checked == true)
{
command = "gear2_3";
}
else if (rb_gear4.Checked == true)
{
command = "gear2_4";
}
else if (rb_gear5.Checked == true)
{
command = "gear2_5";
}
else if (rb_gear6.Checked == true)
{
command = "gear2_6";
}
else if (rb_gear7.Checked == true)
{
command = "gear2_7";
}
else if (rb_gear8.Checked == true)
{
command = "gear2_8";
}
else if (rb_gear_auto.Checked == true)
{
command = "gear2_9";
}
}
else if (rb_current.Checked == true)//"电流mA挡"选中
{
command = "gear3";
}
else if (rb_zero.Checked == true)//"调零mV档"选中
{
command = "gear4";
}
}
public void send_message()//向友善之臂开发板发送数据
{
int port = 3333;
IPAddress ip = IPAddress.Parse(IP); //将IP地址字符串转换成IPAddress实例
ClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//使用指定的地址簇协议、套接字类型和通信协议
IPEndPoint endPoint = new IPEndPoint(ip, port); // 用指定的ip和端口号初始化IPEndPoint实例
ClientSocket.Connect(endPoint); //与远程主机建立连接
//Console.WriteLine("开始发送消息");
byte[] message = Encoding.ASCII.GetBytes(command); //通信时实际发送的是字节数组,所以要将发送消息转换字节
ClientSocket.Send(message);
//Console.WriteLine("发送消息为:" + Encoding.ASCII.GetString(message));
byte[] receive = new byte[200];
int length = ClientSocket.Receive(receive); // length 接收字节数组长度
Console.WriteLine("接收消息为:" + Encoding.ASCII.GetString(receive));
ClientSocket.Close(); //关闭连接
}
private void btn_send_Click(object sender, EventArgs e)
{
DialogResult dialogResult = MessageBox.Show("确认要发送命令吗?","发送确认",MessageBoxButtons.OKCancel,MessageBoxIcon.Question);
if (dialogResult == DialogResult.OK)
{
create_command();//生成命令
send_message();//发送命令
}
else
{
;//无动作
}
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
}
}
}
附: 通过Linux虚拟机远程控制ARM开发板的流程:
1. 找到一台装有图形界面的linux操作系统的ARM开发板.
2. 开启PC机热点.
3. 开机后,按下Ctrl + Alt + T打开终端.
4. 在终端内输入ifconfig以查看ARM开发板的IP地址.
当前ARM开发板的IP地址为192.168.137.178.
5. 在PC机端(ubuntu虚拟机)的终端内输入ping 192.168.137.178.
若能ping通,则说明ARM开发板和ubuntu虚拟机已建立连接.
6. 在ARM开发板的终端内输入ssh [email protected](pi为开发板登录用户名).
7. 输入密码pi.
8. 连接已建立.
9. 按下exit退出连接.