此次板卡的测试,是Linux C开发环境下按键控制LED的操作实录。
这里使用WIN11+WSL2+Ubuntu 20.04进行开发。Ubuntu单独安装在E盘,而不是直接从应用商店下载安装(具体安装方法可以网络搜索,这里不再赘述),因为开发环境需要的空间较大,直接应用商店安装默认位于C盘会导致C盘空间不够,所以独立安装在空间较大的盘。
完美体育·(中国)手机网页版的资料是比较友好和全面的,手册,开发环境等都比较详尽,能方便用户快速熟悉开发环境进行二次开发。
另外参考文档中《MYD-YT507H_Linux软件开发指南V1.1.pdf》中相关信息有误,比如:
MYD-YT507H_Linux软件开发指南V1.1.pdf
本篇测评由电子发烧友的优秀测评者“HonestQiao”提供。
此次板卡的测试,是用macOS将Ubuntu系统烧录到eMMC的操作实录。
完美体育·(中国)手机网页版MYD-YT507H开发板,官方提供了HMI系统和Ubuntu18.04镜像,体验过默认的HMI系统后,我就换上了我喜欢的Ubuntu系统了。
一、系统烧录
参考官方的文档,使用全志的图形界面烧录工具,在Windows下烧录简单又方便:
因为我使用的是macOS系统,所以我是在macOS下进行烧录的,这需要使用命令行来操作。
首先,我们看一下板子上的接口:
在上图中,红色的TV接口上面,有两个Type-C的接口,标注了OTG接口,可以用来烧录固件。标注了Debug的,可以用于串口终端,来进行设备调试。
所以,要进行系H,连接到电脑,就可以准备烧录了。
从官方资料库中,下载Ubuntu镜像和PhoneixSuit的macOS版本:
然后,进入命令行执行:
此时,会等待按键,具体的按键如下:
当 ----enter sem_wait-------
出现后,应先按住FEL按键
不放,再按一下RST按键
,然后烧录就会开始进行,中途会提示百分比信息,此时就可以松开FEL按键了。
如果按键不及时,可以再次运行命令,并按键进入烧录。
烧录完成后,重启即可开始体验新的系统了。
二、串口终端连接
默认的Ubuntu系统,是18.04的,开发板相关的驱动都自带了,但是没有图形界面,所以启动的时候,只能进到命令行界面。即使HDMI连接了显示器,也只会显示MYIR的LOGO,不会显示命令行界面。
参考前面发的开发板的整体图,用Type-C数据线,把Debug接口对应的Type-C口接上,连接到电脑,就可以在电脑上使用串口终端工具,直接连接操作了。
Windows上的串口终端工具,可以使用Putty或者MobaXterm。
Linux或者macOS上,可以使用screen命令或者minicom进行连接。
串口终端连接上以后,就像是在本地操作Ubuntu系统的命令行界面。
登录串口终端,默认的用户如下:
管理员: root / root
普通用户:myir / 123456
串口终端连接后,先简单查看了一下系统的基本信息:
从上图中,我们可以看到:
Linux内核:4.9.170版本
CPU:四核,arm64/aaarch64(AArch64是ARMv8 架构的一种执行状态)
内存:1G
三、远程ssh连接
通过串口终端操作,还是有一些不太方便,那么下一步,就是联网,并能够使用ssh远程登录。
#先切换到root用户 $su root #将网络配置文件,修改为如下的内容 $vim/etc/network/interfaces source-directory/etc/network/interfaces.d auto lo iface lo inet loopback auto eth0 iface eth0 inet dhcp auto eth1 iface eth1 inet dhcp
设置完成后,参考之前开发板的全图,使用网线连接到设备,重启设备,就能够自动联网了。
启动的时候,需要注意的是,要等全部启动过程完成,提示用户Login的时候,才可以使用ssh远程登录。
在ssh远程登录以前,我们需要获得开发板取得的ip地址,这可以通过在路由器管理界面查看来获取,并且最好做mac地址绑定,以防发生变化。
我们也可以在串口终端中登录,然后使用ifconfig来查看。
其中,lo为系统本地回环网络设备,eth0、eth1对应两个有线网口。
ifconfig命令的结果如上图所示,eth0接了网线,所以其获得了ip地址,后续我将使用该地址,进行远程操作。
要进行远程ssh连接,Windows系统可以使用Putty或者MobaXterm,Linux或者macOS系统,则可以直接使用ssh命令来进行连接:
#请注意替换为实际获得的ip地址 ssh myir@192.168.1.239 #登录后,切换到root用户 su root
远程登录成功后,我们开始进行一些基础的设置,让系统更好使用。
注意:在sudo安装配置完成以前,以下的命令,使用root用户执行。
如果图简单省事,可以试用unminimize
命令,一步操作,自动安装成一个完整版本的Ubuntu系统。
但我更喜欢自己来一手调教系统,这样更加的适合自己使用,而不会让系统变得臃肿。
1.将默认的shell,设置为bash:
#执行后,按照提示,选no即 dpkg-reconfigure dash
设置完成,退出ssh,重新连接即可
2.更换Ubuntu apt更新源为国内源:
cp/etc/apt/sources.list/etc/apt/sources.list.bak #换源 echo"#中科大源 deb http://mirrors.ustc.edu.cn/ubuntu-ports/bionic-updates main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-updates main restricted universe multiverse deb http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-security main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-security main restricted universe multiverse deb http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-backports main restricted universe multiverse deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic-backports main restricted universe multiverse deb http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic main universe restricted deb-src http://mirrors.ustc.edu.cn/ubuntu-ports/ bionic main universe restricted" >/etc/apt/sources.list
3.更新系统:更新后,最好重启一遍
apt update apt upgrade
4.时区设置:
#执行后,会提示进行相关的信息选择,依次选:6.Asisa、70.Shanghai即可 apt install tzdata #安装ntp服务 atp install ntp #查看系统当前时间,应该显示正确的本地时间 date
5.sudo安装和设置:
#安装sudo工具 apt update #在最后添加 vim/etc/sudoers myir ALL=(ALL:ALL) NOPASSWD:ALL
sudo安装完成后,后续需要root权限的指令,都可以使用类似sudo apt update
的方式来提权来执行。
经过上述的简单调教,这个系统,已经能够基本使用了,可以方便地在其他电脑上,使用ssh远程登录操作使用。
既然能用了,我们就来点个灯试试吧。
在开发板上,有一个Blue灯,可以被我直接使用控制,具体位置如下:
我们可以使用如下的指令控制它:
#取消触发 echo none > /sys/class/leds/blue/trigger #熄灭 echo 1 > /sys/class/leds/blue/brightness #点亮 echo 0 > /sys/class/leds/blue/brightness #闪烁 echo heartbeat > /sys/class/leds/blue/trigger
执行上面的最后一条指令后,将会看到,蓝色指示灯闪烁起来了。
到这里,我们完成了Ubuntu系统的基本设置,以及能够成功点灯了。
下一篇文章,将会分享如何建立xfce图形界面的建立,如何远程连接图形界面,以及如何在公司摸鱼连接到家里的完美体育·(中国)手机网页版MYD-YT507H开发板,先放个图,预览一下:
想要了解优秀测评者“HonestQiao”关于MYD-YT507H开发板测评原文的可以复制下方链接查看:
https://bbs.elecfans.com/jishu_2293682_1_1.html
本篇测评由电子工程世界的优秀测评者“bloong”提供。
此次测试的项目,是异构处理器间相互通讯项目。
MYD-JX8MPQ配备了一颗异构的Cortex-M7协处理器,可以同时运行Linux和RTOS。本文主要介绍协处理器M7使用方法。M7在运行时可能会涉及到和A53核共用资源,这里列举出会冲突资源如下:
ECSPI0/ECSPI2,FLEXCAN,GPIO1/GPIO5,GPT1,I2C3,I2S3,UART4,PWM4,SDMA1/SDMA2,所以在一起使用时,需要将A53的这些资源关闭,这里就需要用到myd-jx8mp-rpmsg.dtb设备树。这里的M7使用uart4 作为串口。
下文介绍如何使用M7,以及编译M7程序的方法:
M7使用方法
接两个串口:1个是开发板的Debug串口,另一个是M7的UART4串口。
1、Debug口为J4及TYPE-C Debug;
2、UART4口为J6,下图中红色箭头指示位置为pin1即UART4_TXD,据此pin2即UART4_RXD,pin3为GND。接这3根线就可以。
查看分区信息
启动开发板按任意键进入uboot模式,查看vfat分区中存在的文件。
-
u-boot=> fatls mmc 2
-
29209088 Image
-
8208 imx8mp_m7_TCM_hello_world.bin
-
19040 imx8mp_m7_TCM_rpmsg_lite_pingpong_rtos_linux_remote.bin
-
18528 imx8mp_m7_TCM_rpmsg_lite_str_echo_rtos.bin
-
40948 imx8mp_m7_TCM_sai_low_power_audio.bin
-
62815 myd-jx8mp-atk-10.dtb
-
61702 myd-jx8mp-base.dtb
-
62815 myd-jx8mp-hontron-7.dtb
-
62846 myd-jx8mp-lt8912.dtb
-
62555 myd-jx8mp-m190etn01-19.dtb
-
62619 myd-jx8mp-rpmsg.dtb
-
2113024 tee.bin
-
-
12 file(s), 0 dir(s)
-
设置M7设备树
kernel加载的设备树由fdt_file变量决定,这里设置成M7专用设备树。
-
u-boot=> printenv fdt_file
-
fdt_file=myd-jx8mp-base.dtb
-
u-boot=> setenv fdt_file myd-jx8mp-rpmsg.dtb
-
u-boot=> save
-
Saving Environment to MMC... Writing to MMC(2)... OK
-
u-boot=> printenv fdt_file
-
fdt_file=myd-jx8mp-rpmsg.dtb
-
u-boot=>
-
-
u-boot=> printenv fdt_file
-
fdt_file=myd-jx8mp-base.dtb
-
u-boot=> setenv fdt_file myd-jx8mp-rpmsg.dtb
-
u-boot=> save
-
Saving Environment to MMC... Writing to MMC(2)... OK
-
u-boot=> printenv fdt_file
-
fdt_file=myd-jx8mp-rpmsg.dtb
-
u-boot=> setenv m7_image imx8mp_m7_TCM_rpmsg_lite_str_echo_rtos.bin
-
u-boot=> setenv m7_boot_temp_addr 0x48000000
-
u-boot=> setenv m7_boot_addr 0x7E0000
-
u-boot=> setenv m7_run 'fatload mmc ${mmcdev}:${mmcpart} ${m7_boot_tem
-
> p_addr} ${m7_image};cp.b ${m7_boot_temp_addr} ${m7_boot_addr} 0x20000; bo
-
> otaux ${m7_boot_addr}'
-
u-boot=> setenv m7_run 'fatload mmc ${mmcdev}:${mmcpart} ${m7_boot_tem p_addr} ${m7_image};cp.b ${m7_boot_temp_addr} ${m7_boot_addr} 0x20000; bootaux ${m7_boot_addr}'
-
u-boot=> setenv mmcboot "run m7_run;${mmcboot}"
-
u-boot=> save
-
Saving Environment to MMC... Writing to MMC(2)... OK
-
u-boot=>
-
测试M7程序
此时已经设置好了M7启动,只需要重启开发板,那么在A53启动kernel同时,也会启动M7中的程序。启动之后A53的串口执行如下2句,既可以看到M7中串口出现对应的打印。
-
myd-jx8mp login: root
-
root@myd-jx8mp:~# modprobe imx_rpmsg_tty
-
root@myd-jx8mp:~# [ 33.759911] can1-stby: disabling
-
[ 33.763151] can2-stby: disabling
-
[ 33.766408] VSD_3V3: disabling
-
[ 33.769489] m2_keyb_pwr: disabling
-
echo "hi m7!" > /dev/ttyRPMSG30
-
root@myd-jx8mp:~# cat /dev/ttyRPMSG30
-
hi m7!
-
近年来随着信息化的发展,工业智能化、工程机械智能化水平的要求也越来越高,这对各类工程机械主机专用控制器的需求也随之提高。而目前市场上主流的工程机械控制器硬件配置普遍较低,计算能力不能满足智能化控制的需求;主芯片的容量较小,无法进行大容量工况数据的长时间保存,软件部分也不具备数据的分析能力;同时,缺乏对周边设备的支持,接口功能较少,可拓展应用的功能有限。
而市场对工程机械控制器的智能化需求主要体现:更快的计算能力、更大的存储量、更丰富的接口、更强的运动控制能力。首先,工程智能化控制功能的需要实现大量的实时计算,要求主频的MPU处理速度快,计算能力高;其次,智能化的机械设备需要对工作过程数据进行详尽的记录,并且对保存时间有较高要求,所以需要控制器具备大容量存储能力和基本的数据管理能力,存储量要求更大;此外,智能化的机械设备也需要具备多轴联合的运动控制能力。
针对客户需求,完美体育·(中国)手机网页版电子(全称:深圳市完美体育·(中国)手机网页版电子有限公司)提供了基于MYC-Y6ULX-V2核心板的机械智能控制器的应用的解决方案,MYC-Y6ULX-V2核心板采用NXP的i.MX6ULX系列MPU, 搭载单个Arm Cortex-A7内核,运行速度高达800 MHz;标配512M DDR3 和4GB EMMC flash,拥有充足的存储空间;并行LCD显示,分辨率高达WXGA (1366x768),可满足工程机械控制器的高运算速率、大容量的存储空间、操作方便等要求。同时,MYC-Y6ULX-V2核心板可提供2路CAN总线、8路串口、8路ADC、4路I2C等丰富的接口,可根据客户需求进行方案设计,高效赋能机械控制器的智能化应用。
MYC-Y6ULX-V2核心板模块提供最新linux 5.4内核版本操作系统的驱动支持,软件开发完善,并经过长时间老化测试、高低温测试、通断电测试,测试后性能稳定,且核心板的外形小巧,尺寸仅为37mm*39mm,采用邮票孔封装焊接到底板,稳固不易松动,可适应于工程机械产品严苛的工业环境要求。核心板实现OTG,TF卡多种烧写方式,预留140个引脚的外扩接口,其包含了丰富的外设资源,便于灵活开发应用。
完美体育·(中国)手机网页版电子,专注嵌入式处理器模块设计和研发,是领先的嵌入式处理器模块厂商。完美体育·(中国)手机网页版电子在嵌入式领域具有20年的行业技术经验,为客户提供专业的ARM工业控制板、ARM开发板、ARM核心板、ARM开发工具、充电桩计费控制单元及充电控制板等产品和技术服务。此外,完美体育·(中国)手机网页版电子还可通过涵盖众多ARM处理器及操作系统的专业技术提供定制设计解决方案,通过专业且高效率服务帮助客户加速产品上市进程。
由评测者“jf_01801880”提供。
简单吐槽一下这块板子的串口竟然不是usb转串口是排针的,一开始看见有usb线和usb口就怼上去了,发现不行,发现整个板子一个micro usb,那这个口多半是otg烧录镜像的,拿起板子细看发现确实如此,调试串口在电源口旁边焊了三个排针,只能拿出我珍藏多年的CH340串口模块来进行串口登陆啦。
接线正常后进行系统登陆界面,盲猜有一个root可以登陆,果然如此
简单看一下板子的基本信息,ARMV7 架构,Freescale i.MX6 Ultralite
好了,串口没问题,为了方便后期调试,就需要把网络搭起来,接下来进行网络ssh登陆的。
为了简单,我这里使用的板子的网口与电脑的网口直联的,把网线接入板子的eth0口,另一端连接到电脑,输入 ifconfig eth0 192.168.120.3
, 电脑端设置为192.168.120.2,输入ping 192.168.120.20
看看是否配置成功,成功后即可通过ssh登陆板子来进行开发调试啦。
vi /etc/rc.local
打开该文件,在里面配置好ip地址,以后开机自动配置。
接下来就直接ssh登陆即可了。
这样即可以看到板子上的蓝色cpu灯在按照规律的亮灭。
由评测者“华仔stm32”提供。
基于socket建立通信后,偿试建立web服务端,进行演于,是开发板的特有功能之一。这里有linux c建立一个多线程的web服务端。代码如下:
#include#include #include #include #include #include #include #define BUF_SIZE 1024 #define SMALL_BUF 100 void * request_handler(void * arg); //线程入口函数 void send_data(FILE *fp, char * ct, char * file_name); //向浏览器客服端发送数据 char * content_type(char *file); //数据类型 void send_error(FILE *fp); //发送错误处理数据 void error_handling(char *message); //控制台错误打印 int main(int argc, char *argv[]) { int serv_sock, clnt_sock; struct sockaddr_in serv_adr, clnt_adr; socklen_t clnt_adr_sz; char buf[BUF_SIZE]; pthread_t t_id; if (argc != 2) { printf("Usage : %s \n", argv[0]); exit(1); } serv_sock = socket(PF_INET, SOCK_STREAM, 0); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family = AF_INET; serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); serv_adr.sin_port = htons(atoi(argv[1])); if(bind(serv_sock, (struct sockaddr *) &serv_adr, sizeof(serv_adr)) == -1) error_handling("bind() error"); if(listen(serv_sock, 5) == -1) error_handling("listen() error"); while (1) { clnt_adr_sz = sizeof(clnt_adr); clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_sz); printf("Connection Request: %s : %d\n", inet_ntoa(clnt_adr.sin_addr), ntohs(clnt_adr.sin_port));//连接的客服端IP与端口 //多线程 pthread_create(&t_id, NULL, request_handler, (void*) &clnt_sock); pthread_detach(t_id); } close(serv_sock); return 0; } //客服端请求消息处理 void * request_handler(void *arg) { int clnt_sock = *((int *)arg); char req_line[SMALL_BUF]; FILE *clnt_read; FILE *clnt_write; char method[10]; char ct[15]; char file_name[30]; /*将套接字转换为标准I/O操作*/ clnt_read = fdopen(clnt_sock, "r"); clnt_write = fdopen(dup(clnt_sock), "w"); fgets(req_line, SMALL_BUF, clnt_read);//保存请求行数据 if (strstr(req_line, "HTTP/") == NULL) //查看是否为HTTP提出的请求 { send_error(clnt_write); fclose(clnt_read); fclose(clnt_write); return NULL; } strcpy(method, strtok(req_line, " /")); //请求方式 strcpy(file_name, strtok(NULL, " /")); //请求的文件名 strcpy(ct, content_type(file_name)); //请求内容类型 if (strcmp(method, "GET") != 0) //是否为GET请求 { send_error(clnt_write); fclose(clnt_read); fclose(clnt_write); return NULL; } fclose(clnt_read); send_data(clnt_write, ct, file_name); //响应给客服端 return NULL; } //服务端响应消息 void send_data(FILE *fp, char *ct, char *file_name) { char protocol[] = "HTTP/1.0 200 OK\r\n"; //状态行(用HTTP1.1版本进行响应,你的请求已经正确处理) char server[] = "Server: Linux Web Server \r\n"; //服务端名 char cnt_len[] = "Content-length: 2048\r\n"; //数据长度不超过2048 char cnt_type[SMALL_BUF]; char buf[BUF_SIZE]; FILE *send_file; sprintf(cnt_type, "Content-type: %s\r\n\r\n", ct); send_file = fopen(file_name, "r"); //读本地配置文件 if (send_file == NULL) { send_error(fp); return; } /*传输头信息*/ fputs(protocol, fp); fputs(server, fp); fputs(cnt_len, fp); fputs(cnt_type, fp); /*传输请求数据*/ while (fgets(buf, BUF_SIZE, send_file) != NULL) { fputs(buf, fp); fflush(fp); } fflush(fp); fclose(fp); //服务端响应客服端请求后立即断开连接(短链接) } //请求数据的类型 char * content_type(char *file) { char extension[SMALL_BUF]; char file_name[SMALL_BUF]; strcpy(file_name, file); strtok(file_name, "."); strcpy(extension, strtok(NULL, ".")); if (!strcmp(extension, "html") || !strcmp(extension, "htm")) return "text/html"; //html格式的文本数据 else return "text/plain"; } //发送客服端错误处理 void send_error(FILE *fp) { char protocol[] = "HTTP/1.0 400 Bad Request\r\n"; //请求文件不存在 char server[] = "Server: Linux Web Server \r\n"; char cnt_len[] = "Content-length: 2048\r\n"; char cnt_type[] = "Content-type: text/html\r\n\r\n"; char content[] = "发生错误!查看请求文件名和请求方式!"; fputs(protocol, fp); fputs(server, fp); fputs(cnt_len, fp); fputs(cnt_type, fp); fputs(content, fp); fflush(fp); fclose(fp); } //控制台错误打印 void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
代码写好后,进行编译,拷贝给开发板:
同时书写一个测试的index.html:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>我的完美体育·(中国)手机网页版测试</title>
</head>
<body>
这是基于完美体育·(中国)手机网页版Y6UL的web测试页面!
</body>
</html>
在开发板上运行:./myserver 8000(创建一个8000端口的web服务)
打开浏览器输入网址:192.168.3.181:8000/index.html效果如下:
致此创建简单的web服务器已完成。
由评测者“华仔stm32”提供。
工控板驱动步进电机,是项目中常用的场景。今天试着用三个IO驱动步进电机:
【硬件准备】
1、开发板。
2、步进电机驱动器,前面活动中申请到【免费试用13期】TB5128FTG步进电机驱动套件
3、步进电机一个。
4、杜绑线。
5、可调数控电源。
【接线原理图】
【程序】
#include#include #include #include #include #include FILE *fp=NULL; int init_io(void) { fp = fopen("/sys/class/gpio/export", "w"); if (fp == NULL) { perror("fopen"); return -1; } fprintf(fp,"%d",24); fclose(fp); fp = fopen("/sys/class/gpio/export", "w"); if (fp == NULL) { perror("fopen"); return -1; } fprintf(fp,"%d",25); fclose(fp); fp = fopen("/sys/class/gpio/export", "w"); if (fp == NULL) { perror("fopen"); return -1; } fprintf(fp,"%d",26); fclose(fp); fp = fopen("/sys/class/gpio/gpio24/direction","w"); fprintf(fp,"out"); fclose(fp); fp = fopen("/sys/class/gpio/gpio25/direction","w"); fprintf(fp,"out"); fclose(fp); fp = fopen("/sys/class/gpio/gpio26/direction","w"); fprintf(fp,"out"); fclose(fp); } void io_ena(int value) { fp = fopen("/sys/class/gpio/gpio26/value", "w"); fprintf(fp,"%d", value); fclose(fp); } void io_Dir(int value) { fp = fopen("/sys/class/gpio/gpio25/value", "w"); fprintf(fp,"%d", value); fclose(fp); } void close_IO(void) { fp = fopen("/sys/class/gpio/unexport","w"); fprintf(fp,"%d",24); fclose(fp); fp = fopen("/sys/class/gpio/unexport","w"); fprintf(fp,"%d",25); fclose(fp); fp = fopen("/sys/class/gpio/unexport","w"); fprintf(fp,"%d",26); fclose(fp); } void pwm_PULL(int times) { int i; for (i = 0; i < times; i++) { fp = fopen("/sys/class/gpio/gpio24/value", "w"); fprintf(fp,"%d", 1); usleep(10); fclose(fp); fp = fopen("/sys/class/gpio/gpio24/value", "w"); fprintf(fp,"%d", 0); usleep(10); fclose(fp); } } int main(void) { init_io(); io_ena(1);//diable en io_Dir(1); //正转 io_ena(0); pwm_PULL(500); sleep(1); io_Dir(0); //正转 pwm_PULL(500); close_IO(); return 0; }
由评测者“华仔stm32”提供。
对GPIO操作是最基本的功能之一。
【材料准备】
1、LED灯3个
2、杜绑线4条
3、面包板一块
【软件准备】
1、查看\01_Documents\User_Manual\Chinese\硬件手册 下面的MYD-Y6ULX-V2 硬件用户手册-V1.0.pdf。找到第23页4.7. GPIO/I2C/SPI/UART 接口。由于没有给出专门的GPIO接品,这里利用UART3接口来做GPIO输出。
2、查看IMX6ULL数据手册(工业级)找到对UART3的端口定义:
这里我们采用GPIO1_24 到26作为输出端口
J14的5、6、8分析接到面包板LED的正极,3脚接到面包板的地
3、编写led.c代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(void)
{
FILE *fp=NULL;
int i=0;
fp = fopen("/sys/class/gpio/export", "w");
if (fp == NULL)
{
perror("fopen");
return -1;
}
fprintf(fp,"%d",24);
fclose(fp);
fp = fopen("/sys/class/gpio/export", "w");
if (fp == NULL)
{
perror("fopen");
return -1;
}
fprintf(fp,"%d",25);
fclose(fp);
fp = fopen("/sys/class/gpio/export", "w");
if (fp == NULL)
{
perror("fopen");
return -1;
}
fprintf(fp,"%d",26);
fclose(fp);
fp = fopen("/sys/class/gpio/gpio24/direction","w");
fprintf(fp,"out");
fclose(fp);
fp = fopen("/sys/class/gpio/gpio25/direction","w");
fprintf(fp,"out");
fclose(fp);
fp = fopen("/sys/class/gpio/gpio26/direction","w");
fprintf(fp,"out");
fclose(fp);
for(i=0; i<100;i++) {
fp = fopen("/sys/class/gpio/gpio24/value", "w");
fprintf(fp,"%d",1);
fclose(fp);
fp = fopen("/sys/class/gpio/gpio25/value", "w");
fprintf(fp,"%d",0);
fclose(fp);
fp = fopen("/sys/class/gpio/gpio26/value", "w");
fprintf(fp,"%d",0);
fclose(fp);
sleep(1);
fp = fopen("/sys/class/gpio/gpio24/value", "w");
fprintf(fp,"%d",0);
fclose(fp);
fp = fopen("/sys/class/gpio/gpio25/value", "w");
fprintf(fp,"%d",1);
fclose(fp);
fp = fopen("/sys/class/gpio/gpio26/value", "w");
fprintf(fp,"%d",0);
fclose(fp);
sleep(1);
fp = fopen("/sys/class/gpio/gpio24/value", "w");
fprintf(fp,"%d",0);
fclose(fp);
fp = fopen("/sys/class/gpio/gpio25/value", "w");
fprintf(fp,"%d",0);
fclose(fp);
fp = fopen("/sys/class/gpio/gpio26/value", "w");
fprintf(fp,"%d",1);
fclose(fp);
sleep(1);
}
fp = fopen("/sys/class/gpio/unexport","w");
fprintf(fp,"%d",24);
fclose(fp);
fp = fopen("/sys/class/gpio/unexport","w");
fprintf(fp,"%d",25);
fclose(fp);
fp = fopen("/sys/class/gpio/unexport","w");
fprintf(fp,"%d",26);
fclose(fp);
return 0;
}
编译好后上传开发板,运行程序,就可以看到三个LED交替闪烁了。
由评测者“华仔stm32”提供。
前面创建了socket 单线程的通信。如果客端连接断开后,主服务端也就断开。学习了博客园的@liangf27的帖子来实现单线程服务多个客户端。
修改main.c代码如下:
#include#include #include // open function #include // close function #include #include #include #include #include #include #include "ssd1306.h" const int PORT = 8888; /* listen_loop(): epoll监听套接字,作不同处理 accept_conn(): 新的客户端连接进来,执行accept,将fd加入epoll set recv_message(): recv并且重复输出一份给客户端 */ void listen_loop(); void accept_conn(unsigned int sock_fd, unsigned int epollfd); void recv_message(unsigned int sock_fd); int main(void) { int sock_fd; struct sockaddr_in server_addr; SSD1306_init(); SSD1306_clearDisplay(); SSD1306_setBrightness(255); SSD1306_setPageMode(); SSD1306_setTextXY(0,0); SSD1306_putString("HELLO MYD-Y6ULX!"); SSD1306_setTextXY(1,0); SSD1306_putString("SSD1306 DEMO"); SSD1306_setTextXY(2,0); SSD1306_putString("2022-11-07"); //初始化socket sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (sock_fd < 0) { perror("socket:"); return 0; } //编辑地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET;//ipv_4 server_addr.sin_port = htons(PORT);//监听端口8888 server_addr.sin_addr.s_addr = INADDR_ANY;//本地的任意地址 //绑定然后监听 if (bind(sock_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("bind:"); return 0; } if (listen(sock_fd, 10) < 0) { perror("listen"); return 0; } listen_loop(sock_fd); return 0; } void accept_conn(unsigned int sock_fd, unsigned int epollfd) { struct sockaddr_in clientaddr; struct epoll_event event; socklen_t len = sizeof(struct sockaddr); int accept_fd = 0; accept_fd = accept(sock_fd, (struct sockaddr*)&clientaddr, &len); if (accept_fd <= 0) { perror("accept error"); return; } //将新建连接加入epoll set event.data.fd = accept_fd; event.events = EPOLLIN | EPOLLET | EPOLLRDHUP; epoll_ctl(epollfd, EPOLL_CTL_ADD, accept_fd, &event); return; } void recv_message(unsigned int sock_fd) { char recv_buf[1024], send_buf[1024]; memset(recv_buf, 0, sizeof(recv_buf)); memset(send_buf, 0, sizeof(send_buf)); recv(sock_fd, recv_buf, sizeof(recv_buf), 0); //接收到数据后,在这里写处理逻辑,我这里把接收到的数据显示到OLED屏上,并原样的回发给客户端。 fputs(recv_buf, stdout); strcpy(send_buf, recv_buf); SSD1306_clearDisplay(); SSD1306_setTextXY(3,0); SSD1306_putString(send_buf); send(sock_fd, send_buf, sizeof(send_buf), 0); return; } void listen_loop(unsigned int sock_fd) { int epollfd, i, ret; int timeout = 300; struct epoll_event event; struct epoll_event eventList[10]; /*创建epoll监听事件*/ epollfd = epoll_create(10); event.events = EPOLLIN | EPOLLET; event.data.fd = sock_fd; /*注册epoll监听事件.*/ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sock_fd, &event) < 0) { printf("register epoll event err !"); return; } while (1) { ret = epoll_wait(epollfd, eventList, 10, timeout); /*epoll事件错误.*/ if (ret < 0) { printf("epoll event err!"); break; } /*无事件返回.*/ else if (ret == 0) { continue; } /*epoll返回事件.*/ for (i = 0; i < ret; i++) { /*epoll 错误*/ if ((eventList[i].events & EPOLLERR) || (eventList[i].events & EPOLLHUP) || !(eventList[i].events & EPOLLIN)) { printf("epoll error\n"); close(eventList[i].data.fd); exit(-1); } //half connection if (eventList[i].events & EPOLLRDHUP) { printf("//one client close the conne.//\n"); close(eventList[i].data.fd); } /*accept事件*/ if (eventList[i].data.fd == sock_fd) { accept_conn(sock_fd, epollfd); } /*非sock_fd则为其他事件.*/ else { recv_message(eventList[i].data.fd); } } } close(epollfd); close(sock_fd); return; }
由评测者“华仔stm32”提供。
在上个驱动ssd1306的基础上,新增tcp_server的通信。
修改main.c主程序如下:
#include#include #include #include #include #include #include #include #include #include #include #include #include "ssd1306.h" #define SERVER_PORT 3861 #define LISENT_NUM 10 int main() { int sfd, cfd; struct sockaddr_in clientaddr; struct sockaddr_in serverAddr; char buff[1024]; int size = sizeof(struct sockaddr); pthread_t client_thread[LISENT_NUM]; SSD1306_init(); SSD1306_clearDisplay(); SSD1306_setBrightness(255); SSD1306_setPageMode(); SSD1306_setTextXY(0,0); SSD1306_putString("HELLO MYD-Y6ULX!"); SSD1306_setTextXY(1,0); SSD1306_putString("SSD1306 DEMO"); SSD1306_setTextXY(2,0); SSD1306_putString("2022-11-07"); if((sfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket"); exit(-1); } memset(&serverAddr, 0, sizeof(struct sockaddr)); serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(SERVER_PORT); if (bind(sfd, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr)) == -1) { perror("bind"); close(sfd); exit(-1); } if(listen(sfd, LISENT_NUM) == -1) { perror("listen"); close(sfd); exit(-1); } printf("#@ listen SERVER_PORT %d\n", SERVER_PORT); printf("main: server waiting connect...\n"); if ((cfd = accept(sfd, (struct sockaddr *)&clientaddr, (socklen_t*)&size)) == -1) { perror("accept"); close(sfd); return 0; } printf("client (ip = %s : SERVER_PORT = %d) connect success\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port)); usleep(1000*10); if (send(cfd, "hello Purple Pi", 6, MSG_NOSIGNAL) == -1) { perror("send"); exit(-1); } printf("send: hello Purple Pi\n"); usleep(1000*10); while (1) { if(recv(cfd, buff, sizeof(buff), 0) == -1) { perror("recv"); exit(-1); } printf("receive: %s\n", buff); SSD1306_setTextXY(4,0); SSD1306_putString(buff); } close(ssd1306_i2c_devfd); return 0; }
这样就创建了一个端口为3861的TCP服务端:
编译通过后,上传开发板,运行:
用tcp测试工具连接上开发板:
客户端发送信息:hello Y6ULX,在OLED屏上显示接收到的信息:
到此tcp_server通信实验调试通过,但是这个只是单线程的,下一期修改为同时可以接受多个客户端案例。