欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

通过TCP/IP实现PC(客户端)远程控制开发板(服务器)上LED灯的实验

程序员文章站 2022-06-08 22:09:54
...

这篇我们来做个小实验,实现开发板上运行服务端,可以直接控制led灯,客户端通过socket连接到服务端,通过发送指令来远程控制服务端的led灯。

我们用平台总线的思想来编写驱动程序,这里分为LED平台驱动程序和LED平台设备程序。

先看看LED平台设备程序plat_led_pdev.c的代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>

//led4、led5
#define GPF3_CON 0x114001e0
#define GPF3_SIZE 24
//led3
#define GPX1_CON 0x11000C20
#define GPX1_SIZE 24

struct resource	led_res[] = {
	//内存资源
	[0] = {
		.start = GPF3_CON,
		.end = GPF3_CON+GPF3_SIZE-1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = GPX1_CON,
		.end = GPX1_CON+GPX1_SIZE-1,
		.flags = IORESOURCE_MEM,
	},
	//中断资源,通过中断号去描述
	[2] = {
		.start = 67,//自定义的中断号
		.end = 67,
		.flags = IORESOURCE_IRQ,
	},
};

struct platform_device led_pdev = {
	.name = "exynos4412_led",
	.id= -1,
	.num_resources = ARRAY_SIZE(led_res),
	.resource = led_res,
};


static int __init plat_led_dev_init(void)
{
	return platform_device_register(&led_pdev);
}

static void __exit plat_led_dev_exit(void)
{
	platform_device_unregister(&led_pdev);
}

module_init(plat_led_dev_init);
module_exit(plat_led_dev_exit);
MODULE_LICENSE("GPL");

再看看LED平台驱动程序plat_led_pdrv.c的代码:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/slab.h>

//封装设备信息
struct led_dev{
	int dev_major;
	struct class * cls;
	struct device *dev;
	struct resource * res;
	void *reg_base;
};
//定义设备信息
struct led_dev *samsung_led;

ssize_t led_pdrv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fops)
{
	int ret;
	int value;
	
	ret = copy_from_user(&value, buf, count);
	if(ret){
		printk("copy_from_user error\n");
		return -EFAULT;
	}
	if(value){
		//数据寄存器置1
		writel(readl(samsung_led->reg_base+4) | (0x3<<4), samsung_led->reg_base+4);
	}else{
		writel(readl(samsung_led->reg_base+4) & ~(0x3<<4), samsung_led->reg_base+4);
	}
	
	return count;
}

int led_pdrv_open(struct inode *inode, struct file *filp)
{
	printk("---------%s---------\n",__FUNCTION__);

	return 0;
}

int led_pdrv_close(struct inode *inode, struct file *filp)
{
	printk("---------%s---------\n",__FUNCTION__);

	return 0;
}


const struct file_operations led_fops = {
	
.write = led_pdrv_write,
	.open = led_pdrv_open,
	.release = led_pdrv_close,
};
	
//实现初始化
int led_pdrv_probe(struct platform_device *pdev)
{
	printk("---------%s---------\n",__FUNCTION__);
	
	samsung_led = kzalloc(sizeof(struct led_dev),GFP_KERNEL);
	if(samsung_led == NULL){
		printk("kzalloc error\n");
		return -ENOMEM;
	}
	samsung_led->dev_major = register_chrdev(0,"led_drv", &led_fops);

	samsung_led->cls = class_create(THIS_MODULE,"led_cls");
	samsung_led->dev = device_create(samsung_led->cls, NULL, MKDEV(samsung_led->dev_major, 0), NULL,"led0");
	//获取资源
	samsung_led->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	//地址映射
	samsung_led->reg_base = ioremap(samsung_led->res->start, resource_size(samsung_led->res));

	//设置寄存器状态,这里时输出状态

	writel((readl(samsung_led->reg_base) & ~(0xff<<16))| (0x11<<16), samsung_led->reg_base);

	return 0;
}

int led_pdrv_remove(struct platform_device *pdev)
{
	iounmap(samsung_led->reg_base);
	device_destroy(samsung_led->cls, MKDEV(samsung_led->dev_major, 0));
	class_destroy(samsung_led->cls);
	unregister_chrdev(samsung_led->dev_major, "led_drv");	
	kfree(samsung_led);
	return 0;
}
//用于匹配
const struct platform_device_id led_id_table[] = {
	{"exynos4412_led",0x4444},
	{"s5pv210_led",0x3333},
	{"e3c6410_led",0x2222},
};

struct platform_driver led_pdrv = {
	.probe = led_pdrv_probe,
	.remove = led_pdrv_remove,
	.driver = {
		.name = "samsung_led_drv",//可以用于做匹配,也可以不用于做匹配
	},
	.id_table = led_id_table,
};

static int __init plat_led_drv_init(void)
{
	return platform_driver_register(&led_pdrv);
}

static void __exit plat_led_drv_exit(void)
{
	platform_driver_unregister(&led_pdrv);
}

module_init(plat_led_drv_init);
module_exit(plat_led_drv_exit);
MODULE_LICENSE("GPL");

接着看服务器led_server.c的代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SERVER_PORT 5001

int main(int argc,char **argv)
{
	int fd;
	int newfd;
	int len;
	int flag = 1;
	//设备节点的pfd
	int pfd;
	int value;
	struct sockaddr_in ipv4addr;
	struct sockaddr_in clientaddr;
	char ipaddr[16];

	fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd == -1){
		printf("server socket error\n");
		exit(1);
	}

	bzero(&ipv4addr,sizeof(ipv4addr));
	ipv4addr.sin_family = AF_INET;
	ipv4addr.sin_port = htons(SERVER_PORT);	
	ipv4addr.sin_addr.s_addr = htonl(INADDR_ANY);	
	if(bind(fd, (struct sockaddr*)&ipv4addr,sizeof(ipv4addr)) < 0){
		perror("bind");
		exit(1);
	}

	if(listen(fd,5) < 0){
		perror("listen");
		exit(1);
	}
	printf("server starting...\n");
	//地址复用
	if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(int))==-1)
	{
    	perror("setsockopt"); 
      	exit(1); 
	}
	
	len = sizeof(clientaddr);
	newfd = accept(fd, (struct sockaddr*)&clientaddr,&len);
	if(newfd == -1)
	{
		perror("accept");
		exit(1);
	}
	//打印客户端的ip和port
	if(!inet_ntop(AF_INET,(void*)&clientaddr.sin_addr.s_addr,ipaddr,sizeof(clientaddr))){
		perror("inet_ntop");
		exit(1);
	}
	printf("client ip:%s,port:%d is connected\n",ipaddr,ntohs(clientaddr.sin_port));

	pfd = open("/dev/led0",O_RDWR);
	if(pfd < 0){
		perror("open");
		exit(1);
	}

	while(1){
		//读取客户端通过socket发来的数据
		read(newfd,&value,4);
		if(value == 1 || value ==0)
			//间接控制设备
			write(pfd,&value,4);
		else{
			printf("client quiting...\n");
			break;
		}
	}

	close(newfd);
	close(fd);
	close(pfd);

	return 0;
}

最后看客户端led_client.c的代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>          
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SERVER_PORT 5001
//开发板ip地址
#define SERVER_IP "192.168.1.100"

int main(int argc,char **argv)
{
	int fd;
	int value;

	struct sockaddr_in ipv4addr;

	fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd == -1){
		printf("client socket error\n");
		exit(1);
	}

	bzero(&ipv4addr,sizeof(ipv4addr));
	ipv4addr.sin_family = AF_INET;
	ipv4addr.sin_port = htons(SERVER_PORT);
	if(inet_pton(AF_INET,SERVER_IP,(void *)&ipv4addr.sin_addr.s_addr) != 1){
		perror("client inet_pton");
		exit(1);
	}

	printf("client starting...\n");
	
 	if(connect(fd,(struct sockaddr *)&ipv4addr,sizeof(ipv4addr)) < 0){
		perror("connect");
		exit(1);
	}

	while(1){
		//1代表点亮led,0代表熄灭led,其他数字代表客户端退出程序
		printf("please select to led on/off(1:on/0:off/other:quit)");
		scanf("%d",&value);
		if(value == 1 || value == 0)
			write(fd,&value,4);
		else{
			write(fd,&value,4);
			printf("client quiting...\n");
			break;
		}
		//吸收垃圾字符
		getchar();
	}

	close(fd);

	return 0;
}

加载驱动并在开发板上运行服务器程序:

通过TCP/IP实现PC(客户端)远程控制开发板(服务器)上LED灯的实验

这里我直接在ubuntu上运行客户端,此时服务器已经连接上客户端,ubuntu上运行客户端如下:

通过TCP/IP实现PC(客户端)远程控制开发板(服务器)上LED灯的实验

我们在客户端上输入1,代表点亮led,输入0代表熄灭led,其他字符代表客户端退出。这里我们仅操作led4和led5

通过TCP/IP实现PC(客户端)远程控制开发板(服务器)上LED灯的实验

看看开发板的led情况,输入1时:

通过TCP/IP实现PC(客户端)远程控制开发板(服务器)上LED灯的实验

输入0时:

通过TCP/IP实现PC(客户端)远程控制开发板(服务器)上LED灯的实验



相关标签: 嵌入式驱动